エンジニア同士で話していると、CIどうしよう、今何がいいかなぁという話にたびたびなります。
CIサービスは複数ありますが、サーバーサイドビルドでSaaS 型CI なら CircleCI が今のところいいい感じです。(2.1を前提とする)
あるいはGitHub Actions もとてもいい感触です。 現状Beta で push イベント駆動なのでlintやビルドにはいい感じで利用できます。他のTagなどのイベントを利用できるようになるのが楽しみです。
今回は、CircleCI 2.1 で利用できる Orb についてPR送ることを通して学んでみます。
目次
TL;DR
PRを出したくて仕組みを理解していくと何かと捗るのでいいですよ。 実情は、適切に動かすためにコンセプトと仕様を理解することに徹するだけです。 モチベーションだいじ。
Orb とは
CircleCI 2.1 から利用できる機能で、JobsやCommand に定義していた処理を公開し、それを利用できる機能です。
利用方法はいたって簡単です。
YAML で version: 2.1
を宣言し、orbs
で利用するorbを定義、command/jobs/workflow のいずれかでOrbに定義されたcommandやjob を namespace/コマンド
のような記述で利用するだけです。
ドキュメントにあるミニマムな例が分かりやすいでしょう。
version: 2.1 orbs: hello: circleci/hello-build@0.0.5 workflows: "Hello Workflow": jobs: - hello/hello-build
いざ使うときに気になる「どんな Orbがあるのか、使い方は?」は、Orb Explorer を使うことで探すことができます。
Orbを利用するにあたり困らない例と困る例
さて公開されているOrbですが、いい感じで使える一方であと一歩これが足りない、というケースがあります。
例えば、circleci/aws-s3@1.0.6
を見てみましょう。
このOrbがよく考えられているのが、arguments パラメーターを公開しておりOrbに定義がないパラメーターを受付可能にしていることです。 このパラメーターがあるおかげで、aws cli にOrbが想定していないパラメーターを渡すことができます。
version: 2.1 orbs: aws-s3: circleci/aws-s3@1.0.0 jobs: build: docker: - image: 'circleci/python:2.7' steps: - checkout - run: mkdir bucket && echo "lorum ipsum" > bucket/build_asset.txt - aws-s3/sync: from: bucket to: 's3://my-s3-bucket-name/prefix' arguments: | --acl public-read \ --cache-control "max-age=86400" overwrite: true - aws-s3/copy: from: bucket/build_asset.txt to: 's3://my-s3-bucket-name' arguments: '--dryrun'
一方で、circleci/aws-code-deploy@0.0.7
を見るとこの arguments パラメーターがないために、ごにょごにょできません。
version: 2.1 orbs: aws-code-deploy: circleci/aws-code-deploy@1.0.0 workflows: deploy_application: jobs: - aws-code-deploy/deploy: application-name: myApplication deployment-group: myDeploymentGroup service-role-arn: myDeploymentGroupRoleARN bundle-bucket: myApplicationS3Bucket bundle-key: myS3BucketKey
AWS CLI を Circle CI で使うときの注意
aws関連のOrbに arguments を渡す口がなくて困る例として、aws cliの処理を assume role による認証で行いたい場合があります。
assume role を使うことで、認証は1つで、assume 先のrole定義でアクセス先を切り替えることができます。
また、アクセス先の認証も assume role の policy で縛ることができるため、AWS のアクセス制御的にも相性が良くなっています。
assume role を利用するには、そのプロファイル定義と role_arn
と source_profile
を定義します。
[profile marketingadmin] role_arn = arn:aws:iam::123456789012:role/marketingadmin source_profile = user1
あとは、環境変数でAWS_DEFAULT_PROFILE
を定義するかAWS_PROFILE
で暗黙的に解決させる、あるいはコマンド実行時に --profile プロファイル名
が必要です。
aws s3 cp .... s3://.... --marketingadmin
CircleCIの事情を考えると、circleci の aws 系のOrbは circleci/aws-cli
を必ず利用しています。
この Orbは非常に使い勝手が良く、AWS_ACCESS_KEY
、AWS_SECRET_ACCESS_KEY
、AWS_DEFAULT_REGION
をCircle CI のProject にあるEnvironment Variable に定義しておくだけで aws configure が行われます。
一方で、このOrbは default profile を使う前提であるため任意の Profile を追加で生成できません。そのためassume role を使うときには別途プロファイルを追加する必要があります。
- run: command: | mkdir -p ~/.aws echo "[assume_role]" >> ~/.aws/credentials echo "source_profile = default" >> ~/.aws/credentials echo "role_arn = << parameters.role_arn >>" >> ~/.aws/credentials
circleci/aws-cli を自分で実行しない場合は、事前に ~/.aws
を作ってあげましょう。もしcircleci/aws-cli/install
と circleci/aws-cli/configure
を自分で実行するなら不要です。
あとはコマンドに Profile を渡せばいいです。
で、前節のcircleci/aws-s3
の場合は、arguments: '--profile assume_role'
とOrb実行時に渡せばいいのでok、となります。
余談ですがcircle ci のaws-cli/configure
を使うなら、AWS_DEFAULT_PROFILE
と AWS_PROFILE
は使えません。
これらを定義しているとaws-cli/configure
でコケます、AWS_DEFAULT_PROFILE
は aws configure
が終わっている前提なのでまぁシカタナイ。
Traceback (most recent call last): File "/usr/local/bin/aws", line 27, in <module> sys.exit(main()) File "/usr/local/bin/aws", line 23, in main return awscli.clidriver.main() File "/usr/local/lib/python2.7/site-packages/awscli/clidriver.py", line 59, in main rc = driver.main() File "/usr/local/lib/python2.7/site-packages/awscli/clidriver.py", line 193, in main command_table = self._get_command_table() File "/usr/local/lib/python2.7/site-packages/awscli/clidriver.py", line 102, in _get_command_table self._command_table = self._build_command_table() File "/usr/local/lib/python2.7/site-packages/awscli/clidriver.py", line 122, in _build_command_table command_object=self) File "/usr/local/lib/python2.7/site-packages/botocore/session.py", line 671, in emit return self._events.emit(event_name, **kwargs) File "/usr/local/lib/python2.7/site-packages/botocore/hooks.py", line 356, in emit return self._emitter.emit(aliased_event_name, **kwargs) File "/usr/local/lib/python2.7/site-packages/botocore/hooks.py", line 228, in emit return self._emit(event_name, kwargs) File "/usr/local/lib/python2.7/site-packages/botocore/hooks.py", line 211, in _emit response = handler(**kwargs) File "/usr/local/lib/python2.7/site-packages/awscli/customizations/preview.py", line 69, in mark_as_preview service_name=original_command.service_model.service_name, File "/usr/local/lib/python2.7/site-packages/awscli/clidriver.py", line 318, in service_model return self._get_service_model() File "/usr/local/lib/python2.7/site-packages/awscli/clidriver.py", line 335, in _get_service_model api_version = self.session.get_config_variable('api_versions').get( File "/usr/local/lib/python2.7/site-packages/botocore/session.py", line 233, in get_config_variable logical_name) File "/usr/local/lib/python2.7/site-packages/botocore/configprovider.py", line 226, in get_config_variable return provider.provide() File "/usr/local/lib/python2.7/site-packages/botocore/configprovider.py", line 323, in provide value = provider.provide() File "/usr/local/lib/python2.7/site-packages/botocore/configprovider.py", line 382, in provide config = self._session.get_scoped_config() File "/usr/local/lib/python2.7/site-packages/botocore/session.py", line 334, in get_scoped_config raise ProfileNotFound(profile=profile_name) botocore.exceptions.ProfileNotFound: The config profile (assume_role) could not be found Exited with code 1
circleci/aws-code-deploy で arguments 対応をPRする
このあたりを理解した上で、一時対処として自分の Commands で対応していたのですが、公式にあった方がいいので対応しました。
circleci/aws-code-deploy@0.0.9
から arguments が利用できます。
なお、#140 で対応漏れがあったので #144 をすぐに出しましたが... はずかしい、Orbのテストがないと厳しいですね。
対応は単純で arguments パラメーターをつけて回るだけです。この時、when が string型に対しては ''
をfalse と認識することを利用しています。
Empty strings are treated as a falsy value in evaluation of when clauses, and all other strings are treated as truthy. Using an unquoted string value that YAML interprets as a boolean will result in a type error.
加えて、<<# parameters.xxxxx >> <</parameters.xxxxx>>
によってパラメーターが true の場合にのみ埋め込まれるのを使うとこう書けばいいことが分かります。
<<# parameters.arguments >> << parameters.arguments >><</parameters.arguments >>
これで、argument を使って --profile
を指定可能になりましたとさ、めでたしめでたし。
version: 2.1 orbs: aws-code-deploy: circleci/aws-code-deploy@1.0.0 workflows: deploy_application: jobs: - aws-code-deploy/deploy: application-name: myApplication deployment-group: myDeploymentGroup service-role-arn: myDeploymentGroupRoleARN bundle-bucket: myApplicationS3Bucket bundle-key: myS3BucketKey arguments: '--profile assume_role'