前人未踏の領域へ WEB・インフラ・プログラミング全般編

フロントエンド、バックエンド、インフラ、言語など、アプリ開発、IOT以外の記録

RRULEに関するメモ書き

カレンダーの予定のような繰り返しのデータ定義を実装するにあたり、RFC5545(旧RFC2445)のRRULEというのがあるのを知ったので それについて調べたメモ。中でも 3.3.10. Recurrence Rule についてのみ取り扱っている。

比べると、記述方法が若干変わっていた。RFC5545の方が説明が増えて読みやすくなっている。
今回は繰り返しのデータ定義方法の参考にしたいだけなのでどちらでも特に問題はない。

書式

/ は ORの意味

   recur           = recur-rule-part *( ";" recur-rule-part )
  • recur-rule-part; で区切りながら連続表記する。
  • ルールの順序は考慮しない
  • FREQ ルールは必須。ただし複数回登場してはならない
  • UNTIL または COUNTはオプション。ただし、UNTIL と COUNT は同時には指定できない
  • それ以外はオプションで指定可能。ただし複数回登場してはならない
recur-rule-part = ( "FREQ" "=" freq )
                       / ( "UNTIL" "=" enddate )
                       / ( "COUNT" "=" 1*DIGIT )
                       / ( "INTERVAL" "=" 1*DIGIT )
                       / ( "BYSECOND" "=" byseclist )
                       / ( "BYMINUTE" "=" byminlist )
                       / ( "BYHOUR" "=" byhrlist )
                       / ( "BYDAY" "=" bywdaylist )
                       / ( "BYMONTHDAY" "=" bymodaylist )
                       / ( "BYYEARDAY" "=" byyrdaylist )
                       / ( "BYWEEKNO" "=" bywknolist )
                       / ( "BYMONTH" "=" bymolist )
                       / ( "BYSETPOS" "=" bysplist )
                       / ( "WKST" "=" weekday )

ぱっと見わかりにくいのだけど、続く文章で各項目を説明している。

freq(周期)

  • SECONDLY / MINUTELY / HOURLY / DAILY / WEEKLY / MONTHLY / YEARLY のいずれか

enddate (終了日)

日付または日時

enddate     = date / date-time

byseclist (秒指定)

秒を指定。秒をカンマ区切りで複数回指定できる。

byseclist   = ( seconds *("," seconds) )

seconds     = 1*2DIGIT       ;0 to 60 1から2桁の整数。0 〜 60まで。0と60の違いは不明。

byminlist (分指定)

分を指定。分をカンマ区切りで複数回指定できる

byminlist   = ( minutes *("," minutes) )

minutes     = 1*2DIGIT       ;0 to 59  #1から2桁の整数。0 〜 59まで

byhrlist (時間指定)

時間を指定。カンマ区切りで複数回指定できる。

byhrlist    = ( hour *("," hour) )

hour        = 1*2DIGIT       ;0 to 23 # 1から2桁の整数。0〜23まで

bywdaylist(曜日指定)

weekdaynumをカンマ区切りで複数回指定できる。

bywdaylist  = ( weekdaynum *("," weekdaynum) )

weekdaynum  = [[plus / minus] ordwk] weekday # その年の何週目の何曜日か。
plus         = "+"
minus      = "-"
ordwk      = 1*2DIGIT       ;1 to 53 # 年の何週目か。1または2桁。1から53まで
weekday = "SU" / "MO" / "TU" / "WE" / "TH" / "FR" / "SA"
(それぞれ SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY に対応)

bymodaylist (月日指定)

月の何日目かを指定する。複数回指定可能

bymodaylist = ( monthdaynum *("," monthdaynum) )

monthdaynum = [plus / minus] ordmoday # 何日目かを指定する
ordmoday    = 1*2DIGIT       ;1 to 31 # 1または2桁。1から31まで

byyrdaylist(年の何日目かを指定)

yeardaynum を複数回指定できる

byyrdaylist = ( yeardaynum *("," yeardaynum) )

yeardaynum  = [plus / minus] ordyrday # 年の何日目か
ordyrday    = 1*3DIGIT      ;1 to 366 # 1桁から3桁。 1から366まで

bywknolist(何番目の週か)

bywknolist  = ( weeknum *("," weeknum) )

weeknum     = [plus / minus] ordwk
ordwk #1または2桁。1から53まで

bymolist(月指定)

bymolist    = ( monthnum *("," monthnum) ) # 月をカンマ区切りで複数回

monthnum    = 1*2DIGIT       ;1 to 12 #1桁または2桁。 1から12

bysplist(日の位置指定)

