名前付きテンプレート

単一のテンプレートを超えて、他のテンプレートを作成する時が来ました。このセクションでは、1つのファイルに*名前付きテンプレート*を定義し、他の場所でそれらを使用する方法を説明します。 *名前付きテンプレート*(*パーシャル*または*サブテンプレート*と呼ばれることもあります)とは、ファイル内に定義され、名前が付けられたテンプレートのことです。作成方法を2つ、使用方法をいくつか紹介します。

フロー制御セクションでは、テンプレートを宣言および管理するための3つのアクション、`define`、`template`、`block`を紹介しました。このセクションでは、これら3つのアクションについて説明し、`template`アクションと同様に機能する特別な用途の`include`関数も紹介します。

テンプレートに名前を付ける際の重要な注意点:**テンプレート名はグローバルです**。同じ名前のテンプレートを2つ宣言した場合、最後に読み込まれた方が使用されます。サブチャートのテンプレートはトップレベルのテンプレートと一緒にコンパイルされるため、*チャート固有の名前*でテンプレートに名前を付けるように注意してください。

一般的な命名規則の1つは、定義された各テンプレートにチャートの名前をプレフィックスとして付けることです:`{{ define "mychart.labels" }}`。特定のチャート名をプレフィックスとして使用することにより、同じ名前のテンプレートを実装する2つの異なるチャートが原因で発生する可能性のある競合を回避できます。

この動作は、チャートの異なるバージョンにも適用されます。`mychart`バージョン`1.0.0`でテンプレートをある方法で定義し、`mychart`バージョン`2.0.0`で既存の名前付きテンプレートを変更した場合、最後に読み込まれた方が使用されます。チャートの名前にバージョンを追加することで、この問題を回避できます:`{{ define "mychart.v1.labels" }}`と`{{ define "mychart.v2.labels" }}`。

パーシャルと`_`ファイル

これまでは、1つのファイルを使用しており、そのファイルには単一のテンプレートが含まれていました。しかし、Helmのテンプレート言語では、名前付きの埋め込みテンプレートを作成し、他の場所で名前でアクセスできます。

これらのテンプレートを作成する具体的な方法を説明する前に、言及する価値のあるファイル命名規則があります

  • `templates/`にあるほとんどのファイルは、Kubernetesマニフェストが含まれているかのように扱われます
  • `NOTES.txt`は例外の1つです
  • ただし、名前がアンダースコア(`_`)で始まるファイルは、内部にマニフェストが*ない*と見なされます。これらのファイルはKubernetesオブジェクト定義にはレンダリングされませんが、他のチャートテンプレート内でどこでも使用できます。

これらのファイルは、パーシャルとヘルパーを格納するために使用されます。実際、最初に`mychart`を作成したとき、`_helpers.tpl`というファイルが表示されました。このファイルは、テンプレートパーシャルのデフォルトの場所です。

`define`と`template`を使用したテンプレートの宣言と使用

`define`アクションを使用すると、テンプレートファイル内に名前付きテンプレートを作成できます。構文は次のとおりです

{{- define "MY.NAME" }}
  # body of template here
{{- end }}

たとえば、Kubernetesのラベルブロックをカプセル化するテンプレートを定義できます

{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}

これで、このテンプレートを既存のConfigMapに埋め込み、`template`アクションでインクルードできます

{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "mychart.labels" }}
data:
  myvalue: "Hello World"
  {{- range $key, $val := .Values.favorite }}
  {{ $key }}: {{ $val | quote }}
  {{- end }}

テンプレートエンジンがこのファイルを読み取ると、`template "mychart.labels"`が呼び出されるまで`mychart.labels`への参照を保存します。次に、そのテンプレートをインラインでレンダリングします。そのため、結果は次のようになります

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: running-panda-configmap
  labels:
    generator: helm
    date: 2016-11-02
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"

注:この例のように、`define`は`template`で呼び出されない限り出力を生成しません。

慣例により、Helmチャートはこれらのテンプレートをパーシャルファイル(通常は`_helpers.tpl`)に配置します。この関数をそこに移動しましょう

{{/* Generate basic labels */}}
{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}

慣例により、`define`関数には、その機能を説明する簡単なドキュメントブロック(`{{/* ... */}}`)が必要です。

この定義は`_helpers.tpl`にありますが、それでも`configmap.yaml`でアクセスできます

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "mychart.labels" }}
data:
  myvalue: "Hello World"
  {{- range $key, $val := .Values.favorite }}
  {{ $key }}: {{ $val | quote }}
  {{- end }}

前述のように、**テンプレート名はグローバルです**。このため、同じ名前のテンプレートが2つ宣言されている場合、最後に発生した方が使用されます。サブチャートのテンプレートはトップレベルのテンプレートと一緒にコンパイルされるため、*チャート固有の名前*でテンプレートに名前を付けることをお勧めします。一般的な命名規則は、定義された各テンプレートにチャートの名前をプレフィックスとして付けることです:`{{ define "mychart.labels" }}`。

