はじめに
Kubernetesで Kyverno Policyを使う際に難しいと感じたのが、配列(リスト)フィールドの書き換えです。
List型で複数の値を指定するプロパティに対して、patchStrategicMergeで配列型を指定して更新しようとすると、「型が違う」というエラーや、パッチがうまく反映されないなどのエラーになりました。
今回の目的は、異なるデータソースから取得した2つの配列型と1つの文字列を、1つの配列に結合して、最終的に[aaa, bbb, ccc, ddd]という単一のリストとしてリソースに書き込むことでした。
| データ名 | 元データ(例) | 必要な型 |
|---|---|---|
arrayOne |
["aaa", "bbb"] |
配列 |
arrayTwo |
["ccc"] |
配列 |
stringD |
"dddd" |
文字列 |
今回のケースとしては、AuthorizationPolicyのnotValuesでヘッダーの特定の値を指定し、その値でない場合はリクエストをDENYするといったことをしたく、Secretsから取得した複数の値を指定する必要がありました。
エラー原因
KyvernoのpatchStrategicMergeは、Mutateパッチのデータ型のチェックが厳格なようです。
NGパターン1: concat / flatten 関数の使用
KyvernoPolicy内ではJMESPathによって柔軟な文字列処理が可能そうですが、
jmesPath: "concat(arrayOne, arrayTwo)" や ... | flatten(@) といった関数を使うと、Kyvernoは加工されてできた配列を、配列型だと認識できないようです。
# 失敗する書き方
jmesPath: "[arrayOne[*], arrayTwo[*], stringD] | flatten(@)"
内部的に、Kyvernoがパッチを生成する際に必要な「型情報」が失われてしまい、Mutateが実行されなくなります。
NGパターン2: YAMLリストでの展開
mutateブロックで以下のように記述すると、Mutateは失敗します。
annotaionsに複数の値を設定する際のNG例です。
# 失敗する書き方
annotations:
- "{{ arrayOne[] }}"
- "{{ arrayTwo[] }}"
Kyvernoは、patchStrategicMergeにおいて単一行の式で配列全体を置き換えることを期待しており、このような複数行のリスト展開は正しく処理できません。
解決方法
この問題を解決する方法は、Kyvernoが信頼できるシンプルなJMESPath構文だけを使って配列を結合しMutateすることです。
Kyvernoはconcat関数を信頼しませんが、JMESPathの配列リテラル ([...]) 内で要素を展開する構文 (array[]) は問題なく利用できました。
# 成功する context 変数の定義
context:
# 1. 最初の配列
- name: arrayOne
variable:
value: ["aaa", "bbb"]
# 2. 2番目の配列
- name: arrayTwo
variable:
value: ["ccc"]
# 3. 単一文字列
- name: stringD
variable:
value: dddd
# 4. ★★★成功する配列の結合★★★
- name: combinedArray
variable:
# 配列リテラル [ ] の中で、arrayOne[] と arrayTwo[] の要素を広げ、
# 最後に stringD を文字列として追加する
jmesPath: "[arrayOne[], arrayTwo[], '{{ stringD }}']"
-
arrayOne[] と arrayTwo[]...
arrayOneの中身(aaa,bbb)とarrayTwoの中身(ccc)をフラットな要素に展開します。 -
'{{ stringD }}'... 単なる文字列である
stringDは、Goテンプレートとして評価され、配列の最後の要素として追加されます -
結果...
combinedArrayには、[aaa, bbb, ccc, ddd]という、Kyvernoが信頼できるネストのない一次元配列が生成されます
mutateブロックでは、このcombinedArrayを使い配列全体を置き換えます。
mutate:
patchStrategicMerge:
metadata:
annotations:
# 単一行の式と、末尾の [] オペレーターによる配列展開
combined-list-key: "{{ combinedArray[] }}"
patchStrategicMergeは、この[]を見ると、「combinedArray変数は配列である」と認識し、「この変数の内容全体(4つの要素)で、ターゲットのcombined-list-keyフィールドの値を置き換える」というパッチを生成してくれます。
最終的なマニフェスト(コード全文)
異なる複数の配列を結合し、patchStrategicMergeの型チェックを回避してリスト型に反映させる方法を紹介しました。
Context内でフラットな一次元配列を作成し、mutateブロックでそれを[]オペレータでList型に展開することで実現できました。
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: mutate-combined-arrays
spec:
rules:
- name: combine-and-mutate
context:
# 1. 最初の配列: [aaa, bbb]
- name: arrayOne
variable:
value: ["aaa", "bbb"]
# 2. 2番目の配列: [ccc]
- name: arrayTwo
variable:
value: ["ccc"]
# 3. 単一文字列: ddd
- name: stringD
variable:
value: ddd
# 4. 4つの要素を結合した配列を作成する
- name: combinedArray
variable:
jmesPath: "[arrayOne[], arrayTwo[], '{{ stringD }}']"
match:
any:
- resources:
kinds:
- Pod
selector:
matchLabels:
run: nginx
mutate:
patchStrategicMerge:
metadata:
annotations:
# []オペレータで展開し[aaa, bbb, ccc, ddd] をリストに置き換える
combined-list-key: "{{ combinedArray[] }}"
Kyverno Playground の紹介
Kyverno Policyを書く際にハマった場合は、Kyverno Playgroundが非常に便利です。
いったん最小構成で記載してMutateの挙動を確認することをお勧めします。