bysplist    = ( setposday *("," setposday) )  # 何番目の日かを指定

setposday   = yeardaynum
yeardaynum  = [plus / minus] ordyrday # 年の何日目か
ordyrday    = 1*3DIGIT      ;1 to 366 # 1桁から3桁。 1から366まで

FREQ

  • SECONDLY、1秒以上の間隔に基づいて繰り返しイベントを指定
  • MINUTELY、1分以上の間隔に基づいて繰り返しイベントを指定
  • HOURLY、1時間以上の間隔に基づいて繰り返しイベントを指定
  • DAILY、1日以上の間隔に基づいて繰り返しイベントを指定
  • WEEKLY、1週間以上の間隔に基づいて繰り返しイベントを指定
  • MONTHLY、1ヶ月以上の間隔に基づいて繰り返しイベントを指定
  • YEARLY、1年以上の間隔に基づいて繰り返しイベントを指定

INTERVAL

どの間隔で繰り返すかを指定。デフォルトは1で、毎秒、毎分、毎時、毎日、毎週、毎月、毎年を表す。 例えば FREQ=DAILY;INTERVAL=8 は8日ごとを表す。

UNTIL

  • 繰り返しの終了日('DATE')または終了日時(DATE-TIME)を表す。
  • UNTILルール部は、繰り返しルールの境界を含むDATEまたはDATE-TIME値を包括的に定義する
  • UNTIL で指定された値が指定された繰り返しと同期している場合は、この'DATE'または'DATE-TIME'が繰り返しの最後のインスタンスになる
  • 'UNTIL'ルールパートの値は DTSTARTプロパティと同じ値型を持たなければならない。
    さらに、 DTSTARTプロパティが現地時間の日付として指定されている場合、UNTILルール部分も現地時間の日付として指定されなければならない
  • DTSTARTプロパティがUTC時間付きの日付または現地時間とタイムゾーン参照付きの日付として指定されている場合、UNTILルール部分はUTC時間付きの日付として指定されなければならない
  • STANDARDDAYLIGHTサブコンポーネントの場合、UNTILルールパートは常にUTC時間付きの日付として指定されなければならない
  • DATE-TIME値として指定されているなら、それはUTC時間フォーマットで指定されなければならない
  • UNTIL が存在しない場合、およびCOUNTルール部分も存在しない場合、RRULE は永遠に繰り返されると見なされる。

COUNT

  • 出現回数を定義
  • DTSTARTの値は最初の出現としてカウントされる

BYSECOND

  • 1分以内の秒。カンマ区切りのリストで表される
  • 有効な値は0から60
  • DTSTARTDATE の場合は指定できない

BYMINUTE

  • 1時間以内の分。カンマ区切りのリストで表される
  • 有効な値は 0 - 59
  • DTSTARTDATE の場合は指定できない

BYHOUR

  • 1日の時間。カンマ区切りのリストで表される
  • 有効な値は 0 - 23
  • DTSTARTDATE の場合は指定できない

BYDAY

  • カンマ区切りの曜日のリストを表す
  • FREQ=MONTHLY ルールでは +1MO はその月の最初の月曜日を意味し、 -1MO は最後の月曜を意味する
  • FREQ=YEARLY の場合、BYMONTH で指定した月のオフセットに対応し、 BYWEEKNO または BYMONTH が存在する年内のオフセットに対応する。 (注:仕様書上BYMONTHが2回出てくるので文章が間違ってるかも)
  • 数値が指定されていない場合、毎週実行される
  • FREQ=MONTHLYまたはYEARLYでない場合は数値は指定できない
  • FREQ=YEARLYBYWEEKNO が指定されている場合は BYDAY に数値は指定できない

BYMONTHDAY

  • カンマ区切りの月の日のリストを表す
  • 有効な値は1から31または-31から-1
  • -10はその月の最後の日から10日を表す
  • FREQ=WEEKLY の場合には指定できない

BYYEARDAY

  • カンマ区切りの年の通算日を表す
  • 有効な値は1から366または-366から-1
  • -1は年の最後の日(12月31日)を表す
  • -306は、その年の最後の日から306日前(3月1日)を表す
  • FREQDAILY,WEEKLY, MONTHLYのいずれかの場合は指定できない

BYWEEKNO

  • カンマ区切りの年の何番目の週かを表す
  • 有効値は1〜53 または-53から-1
  • 週はWKSTで定義された曜日から始まる7日間を表す
  • 暦年の週番号1は、その暦年の最低4日を含む最初の週
  • FREQ=YEARLY 以外では指定できない
  • BYWEEKNO=3 は年の3番目の週を表す

