Helm Templates: Difference between revisions
Line 50: | Line 50: | ||
=<span id='Template_Directive'></span>Template Directives= | =<span id='Template_Directive'></span>Template Directives= | ||
A template directive, sometimes also referred as tag, is enclosed in {{ and }} blocks, and it is recommended to pad the directive with space at its left and right. | A template directive, sometimes also referred as tag, is enclosed in <code>{{</code> and <code>}}</code> blocks, and it is recommended to pad the directive with space at its left and right. | ||
<span id='Simple_Replacement'></span>The simplest directive renders a value. A value is a namespaced object, where each dot (.) separates each namespaced element. A leading dot indicates that we start with the top-most namespace for the [[#Scope|scope]]. | <span id='Simple_Replacement'></span>The simplest directive renders a value. A value is a namespaced object, where each dot (.) separates each namespaced element. A leading dot indicates that we start with the top-most namespace for the [[#Scope|scope]]. | ||
Revision as of 22:36, 5 February 2022
External
- The Chart Template Developer’s Guide https://helm.sh/docs/chart_template_guide/#the-chart-template-developer-s-guide
- Go Templates: https://godoc.org/text/template
- sprig Template Functions: https://godoc.org/github.com/Masterminds/sprig
Internal
Overview
Templates are a set of Kubernetes parameterized manifests that form an application. They live under a chart's templates/
directory. They are written in YAML with Helm templates extensions. Upon processing by Helm, they become Kubernetes manifest files. Helm template extensions are written in the Help template language, which is based on Go templates.
The templates/ Directory
The templates/
directory contains templates that, after combination with values, in a process named "rendering", become Kubernetes manifests. Helm sends all files found in the directory through the template rendering engine, then collect the results for the files that contain manifests, and sends the rendered manifests to Kubernetes. The NOTES.txt
and _helpers.tpl
files are also rendered, but they are not sent to Kubernetes as manifests.
The files whose names begin with an underscore ('_') are assumed to not have a manifest inside, so they are not turned into Kubernetes API resource definitions. However, they are available everywhere within other chart templates for use. These files are conventionally used to store sub-templates and helpers. _helpers.tpl
is the default location for small sub-templates. If a sub-template is large enough, it can be stored in its own '_'-prefixed file. For more details about sub-templates, see:
Template Name
Template names do not follow a rigid naming pattern. It is, however, recommended to use the suffix .yaml
for YAML files and .tpl
for helpers.
Installation and De-Installation Order
During installation, Helm collects all of the resources in a given chart and its dependences, groups them by resource type, and installs them in the order specified here https://github.com/helm/helm/blob/release-2.14/pkg/tiller/kind_sorter.go#L29-L57. Upon de-installation the order is reversed: https://github.com/helm/helm/blob/release-2.14/pkg/tiller/kind_sorter.go#L62-L90
For more details on dependencies, see:
Template Comments
# This is a comment
{{/* Generate basic labels */}}
Multi-Line Comments
{{- /*
This is another
multi-line
comment
*/ -}}
Template Directives
A template directive, sometimes also referred as tag, is enclosed in {{
and }}
blocks, and it is recommended to pad the directive with space at its left and right.
The simplest directive renders a value. A value is a namespaced object, where each dot (.) separates each namespaced element. A leading dot indicates that we start with the top-most namespace for the scope.
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
Potentially any element of the manifest, including keys, can be represented as a template directive:
apiVersion: v1
kind: Secret
stringData:
{{ .Values.secret.fileName }}: |
something
...
Directives may also include functions and other constructs.
With the exception of the multi-line comments, all directives must be specified on one line only. Space present inside a directive is irrelevant, the following formats are equivalent:
{{.Values.color}}
{{ .Values.color }}
{{ .Values.color }}
The recommended format is to allow for a space after '{{' and before '}}':
{{ .Values.color }}
Directives and Whitespace Handling
The spaces inside a directive is irrelevant (we cannot have newlines inside a directive, all directives must be specified on a single line). The '{{' and '}}' directive delimiters, without any other modifications, leave the template whitespace surrounding them alone, and do not interfere with it in any way. A hyphen '-' placed after the '{{' delimiter or before the '}}' delimiter instructs the rendering engine to trim the whitespace preceding, respectively trailing the delimiter. Whitespace includes spaces, tabs, newline ('\n') and carriage return ('\r'). When encountering "-", the template engine will simply drop the corresponding whitespace until a non-whitespace character is found.
metadata:
color: {{ .Values.color }}
spec:
will produce (assuming that "color" is declared to be "blue" in values.yaml:
metadata:
color: blue
spec:
To trim preceding whitespace, use '{{-'.
metadata:
color: {{- .Values.color }}
spec:
will produce:
metadata:
color:blue
spec:
To trim trailing whitespace (whitespace includes newlines), use '-}}'.
metadata:
color: {{ .Values.color -}}
spec:
will produce:
metadata:
color: bluespec:
(which is something you most like don't want).
For details related to whitespace handling when declaring named templates, see Named Templates and Whitespace Handling, below.
Scope
A scope, in this context, is a data structure, specifically a dict instance.
The "." root scope is a dict instance that carries by default the following sub-scopes, containing the "built-in objects":
. ├─ Values ├─ Chart ├─ Release ├─ Capabilities ├─ Files └─ Template
A scope must be specified when a sub-template is rendered with include or template, otherwise the built-in objects referred from the sub-template may not be resolvable.
The root scope can be manipulates as follows:
{{- $Args := dict "Name" "example" -}}
{{- $_0 := set . "Args" $Args -}}
Scopes are declared with 'with'.
Template Objects
Objects are passed into a template from the template engine. The template directives can create new objects and pass them around. There are also built-in objects, which are made available by default. Objects can be simple - have just one value -, or they can contain other objects or functions. For example the "Release" built-in object contains several other objects (like "Release.Name"). The "Files" object contains functions.
Built-in Objects
Built-in objects are a way to access several types of values, some of which are directly configured by operators - in values.yaml, for example -, while others are generated dynamically by Helm or taken from other parts of the chart.
The built-in values always begin with a capital letter, based on Go's naming convention. For a fully working examples of built-in objects replacement see:
Chart
This object contains value passed into the template from the Chart.yaml file. An existing field is available as (note leading dot) .Chart.<UpperCasedFirstLetterFieldName>. It is important to capitalize the first letter of the field name, otherwise the directive evaluation fails.
Example:
{{ .Chart.Name }} {{ .Chart.Version }}
Chart.Version
Values
This object provides access the effective values of all configuration element, as present in the runtime configuration tree, and it essentially exposes the chart configuration to templates. The value of an existing configuration element can be access using the following syntax: (note leading dot) .Values.<fieldName>
. Unlike in Chart's case, the fields are allowed to keep their original capitalization. For example, a value declared as such in values.yaml:
size: 10
can be references in a template as:
kind: ConfigMap
...
data:
size: {{ .Values.size }}
Values may contain structured content:
characteristics:
size: 10
shape: "large"
can be referenced in template as:
kind: ConfigMap
...
data:
size: {{ .Values.characteristics.size }}
shape: {{ .Values.characteristics.shape }}
While structuring data this way is possible, the recommendation is to keep values trees shallow, favoring flatness.
In case the structure contains an array, individual elements can be referred from the template with the index function.
Field Names and Dashes
Field names that contain dashes, while supported, are not rendered in a straightforward manner. For more details, see:
Release
This object describes the release itself.
Release.Name
Exposes the release name:
{{ .Release.Name }}
Release.Namespace
Exposes the namespace the release made into:
{{ .Release.Namespace }}
This value is specified on command line with -n|--namespace. If not specified, it explicitly defaults to "default".
Release.Revision
Exposes the release revision:
{{ .Release.Revision }}
Release.Time
Exposes the time of the release:
{{ .Release.Time }}
Release.IsUpgrade
This is set to true if the current operation is an upgrade or rollback.
{{ .Release.IsUpgrade }}
Release.IsInstall
This is set to true if the current operation is an install.
{{ .Release.IsInstall }}
Release.Service
Exposes the releasing service - always Tiller
Files
The object provide access to all non-special files in the chart. It cannot be used to access templates. The access is provided via several functions:
Files.Get
{{ .Files.Get <file-name> }}
Files.GetBytes
Accessing Files inside Templates
Capabilities
Provides information about the capabilities of the Kubernetes cluster:
{{ .Capabilities.APIVersions }} {{ .Capabilities.APIVersions.Has }} {{ .Capabilities.KubeVersion }} {{ .Capabilities.KubeVersion.Major| Minor|GitVersion|GitCommit|GitTreeState|BuildDate|GoVersion|Compiler|Platform}} {{ .Capabilities.TillerVersion }}
Template
Contains information about the current template that is being executed:
{{ .Template.Name }} {{ .Template.BasePath }}
Data Types
- string: A string of text
- bool: a true or false
- int: an integer value
- float64: a 64-bit floating point value
- a byte slice ([]byte), used to hold potentially binary data
- struct: an object with properties and methods
- a slice (indexed list) of one of the previous types
- a string-keyed map (map[string]interface{}) where the value is one of the previous types.
The easiest way to debug an object's type is to pass it through printf "%t"
in a template, which will print the type. Also see the typeOf and kindOf functions.
Notable Values
Action vs. Functions
Helm templates use both actions and functions. For an action, the data is simply inserted in-line. For a function, the output of a function can be passed to another function. Template control structures, such as if/else, with, range, define and template are actions.
Template Functions
A template function modifies data provided to the template via template objects, and it is declared inside the template, in a template directive. Template functions follow the syntax:
functionName arg1 arg2 ...
Example:
{{ quote .Values.color }}
Helm Template Function Reference
- default
- printf
- quote
- repeat
- upper
- now
- htmlDate
- replace "+" "_"
- trunc 63
- trimSuffix "-"
- indent
- nindent
- index
- toYaml
- list
- title
- typeOf
- typeIs
- kindOf
- kindIs
- join
- empty
- int
- fail
Operators
Operators are implemented as functions that return a boolean value:
{{ eq Values.color "blue" }} {{ ne }} {{ lt }} {{ gt }} {{ and }} {{ or }} {{ not }}
Storage
Accessing Array Elements
To access a specific element of an array data structure, use the index function. The index function is 0-based.
Assuming we declare a simple array:
colors:
- 'blue'
- 'red'
- 'green'
then the first element of the array can be accessed with:
{{ index .Values.colors 0 }}
The directive is rendered to "blue".
When the array contains maps:
colors:
- name: blue
shade: dark
- name: red
shade: light
the fields of the maps can be accessed with - note (...):
{{ (index .Values.colors 0).name }}
This pattern can be extrapolated to more complex data structures.
Using index to Read Values whose Field Names Contain Dashes
index can be used to access the values for those fields whose names contain dashes. For more details, see:
include Function
Template Pipelines
{{ <object> | <function1> | <function2> }}
{{ .Values.color | upper | repeat 5 }}
Template Control Structures
Flow control structures are called "actions".
if/else
with
with
narrows the scope of the context for better readability and more expressive template blocks. with specifies a scope:
{{- with .Values.deployment }}
strategy:
rollungUpdate:
maxUnavailable: {{ .maxUnavailable }}
maxSurge: {{ .maxSurge }}
revisionHistoryLimit: {{ .revisionHistoryLimit }}
minReadySeconds: {{ .minReadySeconds }}
{{- end }}
TODO.
range
Named Templates (Sub-templates, Partials)
Variables
Configmap and Secrets Utility Functions
TODO.
Debugging Templates
TODO.
Failing in a Template
Some times, failing early and loudly with an explicit message is the best approach to invalid output.
{{- fail "Invalid input" }}
To print an argument:
{{- fail (printf "Invalid input: %s" .Values.someValue) }}
TODO: https://austindewey.com/2018/12/28/helm-tricks-input-validation-with-required-and-fail/
Template Recipes
Transfer the Content of a Map from values.yaml to a Template
This patterns is useful to transform free-format annotations:
values.yaml:
data:
color: 'red'
shape: 'triangle'
template:
metadata:
{{- if .Values.data }}
annotations:
{{- range $key, $value := .Values.data }}
{{ $key }}: {{ $value | quote -}}
{{ end }}
{{- end}}
For more details see:
Transfer the Content of a List from values.yaml to a Template
TODO
- https://helm.sh/docs/chart_best_practices/#templates
- https://helm.sh/docs/developing_charts/#templates-and-values
- https://helm.sh/docs/developing_charts/#know-your-template-functions
- https://helm.sh/docs/developing_charts/#quote-strings-don-t-quote-integers
- https://helm.sh/docs/developing_charts/#using-the-include-function
- https://helm.sh/docs/developing_charts/#using-the-required-function
- https://helm.sh/docs/developing_charts/#using-the-tpl-function
- https://helm.sh/docs/developing_charts/#creating-image-pull-secrets
- https://helm.sh/docs/developing_charts/#automatically-roll-deployments-when-configmaps-or-secrets-change
- https://helm.sh/docs/developing_charts/#tell-tiller-not-to-delete-a-resource
- https://helm.sh/docs/developing_charts/#using-partials-and-template-includes
- https://helm.sh/docs/chart_best_practices/#pods-and-podtemplates
- https://helm.sh/docs/chart_best_practices/#images
- https://helm.sh/docs/chart_best_practices/#imagepullpolicy
- https://helm.sh/docs/chart_best_practices/#podtemplates-should-declare-selectors