フロー制御

制御構造(テンプレート用語では「アクション」と呼ばれます)は、テンプレートの作成者であるあなたに、テンプレートの生成フローを制御する機能を提供します。 Helmのテンプレート言語は、以下の制御構造を提供します。

  • 条件付きブロックを作成するためのif/else
  • スコープを指定するためのwith
  • 「for each」スタイルのループを提供するrange

これらに加えて、名前付きテンプレートセグメントを宣言および使用するためのいくつかのアクションが提供されています。

  • defineは、テンプレート内に新しい名前付きテンプレートを宣言します。
  • templateは、名前付きテンプレートをインポートします。
  • blockは、特別な種類の埋め込み可能なテンプレート領域を宣言します。

このセクションでは、ifwith、およびrangeについて説明します。 その他については、このガイドの後半にある「名前付きテンプレート」セクションで説明します。

If/Else

最初に説明する制御構造は、テンプレートにテキストブロックを条件付きで含めるためのものです。 これはif/elseブロックです。

条件の基本構造は次のとおりです。

{{ if PIPELINE }}
  # Do something
{{ else if OTHER PIPELINE }}
  # Do something else
{{ else }}
  # Default case
{{ end }}

ここで、値ではなく*パイプライン*について説明していることに注意してください。 この理由は、制御構造は値を評価するだけでなく、パイプライン全体を実行できることを明確にするためです。

パイプラインは、値が以下の場合、*false*と評価されます。

  • ブール値のfalse
  • 数値のゼロ
  • 空の文字列
  • nil(空またはnull)
  • 空のコレクション(mapslicetupledictarray

その他すべての条件では、条件はtrueです。

ConfigMapに簡単な条件を追加してみましょう。飲み物がコーヒーに設定されている場合に、別の設定を追加します。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | default "tea" | quote }}
  food: {{ .Values.favorite.food | upper | quote }}
  {{ if eq .Values.favorite.drink "coffee" }}mug: "true"{{ end }}

前回の例ではdrink: coffeeをコメントアウトしたので、出力にはmug: "true"フラグは含まれません。 ただし、その行をvalues.yamlファイルに戻すと、出力は次のようになります。

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: eyewitness-elk-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  mug: "true"

空白の制御

条件付きを検討している間に、テンプレートで空白がどのように制御されるかを見てみましょう。 前の例を取り上げて、少し読みやすくフォーマットしてみましょう。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | default "tea" | quote }}
  food: {{ .Values.favorite.food | upper | quote }}
  {{ if eq .Values.favorite.drink "coffee" }}
    mug: "true"
  {{ end }}

最初は良さそうです。 しかし、テンプレートエンジンで実行すると、残念ながら次のような結果になります。

$ helm install --dry-run --debug ./mychart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/helm.sh/helm/_scratch/mychart
Error: YAML parse error on mychart/templates/configmap.yaml: error converting YAML to JSON: yaml: line 9: did not find expected key

何が起こったのでしょうか? 上記の空白のために、正しくないYAMLが生成されました。

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: eyewitness-elk-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
    mug: "true"

mugのインデントが間違っています。 その1行のインデントを外して、再実行してみましょう。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | default "tea" | quote }}
  food: {{ .Values.favorite.food | upper | quote }}
  {{ if eq .Values.favorite.drink "coffee" }}
  mug: "true"
  {{ end }}

送信すると、有効なYAMLが得られますが、まだ少し奇妙に見えます。

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: telling-chimp-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"

  mug: "true"

YAMLに空行がいくつかあることに注意してください。 なぜでしょうか? テンプレートエンジンが実行されると、{{}}の間の内容は*削除*されますが、残りの空白はそのまま残ります。

YAMLは空白に意味を割り当てるため、空白の管理は非常に重要になります。 幸いなことに、Helmテンプレートには、役立つツールがいくつかあります。

まず、テンプレート宣言の中括弧構文は、特殊文字を使用して変更することで、テンプレートエンジンに空白を削除するように指示できます。 {{-(ダッシュとスペースが追加されています)は、左側の空白を削除する必要があることを示し、-}}は右側の空白を使用する必要があることを意味します。 *注意してください! 改行も空白です!*

-とディレクティブの残りの部分の間にスペースがあることを確認してください。 {{- 3 }}は「左側の空白をトリミングして3を出力する」ことを意味し、{{-3 }}は「-3を出力する」ことを意味します。

この構文を使用すると、テンプレートを変更してこれらの改行を削除できます。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | default "tea" | quote }}
  food: {{ .Values.favorite.food | upper | quote }}
  {{- if eq .Values.favorite.drink "coffee" }}
  mug: "true"
  {{- end }}

この点を明確にするために、上記を調整し、このルールに従って削除される各空白を*に置き換えてみましょう。 行末の*は、削除される改行文字を示します。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | default "tea" | quote }}
  food: {{ .Values.favorite.food | upper | quote }}*
**{{- if eq .Values.favorite.drink "coffee" }}
  mug: "true"*
**{{- end }}

これを念頭に置いて、テンプレートをHelmで実行して結果を確認できます。

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: clunky-cat-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  mug: "true"

削除修飾子には注意してください。 次のようなことを誤って行うのは簡単です。

  food: {{ .Values.favorite.food | upper | quote }}
  {{- if eq .Values.favorite.drink "coffee" -}}
  mug: "true"
  {{- end -}}

これは、両側の改行が使用されるため、food: "PIZZA"mug: "true"が生成されます。

テンプレートでの空白制御の詳細については、公式のGoテンプレートドキュメントを参照してください。

