Template Functions and Pipelines

So far, we’ve seen how to place information into a template. But thatinformation is placed into the template unmodified. Sometimes we want totransform the supplied data in a way that makes it more useable to us.

Let’s start with a best practice: When injecting strings from the .Valuesobject into the template, we ought to quote these strings. We can do that bycalling the quote function in the template directive:

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: {{ .Release.Name }}-configmap
  5. data:
  6. myvalue: "Hello World"
  7. drink: {{ quote .Values.favorite.drink }}
  8. food: {{ quote .Values.favorite.food }}

Template functions follow the syntax functionName arg1 arg2…. In the snippetabove, quote .Values.favorite.drink calls the quote function and passes it asingle argument.

Helm has over 60 available functions. Some of them are defined by the Gotemplate language itself. Most of the othersare part of the Sprig templatelibrary. We’ll see many of themas we progress through the examples.

While we talk about the “Helm template language” as if it is Helm-specific, itis actually a combination of the Go template language, some extra functions,and a variety of wrappers to expose certain objects to the templates. Manyresources on Go templates may be helpful as you learn about templating.

Pipelines

One of the powerful features of the template language is its concept ofpipelines. Drawing on a concept from UNIX, pipelines are a tool for chainingtogether a series of template commands to compactly express a series oftransformations. In other words, pipelines are an efficient way of gettingseveral things done in sequence. Let’s rewrite the above example using apipeline.

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: {{ .Release.Name }}-configmap
  5. data:
  6. myvalue: "Hello World"
  7. drink: {{ .Values.favorite.drink | quote }}
  8. food: {{ .Values.favorite.food | quote }}

In this example, instead of calling quote ARGUMENT, we inverted the order. We“sent” the argument to the function using a pipeline (|):.Values.favorite.drink | quote. Using pipelines, we can chain severalfunctions together:

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: {{ .Release.Name }}-configmap
  5. data:
  6. myvalue: "Hello World"
  7. drink: {{ .Values.favorite.drink | quote }}
  8. food: {{ .Values.favorite.food | upper | quote }}

Inverting the order is a common practice in templates. You will see .val |quote more often than quote .val. Either practice is fine.

When evaluated, that template will produce this:

  1. # Source: mychart/templates/configmap.yaml
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5. name: trendsetting-p-configmap
  6. data:
  7. myvalue: "Hello World"
  8. drink: "coffee"
  9. food: "PIZZA"

Note that our original pizza has now been transformed to "PIZZA".

When pipelining arguments like this, the result of the first evaluation(.Values.favorite.drink) is sent as the last argument to the function. Wecan modify the drink example above to illustrate with a function that takes twoarguments: repeat COUNT STRING:

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: {{ .Release.Name }}-configmap
  5. data:
  6. myvalue: "Hello World"
  7. drink: {{ .Values.favorite.drink | repeat 5 | quote }}
  8. food: {{ .Values.favorite.food | upper | quote }}

The repeat function will echo the given string the given number of times, sowe will get this for output:

  1. # Source: mychart/templates/configmap.yaml
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5. name: melting-porcup-configmap
  6. data:
  7. myvalue: "Hello World"
  8. drink: "coffeecoffeecoffeecoffeecoffee"
  9. food: "PIZZA"

Using the default function

One function frequently used in templates is the default function: defaultDEFAULT_VALUE GIVEN_VALUE. This function allows you to specify a default valueinside of the template, in case the value is omitted. Let’s use it to modify thedrink example above:

  1. drink: {{ .Values.favorite.drink | default "tea" | quote }}

If we run this as normal, we’ll get our coffee:

  1. # Source: mychart/templates/configmap.yaml
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5. name: virtuous-mink-configmap
  6. data:
  7. myvalue: "Hello World"
  8. drink: "coffee"
  9. food: "PIZZA"

Now, we will remove the favorite drink setting from values.yaml:

  1. favorite:
  2. #drink: coffee
  3. food: pizza

Now re-running helm install —dry-run —debug ./mychart will produce thisYAML:

  1. # Source: mychart/templates/configmap.yaml
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5. name: fair-worm-configmap
  6. data:
  7. myvalue: "Hello World"
  8. drink: "tea"
  9. food: "PIZZA"

In an actual chart, all static default values should live in the values.yaml,and should not be repeated using the default command (otherwise they would beredundant). However, the default command is perfect for computed values, whichcan not be declared inside values.yaml. For example:

  1. drink: {{ .Values.favorite.drink | default (printf "%s-tea" (include "fullname" .)) }}

In some places, an if conditional guard may be better suited than default.We’ll see those in the next section.

Template functions and pipelines are a powerful way to transform information andthen insert it into your YAML. But sometimes it’s necessary to add some templatelogic that is a little more sophisticated than just inserting a string. In thenext section we will look at the control structures provided by the templatelanguage.

Operators are functions

For templates, the operators (eq, ne, lt, gt, and, or and so on) areall implemented as functions. In pipelines, operations can be grouped withparentheses ((, and )).

Now we can turn from functions and pipelines to flow control with conditions,loops, and scope modifiers.