BYMONTH

  • カンマ区切りの年の月を表す
  • 有効値は1〜12

WKST

  • 週の始まりの曜日を指定する(MO, TU, WE, TH, FR, SA, and SU)
  • デフォルトは月曜

BYSETPOS

  • ルールで指定された繰り返しのn番目の発生に対応する値をカンマ区切りで指定する
  • 1つのINTERVAL 内の一連の繰り返しに対して処理する
  • 例えば FREQ=WEEKLYの場合、毎週の先頭に対して作用する
  • 有効な値は1から366または-366から-1
  • BYxxxと共に使用される必要がある
  • もし指定された場合、特定のルールの条件を満たすもののなかでn番目に発生するものを示す

例:月の最終就業日

FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1

その他

  • 繰り返しルールは無効な繰り返しインスタンスを生成する可能性がある
  • それらは無視し、ルールとしてカウント対象として認めない。
  • ルールに含まれない情報はDTSTART から派生(継承)する
  • FREQ=YEARLY;BYMONTH=1 は月の何日かを指定していないが、DTSTARTの日が使われる
  • BYxxxは何らかの方法で繰り返し回数を修正する。
  • frequencyより大きいBYxxxの指定は通常発生回数を減らす
    • FREQ = DAILY; BYMONTH = 1 は毎日→1月のみになる
  • frequencyより小さいBYxxxの指定は通常発生回数を増やす
  • FREQ = YEARLY; BYMONTH = 1,2 は 毎年1回から2回になる

評価順について

  • 複数のBYxxxが指定されている場合、FREQINTERVALが評価されたあとで以下の順に適用される
    1. BYMONTH
    2. BYWEEKNO
    3. BYYEARDAY
    4. BYMONTHDAY
    5. BYDAY
    6. BYHOUR
    7. BYMINUTE
    8. BYSECOND
    9. BYSETPOS
    10. COUNT / UNTIL

依存関係

表の意味

  • N/A はそのFREQでは使用できない
  • BYDAYFREQに応じて特殊な振る舞いをする(Note1, 2を参照)
  • LimitはFREQの対象が狭まる
  • ExpandはFREQの対象が広がる
SECONDLY MINUTELY HOURLY DAILY WEEKLY MONTHLY YEARLY
BYMONTH Limit Limit Limit Limit Limit Limit |Expand
BYWEEKNO N/A N/A N/A N/A N/A N/A |Expand
BYYEARDAY Limit Limit Limit N/A N/A N/A |Expand
BYMONTHDAY Limit Limit Limit Limit N/A Expand |Expand
BYDAY Limit Limit Limit Limit Expand Note 1 |Note 2
BYHOUR Limit Limit Limit Expand Expand Expand |Expand
BYMINUTE Limit Limit Expand Expand Expand Expand |Expand
BYSECOND Limit Expand Expand Expand Expand Expand |Expand
BYSETPOS Limit Limit Limit Limit Limit Limit |Limit
  • Note 1. BYMONTHDAYがある場合は制限される。そうでなければMONTHLY用にExpandされる
  • Note 2. BYYEARDAY or BYMONTHDAY がある場合は制限される。
    • そうでなくBYWEEKNO がある場合は WEEKLY が拡張される
    • そうでなくBYMONTH がある場合は MONTHLY が拡張される
    • そうでない場合は YEARLYが拡張される

参考

S3のバケットをローカルに一括コピーする

課題

AWSのS3にバケットを作成したが、ローカルに保存して作業する必要が発生した。

対応

1ファイルずつならAWSのコンソールからダウンロードも可能だが、ファイル数が多い場合はAWS CLIで 取得する。バケットの中身を一括で取得するなら syncコマンドがよい。

AWS CLIセットアップ

この辺を参考にして aws configure する docs.aws.amazon.com

実行

#バケット保存先となるローカルディレクトリに移動
$ cd <任意のディレクトリ>  

#バケット一覧を確認
$ aws s3 ls

#同期実行
$ aws s3 sync s3://バケット名/ ./ 

上記は S3からローカルへ である。 同期元と同期先を間違えると消えちゃうような気がするので注意(未確認)。

その他

s3 sync じゃなくても移動とかコピーとか通常のLinuxコマンドみたいのが一通りあるので 細かいことをしたい場合はそっちを使う。

$ aws s3 help

Editing Swagger UIを使ったAPI仕様書の閲覧

はじめに

swagger.jsonまたはswagger.yamlによって書かれた
ドキュメントをHTMLで参照するには Swagger UI を使用する

Swagger UI Serverの準備

ダウンロード

任意のディレクトリに移動し、swagger-ui.gitをチェックアウトする

