mirror of https://github.com/helm/helm
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
345 lines
11 KiB
345 lines
11 KiB
# Named Templates
|
|
|
|
It is time to move beyond one template, and begin to create others. In this section, we will see how to define _named templates_ in one file, and then use them elsewhere. A _named template_ (sometimes called a _partial_ or a _subtemplate_) is simply a template defined inside of a file, and given a name. We'll see two ways to create them, and a few different ways to use them.
|
|
|
|
In the "Conditionals and Loops" section we introduced three actions for declaring and managing templates: `define`, `template`, and `block`. In this section, we'll cover those three actions, and also introduce a special-purpose `include` function that works similarly to the `template` action.
|
|
|
|
## Partials and `_` files
|
|
|
|
So far, we've used one file, and that one file has contained a single template. But Helm's template language allows you to create named embedded templates, that can be accessed by name elsewhere.
|
|
|
|
Before we get to the nuts-and-bolts of writing those templates, there is file naming convention that deserves mention:
|
|
|
|
- Most files in `templates/` are treated as if they contain Kubernetes manifests
|
|
- The `NOTES.txt` is one exception
|
|
- But files whose name begins with an underscore (`_`) are assumed to _not_ have a manifest inside. These files are not rendered to Kubernetes object definitions, but are available everywhere within other chart templates for use.
|
|
|
|
These files are used to store partials and helpers. In fact, when we first created `mychart`, we saw a file called `_helpers.tpl`. That file is the default location for template partials.
|
|
|
|
## Declaring and using templates with `define` and `template`
|
|
|
|
The `define` action allows us to create a named template inside of a template file. Its syntax goes like this:
|
|
|
|
```yaml
|
|
{{ define "MY_NAME" }}
|
|
# body of template here
|
|
{{ end }}
|
|
```
|
|
|
|
For example, we can define a template to encapsulate a Kubernetes block of labels:
|
|
|
|
```yaml
|
|
{{- define "my_labels" }}
|
|
labels:
|
|
generator: helm
|
|
date: {{ now | htmlDate }}
|
|
{{- end }}
|
|
```
|
|
|
|
Now we can embed this template inside of our existing ConfigMap, and then include it with the `template` action:
|
|
|
|
```yaml
|
|
{{- define "my_labels" }}
|
|
labels:
|
|
generator: helm
|
|
date: {{ now | htmlDate }}
|
|
{{- end }}
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: {{ .Release.Name }}-configmap
|
|
{{- template "my_labels" }}
|
|
data:
|
|
myvalue: "Hello World"
|
|
{{- range $key, $val := .Values.favorite }}
|
|
{{ $key }}: {{ $val | quote }}
|
|
{{- end }}
|
|
```
|
|
|
|
When the template engine reads this file, it will store away the reference to `my_labels` until `template "my_labels"` is called. Then it will render that template inline. So the result will look like this:
|
|
|
|
```yaml
|
|
# 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"
|
|
```
|
|
|
|
Conventionally, Helm charts put these templates inside of a partials file, usually `_helpers.tpl`. Let's move this function there:
|
|
|
|
```yaml
|
|
{{/* Generate basic labels */}}
|
|
{{- define "my_labels" }}
|
|
labels:
|
|
generator: helm
|
|
date: {{ now | htmlDate }}
|
|
{{- end }}
|
|
```
|
|
|
|
By convention, `define` functions should have a simple documentation block (`{{/* ... */}}`) describing what they do.
|
|
|
|
Even though this definition is in `_helpers.tpl`, it can still be accessed in `configmap.yaml`:
|
|
|
|
```yaml
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: {{ .Release.Name }}-configmap
|
|
{{- template "my_labels" }}
|
|
data:
|
|
myvalue: "Hello World"
|
|
{{- range $key, $val := .Values.favorite }}
|
|
{{ $key }}: {{ $val | quote }}
|
|
{{- end }}
|
|
```
|
|
|
|
There is one _really important detail_ to keep in mind when naming templates: **template names are global**. If you declare two templates with the same name, whichever one is loaded last will be the one used. Because templates in subcharts are compiled together with top-level templates, you should be careful to name your templates with chart-specific names.
|
|
|
|
One popular naming convention is to prefix each defined template with the name of the chart: `{{ define "mychart.labels" }}` or `{{ define "mychart_labels" }}`.
|
|
|
|
## Setting the scope of a template
|
|
|
|
In the template we defined above, we did not use any objects. We just used functions. Let's modify our defined template to include the chart name and chart version:
|
|
|
|
```yaml
|
|
{{/* Generate basic labels */}}
|
|
{{- define "my_labels" }}
|
|
labels:
|
|
generator: helm
|
|
date: {{ now | htmlDate }}
|
|
chart: {{ .Chart.Name }}
|
|
version: {{ .Chart.Version }}
|
|
{{- end }}
|
|
```
|
|
|
|
If we render this, the result will not be what we expect:
|
|
|
|
```yaml
|
|
# Source: mychart/templates/configmap.yaml
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: moldy-jaguar-configmap
|
|
labels:
|
|
generator: helm
|
|
date: 2016-11-02
|
|
chart:
|
|
version:
|
|
```
|
|
|
|
What happened to the name and version? They weren't in the scope for our defined template. When a named template (created with `define`) is rendered, it will receive the scope passed in by the `template` call. In our example, we included the template like this:
|
|
|
|
```yaml
|
|
{{- template "my_labels" }}
|
|
```
|
|
|
|
No scope was passed in, so within the template we cannot access anything in `.`. This is easy enough to fix, though. We simply pass a scope to the template:
|
|
|
|
```yaml
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: {{ .Release.Name }}-configmap
|
|
{{- template "my_labels" . }}
|
|
```
|
|
|
|
Note that we pass `.` at the end of the `template` call. We could just as easily pass `.Values` or `.Values.favorite` or whatever scope we want. But what we want is the top-level scope.
|
|
|
|
Now when we execute this template with `helm install --dry-run --debug ./mychart`, we get this:
|
|
|
|
```yaml
|
|
# Source: mychart/templates/configmap.yaml
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: plinking-anaco-configmap
|
|
labels:
|
|
generator: helm
|
|
date: 2016-11-02
|
|
chart: mychart
|
|
version: 0.1.0
|
|
```
|
|
|
|
Now `{{ .Chart.Name }}` resolves to `mychart`, and `{{ .Chart.Version }}` resolves to `0.1.0`.
|
|
|
|
## Creating override-able sections with `block`
|
|
|
|
Say we want to create a template in our `_helpers.tpl` file, but then override part of its behavior in our template. This is what blocks are for. Sometimes we don't want to just insert a template with `template`, but we want to sketch out a default and let another template override our default. This makes it possible for one chart to define a base template, but allow another chart to strategically override some of its behavior.
|
|
|
|
Blocks are declared like this:
|
|
|
|
```yaml
|
|
{{ block "NAME" PIPELINE }}
|
|
{{ end }}
|
|
```
|
|
|
|
Here, "NAME" is the name that a `define` block can use to override it, and PIPELINE is the pipeline that will set the scope. So let's rewrite our `labels:` section to use this strategy. We'll create a basic labels section in our `_helpers.tpl` file, but add some extra labels in the `configmap.yaml` template.
|
|
|
|
Let's start with `_helpers.tpl`:
|
|
|
|
```yaml
|
|
{{- define "my_labels" }}
|
|
labels:
|
|
chart: {{ .Chart.Name }}
|
|
version: {{ .Chart.Version }}
|
|
{{ block "my_extra_labels" . }}extras: false{{ end }}
|
|
{{- end `u}}
|
|
```
|
|
|
|
Inside of our `my_labels` template, we now declare a block called `my_extra_labels`. By default, this section will have one extra label: `extras: false`. If we were to execute this using the same `configmap.yaml` file from last time, we'd get this:
|
|
|
|
```yaml
|
|
# Source: mychart/templates/configmap.yaml
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: tinseled-womba-configmap
|
|
labels:
|
|
chart: mychart
|
|
version: 0.1.0
|
|
extras: false
|
|
data:
|
|
myvalue: "Hello World"
|
|
drink: "coffee"
|
|
food: "pizza"
|
|
```
|
|
|
|
But inside of our `configmap.yaml` template, we can override `my_extra_labels`:
|
|
|
|
```yaml
|
|
{{- define "my_extra_labels" }}chart: {{ .Chart.Name }}{{ end -}}
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: {{ .Release.Name }}-configmap
|
|
{{- template "my_labels" . }}
|
|
data:
|
|
myvalue: "Hello World"
|
|
{{- range $key, $val := .Values.favorite }}
|
|
{{ $key }}: {{ $val | quote }}
|
|
{{- end }}
|
|
```
|
|
|
|
On the first line, we redefine `my_extra_labels` to include `chart: {{ .Chart.Name }}`. If we
|
|
run this, we will get:
|
|
|
|
```yaml
|
|
# Source: mychart/templates/configmap.yaml
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: ignorant-scorp-configmap
|
|
labels:
|
|
chart: mychart
|
|
version: 0.1.0
|
|
chart: mychart
|
|
data:
|
|
myvalue: "Hello World"
|
|
drink: "coffee"
|
|
food: "pizza"
|
|
```
|
|
|
|
Gone is the `extras: false` section, since that part of the template is now overridden by our new template, which placed `chart: mychart` into the output.
|
|
|
|
Blocks are not frequently used in Helm charts. But they do provide one mechanism for creating "abstract" charts, and then selectively overriding parts of the abstract template with concrete implementations.
|
|
|
|
## The `include` function
|
|
|
|
Say we've defined a simple template that looks like this:
|
|
|
|
```
|
|
{{- define "mychart_app" -}}
|
|
app_name: {{ .Chart.Name }}
|
|
app_version: "{{ .Chart.Version} }+{{ .Release.Time.Seconds }}"
|
|
{{- end -}}
|
|
```
|
|
|
|
Now say I want to insert this both into the `labels:` section of my template, and also the `data:` section:
|
|
|
|
```yaml
|
|
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" . }}
|
|
|
|
```
|
|
|
|
The output will not be what we expect:
|
|
|
|
```yaml
|
|
# Source: mychart/templates/configmap.yaml
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: measly-whippet-configmap
|
|
labels:
|
|
app_name: mychart
|
|
app_version: "0.1.0+1478129847"
|
|
data:
|
|
myvalue: "Hello World"
|
|
drink: "coffee"
|
|
food: "pizza"
|
|
app_name: mychart
|
|
app_version: "0.1.0+1478129847"
|
|
```
|
|
|
|
Note that the indentation on `app_version` is wrong in both places. Why? Because the template that is substituted in has the text aligned to the right. Because `template` is an action, and not a function, there is no way to pass the output of a `template` call to other functions; the data is simply inserted inline.
|
|
|
|
To work around this case, Helm provides an alternative to `template` that will import the contents of a template into the present pipeline where it can be passed along to other functions in the pipeline.
|
|
|
|
Here's the example above, corrected to use `indent` to indent the `mychart_app` template correctly:
|
|
|
|
```yaml
|
|
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 }}
|
|
```
|
|
|
|
Now the produced YAML is correctly indented for each section:
|
|
|
|
```yaml
|
|
# Source: mychart/templates/configmap.yaml
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: edgy-mole-configmap
|
|
labels:
|
|
app_name: mychart
|
|
app_version: "0.1.0+1478129987"
|
|
data:
|
|
myvalue: "Hello World"
|
|
drink: "coffee"
|
|
food: "pizza"
|
|
app_name: mychart
|
|
app_version: "0.1.0+1478129987"
|
|
```
|
|
|
|
> It is considered preferable to use `include` over `template` in Helm templates simply so that the output formatting can be handled better for YAML documents.
|
|
|
|
Sometimes we want to import content, but not as templates. That is, we want to import files verbatim. We can achieve this by accessing files through the `.Files` object described in the next section.
|