kubernetes-helmgo-templates

Preserving Indentation / Whitespace in Helm Named Templates


Recently I've been refactoring a ton of older Helm charts and began using Named Templates as part of this effort to encourage reuse and less of the usual copy-pasting that so commonly happens.

One challenge that has continually come up has been with preserving indentation levels as part of this. Consider the following very simple named template:

{{- define "example.volumemounts.gcs" -}}
  {{- if not .Values.local -}}
- name: /example/gcs-creds
  value: gcs-key
  {{- end -}}
{{- end -}}

The idea here is that if a given value isn't local, then we need to render a section that will be available under the volumeMounts for a given container like so:

spec:
  ...
  podTemplate:
    ...
    spec:
      containers:
        - name: my-container
          ...
          volumeMounts:
            {{ template "example.volumemounts.gcs" .  }}
            - name: some-other-volume-mount
              value: test

What I've found is that the template itself will properly respect the indentation level for the first line of output from the template, but the second will not as seen below:

spec:
  ...
  podTemplate:
    ...
    spec:
      containers:
        - name: my-container
          ...
          volumeMounts:
            - name: /example/gcs-creds
  value: gcs-key
            - name: some-other-volume-mount
              value: test

What is the preferred way of handling this? I'd like to minimize white-space/blank lines where possible but also ensure the baseline indentation level of where the template is called would affect rendering (i.e. indentation changes would be reflected in the output).

I was trying to avoid decorating all of these calls with nindent and indent respectively. Just trying to gauge if there's a better approach for constructing the templates themselves.


Solution

  • You need to fairly rigorously use indent; there isn't another way.

    The templating engine Helm uses doesn't "know anything" about YAML, so when you template or include a helper template, the result of the template is directly inserted in the output as a string without any reformatting. In your example, the line that includes value: begins with exactly two spaces, so exactly two spaces will appear in the output, regardless of the YAML context.

    The usual pattern I use is:

    1. The helper template content is correct YAML, but beginning at the first column.
    2. The line where you include the template is unindented, regardless of context, and does not have a - whitespace control on either side.
    3. When you include the template, use the Helm-specific include in combination with a correct indent.

    In your example,

              volumeMounts:
    {{ include "example.volumemounts.gcs" . | indent 12 }}
                - name: some-other-volume-mount
                  value: test
    

    If you want to indent the line, then you need to modify these rules to (2) include a - whitespace control at the beginning of the line (which will delete the whitespace and the preceding newline) and then (3) change to nindent (which will reinsert that whitespace). Any amount of whitespace can precede this invocation, so you could even put the call on the same line.

    metadata:
      labels: {{- include "example.labels" . | nindent 4 }}
    spec:
      podTemplate:
        spec:
          containers:
            - volumeMounts:
                {{- include "example.volumemounts.gcs" . | nindent 12 }}
    

    In principle you could use ... | indent $n | trim to indent the helper template content but then remove the leading whitespace from the first line. But there's no way to deduce the indentation amount from context, you always have to know how what value you need to indent.