Kyverno Chainsawとは
Kyverno Chainsaw(以下、Chainsaw)は、Kubernetes上で動作するコントローラーやオペレーターのエンドツーエンド(E2E)テストを宣言的に行うためのオープンソースツールです (Kyverno Chainsaw | Kyverno) (GitHub - kyverno/chainsaw: Declarative K8s e2e testing)。元々はKubernetesのポリシーエンジンであるKyvernoのE2Eテストを行う目的で開発され、現在はKyvernoプロジェクトのサブプロジェクトとして管理されています (GitHub - kyverno/chainsaw: Declarative K8s e2e testing)。Chainsawを使うことで、Kubernetesのオペレーター開発者は複雑なテストコードを書くことなく、YAMLマニフェストだけでシナリオを記述し、実際のクラスタ上でテストを実行できます。
Chainsawの目的は、Kubernetesオペレーターのテストを簡潔かつ宣言的にすることです。その特徴として、テストケースをすべてYAMLで定義でき、追加のプログラミングが不要な点が挙げられます (GitHub - kyverno/chainsaw: Declarative K8s e2e testing)。実際、Kyverno Chainsawの登場によって、Kyverno本体のテスト網羅率は飛躍的に向上し、バグ修正時には関連するマニフェストをコピー&ペーストするだけで回帰テストを追加できるようになったと報告されています (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。このようにChainsawは、Kubernetesリソースの挙動を**「ポリシー(前提)→リソース作成(操作)→期待結果の検証→クリーンアップ」という一連の手順**で確認するテストを容易に記述できるよう設計されています (Getting started - Chainsaw)。
Kyverno(ポリシーエンジン)の例で言えば、典型的なテストシナリオは次のようになります (Getting started - Chainsaw):
- ポリシーを作成: まずテスト対象となるKyvernoポリシーをクラスタに適用します。
- リソースを作成: 次に、そのポリシーが適用されるKubernetesリソース(例: PodやConfigMapなど)を作成します。
- 結果を検証: ポリシーによって期待される検証や変形が実際に行われたか、クラスタの状態をチェックします(例: リソースが拒否された、あるいは特定のラベルが付与された等)。
- クリーンアップ: テストで生成したリソースやポリシーを削除し、環境を元に戻します。
Chainsawは上記の手順をすべてYAMLのテスト定義で表現し、自動的に実行してくれます。そのため、開発者はテストフレームワークの実装に煩わされることなく、テストしたいシナリオの内容そのものに集中できます (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。Kyverno Chainsawのスローガンは「エンドツーエンドテストを完全宣言型かつシンプルで楽しいものにする」とされており (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)、このツールによってオペレーターの品質向上と開発効率の両立が期待できます。
セットアップ方法
Chainsawを利用するためには、まずテスト用のKubernetesクラスタとChainsaw本体のインストールが必要です。以下では、必要な環境とインストール手順、初期設定について解説します。
必要な環境
Chainsaw自体はクラスタ管理ツールではないため、あらかじめ有効なKubernetesクラスタを用意しておく必要があります (Run tests - Chainsaw)。クラスタはローカルのKind(Kubernetes in Docker)やMinikube、または既存の開発用クラスタでも構いません。重要なのは、kubectl
コマンド等でアクセス可能なコンテキスト(KUBECONFIG
)が設定されていることです。Chainsawは自分でクラスタを作成しないため、利用者がテスト実行前にクラスタの準備とコンテキスト設定を行ってください (Run tests - Chainsaw)。
例えば、ローカル環境で手軽にクラスタを用意するならKindが便利です。Kindを使用する場合、以下のコマンドでKubernetesクラスタ(ノードイメージのバージョンは例)を作成できます (Run tests - Chainsaw):
# Kindによるローカルクラスタ作成例
kind create cluster --image "kindest/node:v1.29.4"
上記によりローカルDocker内にシングルノードクラスタが構築され、~/.kube/config
にコンテキストが追加されます。kubectl cluster-info
やkubectl get nodes
等で正常にクラスタに接続できることを確認しておきましょう。
インストール手順
Chainsawのインストール方法はいくつかあります。公式から提供されているプレコンパイル済みバイナリを使う方法、Homebrew経由のインストール、Goツールチェインを使ったビルド、Dockerイメージの利用などです。ここでは代表的な方法を紹介します。
-
Homebrew(macOS)でのインストール: macOS環境ではHomebrewが利用可能です。ただし、
chainsaw
という名前の別ツールがHomebrewコアに存在するため、KyvernoのTapを明示的に指定する必要があります (Installation - Chainsaw)。以下のようにTap追加とインストールを行います。brew tap kyverno/chainsaw https://github.com/kyverno/chainsaw # Tapを追加 brew install kyverno/chainsaw/chainsaw # Chainsawをインストール
※
brew install chainsaw
のように短縮せず、必ずkyverno/chainsaw/chainsaw
とTap名を含めて指定する点に注意してください (Installation - Chainsaw)。 -
バイナリのダウンロード(Linux/Windows等): GitHubのリリースページからOSごとの実行ファイルをダウンロードし、パスの通った場所に配置します (Installation - Chainsaw)。たとえばLinuxの場合、最新バージョンのアーカイブを取得して解凍し、
/usr/local/bin/chainsaw
にコピーすることでインストールできます。 -
Go経由のインストール: Go言語の環境がある場合、
go install
コマンド一発で取得することも可能です (Installation - Chainsaw)。go install github.com/kyverno/chainsaw@latest
これにより、
$GOPATH/bin
(通常は~/go/bin
)にchainsaw
バイナリがビルド・配置されます。 -
Dockerイメージの利用: ChainsawはDockerイメージとしても提供されています (Installation - Chainsaw)。Docker上で実行する場合、テスト定義ファイルのディレクトリとKubeconfigをコンテナにマウントする必要があります (Installation - Chainsaw)。例えば以下のように実行できます。
docker pull ghcr.io/kyverno/chainsaw:<バージョンタグ> docker run --rm \ -v ./tests/:/chainsaw/ \ # テストYAMLディレクトリをマウント -v $HOME/.kube/:/etc/kubeconfig/ \ # Kubeconfigをマウント -e KUBECONFIG=/etc/kubeconfig/config \ --network=host \ ghcr.io/kyverno/chainsaw:<バージョンタグ> test /chainsaw
上記では現在ディレクトリの
./tests/
にテスト定義があるものとして、それをコンテナ内/chainsaw/
にマウントし、chainsaw test /chainsaw
を実行しています。ネットワークモードをホストにし、適切にKUBECONFIGを渡すことで、コンテナ内からホストのクラスタへアクセス可能にしています。
インストール後、chainsaw version
コマンドでバージョン情報が表示されればセットアップは成功です。 (Run tests - Chainsaw)
初期設定
Chainsawは特別な初期設定ファイルなしでもデフォルト設定で動作しますが、必要に応じて設定をカスタマイズできます。デフォルトでは、Chainsaw実行時に現在のKubernetesコンテキストを用いてクラスタに接続し、各テストケースごとに一時的なネームスペースを自動生成します (Namespace options - Chainsaw)。この一時ネームスペースはテスト名に基づいたランダムな文字列を含む名前となり、テストの開始時に作成され、テスト完了後に削除されます (Run tests - Chainsaw) (Namespace options - Chainsaw)。これにより、テストごとにクリーンな隔離環境が確保され、テスト実行中に他のリソースと干渉しないようになっています。
特に指定がなければこのデフォルト動作で問題ありませんが、必要ならChainsawの設定ファイル(Configuration
リソース)やコマンドラインオプションで以下のような調整が可能です。
-
タイムアウト値の変更: リソース適用(Apply)やアサーション(Assert)待ちのタイムアウト時間はデフォルトで5秒~30秒程度に設定されています (Run tests - Chainsaw)。ネットワークの遅延やリソースの準備に時間がかかるケースでは、
--assert-timeout 60s
のようなフラグで待ち時間を伸ばすことができます。 - ネームスペース名の固定: デフォルトのランダムネームスペースではなく特定の名前空間でテストを実行したい場合、設定でnamespaceを指定できます (Namespace options - Chainsaw)(ただし一般にはランダムな隔離環境を用いる方が安全です)。
-
クリーンアップ動作の制御: デフォルトではテスト後にリソースとネームスペースを削除しますが、デバッグのために削除をスキップするオプション(例えば
--skip-delete
)もあります。これを利用すると、テスト失敗時にリソースが残るため、手動で状態を調査できます。
通常はこれらを変更せずとも十分動作しますので、まずはデフォルト設定で動かし、必要に応じて追加設定を検討するとよいでしょう。
テストの作成と実行
ここでは、Chainsawを用いたテスト定義の基本構造と、簡単なサンプルテストの作成方法、そしてテストの実行手順と結果の確認方法について説明します。
Chainsawテストの基本構造
ChainsawのテストはYAML形式で記述し、Kubernetesリソースの一種として定義します(カスタムリソース定義: CRD を利用)。各テストはapiVersion: chainsaw.kyverno.io/v1alpha1
かつkind: Test
というリソースとして表現され、その中にテスト名や手順(steps)を記載します (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。
典型的なChainsawのテストYAMLの構造は次のとおりです。
- metadata.name: テストケースの名前を識別します。出力ログやレポートにこの名前が使われます。
-
spec.steps: テストの手順を順番に記述する配列です。各ステップはさらに
try
,catch
,finally
といったブロックを持つことができます(catch
やfinally
は省略可能) (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。-
try: 実際のテスト手順を記述するメインのブロックです。この中に複数のオペレーション(操作)をリスト形式で列挙します (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。通常は
apply
(リソース適用)やassert
(検証)などを順に実行します。 -
catch:
try
内で失敗(例外)が発生した場合に実行する操作を記述します。エラー時の後処理やリソースクリーンアップを指定できます。 - finally: 成否に関わらず最後に必ず実行する操作を記述します。後片付けなどを行うのに使用します。
-
try: 実際のテスト手順を記述するメインのブロックです。この中に複数のオペレーション(操作)をリスト形式で列挙します (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。通常は
各ステップ内で実行できるオペレーション(operation)にはいくつか種類があります (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno) (Negative testing - Chainsaw):
-
apply: 指定したマニフェストをクラスタに適用します(
kubectl apply
と類似の動作)。YAMLファイルを参照してリソースを作成・更新するのが一般的ですが、その場でYAMLをインライン指定することもできます。apply
はリソースの作成後、自動的にその結果(作成されたオブジェクト)を追跡します。 -
create: こちらもリソース作成用ですが、常に新規作成を想定した動作です。既に存在する場合はエラーになります(対して
apply
は存在すればパッチ適用する場合もある)。 -
assert: クラスタ内のリソース状態を検証します。指定したYAML(期待する状態)と実際のリソースを比較し、一致しなければテストを失敗とします (Create a test - Chainsaw)。YAML内で
metadata.name
を省略した場合は、指定した条件に合致するリソースを検索して内容を比較します (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。このようにアサーションでは完全一致だけでなく、一部のフィールドだけを検証対象にすることも可能です。 - error: 指定したYAMLに合致するリソースが存在しないことを確認します (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。主に「リソースが削除されたこと」を検証するために使われる操作です(期待するリソースが見つかった場合は逆にテスト失敗とみなします)。
-
delete: 指定したリソースを削除します (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。
apply
で作成したオブジェクトを片付ける際などに使用します。削除対象の指定にはref
(apiVersionやkind、nameなどでリソースを特定する)やfile
(マニフェストファイル)で行います。 -
command(または
script
): 外部コマンドの実行を行います。例えばkubectl
やスクリプトを実行したりできます。これは高度な用途向けで、基本的には上記の組み込み操作で対応できない場合に使用します。
Chainsawでは、基本的なテストであれば**apply
とassert
の組み合わせ**だけで記述できます。try
ブロック内でリソースを適用し、続けて期待通りに動作したかをアサートする、という流れです。複雑なシナリオではdelete
やerror
を加えて、リソースの削除処理や存在しないことの確認も組み込めます。catch
やfinally
ブロックは必要に応じて明示的にクリーンアップ手順を書きたい場合に利用できますが、前述のようにChainsawはデフォルトで自動クリーンアップ(各テスト用ネームスペースの削除等)を行うため、シンプルなケースではtry
だけで十分です。
サンプルテストの作成
実際に簡単なテストを書いてみましょう。ここでは、ConfigMapを作成してその内容を検証するという最も単純なケースを試してみます (Create a test - Chainsaw)。この例ではKyverno固有の要素は登場しませんが、Chainsawの基本的な使い方の理解を目的とします。
まず、テスト用のディレクトリを作成し、その中にテストで使用するリソースマニフェストとテスト定義ファイルを準備します (Create a test - Chainsaw)。
# テスト用ディレクトリの作成
mkdir chainsaw-quick-start
cd chainsaw-quick-start
次に、テストで使用するConfigMapのマニフェストファイルを作成します (Create a test - Chainsaw)。
# ConfigMapマニフェストの作成
cat > configmap.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: chainsaw-quick-start
data:
foo: bar
EOF
上記のconfigmap.yaml
は、名前chainsaw-quick-start
を持ち、data
にキーfoo: bar
を含むシンプルなConfigMap定義です。
最後に、Chainsawのテスト定義ファイルchainsaw-test.yaml
を作成します (Create a test - Chainsaw) (Create a test - Chainsaw)。
# テスト定義ファイルの作成
cat > chainsaw-test.yaml <<EOF
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: quick-start
spec:
steps:
- try:
# 1つ目の操作: ConfigMapを適用
- apply:
file: configmap.yaml # 上で作成したマニフェストを適用
# 2つ目の操作: ConfigMapが期待通り作成されたか確認
- assert:
file: configmap.yaml # 同じマニフェストを期待結果として使用
EOF
ポイントは、apply
とassert
の双方で同じconfigmap.yaml
ファイルを参照していることです。apply
でこのファイルを適用するとConfigMapがクラスタに作成され、その後のassert
ではクラスタ内のConfigMapとファイルの内容を比較します (Create a test - Chainsaw)。この例では、作成したConfigMapの内容(キーfoo: bar
)が期待通りかどうかを確認しています。assert
に用いるファイルにも名前やデータが書かれているため、実際のリソースがそれと一致すればテスト成功、異なれば失敗となります。
Chainsawはデフォルトで各ディレクトリ内のchainsaw-test.yaml
というファイルをテスト定義として自動検出します (Create a test - Chainsaw)。そのため上記のようにファイル名を統一しておけば、特別な設定無しにテストを見つけて実行してくれます。複数のテストケースがある場合はディレクトリを分けてそれぞれにchainsaw-test.yaml
を置くか、あるいは1つのファイルに複数のTest
リソースをリスト形式で記述することも可能です。
テストの実行方法
テストの作成ができたら、早速Chainsawで実行してみましょう。基本的な実行コマンドは**chainsaw test
**です。作業ディレクトリを先ほどのchainsaw-quick-start
に移動し、以下のように実行します。
# テストの実行
chainsaw test
実行すると、Chainsawは現在のディレクトリ(指定がなければ.
を探索します (Run tests - Chainsaw))からテスト定義を読み込み、順次テストを実行します。コンソールには詳細なログが表示され、各ステップの進行状況と結果が確認できます。例えば、上記ConfigMapテストの場合、出力の一部は以下のようになります。
- Using test file: chainsaw-test.yaml ([Run tests - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/quick-start/run-tests/#:~:text=Now%20you%20can%20run%20the,command))
- TestDirs [.]
...
=== RUN chainsaw/quick-start
| 10:44:26 | quick-start | @setup | CREATE | OK | v1/Namespace @ chainsaw-xxxxxxx ([Run tests - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/quick-start/run-tests/#:~:text=%7C%2010%3A44%3A26%20%7C%20quick,start))
| 10:44:26 | quick-start | step-1 | APPLY | RUN | v1/ConfigMap @ chainsaw-xxxxxxx/chainsaw-quick-start ([Run tests - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/quick-start/run-tests/#:~:text=%7C%2010%3A44%3A26%20%7C%20quick,start))
| 10:44:26 | quick-start | step-1 | CREATE | OK | v1/ConfigMap @ chainsaw-xxxxxxx/chainsaw-quick-start ([Run tests - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/quick-start/run-tests/#:~:text=%7C%2010%3A44%3A26%20%7C%20quick,start))
| 10:44:26 | quick-start | step-1 | ASSERT | RUN | v1/ConfigMap @ chainsaw-xxxxxxx/chainsaw-quick-start ([Run tests - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/quick-start/run-tests/#:~:text=chainsaw,v1%2FConfigMap))
| 10:44:26 | quick-start | step-1 | ASSERT | DONE | v1/ConfigMap @ chainsaw-xxxxxxx/chainsaw-quick-start ([Run tests - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/quick-start/run-tests/#:~:text=chainsaw,v1%2FConfigMap))
| 10:44:26 | quick-start | @cleanup | DELETE | RUN | v1/ConfigMap @ chainsaw-xxxxxxx/chainsaw-quick-start ([Run tests - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/quick-start/run-tests/#:~:text=chainsaw,v1%2FNamespace))
| 10:44:26 | quick-start | @cleanup | DELETE | OK | v1/ConfigMap @ chainsaw-xxxxxxx/chainsaw-quick-start ([Run tests - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/quick-start/run-tests/#:~:text=chainsaw,v1%2FNamespace))
| 10:44:26 | quick-start | @cleanup | DELETE | RUN | v1/Namespace @ chainsaw-xxxxxxx ([Run tests - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/quick-start/run-tests/#:~:text=%7C%2010%3A44%3A26%20%7C%20quick,PASS%3A%20chainsaw%20%280.00s))
| 10:44:26 | quick-start | @cleanup | DELETE | OK | v1/Namespace @ chainsaw-xxxxxxx ([Run tests - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/quick-start/run-tests/#:~:text=%7C%2010%3A44%3A26%20%7C%20quick,PASS%3A%20chainsaw%20%280.00s))
--- PASS: chainsaw/quick-start (5.25s) ([Run tests - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/quick-start/run-tests/#:~:text=,Skipped%20tests%200%20Done))
PASS
Tests Summary...
- Passed tests 1 ([Run tests - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/quick-start/run-tests/#:~:text=PASS%20Tests%20Summary...%20,Skipped%20tests%200%20Done))
- Failed tests 0
上記のログから、Chainsawが以下の処理を行っていることが読み取れます。
- 最初に
@setup
フェーズでテスト用の一時ネームスペース(ここではchainsaw-xxxxxxx
というランダム名)が作成されています (Run tests - Chainsaw)。 -
step-1
として、まずAPPLY
操作でConfigMapを作成し(CREATE OK
)、続いてASSERT
操作でその内容を検証しています (Run tests - Chainsaw)。DONE
と表示されているのでアサーションが成功したことがわかります (Run tests - Chainsaw)。 - テストケース終了後、
@cleanup
フェーズで自動的にConfigMapとネームスペースの削除(DELETE OK
)が行われています (Run tests - Chainsaw) (Run tests - Chainsaw)。 - 最後に
PASS
の結果とテストの実行時間が表示され、1件のテストが成功、失敗は0件であった旨がサマリとして報告されています (Run tests - Chainsaw)。
このように、Chainsawを実行すると詳細な逐次ログと最終結果がコンソールに出力されます。特にASSERT
に失敗した場合は、その場でエラーの詳細が表示されます。Chainsawはアサーションエラー時に期待値と実際の差分(diff)を示す丁寧なレポート機能を持っており、どのフィールドが異なったのかを教えてくれます (Use assertions - Chainsaw)。例えば、あるDeploymentのPodに期待していたアノテーションfoo: bar
が付与されていなかった場合、エラーメッセージとともに期待値と実物のリソース差分が以下のように表示されます (Use assertions - Chainsaw) (Use assertions - Chainsaw)。
=== ERROR
---------------------------------------------------
v1/Pod/chainsaw-rare-liger/example-5477b4ff8c-tnhd9
---------------------------------------------------
* metadata.annotations.foo: Invalid value: "null": Expected value: "bar"
--- expected
+++ actual
@@ -1,10 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
- annotations:
- foo: bar
labels:
app: nginx
+ name: example-5477b4ff8c-tnhd9
namespace: chainsaw-rare-liger
...
上記のように、期待では存在していたmetadata.annotations.foo: bar
が実際にはnull
だった、という差分を見やすく表示してくれます (Use assertions - Chainsaw) (Use assertions - Chainsaw)。これにより、テスト失敗時の原因究明が容易になります。
結果の確認とレポート
テスト実行後は、コンソール出力を確認することで各テストケースの成功/失敗や詳細を把握できます。1件でもFAIL
が出ればCIパイプラインではジョブを失敗とみなすことができますし、ローカルで実行している場合もすぐに問題箇所を特定できます。
なお、Chainsawにはレポート出力オプションも用意されています。--report-format junit
等と指定すれば、JUnit形式のレポートをファイルに出力することも可能です(CIとの連携でテスト結果を統合したい場合に便利です)。また、テスト実行前にchainsaw lint
コマンドでテスト定義YAMLの静的チェックを行うこともできます (Installation - Chainsaw)。これらの機能は大規模なテストスイートを運用する際に役立つでしょう。
詳細な解説
ここでは、Chainsawの内部動作やテストYAML定義の工夫、そしてテストを書く上で遭遇しやすい問題とその対処法について詳しく解説します。
Kyverno Chainsawの動作原理
Chainsawはテスト定義に従ってKubernetes上で操作を順次実行し、状態を検証します。その動作原理を整理すると次の通りです。
-
テスト定義の読み込み:
chainsaw test
コマンドを実行すると、指定されたディレクトリ以下のファイルを再帰的に走査し、YAML内に含まれるkind: Test
リソースをすべて読み込みます (Run tests - Chainsaw)。特にファイル名に制約はありませんが、デフォルトでは各フォルダ内のchainsaw-test.yaml
を探すため、この命名を利用するのが簡便です (Create a test - Chainsaw)。 - テスト環境のセットアップ: Chainsawは各テストごとにアイソレーション環境を用意します。デフォルトでは前述のようにネームスペースを動的に生成し、そのネームスペース内でテストの一連の操作を行います (Run tests - Chainsaw)。これにより、テスト同士や既存リソースとの干渉が防がれます。同時に、テスト対象のカスタムリソース定義(CRD)などが必要であれば、事前にそれらがクラスタに存在していることが前提となります(例えばKyvernoのポリシーをテストするなら、Kyverno自体と対象ポリシーCRDがクラスタにデプロイ済みである必要があります)。
-
手順の実行: テスト定義に書かれた各
steps
を順番に実行します。基本的には1つのsteps配列にシナリオを順に書きますが、Chainsawは各Testリソースを並列実行することも可能です。デフォルトでは全テストケースを直列に処理しますが、設定により並列度を上げて同時に複数のテストを走らせることもできます (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。いずれにせよ、個々のテスト内部では記述された順序で操作が実行されます。 -
アサーションの評価:
assert
操作が実行されると、Chainsawは指定されたYAML(期待値)をもとにクラスタ内のリソース状態を照合します。具体的には、まずapiVersion
やkind
、metadata.name
(あれば)などで対象のリソースをクラスタから取得し、YAMLに記述されたフィールドと比較します。一致しない場合は一定時間リトライを行います(デフォルトで最大30秒間 (Run tests - Chainsaw)、例えばリソースが作成直後で内容が整うまで少し待つ必要がある場合など)。それでも条件を満たさなければテストを失敗とし、エラーメッセージと差分を出力します (Use assertions - Chainsaw)。一方、一致が確認できればそのステップは成功となります。なお、期待YAMLに書かれていないフィールドについては無視しますので、リソースの一部のみを検証するといった柔軟なテストが可能です(例:Podのstatus
や自動付与されるresourceVersion
などは通常記述しない)。 -
エラー処理と例外:
apply
等の操作でエラーが発生した場合(たとえばYAMLに誤りがある、対象のCRDが無い、Admissionコントローラーにより作成が拒否された等)、該当ステップで例外が発生します。基本的にはそのテストケースは即失敗となり、以降のtry
内の操作はスキップされます。ただし、テスト定義にcatch
ブロックがあれば、そこに記載された操作を実行します。catch
ではエラー時でも実行したいクリーンアップ処理などを書くことができます。またfinally
ブロックがあれば、成功・失敗に関わらず最後に実行されます。Chainsaw自体も内部的に全テスト終了後に、生成した全てのネームスペースとリソースのクリーンアップを行います (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno) (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。 -
後処理と終了: テスト手順がすべて終わると、Chainsawは自動で各テスト用のリソース削除(
@cleanup
)を実施します (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno) (Run tests - Chainsaw)。これによりテスト開始前の状態にクラスタが戻されます。最後にテスト全体の成績が集計され、件数と成功/失敗が表示されます (Run tests - Chainsaw)。複数テストがある場合は、それぞれについてPASS/FAILが報告され、すべてPASSならプロセスの終了コード0、失敗があれば非0となります。
以上がChainsawの大まかな流れです。要点として、**「YAMLに書いたとおりにクラスタ上で操作し、その結果を比較して評価する」**ことを自動化してくれるツールだと言えます。
YAMLによるテスト定義のテクニック
ChainsawのテストYAMLを書く際に役立ついくつかのテクニックやポイントを紹介します。
-
部分的一致(Assertion Tree): 前述の通り、
assert
用のYAMLには検証したいフィールドだけを書けば良いです。例えばリソース名がランダムに決まるようなケースでは、期待YAMLのmetadata.name
を省略し、代わりに固有のラベルやオーナー参照などで対象リソースを特定する方法があります (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。Chainsawは与えられたYAMLをアサーションツリーとして解釈し、指定された要素が実際のリソースに含まれているかチェックします (Use assertions - Chainsaw)。これにより、テストは動的な値に柔軟に対応できます。 -
変数バインディングとテンプレート: Chainsaw 0.1.4以降では、テストに動的な値を差し込むテンプレーティング機能が追加されています (Kyverno Chainsaw 0.1.4 - Awesome new features! | Kyverno)。例えば現在のネームスペース名をリソースマニフェスト内で利用したり、テストごとに異なるパラメータを与えることができます。YAML中で
${...}
やJMESPath式を用いた記法で、実行時に値を埋め込むことが可能です (Kyverno Chainsaw 0.1.4 - Awesome new features! | Kyverno) (Kyverno Chainsaw 0.1.4 - Awesome new features! | Kyverno)。この機能を有効にするには、テスト定義のspec.template: true
を設定するか、各apply
操作の中で個別にtemplate: true
を指定します (Kyverno Chainsaw 0.1.4 - Awesome new features! | Kyverno)。高度なシナリオ(例: 複数の類似リソースをループさせてテストする等)で有用ですが、シンプルなテストでは必須ではありません。 -
マルチクラスターのテスト: Chainsawは1つのテストケース内で複数のKubernetesクラスターに対して操作を行うこともできます(v0.2以降の機能) ([Feature] Create ephemeral namespace in all registered clusters ...)。テスト定義内で
contexts
を指定し、異なるコンテキスト名ごとに操作を振り分けることで、例えばフェデレーション環境やマルチクラウド環境下での一貫したテストが記述できます。この場合も基本はYAMLで書けるため、異なるクラスタへの適用も宣言的に表現できます。 -
外部コマンドとの組み合わせ: 原則としてChainsawの操作セットで大抵のことは可能ですが、それでも足りない場合は
command
操作で外部プログラムを実行できます (Negative testing - Chainsaw)。例えば、テスト中に特定のPod内でスクリプトを動かしたり、クラスタ外のAPIを呼び出す、といったことも組み込めます。ただし、command
で作成したリソースなどはChainsawが直接認識しないため、手動でクリーンアップが必要になる点に注意が必要です (Kyverno Chainsaw 0.1.4 - Awesome new features! | Kyverno)。極力apply
やassert
で実現し、どうしてもという場合のみ使うのがおすすめです。
よくあるエラーとトラブルシューティング
Chainsawでテストを書いていると、いくつかハマりやすいポイントやエラーに直面することがあります。以下に代表的な事例と対処法を挙げます。
-
クラスタ未接続エラー:
chainsaw test
を実行した際に「unable to connect to the server」や「no KUBECONFIG found」のようなエラーが出る場合、Kubernetesクラスタへの接続設定に問題があります。まずkubectl get nodes
等が動作するか確認し、KUBECONFIG
環境変数や~/.kube/config
の設定を見直してください。Chainsaw自体はkubectlと同じ設定を参照するため、kubectlで接続できていればChainsawも動作するはずです。 -
CRDが見つからない: テストで扱うカスタムリソースのCRDがクラスタに登録されていないと、
apply
時にエラーとなります。例えばKyvernoのClusterPolicy
を適用しようとしたのにKyvernoコントローラーがデプロイされていない場合などです。事前に必要なCRDやオペレーター本体をデプロイしてからテストを実行しましょう。なお、Chainsaw自体はCRDのインストール機能は持たないので、これはユーザー側で準備が必要です。 -
アサーション失敗:
ASSERT ERROR
が出た場合は、前述の通り詳細な差分が出力されます (Use assertions - Chainsaw)。まずその内容を確認し、期待値YAMLと実際のリソース状態の差を把握します。多くの場合、テストの期待値の書き漏れ(ポリシーで自動付与されるフィールドを期待YAMLに含めていなかった等)や、テスト対象の挙動そのものの不具合が原因です。もしリソースが生成されていない/削除されていないことが原因であれば、ChainsawのAssertTimeout
設定を延長して再試行してみてください。リソース反映に時間がかかってタイムアウトしていたケースでは、時間を延ばすと解決する場合があります (Run tests - Chainsaw)。 -
テストケース間の干渉: あるテストが他のテストの副作用で失敗する場合、ネームスペースの分離が正しく機能しているか確認します。Chainsawはデフォルトでテストごとに別ネームスペースを使いますが、もし
spec.namespace
を明示して全テストが同一ネームスペースで動いていると、リソースが衝突する可能性があります (Namespace options - Chainsaw)。基本はnamespaceを指定しない(つまり自動分離に任せる)運用が安全です。 -
クリーンアップされないリソース: 通常Chainsaw実行後にテスト用リソースは削除されますが、稀に途中でChainsawプロセス自体が強制終了した場合などはネームスペースが残ることがあります。その際は手動で
kubectl delete ns <chainsaw-...>
で削除してください。継続的にテストを書く際は、定期的に残骸のリソースがないか確認すると良いでしょう。また、オプション--skip-delete
を使ったテストデバッグの後は、忘れずに残ったリソースを片付けます。
以上の点に留意すれば、Chainsawでのテスト作成は比較的スムーズに進められるはずです。エラーメッセージがわかりやすく作られているので、困ったときは出力を注意深く読むことで解決策が見えてきます。
実際のユースケース
最後に、Kyverno Chainsawの実践的な活用シーンについて紹介します。Chainsawは単なるローカルテストツールに留まらず、継続的インテグレーション(CI)/継続的デプロイ(CD)環境やポリシーの事前検証など様々な場面で役立ちます。
CI/CDパイプラインでの利用
ChainsawはCI/CDパイプラインに統合して使用することに非常に適しています (GitHub - kyverno/chainsaw: Declarative K8s e2e testing)。テストがすべてYAMLで定義されコード依存がないため、リポジトリにテストシナリオを含めておけば、CI上でChainsawを実行して自動テストを回せます。例えば、GitHub Actionsを使用している場合、Kyverno Chainsaw公式のセットアップアクションが提供されているため、それを利用して簡単にChainsawをジョブ内にインストールできます (GitHub action - Chainsaw)。Kindなどで一時的なKubernetesクラスタを構築し(必要ならば)、次にChainsawを実行するジョブを組み込むことで、コード変更毎にE2Eテストを実施可能です。
具体的な例として、あるKubernetesオペレーターのリポジトリでChainsawを導入する場合を考えます。まずそのリポジトリにChainsawのテスト(YAMLファイル群)を追加します。次にGitHub Actionsのワークフローに以下のようなステップを組み込みます。
- uses: azure/setup-kubectl@v3 # kubectlやKindのセットアップ
- uses: kind-ci/actions/kind@v1.2.0 # Kindでテスト用クラスタ起動
- uses: kyverno/action-install-chainsaw@v0.1.0 # Chainsawインストール ([GitHub action - Chainsaw](https://kyverno.github.io/chainsaw/0.2.3/cicd/gh-action/#:~:text=uses%3A%20kyverno%2Faction,optional))
- run: chainsaw test path/to/tests # テスト実行(テストディレクトリを指定)
上記のようなフローにより、GitHub上で自動的にChainsawテストが実行され、結果に応じてCIの成否が判定されます。Chainsawの出力をJUnitレポートとして収集することで、GitHubのテスト結果タブやCIレポートに詳細を表示することもできます。これにより、コードの変更によってポリシーやオペレーターの挙動に不具合が生じていないかを継続的に検証できます。実際、Kyverno開発チームもChainsawをCIに組み込むことでリグレッションの早期発見とテストカバレッジ向上に成功しています (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。
ポリシー適用の事前検証
Kyverno ChainsawはKyvernoポリシーの挙動検証にも大いに役立ちます。Kubernetesクラスターにポリシーを適用する前に、事前に期待通りの動きをするかテストで確認することで、本番適用時のトラブルを未然に防げます。例えば、次のようなユースケースが考えられます。
-
Podへのラベル付与ポリシーの検証: Kyvernoで「全てのPodに特定のラベルを付与する」Mutateポリシーを書くとします。Chainsawのテストでは、まずそのポリシーをクラスタに
apply
し、次にラベル無しのPodマニフェストをapply
します。続けて、そのPodに正しくラベルが付与されたかassert
で確認します。期待どおりラベルが付いていればテスト成功、付いていなければポリシーかWebhookの設定に問題があると分かります。 -
リソース拒否ポリシーの検証: KyvernoのValidateポリシーで違反するリソースを拒否するケースもテストできます。例えば「特定の名前空間にはConfigMap作成を許可しない」というポリシーを検証する場合です。ポリシー適用後、その名前空間にConfigMapを作成する操作を
apply
します。このapply
はKyvernoによって拒否され失敗するはずなので、Chainsaw側ではエラーが発生します。Chainsawのcatch
ブロックやerror
操作を活用すれば、「エラーが発生すること自体が期待どおり」であるとテストケースに表現できます。つまり、意図した失敗を確認するテストも書けるわけです (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。 - ポリシーの相互作用テスト: 複数のKyvernoポリシーが存在する環境下で、その組み合わせで問題が起きないか検証することもできます。Chainsawは一連のステップの中で複数リソースを適用できますから、様々なポリシーを順に適用してからテスト用リソースを投入し、その結果をチェックするといったシナリオも構築できます。
以上のように、Chainsawはポリシー開発・運用フェーズでの安全網として機能します。CIに組み込めば、新しいポリシーのプルリクエストに対して自動でテストを走らせ、想定外の挙動がないか確認することもできます。これはPolicy-as-Codeの実践とも言え、インフラやガバナンスの変更に対してテストを伴わせることで信頼性を高める手法です。
まとめ
Kyverno Chainsawは、Kubernetesの世界におけるエンドツーエンドテストを飛躍的に簡素化する強力なツールです。宣言的なYAMLでテストを書けるため、Kubernetesリソースに精通した開発者であれば直感的に扱うことができ、テストコードの学習コストや保守コストを大幅に削減できます (GitHub - kyverno/chainsaw: Declarative K8s e2e testing)。実際にChainsawを導入することで、これまで煩雑さゆえに省略されがちだったオペレーターの包括的なテストが実現可能となり、結果としてプロダクトの堅牢性が向上します。
メリットとしては以下の点が挙げられます。
- 迅速なテスト開発: マニフェストのコピー&ペースト程度の手間で新たなテストケースを追加できるため、不具合修正時にその内容を確実にテストに反映できます (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)。これにより同じバグを繰り返さない安心感が得られます。
- 高い可読性と共有性: テストケースがYAMLで記述されるため、チームメンバー間で理解・レビューしやすくなります。Kubernetesリソースの知識さえあればテスト内容を把握できるため、運用担当者と開発者でテスト定義を共有しポリシーの振る舞いを確認し合う、といったことも容易です。
- CI/CDとの親和性: CLIツールとしてシンプルに実行でき、また公式のGitHub Actionも提供されているため、既存のCI/CDに組み込みやすいです (GitHub action - Chainsaw)。自動テストをパイプラインに追加することで、変更のたびにリグレッションテストが走り、信頼性の高い継続的デリバリーを実現できます。
- 汎用性: 名前に"Kyverno"と付いていますが、Kyverno自体に限らずあらゆるKubernetesコントローラー/オペレーターのテストに利用可能です (GitHub - kyverno/chainsaw: Declarative K8s e2e testing)。自作のコントローラーの挙動確認、他社製オペレーターの検証、Kubernetesプラットフォーム全体の統合テストなど、応用範囲は広いです。
今後の展望として、Chainsawはコミュニティの積極的な開発により機能拡張が続けられています。将来的なバージョンではOperator SDKとの統合やさらなる記法の拡充なども計画されています(ロードマップより)。また、CNCF(Cloud Native Computing Foundation)でもKyverno Chainsawは有望な宣言的テストツールとして注目され始めています (Kyverno Chainsaw - The ultimate end to end testing tool!)。これから利用者が増え、事例が蓄積することで、より洗練されたベストプラクティスが共有されていくでしょう。
以上、Kyverno Chainsawの概要からセットアップ、テストの書き方と実行方法までを解説しました。Kubernetesポリシーやオペレーターの品質向上を目指すエンジニアにとって、Chainsawは強力な助っ人となるはずです。ぜひ自身の環境でも試し、効果的なE2Eテストの構築に役立ててください。 (Kyverno Chainsaw | Kyverno) (Kyverno Chainsaw - The ultimate end to end testing tool! | Kyverno)