Helm Template Indention Error Using Dict Split and Range

11/5/2021

I am trying to setup the ability to have a dynamic number of volume and volumeMounts in my helm chart. Here is how my deployment.yaml looks:

    volumeMounts:
    {{- if .Values.deployment.hostPath }} {{ $dict := split ":" .Values.deployment.hostPath }} {{ range $idx, $val := $dict }} {{ $mnt := split "," $val }}
    - mountPath: {{ $mnt._1 }}
      name: {{ $mnt._0 }}
    {{ end }}
    {{ end }}
  volumes:
  {{- if .Values.deployment.hostPath }} {{ $dict := split ":" .Values.deployment.hostPath }} {{ range $idx, $val := $dict }} {{ $mnt := split "," $val }}
  - name: {{ $mnt._0 }}
    hostPath:
      path: {{ $mnt._2 }}
      {{- if $mnt._3 }}
      type: {{ $mnt._3 }}
      {{ end }}
  {{ end }}
  {{ end }}

In the values.yaml I have something like this:

Deployment:
  hostPath: test-host-path,/path/on/pod,/path/on/node,Directory

What I am doing is checking if there is anything definied for hostPath. If there is I create a dict, spltting items by ':'. In this first example, there is only one string of values, because the : delimeter isn't there. It then does a range to loop (in case there are mutiple sets of values separated by ':') and then it splits a set of values by commas.

So with this first example, it works just fine using helm install:

values.yaml -->   hostPath: test-host-path,/path/on/pod,/path/on/node,Directory
        volumeMounts:
        - mountPath: /path/on/pod
          name: test-host-path

      volumes:
      - name: test-host-path
        hostPath:
          path: /path/on/node
          type: Directory

The type field is optional so I have a block to check if its defined, if not it doesnt render it in the yaml. So on my next example when I just have no value after the last comma it also works:

values.yaml -->   hostPath: test-host-path,/path/on/pod,/path/on/node,
        volumeMounts:
        - mountPath: /path/on/pod
          name: test-host-path

      volumes:
      - name: test-host-path
        hostPath:
          path: /path/on/node

But now if I try to add a second set of data, separated by a ':', it fails on helm install:

Deployment:
  hostPath: test-host-path,/path/on/pod,/path/on/node,Directory:test-host-path-two,/path/on/pod/two,/path/on/node/two,Directory

Error: YAML parse error on chart/templates/deployment.yaml: error converting YAML to JSON: yaml: line 152: found a tab character that violates indentation
helm.go:94: [debug] error converting YAML to JSON: yaml: line 152: found a tab character that violates indentation

Of course it's mentioning a tab, but I can't quite figure out what the issue is. I have used this same code (dict,range,split) to populate multiple entries in a configmap so I think it can work. But clearly I am missing something here. Any help would be appreciated.

-- ssbsts
go-templates
helm3
kubernetes
kubernetes-helm
yaml

1 Answer

11/5/2021

It's not totally obvious where a tab character might be sneaking in. One thing that does jump out is there's some extra whitespace hanging out in the templating; for example, there's a space after the start of the {{ range }} loop that potentially throws things off.

If you're having whitespace or indentation problems, one good thing to check is the helm template output; Helm 3 will print "error converting YAML" as you've shown if it's really wrong, but adding a helm template --debug option will print it anyways.

Other useful rules of thumb are to put only one templating construct on a line; to put a - "swallow whitespace" marker inside opening but not closing curly braces; and generally put templating constructs at the first column. Those rules would get you something like

    volumeMounts:
{{- if .Values.deployment.hostPath }}
{{- $dict := split ":" .Values.deployment.hostPath }}
{{- range $idx, $val := $dict }}
{{- $mnt := split "," $val }}
    - mountPath: {{ $mnt._1 }}
      name: {{ $mnt._0 }}
{{- end }}
{{- end }}
  volumes:
{{- if .Values.deployment.hostPath }}
{{- $dict := split ":" .Values.deployment.hostPath }}
{{- range $idx, $val := $dict }}
{{- $mnt := split "," $val }}
  - name: {{ $mnt._0 }}
    hostPath:
      path: {{ $mnt._2 }}
{{- if $mnt._3 }}
      type: {{ $mnt._3 }}
{{- end }}
{{- end }}
{{- end }}

You should be able to tell by inspection with this layout that the indentation is correct. In the first example, there is a newline and spaces before - mountPath:, for example, and a newline following the last {{- end }}, but it should be clear there are not stray newlines or spaces between the templating code lines.

-- David Maze
Source: StackOverflow