git clone https://github.com/swagger-api/swagger-ui.git
cd swagger-ui/

Swaggerファイルのコピー

swagger-ui自身でファイルをサーブできるよう
swagger.yamlをdistにコピーする
Swager-UIから参照可能な場所においてあれば特にこれは必須ではない

cp -pi swagger.yaml ./dist/

Dockerで起動

docker build -t swagger-ui-builder .
docker run -p 127.0.0.1:8080:8080 swagger-ui-builder

アクセス

ブラウザでアクセスする

http://localhost:8080`

URL入力欄に以下を入力

http://localhost:8080/swagger.yaml

上手く行けば表示されるはず

参考

デフォルトでPetstoreのDemoサイトのURLが設定されててしかも動かないので変更したい場合は下記を参考。

Docker systemコマンドとclean-up系コマンド

Docker 1.13でCLIが改善されたとの記事があったのでお試し。

https://www.infoq.com/jp/news/2017/02/docker-1.13

docker system - Docker

Docker System

docker system df

Dockerのディスク利用状況がわかる。便利。

$ docker system df
TYPE                TOTAL               ACTIVE              SIZE                RECLAIMABLE
Images              24                  2                   9.816 GB            9.205 GB (93%)
Containers          3                   0                   0 B                 0 B
Local Volumes       3                   3                   318.9 MB            0 B (0%)

docker system events

サーバーからのリアルタイムイベントを取得して標準出力してくれる。 オプション無しだとログが長くて読みにくいのでfilterして使うのが良さそう。

$ docker system events
2017-02-11T19:32:45.900852754+09:00 network create 1560fdef4fcd98b68e64dc5815b9616e77167ce1ba8d0d899c680a1e8ddb8ef2 (name=ultimatecatalogapi_default, type=bridge)
2017-02-11T19:32:45.937009381+09:00 volume create ultimatecatalogapi_uc-data (driver=local)
2017-02-11T19:32:46.352153980+09:00 container create 84d93c1fc07a683ec59887202fd4fb45b7ff8894688bfd7dad56c5c7b55af1ee (com.docker.compose.config-hash=996c535a97366837fb156707e1cf1a55346d5e9adc522cf1a78a6b0970393348, com.docker.compose.container-number=1, com.docker.compose.oneoff=False, com.docker.compose.project=ultimatecatalogapi, com.docker.compose.service=api, com.docker.compose.version=1.11.1, image=ultimatecatalogapi_api, name=ultimatecatalogapi_api_1)
2017-02-11T19:32:46.368757935+09:00 container attach 84d93c1fc07a683ec59887202fd4fb45b7ff8894688bfd7dad56c5c7b55af1ee (com.docker.compose.config-hash=996c535a97366837fb156707e1cf1a55346d5e9adc522cf1a78a6b0970393348, com.docker.compose.container-number=1, com.docker.compose.oneoff=False, com.docker.compose.project=ultimatecatalogapi, com.docker.compose.service=api, com.docker.compose.version=1.11.1, image=ultimatecatalogapi_api, name=ultimatecatalogapi_api_1)
2017-02-11T19:32:46.443896827+09:00 network disconnect 1560fdef4fcd98b68e64dc5815b9616e77167ce1ba8d0d899c680a1e8ddb8ef2 (container=84d93c1fc07a683ec59887202fd4fb45b7ff8894688bfd7dad56c5c7b55af1ee, name=ultimatecatalogapi_default, type=bridge)
2017-02-11T19:32:46.592015158+09:00 container create 8c6e0aac2ec66d23d8bb00895f7cba2a69963a9c199889613c6f9221006b0968 (com.docker.compose.config-hash=d8d867edf8af8cda04ecd661b84a4183b81c7575612e608c533c8d4e0be3fb39, com.docker.compose.container-number=1, com.docker.compose.oneoff=False, com.docker.compose.project=ultimatecatalogapi, com.docker.compose.service=mongo, com.docker.compose.version=1.11.1, image=mongo:latest, name=ultimatecatalogapi_mongo_1)

docker system info

システム情報を表示

docker system prune

未使用データの削除。停止中のコンテナの削除などをしてくれる。使い方を間違えなければ便利。

ボリュームの削除

未使用のボリュームの削除

$ docker volume prune
WARNING! This will remove all volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
a22a726badc76f67e9bf6414c3ee9f4104a1ad9caa2fbc4ac8d191f6b778a345
a5af6749f56651087d7fb2b1896e624486fb40fd8fce0e3153671eb35363ed24
fb6974503d635b5f9d97a3951901794acdd6afd279209e95da7f15ffb91b28ae
ultimatecatalogapi_uc-data
4426cb4bdd449b750f29c83a835bd211bd664fd27e69644616ddb81b09a2557d
a20b31b370e79a43c7bb9b00243b83016dc938621245f119e231f520325b4473
d94fab1718e74c35b1970a63e4c61060f4db979b9919d63c4c04aef9ca908e47

