Template Functions and Pipelines

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

    Template functions follow the syntax functionName arg1 arg2.... In the snippet above, quote .Values.favorite.drink calls the quote function and passes it a single argument.

    Helm has over 60 available functions. Some of them are defined by the Go template language itself. Most of the others are part of the . We’ll see many of them as we progress through the examples.

    One of the powerful features of the template language is its concept of pipelines. Drawing on a concept from UNIX, pipelines are a tool for chaining together a series of template commands to compactly express a series of transformations. In other words, pipelines are an efficient way of getting several things done in sequence. Let’s rewrite the above example using a pipeline.

    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 several functions 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 }}

    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 .

    The repeat function will echo the given string the given number of times, so we 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"

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

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

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

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

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

    Now re-running helm install --dry-run --debug fair-worm ./mychart will produce this YAML:

    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 , and should not be repeated using the default command (otherwise they would be redundant). However, the default command is perfect for computed values, which cannot 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 and then insert it into your YAML. But sometimes it’s necessary to add some template logic that is a little more sophisticated than just inserting a string. In the next section we will look at the control structures provided by the template language.

    The lookup function can be used to look up resources in a running cluster. The synopsis of the lookup function is lookup apiVersion, kind, namespace, name -> resource or resource list.

    The following combination of parameters are possible:

    BehaviorLookup function
    kubectl get pod mypod -n mynamespacelookup “v1” “Pod” “mynamespace” “mypod”
    kubectl get pods -n mynamespacelookup “v1” “Pod” “mynamespace” “”
    kubectl get pods —all-namespaceslookup “v1” “Pod” “” “”
    kubectl get namespace mynamespacelookup “v1” “Namespace” “” “mynamespace”
    kubectl get namespaceslookup “v1” “Namespace” “” “”

    When lookup returns an object, it will return a dictionary. This dictionary can be further navigated to extract specific values.

    The following example will return the annotations present for the mynamespace object:

    1. (lookup "v1" "Namespace" "" "mynamespace").metadata.annotations

    When lookup returns a list of objects, it is possible to access the object list via the items field:

    When no object is found, an empty value is returned. This can be used to check for the existence of an object.

    The lookup function uses Helm’s existing Kubernetes connection configuration to query Kubernetes. If any error is returned when interacting with calling the API server (for example due to lack of permission to access a resource), helm’s template processing will fail.

    Keep in mind that Helm is not supposed to contact the Kubernetes API Server during a helm template or a helm install|upgrade|delete|rollback --dry-run, so the lookup function will return an empty list (i.e. dict) in such a case.

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

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