Skip to Content
Jora K

7 mins read


Helm cheatsheet: work with context in named templates

Small note about working and managing context in include and template functions with named templates in Helm.


Overview

Let's look at a simple example of defining named templates using define, working with context in such templates, defining variables and passing them to a custom context.

Getting started

First, let's create a chart for experiments using the command

helm create helm-context-demo

Create separate yaml file for our experiments templates/test/test-helm.yaml. Now the project files we are interested in are represented by the following hierarchy

mychart/
  Chart.yaml
  values.yaml
  charts/
  templates/
    _helpers.tpl
    test/
      test-helm.yaml
  ...

Let's quickly recall the purpose of each file and folder:

  • Chart.yaml - defines the chart and its metadata.
  • values.yaml - contains default values for the chart.
  • templates/ - directory intended for template files: all files in this directory are rendered into the output.
  • _helpers.tpl - file may contain named templates that can be reused in templates.

Call rendering using the following commands

helm template . # render all templates from /templates
helm template  -s templates/tests/test-helm.yaml . # render only one file

Create a named template

Create a simple named template using define, and then use it in test-helm.yaml.

{{ define "my.first.template" }}
simple_key: simple_value
chart_name: {{ .Chart.name }}
{{end}}

It is possible to use this template like that {{ include "my.first.template" . }}. In this case, we pass the current context. You can also override the context by passing, for example, an empty string {{ include "my.first.template" "" }}. In this case, the render call will result in an error. You can pass any variable or any object into the context and then work with it, for example:

{{ include "my.first.template" (dict "a" "a_val") }}

Or using variable

{{ $custom_context := (dict "a" "a_val") }}
{{ include "my.first.template" $custom_context }}

The result of the template call can also be saved as a string into a variable, and then its value can be output:

{{ $custom_context := (dict "a" "a_val") }}
{{ $result := (include "my.first.template" $custom_context) }}
{{ $result }}

You can modify the template so that it runs without errors every time you use it by adding checks for an empty value in the context

{{- define "my.first.template" -}}
simple_key: simple_value
{{- if and (not (empty .)) (not (empty .Chart))  }}
chart_name: {{ .Chart.Name }}
{{- end }}
{{- if and (not (empty .)) (not (empty .a))  }}
a: {{ .a }}
{{- end }}
{{- end -}}

Now when calling the example with $custom_context the following template will be generated

---
# Source: helm-context/templates/tests/test-helm.yaml
simple_key: simple_value
a: a_val

NOTE: Syntax `{{-`

The syntax of curly braces in template declarations can be modified using special characters to instruct the template engine to strip whitespace. {{- (with a dash and space added) indicates that whitespace should be stripped from the left, while -}} means it should be stripped from the right. For example, if we didn't use a dash in the previous example next to if, we would get output like this

---
# Source: helm-context/templates/tests/test-helm.yaml
simple_key: simple_value
 
a: a_val

Context, what it contains, how to override

The context in Helm templates represents a collection of available variables and objects that can be used in templates to generate Kubernetes configuration. It includes a global context, which is available in all chart templates, as well as local contexts that can be defined within specific templates. The global context in Helm is available by default in each template. In this context, the following objects are available by default: .Values, .Release, .Chart, .Capabilities, .Files, .Template. Using a dot, you can access objects within the current context. By passing . to a template, we pass the current context to the template at a lower level. If the context is not changed in the template, then using a dot will give the child template access to the parent context. The context can be overridden by passing your own variable. In this case, access to all default objects of the global template will be lost. An exception is the context inside the with and range functions: in such templates, the global context can be visible through $. As I showed earlier, you can override the context in a template like this: {{ include "my.first.template" (dict "a" "a_val") }}. After this call, the .Chart object will not be accessible to the template.

Variables in context

Here is an example of defining a variable inside a template.

{{- define "my.second.template" -}}
{{ $var := "my string var" }}
x: {{ $var | quote }}
{{- end -}}
---
# Source: helm-context/templates/tests/test-helm2.yaml
x: "my string var"

Variables exist only in the context of the template - inside define. You cannot use a variable declared outside a template definition block, but you can pass the variable to the context.

{{- define "my.second.template" -}}
{{ $var := "my string var" }}
x: {{ $var | quote }}
nested: {{ include "my.second.template.nested" (dict "var" $var) | nindent 2 }}
{{- end -}}
 
 
{{- define "my.second.template.nested" -}}
x: {{ .var | quote }}
isNested: true
{{- end -}}

Ouput

# Source: helm-context/templates/tests/test-helm2.yaml
x: "my string var"
nested:
  x: "my string var"
  isNested: true

However, now in the nested template, no objects are available: neither .Values, nor .Chart, nor others. To gain access to them, you can explicitly pass the necessary objects into the context, for example:

{{- define "my.second.template" -}}
{{ $var := "my string var" }}
x: {{ $var | quote }}
nested: {{ include "my.second.template.nested" (dict "var" $var "Chart" .Chart) | nindent 2 }}
{{- end -}}
 
 
{{- define "my.second.template.nested" -}}
x: {{ .var | quote }}
isNested: true
chartName: {{ .Chart.Name }}
{{- end -}}
# Source: helm-context/templates/tests/test-helm2.yaml
x: "my string var"
nested:
  x: "my string var"
  isNested: true
  chartName: helm-context

You cannot modify the existing context - it is immutable.

Let's consider an example using .Values, for this add such a list to values.yaml.

hosts:
  - "www.example0.com"
  - "www.example1.com"
  - "www.example2.com"

Let's define a template that creates server configurations using a random identifier and a list of hosts specified in the .Values.hosts variable. Each server configuration includes a unique identifier, composed of the generated identifier in the parent template and the host's index, along with the host itself.

{{- define "my.third.template" -}}
{{ $id := (randAlphaNum 5) }}
id: {{ $id | quote }}
servers: {{ include "my.third.template.servers" (dict "id" $id "Values" .Values) }}
{{- end -}}
 
{{- define "my.third.template.servers" -}}
{{- $hosts := .Values.hosts -}}
{{- range $i, $host := $hosts }}
- id: {{ $.id }}-{{ $i }}
  host: {{ $host }}
{{- end }}
{{- end -}}

Output:

# Source: helm-context/templates/tests/test-helm3.yaml
id: "pgm7b"
servers:
  - id: pgm7b-0
    host: www.example0.com
  - id: pgm7b-1
    host: www.example1.com
  - id: pgm7b-2
    host: www.example2.com

Profit!

All code samples provided in this article can be found in the repository. If you like this article, please give a star ⭐ to the repo and follow me on GitHub to be notified about new lessons and content.

More info in Helm documentation: