🦾
conftest: 各 Deployment に対応する PodDisruptionBudget が存在するか
open-policy-agent/conftest を使って
「任意の Deployment には、それに対応する PodDisruptionBudget(=PDB) が存在しなければならない」 というポリシーを記述する。
バージョン
$ conftest --version
Version: 0.20.0
Commit: 8415206
Date: 2020-07-16T13:35:34Z
方針
conftest は Rego コード内の deny/violation/warn という3つの変数を調べて実行結果を決定する。そのため、ユーザーが記述する rule は最終的にすべてその3つの変数の値に帰着させることになる。
「Deployment と PDB が対応している」という性質をどう表現するかだが、これは spec.selector
が同じかどうかで判定することにする。
今回は、以下の3つの rule を作る。
- 「Deploymentに対応するPDBの数」と「Deploymentの数」を比較して、前者のほうが少なければ violation に要素を追加
- Deploymentに対応するPDBリスト
- Deploymentリスト
また今回は複数ドキュメントを一度に取り扱うのでconftestには--combine
オプションを渡す。
自分の場合はこんな感じの起動になる。conftestは起動時の設定によってinputの構造が変わってくるので注意。
$ kustomize app/overlays/production | conftest test -p validation.rego --combine -
作例
こんな感じになった。
package main
violation[msg] {
count(pdb) < count(deployment)
msg := sprintf("the number of PodDisruptionBudget is smaller than the number of Deployment. count(pdb)=%d, count(deployment)=%d", [count(pdb), count(deployment)])
}
pdb[pdb.metadata.name] {
resource := input[_]
resource[i].kind == "Deployment"
deploy := resource[i]
resource[j].kind == "PodDisruptionBudget"
pdb := resource[j]
deploy.spec.selector == pdb.spec.selector
}
deployment[name] {
resource := input[_]
resource[i].kind == "Deployment"
name := resource[i].metadata.name
}
ちなみにテストコードはこんな感じ。
test_pdb_is_matched_to_deploy {
input := [[
{
"kind": "Deployment",
"metadata": {
"name": "foo"
},
"spec": {
"selector": {
"matchLabels": {
"foo": "bar"
}
}
}
},
{
"kind": "PodDisruptionBudget",
"metadata": {
"name": "foo"
},
"spec": {
"selector": {
"matchLabels": {
"foo": "bar"
}
}
}
}
]]
count(violation) == 0 with input as input
}
test_pdb_is_not_matched_to_deploy {
input := [[
{
"kind": "Deployment",
"metadata": {
"name": "foo"
},
"spec": {
"selector": {
"matchLabels": {
"foo": "bar"
}
}
}
},
{
"kind": "PodDisruptionBudget",
"metadata": {
"name": "foo"
},
"spec": {
"selector": {
"matchLabels": {
"foo": "aaa"
}
}
}
}
]]
violation with input as input
}
test_pdb_is_smaller_than_deploy {
input := [[
{
"kind": "Deployment",
"metadata": {
"name": "foo"
},
"spec": {
"selector": {
"matchLabels": {
"foo": "bar"
}
}
}
},
{
"kind": "Deployment",
"metadata": {
"name": "fooo"
},
"spec": {
"selector": {
"matchLabels": {
"hoge": "fuga"
}
}
}
},
{
"kind": "PodDisruptionBudget",
"metadata": {
"name": "foo"
},
"spec": {
"selector": {
"matchLabels": {
"foo": "bar"
}
}
}
}
]]
violation with input as input
}
Discussion