タム
2024.07.10
69
こんにちは。タムです。
今回は、コンシューマ駆動契約の2回目の投稿ということで、
前回課題として挙げていた、CI/CDパイプラインに組み込むタスクが完了したため、
具体的にどうやって対応したかを紹介させていただきます。
今回はこちらの構成を作成することをゴールとします。
※厳密には上記の構成とは少し異なり、PRパイプラインには組み込みません。
コンシューマ・プロバイダ共にデプロイパイプラインに組み込みます。
今回実装するのは以下の流れです。
前回の記事を読んでいただけたらおわかりのように、
コンシューマ側でテストに成功すると契約ファイル(JSON)が作成され、
それをプロバイダ側に連携する必要がありました。
そこで前回はローカルで直接ファイルをコピペして置いていましたが、
CI/CDに組み込むにはリモートのサーバにファイルを置く必要があります。
単にファイルを共有するだけであれば、S3で事足ります。
ただ、当然のことながらS3だと本当にファイルを共有することしかできないです。
Pactではコンシューマとプロバイダの間を取り持つBrokerを提供しています。
これを使うと、単にJSONを共有するだけでなく、色々便利な使い方ができるようです。
なのでせっかくコンシューマ駆動を導入するなら、個人的には積極的にBrokerも使っていきたいです。
Pact BrokerにはRuby製アプリ、Docker、Pact Flow(インフラ込み)の3種類があります。
前の2つは自分でホスティングする必要があるので、大きく分類するなら2種類ですね。
Pact Flowの料金は無料プランもありますが、制約が色々あり
(特に2 integrationsが気になります。2つのサービス間までしか利用できないということでしょうか?)
有料プランになると万単位のお金がかかるので、実際に導入するのはなかなかハードルが高いと思います。
というわけで今回は、Dockerを自分でホスティングする方向で検討しました。
次にDockerをどこに配置するかですが、個人利用でたまに動かす程度なので
今回はシンプルにEC2 on Dockerの構成でグローバルIPを付与して直接外部からアクセスできるようにしました。
Brokerにpublishする方法(Pact Broker CLI)ですが、
Dockerイメージ、Ruby製のスタンドアロン実行可能ファイル、NodeJS製のコマンドがあります。
NodeJSであればビルド環境にそのまま組み込めるため楽かなと思ったのですが、
CodeBuildで実行したところ以下のように依存関係でエラーが出て解決に時間がかかりそうだったので断念しました。
[Container] 2024/07/22 13:29:50.176274 Running command npm run test:publish
> auth0-next-custom@0.1.0 test:publish
> ./node_modules/.bin/pact-broker publish ./pacts --consumer-app-version=$(git rev-parse HEAD) --auto-detect-version-properties --broker-base-url=${PACT_BROKER_BASE_URL} --broker-username ${PACT_BROKER_USERNAME} --broker-password ${PACT_BROKER_PASSWORD}
XXXXXXXXXXXXXXXXX/auth0-next-custom/node_modules/@pact-foundation/pact-cli/standalone/linux-x64-2.4.6/pact/lib/ruby/bin.real/ruby: error while loading shared libraries: libcrypt.so.1: cannot open shared object file: No such file or directory
[Container] 2024/07/22 13:29:50.529456 Command did not exit successfully npm run test:publish exit status 127
[Container] 2024/07/22 13:29:50.534863 Phase complete: PRE_BUILD State: FAILED_WITH_ABORT
[Container] 2024/07/22 13:29:50.534880 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: npm run test:publish. Reason: exit status 127
したがって環境起因の問題が起こりにくいDockerイメージを使うことにしました。
ところで、コンシューマはNextJSアプリであり、Amplifyでホスティングしています。
なのでCI/CDもAmplifyで行っていました。
そこで問題なのはAmplifyのCI/CDではDockerを動かすことはできないようです。
調べたところ、AmplifyのCI/CDをトリガーすることは可能なので、
こちらを参考にCodeBuildから呼び出すようにしました。
なお、CodeBuildからDockerを呼び出す場合、特権を付与する必要があるので注意が必要です。
そうすると逆にAmplifyの自動デプロイが邪魔になってくるので、設定をオフにしました。
あとは、publishする際アプリバージョンを指定する必要がありますが、
Gitのコミットハッシュが推奨なのでその場合はCodeBuildからGitコマンドを実行する必要があります。
そのためソースステージの出力アーティファクトで完全クローンを選択する必要があるのでご注意ください。
また、リポジトリがGitHubを利用している場合は完全クローンするための権限を
CodeBuildのサービスロールに付与する必要もあります。
権限を付与するために必要なConnectionArnはCodePipelineのソースステージの「詳細を表示」から確認できます。
コンシューマ側のCI/CDのための修正は以下のようになりました。
diff --git a/buildspec.yml b/buildspec.yml
new file mode 100644
index 0000000..7d35759
--- /dev/null
+++ b/buildspec.yml
@@ -0,0 +1,16 @@
+version: 0.2
+
+phases:
+ install:
+ on-failure: ABORT
+ commands:
+ - npm ci
+ pre_build:
+ on-failure: ABORT
+ commands:
+ - npm run test
+ - npm run publish
+ build:
+ on-failure: ABORT
+ commands:
+ - aws amplify start-job --app-id XXXXXXXXXX --branch-name main --job-type RELEASE
diff --git a/package.json b/package.json
index 3d79d38..22c0710 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,9 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint"
+ "lint": "next lint",
+ "test": "jest",
+ "publish": "docker run --rm -w ${PWD} -v ${PWD}:${PWD} pactfoundation/pact-cli:latest publish ${PWD}/pacts --consumer-app-version=$(git rev-parse HEAD) --branch=$(git branch --contains | cut -d ' ' -f 2) --broker-base-url=${PACT_BROKER_BASE_URL} --broker-username ${PACT_BROKER_USERNAME} --broker-password ${PACT_BROKER_PASSWORD}"
},
"dependencies": {
"@auth0/nextjs-auth0": "^3.5.0",
Providerはpythonで実装されていてAPI Gateway+Lambda構成でCodeBuildでデプロイしていました。
Consumerの方はtestとpublishを別々に実行する必要がありましたが、
pactmanの場合はtestの際のオプションの--pact-broker-url
でURLを指定することでBrokerから契約ファイルをダウンロードし、
--pact-publish-results
で結果をアップロードすることが可能です。
こちらもコンシューマ同様Gitのコミットハッシュでバージョンを指定する場合はそのための設定が必要になってきます。
ですがDockerを使う必要がない分簡単で、CI/CDのための差分は以下のようになりました。
diff --git a/buildspec.yml b/buildspec.yml
index 09cffeb..216944e 100644
--- a/buildspec.yml
+++ b/buildspec.yml
@@ -1,15 +1,22 @@
version: 0.2
phases:
+ install:
+ on-failure: ABORT
+ commands:
+ - pip install -r requirements.txt
+
pre_build:
on-failure: ABORT
commands:
- aws s3 cp s3://XXXXXXXXXXXXXXXXXXXXXXXXX/google_login_back/dev.json .chalice/deployed/dev.json || true
+ - ./test.sh
+
build:
on-failure: ABORT
commands:
- - pip install -r requirements.txt
- chalice deploy
+
post_build:
on-failure: ABORT
commands:
diff --git a/docker-compose.yml b/docker-compose.yml
index cca3044..815fa32 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -11,7 +11,8 @@ services:
environment:
DB_ENDPOINT: http://dynamo:8000
TEST_PORT: 8008
- PYTEST_ADDOPTS: "-v --pact-files=pacts/*.json"
+ PACT_BROKER_BASE_URL: ${PACT_BROKER_BASE_URL}
+ PACT_BROKER_AUTH: ${PACT_BROKER_USERNAME}:${PACT_BROKER_PASSWORD}
command: chalice local --host=0.0.0.0 --port=8002
dynamo:
image: amazon/dynamodb-local
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..2bca591
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+GIT_VERSION=$(git rev-parse HEAD)
+
+pytest tests/ -v --pact-broker-url=${PACT_BROKER_BASE_URL} --pact-provider-name=google-login-back --pact-publish-results --pact-provider-version=${GIT_VERSION}
BrokerでBasic認証している場合はPACT_BROKER_AUTH
環境変数に設定することで通すことが可能です。
(もしくはURLに付与するやり方もあるようです)
今回はPact Brokerを使って契約ファイルをコンシューマ・プロバイダ間で共有し、
CI/CDにPactの検証を組み込むまでを行いました。
気をつけるポイントは以下です。
現状のパイプラインだと、コンシューマ側で契約を変更した際に、
変更後もプロバイダが引き続きサポートしているかどうかがわからないという問題があります。
もしサポートしていなかった場合、そのままデプロイされてしまい、
不具合が発生する可能性があります。
あるべきとしては、コンシューマ側で契約を変更したら、
プロバイダが契約を満たすかどうかをチェックしに行くように
コンシューマ側のパイプラインを組む必要があります。
こちらは今後の課題として、でき次第また記事にしようと思います。
79
タム
2024.10.29
63
タム
2024.09.14