金曜日の壊れた変更
誰かが4時にファイルを直し、アップロードして帰宅しました。チェックは誰も実行しておらず、次の apply は途中で失敗します。
パイプラインは、どの変更に対しても同じチェックを同じ順で実行します。失敗したところで止まるので、悪い変更が apply まで届きません。
このレッスンのゴール
メモ
最初の失敗で止まるステージのパイプラインを書ける。
重要な言葉
| ことば | いみ | れい |
|---|---|---|
| パイプライン | 決まった順に実行される手順 | |
| ステージ | パイプラインの1つの手順 | |
| 終了コード | コマンドが返す数。0 は成功 | echo $? |
| ゲート | チェックが失敗したら止める手順 | |
| プランファイル | apply がそのまま実行する、保存したplan | plan.bin |
学生が入力するセルは、このプレビューでは空白です。
どのコマンドも終了コードで終わる
コマンドは終わるとき、数を1つ返します。これが終了コードです。0 は成功、ほかの数は失敗を表します。
シェルは直前の終了コードを $? に覚えています。パイプラインが読むのは終了コードだけです。0 以外を返すチェックは、実行を止めるチェックです。
パイプライン用のフォルダを作る
mkdir -p ~/iac-demo/pipe && cd ~/iac-demo/pipeここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
リソース1つのプロジェクトを書く
printf '%s\n' 'variable "shop" {' ' type = string' ' default = "駅前の店"' '}' '' 'resource "terraform_data" "door" {' ' input = format("%s: 営業中", var.shop)' '}' > main.tfここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
フォルダを準備する
tofu init -no-color | grep successfullyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
字下げが乱れた2つ目のファイルを書く
printf '%s\n' 'resource "terraform_data" "sign" {' ' input = "営業中"' '}' > sign.tfここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
整形を調べて終了コードを読む
tofu fmt -check; echo "終了コード $?"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
パイプラインは最初の失敗で止まる
スクリプトは、各ステージを実行し、失敗したら止まることができます。ここでは小さな関数 stage がコマンドを実行し、0 以外が返ったら終了します。
ステージは、手で実行する順に並べます。format、validate、plan、ゲート、apply です。失敗したステージがあれば、あとのステージは始まりません。
ゲートを書く
printf '%s\n' '[.resource_changes[].change.actions[]] | index("delete") | not' > gate.jqここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
パイプラインのスクリプトを書く
printf '%s\n' '#!/bin/bash' 'stage() { echo "== ステージ: $1"; name=$1; shift; "$@" > /dev/null || { echo "ステージ $name が失敗したので停止"; exit 1; }; }' 'gate() { tofu show -json plan.bin | jq -e -f gate.jq; }' 'stage format tofu fmt -check -no-color' 'stage validate tofu validate -no-color' 'stage plan tofu plan -no-color -out=plan.bin' 'stage gate gate' 'stage apply tofu apply -no-color plan.bin' 'echo "パイプライン成功"' > ci.shここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
sign.tfが乱れたまま実行する
bash ci.sh; echo "パイプラインの終了コード $?"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
整形を直して、もう一度実行する
tofu fmt -no-color; bash ci.sh; echo "パイプラインの終了コード $?"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある間違い
注意
パイプラインが成功なら、変更は安全だと考える人が多くいます。成功が示すのは、書いたチェックがすべて通ったことだけです。変更が正しい形で、計画され、適用されても、間違った変更のことがあります。安全を守るのは、ゲートと、planを読む人です。
ゲートはapplyの前にplanを読む
plan ステージは、planを plan.bin に保存しました。apply ステージは、そのファイルだけを実行します。ゲートが認めたものが、そのまま実行されます。
tofu show -json は、planをデータに変えます。ゲートのフィルタは、削除されるリソースが1つでもあれば「だめ」と答えます。
リソースを消して実行する
rm sign.tf; bash ci.sh; echo "パイプラインの終了コード $?"; tofu state listここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ホスト型のパイプラインはファイル
パイプラインを提供するサービスでは、ステージをリポジトリ内のファイルに書き、サービスが自分のマシンで実行します。ジョブは needs で別のジョブを待てます。
ここにはホスト型のランナーがないので、実行はできません。書いて、読むことはできます。
ワークフローファイルを書く
mkdir -p .github/workflows && printf '%s\n' 'name: infra' 'on:' ' pull_request:' ' push:' ' branches: [main]' 'jobs:' ' check:' ' runs-on: ubuntu-latest' ' steps:' ' - uses: actions/checkout@v4' ' - run: tofu fmt -check' ' - run: tofu validate' ' - run: tofu plan -out=plan.bin' ' apply:' ' needs: check' " if: github.ref == 'refs/heads/main'" ' runs-on: ubuntu-latest' ' steps:' ' - run: tofu apply plan.bin' > .github/workflows/infra.ymlここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
順番とコマンドを探す
grep -nE "^ (check|apply):|needs:|run:" .github/workflows/infra.ymlここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
実行結果をPythonで計算
# 前のステージに依存するステージのパイプラインを実行します。
jobs = {"check": [], "plan": ["check"], "apply": ["plan"]}
failing = {"plan"}
state = {}
for name in ["check", "plan", "apply"]:
blocked = False
for need in jobs[name]:
if state[need] != "passed":
blocked = True
if blocked:
state[name] = "skipped"
elif name in failing:
state[name] = "failed"
else:
state[name] = "passed"
print(name, state[name])
# failing を空にして、もう一度見てみましょう。