最後に、テンプレートディレクティブの間隔をマスターしようとする代わりに、テンプレートシステムにインデント方法を指示する方が簡単な場合があります。 そのため、indent関数({{ indent 2 "mug:true" }})を使用すると便利な場合があります。

withを使用したスコープの変更

次に説明する制御構造は、withアクションです。 これは変数のスコープを制御します。 .は*現在のスコープ*への参照であることを思い出してください。 したがって、.Valuesはテンプレートに現在のスコープでValuesオブジェクトを見つけるように指示します。

withの構文は、単純なifステートメントに似ています。

{{ with PIPELINE }}
  # restricted scope
{{ end }}

スコープは変更できます。 withを使用すると、現在のスコープ(.)を特定のオブジェクトに設定できます。 たとえば、私たちは.Values.favoriteを使用してきました。 ConfigMapを書き直して、.スコープを.Values.favoriteを指すように変更してみましょう。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  {{- end }}

withの後にあるブロックは、PIPELINEの値が空でない場合にのみ実行されるため、前の演習のif条件は削除されたことに注意してください.

これで、.drink.foodを修飾せずに参照できるようになったことに注意してください。 これは、withステートメントが..Values.favoriteを指すように設定するためです。 {{ end }}の後、.は前のスコープにリセットされます。

ただし、注意が必要です。 制限されたスコープ内では、.を使用して親スコープから他のオブジェクトにアクセスすることはできません。 たとえば、これは失敗します。

  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  release: {{ .Release.Name }}
  {{- end }}

Release.Name.の制限されたスコープ内にないため、エラーが発生します。 ただし、最後の2行を入れ替えると、{{ end }}の後にスコープがリセットされるため、すべてが期待どおりに機能します。

  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  {{- end }}
  release: {{ .Release.Name }}

または、親スコープからオブジェクトRelease.Nameにアクセスするために$を使用できます。 $は、テンプレートの実行が開始されるとルートスコープにマップされ、テンプレートの実行中は変更されません。 次のようにしても機能します。

  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  release: {{ $.Release.Name }}
  {{- end }}

rangeを確認した後、上記のスコープの問題に対する1つの解決策を提供するテンプレート変数を見ていきます。

rangeアクションを使用したループ

多くのプログラミング言語は、forループ、foreachループ、または同様の機能メカニズムを使用してループをサポートしています。 Helmのテンプレート言語では、コレクションを反復処理するには、range演算子を使用します。

まず、values.yamlファイルにピザのトッピングのリストを追加してみましょう。

favorite:
  drink: coffee
  food: pizza
pizzaToppings:
  - mushrooms
  - cheese
  - peppers
  - onions

これで、pizzaToppingsのリスト(テンプレートではsliceと呼ばれます)ができました。 このリストをConfigMapに出力するようにテンプレートを変更できます。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  {{- end }}
  toppings: |-
    {{- range .Values.pizzaToppings }}
    - {{ . | title | quote }}
    {{- end }}    

親スコープからリストValues.pizzaToppingsにアクセスするために$を使用できます。 $は、テンプレートの実行が開始されるとルートスコープにマップされ、テンプレートの実行中は変更されません。 次のようにしても機能します.

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  toppings: |-
    {{- range $.Values.pizzaToppings }}
    - {{ . | title | quote }}
    {{- end }}    
  {{- end }}

toppings:リストを詳しく見てみましょう。 range関数は、pizzaToppingsリストを「範囲指定」(反復処理)します。 しかし、ここで興味深いことが起こります。 with.のスコープを設定するのと同じように、range演算子も設定します。 ループを繰り返すたびに、.は現在のピザのトッピングに設定されます。 つまり、最初は.mushroomsに設定されます。 2回目の反復では、cheeseに設定されます。 以降も同様です。

.の値をパイプラインに直接送信できるため、{{ . | title | quote }}を実行すると、.title(タイトルケース関数)に送信され、次にquoteに送信されます。 このテンプレートを実行すると、出力は次のようになります。

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: edgy-dragonfly-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "PIZZA"
  toppings: |-
    - "Mushrooms"
    - "Cheese"
    - "Peppers"
    - "Onions"    

この例では、トリッキーなことをしました。 toppings: |-行は、複数行の文字列を宣言しています。 したがって、トッピングのリストは実際にはYAMLリストではありません。 大きな文字列です。 なぜこのようなことをするのでしょうか? ConfigMapsのdataは、キーと値の両方が単純な文字列であるキー/値のペアで構成されているためです。 これについての詳細は、Kubernetes ConfigMapドキュメントを参照してください。 ただし、私たちにとっては、この詳細はそれほど重要ではありません。

YAMLの|-マーカーは複数行の文字列を受け取ります。これは、ここに示すように、マニフェスト内に大きなデータブロックを埋め込むための便利な手法です。

テンプレート内でリストをすばやく作成し、そのリストを反復処理できることが役立つ場合があります。Helmテンプレートには、これを簡単にするための関数tupleがあります。コンピュータサイエンスでは、タプルは固定サイズのリストのようなコレクションですが、任意のデータ型を持ちます。これは、tupleの使用方法を大まかに表しています。

  sizes: |-
    {{- range tuple "small" "medium" "large" }}
    - {{ . }}
    {{- end }}    

上記は以下を生成します

  sizes: |-
    - small
    - medium
    - large    

リストとタプルに加えて、rangeを使用して、キーと値を持つコレクション(mapdictなど)を反復処理できます。テンプレート変数を導入する次のセクションで、その方法を説明します。