kubernetesyamlkubernetes-helmgo-templates

Helm Template Indention Error Using Dict Split and Range


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.


Solution

  • 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.