• 命名模板
    • 部分和_文件
    • 声明和使用DEFINE,TEMPALTE函数使用模板
    • 设置模板的范围
    • include函数

    命名模板

    现在是时候超越一个模板,并开始创建其他模板。 在本节中,我们将看到如何在一个文件中定义命名模板,然后在别处使用它们。 命名模板(有时称为部分模板或子模板)只是一个在文件内定义的模板,并给定了一个名称。 我们将看到两种创建方法,以及几种不同的使用方法。

    在“流程控制”部分,我们介绍了三种用于声明和管理模板的操作:define,template和block。 在本节中,我们将介绍这三个操作,并介绍一个与模板操作类似的特殊用途包含功能。

    部分和_文件

    到目前为止,我们已经使用了一个文件,而且一个文件包含一个模板。 但Helm的模板语言允许您创建指定的嵌入模板,可以通过其他名称访问。

    在开始编写这些模板之前,有一些文件命名约定值得一提:

    • 模板中的大多数文件被视为包含Kubernetes声明
    • NOTES.txt是一个例外
    • 但是,名称以下划线(_)开头的文件被假定为没有内部声明。 这些文件不会呈现给Kubernetes对象定义,而是在其他chart模板中随处可用以供使用。

    这些文件用于存储部分和帮助程序。 事实上,当我们第一次创建mychart时,我们看到一个名为_helpers.tpl的文件。 该文件是模板部分的默认位置。

    声明和使用DEFINE,TEMPALTE函数使用模板

    define操作允许我们在模板文件内创建一个命名模板。 它的语法如下所示:

    1. {{ define "MY_NAME" }}
    2. # body of template here
    3. {{ end }}

    例如,我们可以define一个模板来封装一个Kubernetes标签块:

    1. {{- define "my_labels" }}
    2. labels:
    3. generator: helm
    4. date: {{ now | htmlDate }}
    5. {{- end }}

    现在我们可以将此模板嵌入到现有的ConfigMap中,然后将其包含在模板操作中:

    1. {{- define "my_labels" }}
    2. labels:
    3. generator: helm
    4. date: {{ now | htmlDate }}
    5. {{- end }}
    6. apiVersion: v1
    7. kind: ConfigMap
    8. metadata:
    9. name: {{ .Release.Name }}-configmap
    10. {{- template "my_labels" }}
    11. data:
    12. myvalue: "Hello World"
    13. {{- range $key, $val := .Values.favorite }}
    14. {{ $key }}: {{ $val | quote }}
    15. {{- end }}

    当模板引擎读取该文件时,它将保存对my_labels的引用,直到调用模板“my_labels”。 然后它将内联呈现该模板。 所以结果如下所示:

    1. # Source: mychart/templates/configmap.yaml
    2. apiVersion: v1
    3. kind: ConfigMap
    4. metadata:
    5. name: running-panda-configmap
    6. labels:
    7. generator: helm
    8. date: 2016-11-02
    9. data:
    10. myvalue: "Hello World"
    11. drink: "coffee"
    12. food: "pizza"

    传统上,Helm chart将这些模板放在partials文件中,通常是_helpers.tpl。 让我们在这里移动这个功能:

    1. {{/* Generate basic labels */}}
    2. {{- define "my_labels" }}
    3. labels:
    4. generator: helm
    5. date: {{ now | htmlDate }}
    6. {{- end }}

    按照惯例,定义函数应该有一个简单的文档块({{/ /}})来描述它们的功能。

    尽管这个定义在_helpers.tpl中,但它仍然可以在configmap.yaml中访问:

    1. apiVersion: v1
    2. kind: ConfigMap
    3. metadata:
    4. name: {{ .Release.Name }}-configmap
    5. {{- template "my_labels" }}
    6. data:
    7. myvalue: "Hello World"
    8. {{- range $key, $val := .Values.favorite }}
    9. {{ $key }}: {{ $val | quote }}
    10. {{- end }}

    命名模板时要记住一个非常重要的细节:模板名称是全局的。 如果您声明两个具有相同名称的模板,则无论最后加载哪一个模板都将是可使用的模板。 由于子chart中的模板与顶级模板一起编译,因此应该小心地使用特定于chart的名称命名模板。

    一种流行的命名约定是为每个define的模板添加chart名称:

    1. {{ define "mychart.labels" }} or {{ define "mychart_labels" }}.

    设置模板的范围

    在我们上面定义的模板中,我们没有使用任何对象。 我们只是使用函数。 让我们修改我们定义的模板以包含chart名称和chart版本:

    1. {{/* Generate basic labels */}}
    2. {{- define "my_labels" }}
    3. labels:
    4. generator: helm
    5. date: {{ now | htmlDate }}
    6. chart: {{ .Chart.Name }}
    7. version: {{ .Chart.Version }}
    8. {{- end }}

    如果我们这样做,结果将不会是我们所期望的:

    1. # Source: mychart/templates/configmap.yaml
    2. apiVersion: v1
    3. kind: ConfigMap
    4. metadata:
    5. name: moldy-jaguar-configmap
    6. labels:
    7. generator: helm
    8. date: 2016-11-02
    9. chart:
    10. version:

    名称和版本发生了什么变化? 他们不在我们定义的模板的范围内。 当一个已命名的模板(由define创建)被呈现时,它将接收由模板调用传入的作用域。 在我们的例子中,我们包含了这样的模板:

    1. {{- template "my_labels" }}

    没有范围被传入,所以在模板中,我们无法访问任何内容.(句号).但这很容易解决。 我们只需将范围传递给模板:

    1. apiVersion: v1
    2. kind: ConfigMap
    3. metadata:
    4. name: {{ .Release.Name }}-configmap
    5. {{- template "my_labels" . }}

    请注意,在模板调用结束位置我们使用了.(句号)。 我们可以轻松地通过.Values或.Values.favorite或任何我们想要的范围。 但是我们想要的是顶级范围。

    现在当我们用helm install —dry-run —debug ./mychart执行这个模板时,我们得到了这个:

    1. # Source: mychart/templates/configmap.yaml
    2. apiVersion: v1
    3. kind: ConfigMap
    4. metadata:
    5. name: plinking-anaco-configmap
    6. labels:
    7. generator: helm
    8. date: 2016-11-02
    9. chart: mychart
    10. version: 0.1.0

    现在{{.Chart.Name}}解析为mychart,{{.Chart.Version}}解析为0.1.0。

    include函数

    假设我们已经定义了一个如下所示的简单模板:

    1. {{- define "mychart_app" -}}
    2. app_name: {{ .Chart.Name }}
    3. app_version: "{{ .Chart.Version }}+{{ .Release.Time.Seconds }}"
    4. {{- end -}}

    现在说我想将它插入到我的模板的lables:部分以及data:部分:

    1. apiVersion: v1
    2. kind: ConfigMap
    3. metadata:
    4. name: {{ .Release.Name }}-configmap
    5. labels:
    6. {{ template "mychart_app" .}}
    7. data:
    8. myvalue: "Hello World"
    9. {{- range $key, $val := .Values.favorite }}
    10. {{ $key }}: {{ $val | quote }}
    11. {{- end }}
    12. {{ template "mychart_app" . }}

    结果将不会是我们所期望的:

    1. # Source: mychart/templates/configmap.yaml
    2. apiVersion: v1
    3. kind: ConfigMap
    4. metadata:
    5. name: measly-whippet-configmap
    6. labels:
    7. app_name: mychart
    8. app_version: "0.1.0+1478129847"
    9. data:
    10. myvalue: "Hello World"
    11. drink: "coffee"
    12. food: "pizza"
    13. app_name: mychart
    14. app_version: "0.1.0+1478129847"

    请注意,app_version上的缩进在两个地方都是错误的。 为什么? 因为被替换的模板将文本对齐到右侧。 由于模板是一个动作,而不是一个函数,因此无法将模板调用的输出传递给其他函数; 数据只是内嵌插入。

    为了解决这个问题,Helm提供了一个模板的替代方法,它可以将模板的内容导入到当前的管道中,并将它传递给管道中的其他功能。

    以上是上面的示例,更正为使用缩进来正确缩进mychart_app模板:

    1. apiVersion: v1
    2. kind: ConfigMap
    3. metadata:
    4. name: {{ .Release.Name }}-configmap
    5. labels:
    6. {{ include "mychart_app" . | indent 4 }}
    7. data:
    8. myvalue: "Hello World"
    9. {{- range $key, $val := .Values.favorite }}
    10. {{ $key }}: {{ $val | quote }}
    11. {{- end }}
    12. {{ include "mychart_app" . | indent 2 }}

    现在生成的YAML对每个部分都正确缩进:

    1. # Source: mychart/templates/configmap.yaml
    2. apiVersion: v1
    3. kind: ConfigMap
    4. metadata:
    5. name: edgy-mole-configmap
    6. labels:
    7. app_name: mychart
    8. app_version: "0.1.0+1478129987"
    9. data:
    10. myvalue: "Hello World"
    11. drink: "coffee"
    12. food: "pizza"
    13. app_name: mychart
    14. app_version: "0.1.0+1478129987"

    在Helm模板中使用include 导入外部模板被认为是最好的方式,这样可以更好地为YAML文档处理输出格式。

    有时我们想要导入内容,但不是作为模板。 也就是说,我们要逐字输入文件。 我们可以通过下一节中介绍的.Files对象访问文件来实现这一点。