以下の内容はhttps://tech.guitarrapc.com/entry/2019/04/14/075914より取得しました。


CircleCIのOrbをPull Requestを通じて学ぶ

エンジニア同士で話していると、CIどうしよう、今何がいいかなぁという話にたびたびなります。

CIサービスは複数ありますが、サーバーサイドビルドでSaaS型CIならCircleCIが今のところいいい感じです。(2.1を前提とする)

https://circleci.com/

あるいはGitHub Actionsもとてもいい感触です。 現状Betaでpushイベント駆動なのでlintやビルドにはいい感じで利用できます。他のTagなどのイベントを利用できるようになるのが楽しみです。

https://github.com/features/actions

今回は、CircleCI 2.1で利用できるOrbについてPR送ることを通して学んでみます。

概要

PRを出したくて仕組みを理解していくと何かと捗るのでいいですよ。 実情は、適切に動かすためにコンセプトと仕様を理解することに徹するだけです。 モチベーションだいじ。

Orbとは

CircleCI 2.1から利用できる機能で、JobsやCommandに定義していた処理を公開し、それを利用できる機能です。

https://circleci.com/orbs/

利用方法はいたって簡単です。

https://circleci.com/docs/2.0/using-orbs/

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を使うことで探すことができます。

https://circleci.com/orbs/registry/

Orbレジストリから検索

Orbを利用するにあたり困らない例と困る例

さて公開されているOrbですが、いい感じで使える一方、あと一歩これが足りない、というケースがあります。

例えば、circleci/aws-s3@1.0.6を見てみましょう。

https://circleci.com/orbs/registry/orb/circleci/aws-s3

この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による認証で行いたい場合があります。

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-role.html

assume roleを使うことで、認証は1つで、assume先のrole定義でアクセス先を切り替えることができます。 また、アクセス先の認証もassume roleのpolicyで縛ることができるため、AWSのアクセス制御的にも相性が良くなっています。 assume roleを利用するには、そのプロファイル定義とrole_arnsource_profileを定義します。

```~/.aws/credentials [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`を必ず利用しています。

> [https://circleci.com/orbs/registry/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 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が利用できます。

> https://github.com/CircleCI-Public/circleci-orbs/pull/140

なお、#140で対応漏れがあったので #144をすぐに出しましたが... はずかしい、Orbのテストがないと厳しいですね。

> https://github.com/CircleCI-Public/circleci-orbs/pull/144

対応は単純で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.

> https://circleci.com/docs/2.0/reusing-config/#parameter-types

加えて、`<<#  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'




以上の内容はhttps://tech.guitarrapc.com/entry/2019/04/14/075914より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14