Total reclaimed space: 641.6 MB

イメージの削除

タグ付けされていないイメージを削除

$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:1ff2f4c46f286b04e7a932450aed4138349a477f558ded55fcecb5fd4b5cba66
deleted: sha256:bdcd065ab7de14bf53579ac02912701a44e25981e20464cec6de1e20c5c5baee
deleted: sha256:09f28b3a116d6c392851fff8b65a29f153ecdc8442acbb02b0461ce97aa78bb3
deleted: sha256:fabd7e17fbdd44ee2821e345d7ca8d423cd2c3dd24713bf789dfa4995814688d
deleted: sha256:e85ffe4667da329053f6a47b775dcf790d7a957adc7bed72d87a6284bad7dbd6
deleted: sha256:22263a689937489bf399e05edd9682618319e6cb3801b6ec42d9c7415725033d
deleted: sha256:5940666d2088bdb774ac498d4adf7e754b81318641924efab95cf1ed3256faa0

Total reclaimed space: 278.5 MB

DockerにMongoDBのデータをリストアする

課題

既存のMongoDBのデータがあり、そのダンプをDocker化するにあたって初期データとしてリストアしたい。

条件

  • Docker Composeは繰り返し利用するので初期データの投入に関わる処理は記述したくない
  • あとからdocker-compose.ymlを書き直ししたくない
  • ローカル領域を記述して環境依存をさせたくない
  • ローカルPCにmongodbはインストールしたくない

対策

  • volumes を利用する。

docker-compose.yml編集

docker-compose.ymを作る。 データディレクトリを volumes: に定義

version: '3'
services:
  api:
    build: .
    ports:
    - "9080:9080"
  mongo:
    image: "mongo:latest"
    volumes:
     - mongo-data:/data/db
  mongo-express:
    image: "mongo-express:latest"
    ports:
    - "8081:8081"
    links:
     - mongo
volumes:
  mongo-data:

ひとまず起動・停止してvolumeを作っておく。

$ docker-compose up
$ docker-compose down

データ投入用mongo起動

先ほど作成したvolumeを参照するためにmongoを立ち上げる。

$ cd workdir //ダンプファイルが置いてあるディレクトリ
$ docker run --rm -v "projectname_mongo-data:/data/db" -v "$PWD":/work -w "/work" --name mongo -d mongo

-v "projectname_mongo-data:/data/db" でdocker-composeで作成したvolumeに接続するのがポイント。

データリストア

$ docker exec mongo mongorestore /work/mongodb_dump

確認

http://localhost:8081/ にアクセスしてDBのリストアが成功していることが確認できればOK。
普通にポート開けてMongoDBインストール済みのマシンから mongorestore した方が多分楽チン。

EchoサーバーをDocker化する

目的

Echoが起動するDockerイメージを作りたい

下調べ

golangにはDockerのOfficialイメージがあるのでそれを使う

https://hub.docker.com/_/golang/

作業ディレクトリを決める

$ cd ~/myapp

確認用ファイルの準備

ガイドを参考に、server.goファイルを作る

https://echo.labstack.com/guide

package main

import (
    "net/http"
    
    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })
    e.Logger.Fatal(e.Start(":1323"))
}

Dockerfileの編集

Dockerfileを作成する。といっても golang:onbuild 内で 外部ライブラリのインポートなどもしてくれるので最初は1行しか書く必要はない。

FROM golang:onbuild

ビルド

Dockerをビルドする。ここでは echo とタグをつけておく(なんでもよい)。

docker build -t echo .

起動

docker run --rm -p 127.0.0.1:1323:1323 --name echo echo

確認

以下にアクセスして Hello World! と表示されたら成功。

http://localhost:1323/

Golangの公式Dockerファイルがよろしくやってくれるので、 余計なことしないならプロジェクトのディレクトリ上でdockerビルドするだけでいい感じ。 なんというか別にEcho関係ないやん、という結論に。。

MongoDB:外部接続を許可

ローカルホスト以外からの接続を許可したい場合。 net:bindIpを使って定義する。

$ sudo vim /etc/mongod.conf 
# network interfaces
net:
  port: 27017
  bindIp: 0.0.0.0
#  bindIp: 127.0.0.1

0.0.0.0 はどこからでもアクセス可能になるので注意。

/* Responsive: yes */