cfn-lint で CloudFormation のテンプレートの検証をする
CloudFormation のテンプレートを作っている時、 いちいちコンソールにアップして確認していたら時間がかかってしまう。 そんな時に便利なのが cli でテンプレートファイルの チェックをしてくれるcfn-lint。 使い方も簡単で
$ cfn-lint validate ./cf-template.yaml
とするだけ。 こいつがすごいのがフォーマットだけでなく、 存在していないプロパティや関数までチェックしてくれる事。 なので typo が早期発見できて、開発効率がめちゃ上がります。 インストールも簡単で
$ npm install -g cfn-lint
でいけます。 ただし、ARN を設定する所にテンプレート内で定義したリソースを書いた場合 コンソールでやれば問題なく通るのに、 以下のようなエラーが出てしまいます。
Resource: Resources > AlbListener > Properties > LoadBalancerArn Message: Expecting an ARN, got 'mock-ref-Alb' Documentation: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-listener.html
無視すればよいのですが、ちょっと気になります。
shell で tap 関数を作ってみた
ruby にtap メソッドというものがあって、 メソッドチェーンしてる時のデバッグに便利なんですが、 shell でパイプライン使ってる時の デバッグにも使えそうなので、作ってみました。
ただし、処理を入れる事はできなくて 標準エラーに内容を表示するだけです。
#!/bin/bash function tap() { local input input=$(cat -) echo "$input" | sed -e 's/^/> /' >&2 echo "$input" >&1 }
使い方はこんな感じで 内容を表示させたい所に tap をはさみます。
$ echo "hoge\nfuga" | tap | sed -e 's/hoge/fuga/' > hoge > fuga fuga fuga
tap で出力した行には先頭に > をつけてみました。
Swagger を試してみた
そろそろ手書きで API の仕様を作るのやめたいと思って swagger を試してみました。
swagger は別に単一のソフトではなくて、 Swagger Spec という仕様で書かれた定義書を 元にドキュメントを書いたり、コードを生成したりできるものみたいです。
Swagger Editor
オンライン のがありますが、 docker 版もあるのでそちらでやりました。
$ docker pull swaggerapi/swagger-editor $ docker run -d -p 80:8080 swaggerapi/swagger-editor
で localhost にブラウザでアクセスすればよいです。 定義を書いたら右側にドキュメントが表示されて、 さらに上のメニューから、モックとかクライアントを 作ってくれます。
Swagger UI
定義ファイルを読み込んでドキュメントを生成してくれるツールです。 デフォルトでは Swagger Editor と同じデザインですが、 カスタマイズもできるもよう。
まとめ
最初の学習コストはあるけど、導入した方がいいと思いました。 この yaml を git に入れておけば仕様決めの段階から プルリクで議論できるし、CI ツールで マージ→仕様書更新まで自動化できると思います。
ただ、そのままでは yaml の分割に対応してないんですが、 いくつかツールがあるみたいなので、なんとかなりそうな気がします。
loadtest, httperf でお手軽負荷テスト
go で作った WebAPI サーバの速度検証をしてみました。 単に GET リクエストを投げるだけでよいので、JMeter でなくて お手軽にできるツールでやりました。
loadtest
node.js 用のテストツールで 自分でリクエストを好きに定義できるのが特徴です。
例えば、パラメータをランダムに変えたければ requestGeneratorModule.js に
const querystring = require('querystring'); module.exports = function(params, options, client, callback) { const random = (max) => Math.floor(Math.random() * Math.floor(max)); const getParams = { 'id': random(100) }; const paramStr = querystring.stringify(getParams); options.path = options.path + '?' + paramStr return client(options, callback); }
こんな感じで書いて
$ npx loadtest -R requestGeneratorModule.js webapi.example.com/path/to/api/
でいけます。
httperf
loadtest だと秒間あたりのリクエスト数を指定できないので、 こっちも入れてみました。
httperf --server webapi.example.com --port 80 --uri '/path/to/api/&id=100' --num-call 1 --num-conn 100 --rate 100
こんな感じで使えます。 この場合 --rate に 100 を指定してるので秒間 100 リクエストになります。
インストール
brew が使える環境でなかったので github から落としてきて自分でコンパイルしました。
$ git clone git@github.com:httperf/httperf.git $ cd httperf $ autoreconf -i $ ./configure $ make
python のスーパーセットな関数型言語 Coconut を試す
パイプライン演算子が使える言語がないか探していたところ Coconut というものを見つけました。
公式の説明を見ると
なにそれ面白そう。 という事で、ちょっと触ってみました。
インストールは
$ pip install coconut
でいけますが、http://coconut-lang.org/ の上の方に web の実行環境があるので、ちょっと試すならこれで十分です。
さっそく hello world
"hello, world!" |> print
次は無名関数 & 部分適用 & 関数合成。
add = (x, y) -> x + y add1 = add$(1) prd = (x, y) -> x * y prd2 = prd$(2) (add1 .. prd2)(1) |> print # -> 3 (prd2 .. add1)(1) |> print # -> 4
部分適用する関数には $ をつけるみたいですね。 これは Python との互換を保つために必要なんでしょうか。
最後にいつもの偶数自乗和
result = ( range(10) |> filter$((n) -> n % 2 == 0) |> map$((n) -> n * n) |> reduce$((acc, n) -> acc + n) ) print(result) # -> 120
インストール不要のチートシートツール cheat.sh
要は man の代わりに使うツールなんですが、 こいつのすごいところはインストールしなくても使えるところで、 例えば、ls の使い方を知りたい場合
$ curl cheat.sh/ls
だけで OK です。 DL して使うコマンドラインツールもあるみたいなんですが、 サーバーがいい感じに返してくれるんで、そのままでも十分。 他にも
$ curl cheat.sh/go $ curl cheat.sh/go/func $ curl cheat.sh/go/:learn
のように言語の使い方も教えてくれます。 詳しい使い方は
$ curl cheat.sh/ $ curl cheat.sh/:help
とかやると教えてくれます。 ブラウザから見る事もできるので、 気分や状況によって使い分けられるのもいいですね。
Go で errgroup を使って直列 vs 並列
直列 vs 並列シリーズ第 3 弾。 あんまり errgroup を使ったサンプルがなかったのでやってみた。
package main import ( "fmt" "io/ioutil" "net/http" "sync" "time" "golang.org/x/sync/errgroup" ) func createUrl(sec int) string { return fmt.Sprintf("http://httpbin.org/delay/%d", sec) } func requestBody(url string) (string, error) { response, err := http.Get(url) if (err != nil) { return "", err } defer response.Body.Close() bytes, err := ioutil.ReadAll(response.Body) if (err != nil) { return "", err } return string(bytes), nil } // errgroup を使って非同期に関数を実行し、結果を待つ func awaitAll(funcs []func() error) error { eg := errgroup.Group{} for _, fn := range funcs { eg.Go(fn) } if err := eg.Wait(); err != nil { return err } return nil } func serial(urls []string) ([]string, error) { bodies := make([]string, len(urls)) for _, url := range urls { body, err := requestBody(url); if (err != nil) { return nil, err } // fmt.Println(body) bodies = append(bodies, body) } return bodies, nil } func parallel(urls []string) ([]string, error) { mutex := new(sync.Mutex) bodies := make([]string, 0, len(urls)) funcs := make([]func() error, 0, len(urls)) for _, url := range urls { fn := func() error { body, err := requestBody(url); if (err != nil) { return err } // fmt.Println(body) mutex.Lock() bodies = append(bodies, body) mutex.UnLock() return nil } funcs = append(funcs, fn) } if err := awaitAll(funcs); err != nil { return nil, err } return bodies, nil } func main() { max := 4 urls := make([]string, 0, max) for i := 1; i <= max; i++ { url := createUrl(i) urls = append(urls, url) } fmt.Println("serial") startTime := time.Now() serial(urls) fmt.Println(time.Now().Sub(startTime)) fmt.Println("parallel") startTime = time.Now() parallel(urls) fmt.Println(time.Now().Sub(startTime)) }
動かしてみる
$ go version go version go1.9.2 linux/amd64 $ go get golang.org/x/sync/errgroup $ go run concurrent.go serial 11.157001925s parallel 4.398581223s
ちゃんと並列で実行されてる。