小ネタ:runnのカスタムランナーでFizzBuzzやってみた | DevelopersIO
でまずは試しに使ってみましたが、もうちょっと実用的なことも出しておかないなーと思い、やってみました。
何をやるか?
我々のプロダクトでは、API実行した結果、S3にファイルを配置するような処理があります。
こういったシナリオをテストしようと思うと、「今の」runnには専用のランナーがありません。なので、S3をアレコレするカスタムランナーを作ってみようと思います。
方針
カスタムランナーでは既存のランナーを組み合わた処理しか行えないため、目的を達成するにはどうするかを考えたところ、今回はシンプルにAWS CLIを使ったS3への高レベルコマンドをラップする形で作ってみようと思います。
AWS CLI で高レベル (S3) コマンドを使用する - AWS Command Line Interface
できたもの
結果できたカスタムランナーの定義がこちらです。
custom-s3-runner.yaml
desc: AWS CLI S3 High-level command runner
if: included
vars:
profile: '{{ parent.params.profile }}'
command: '{{ keys(parent.nodes)[0] }}'
paths: '{{ parent.nodes[keys(parent.nodes)[0]].paths }}'
options: '{{ parent.nodes[keys(parent.nodes)[0]].options }}'
steps:
-
exec:
command: "aws --profile {{ vars.profile }} s3 {{ vars.command }} {{ vars.paths }} {{ vars.options }}"
bind:
exit_code: current.exit_code
stdout: current.stdout
stderr: current.stderr
このカスタムランナーを使用する側のシナリオはこちらです。
s3-runner.yaml
desc: S3アクセスのカスタムランナーを作ってみる
runners:
s3:
path: s3-runner.yaml
params:
profile: myprofile
steps:
listObjects:
desc: "特定バケットのオブジェクトリストを取得する"
s3:
ls:
paths: s3://runn-s3-runner-sample
options: '--recursive'
test: |
current.exit_code == 0
dump: current.stdout
testExistentObject:
desc: "特定オブジェクトの存在を確認する"
s3:
ls:
paths: s3://runn-s3-runner-sample/some-object.txt
test: |
current.exit_code == 0
testNonExistentObject:
desc: "特定オブジェクトが存在しないことを確認する"
s3:
ls:
paths: s3://runn-s3-runner-sample/nonexistent-object.txt
test: |
current.exit_code != 0
GetObject:
desc: "S3のオブジェクトを取得する"
s3:
cp:
paths: s3://runn-s3-runner-sample/some-object.txt /tmp/some-object.txt
test: |
current.exit_code == 0
CatObject:
desc: "取得したオブジェクトの内容を確認する"
exec:
command: 'cat /tmp/some-object.txt'
test:
current.stdout == 'foo'
実行した結果がこちらです。外部コマンドを使うexec
ランナーを使うため、--scopes runn:exec
オプションを付けてrunn
を実行します。
$ runn run --scopes run:exec custom-s3-runnder.yaml --verbose
=== S3アクセスのカスタムランナーを作ってみる (custom-s3-runnder.yaml)
--- (0) ... ok
2024-03-04 09:27:20 0 any-object.txt
2024-03-03 16:32:18 0 some-object.txt
=== AWS CLI S3 runner (s3-runner.yaml)
--- (0) ... ok
--- (0) ... ok
=== AWS CLI S3 runner (s3-runner.yaml)
--- (0) ... ok
--- (0) ... ok
=== AWS CLI S3 runner (s3-runner.yaml)
--- (0) ... ok
--- (0) ... ok
=== AWS CLI S3 runner (s3-runner.yaml)
--- (0) ... ok
--- 取得したオブジェクトの内容を確認する (CatObject) ... fail
Failure/Error: test failed on "S3アクセスのカスタムランナーを作ってみる".steps.CatObject "取得したオブジェクトの内容を確認する": condition is not true
Condition:
current.stdout == 'foo'
│
├── current.stdout => ""
└── "foo" => "foo"
Failure step (custom-s3-runnder.yaml):
38 CatObject:
39 desc: "取得したオブジェクトの内容を確認する"
40 exec:
41 command: 'cat /tmp/some-object.txt'
42 test:
43 current.stdout == 'foo'
1 scenario, 0 skipped, 1 failure
S3のオブジェクトの存在確認および、取得して内容を確認することができています。
最後のステップでは、あえて中身が空のファイルを使って、テストでエラーになるようにしています。
解説
今回のカスタムランナーのポイントは以下のとおりです。
- カスタムランナーの定義時と実行時に指定する内容を分別する
profile
: 複数回実行しても同じものを使う事が多いため、カスタムランナーの定義で指定させるcommand
、paths
、options
: 実行時に毎回変わるため、実行時変数で指定させる
- カスタムランナー定義、実行時に指定した値は、定義側で取得方法を使い分ける
- カスタムランナー定義時に
params
で指定した値{{ parent.params.<key> }}
で参照できる
- カスタムランナー実行側にてmapで指定した値
keys(parent.nodes)
でキーリストが取得できるparent.nodes.<key>
でmapの値を取り出せるparent.nodes[<key>]
でも可能
- これらを組み合わせることで柔軟な表現が可能になる
- カスタムランナー定義時に
- 実行結果を使用側で参照できるようbindしておく
exit_code
、stdout
、stderr
といったexec
コマンドの結果を、カスタムランナー使用側で参照するには、bind
で束縛する必要があります
まとめ
ある程度実用的なカスタムランナーも、比較的簡単に定義して使えることがわかりました。
ただ、定義するカスタムランナーに「直感的で使いやすいAPI」をつけるのは、設計者の腕の見せ所だなとも感じました。今回の例だと、あくまでAWS CLIのS3高レベルコマンドをラップしただけではありますが、カスタムランナーに与える値を、mapのキー、値どちらで渡したほうがより自然か?みたいな考え方は必要でした。
また、「これはカスタムランナーとして定義すべきものなのか?」も同時に考慮が必要だとも感じました。「DSLを作れる」というのは強い力ですが、ともすれば独りよがりなものにもなってしまいがちなものです。今後活用するにしても、使用箇所を見極めつつ、使ってみてはやっぱりやめたほうがいいとか、そういう試行錯誤していこうと思います。