テンプレートのスコープの設定

上記で定義したテンプレートでは、オブジェクトは使用していません。関数のみを使用しました。チャート名とチャートバージョンを含むように定義されたテンプレートを変更しましょう

{{/* Generate basic labels */}}
{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
    chart: {{ .Chart.Name }}
    version: {{ .Chart.Version }}
{{- end }}

これをレンダリングすると、次のようなエラーが発生します

$ helm install --dry-run moldy-jaguar ./mychart
Error: unable to build kubernetes objects from release manifest: error validating "": error validating data: [unknown object type "nil" in ConfigMap.metadata.labels.chart, unknown object type "nil" in ConfigMap.metadata.labels.version]

レンダリングされた内容を確認するには、`--disable-openapi-validation`を指定して再実行します:`helm install --dry-run --disable-openapi-validation moldy-jaguar ./mychart`。結果は期待どおりになりません

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: moldy-jaguar-configmap
  labels:
    generator: helm
    date: 2021-03-06
    chart:
    version:

名前とバージョンはどうなりましたか?定義されたテンプレートのスコープ内にありませんでした。名前付きテンプレート(`define`で作成)がレンダリングされると、`template`呼び出しによって渡されたスコープを受け取ります。この例では、次のようにテンプレートをインクルードしました

{{- template "mychart.labels" }}

スコープが渡されなかったため、テンプレート内では`.`の何もアクセスできません。ただし、これは簡単に修正できます。テンプレートにスコープを渡すだけです

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "mychart.labels" . }}

`template`呼び出しの最後に`.`を渡していることに注意してください。`.Values`または`.Values.favorite`、または必要なスコープを簡単に渡すこともできます。しかし、私たちが望んでいるのはトップレベルのスコープです。

これで、`helm install --dry-run --debug plinking-anaco ./mychart`でこのテンプレートを実行すると、次のようになります

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: plinking-anaco-configmap
  labels:
    generator: helm
    date: 2021-03-06
    chart: mychart
    version: 0.1.0

これで、`{{ .Chart.Name }}`は`mychart`に、`{{ .Chart.Version }}`は`0.1.0`に解決されます。

`include`関数

次のような単純なテンプレートを定義したとします

{{- define "mychart.app" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}"
{{- end -}}

これをテンプレートの`labels:`セクションと`data:`セクションの両方に挿入したいとします

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  labels:
    {{ template "mychart.app" . }}
data:
  myvalue: "Hello World"
  {{- range $key, $val := .Values.favorite }}
  {{ $key }}: {{ $val | quote }}
  {{- end }}
{{ template "mychart.app" . }}

これをレンダリングすると、次のようなエラーが発生します

$ helm install --dry-run measly-whippet ./mychart
Error: unable to build kubernetes objects from release manifest: error validating "": error validating data: [ValidationError(ConfigMap): unknown field "app_name" in io.k8s.api.core.v1.ConfigMap, ValidationError(ConfigMap): unknown field "app_version" in io.k8s.api.core.v1.ConfigMap]

レンダリングされた内容を確認するには、`--disable-openapi-validation`を指定して再実行します:`helm install --dry-run --disable-openapi-validation measly-whippet ./mychart`。出力は期待どおりになりません

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: measly-whippet-configmap
  labels:
    app_name: mychart
app_version: "0.1.0"
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"
app_name: mychart
app_version: "0.1.0"

`app_version`のインデントが両方の場所で間違っていることに注意してください。なぜでしょうか?置換されたテンプレートのテキストが左揃えになっているためです。`template`はアクションであり、関数ではないため、`template`呼び出しの出力を他の関数に渡す方法はありません。データはインラインに挿入されるだけです。

このケースを回避するために、Helmは`template`の代替手段を提供します。これにより、テンプレートの内容が現在のパイプラインにインポートされ、パイプライン内の他の関数に渡すことができます。

`indent`を使用して`mychart.app`テンプレートを正しくインデントするように修正された上記の例を次に示します

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  labels:
{{ include "mychart.app" . | indent 4 }}
data:
  myvalue: "Hello World"
  {{- range $key, $val := .Values.favorite }}
  {{ $key }}: {{ $val | quote }}
  {{- end }}
{{ include "mychart.app" . | indent 2 }}

これで、生成されたYAMLは各セクションで正しくインデントされます

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: edgy-mole-configmap
  labels:
    app_name: mychart
    app_version: "0.1.0"
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"
  app_name: mychart
  app_version: "0.1.0"

Helmテンプレートでは、YAMLドキュメントの出力フォーマットをより適切に処理するために、templateよりもincludeを使用することが推奨されます。

コンテンツをインポートしたいが、テンプレートとしてはインポートしたくない場合があります。つまり、ファイルをそのままインポートしたい場合があります。これは、次のセクションで説明する.Filesオブジェクトを介してファイルにアクセスすることで実現できます。