Search code examples
kubernetes-helm

How to define a template function to set tolerations either from global or from local tolerations in Helm?


I want to check if tolerations is available in global then use that for all the pods (a parent chart with subcharts) and if each submodule/subchart has its own tolerations then use that instead of the global one. So this is how I defined it:

{{/* Set tolerations from global if available and if not set it from each module */}}
{{- define "helper.tolerations" }}
{{- if .globalTolerations.tolerations }}
tolerations:
  {{toYaml .globalTolerations.tolerations | indent 2}}
{{- else if (.localTolerations.tolerations) }}
tolerations:
  {{toYaml .localTolerations.tolerations | indent 2}}
{{- end }}
{{- end }}

and in each sub-chart, I have this:

spec:
  template:
    spec:
      containers:
        - name:  my-container-name
      {{toYaml (include "helper.tolerations" (dict "globalTolerations" .Values.global "localTolerations" (index .Values "ui-log-collector"))) | indent 6}}

The default values in values.yaml are defined as below:

global:
  tolerations: []
ui-log-collector:
  image: a.b.c
  tolerations: []
some-other-sub-charts:
  image: x.y.z
  tolerations: []

Now when I want to deploy the stack using helm, I pass a values.yaml to override tolerations as below:

global:
  tolerations:
    - key: "key1"
      operator: "Equal"
      value: "value1"
      effect: "NoSchedule"
    - key: "key1"
      operator: "Equal"
      value: "value1"
      effect: "NoExecute"
ui-log-collector:
  tolerations:
    - key: "key2"
      operator: "Equal"
      value: "value2"
      effect: "NoSchedule"
    - key: "key2"
      operator: "Equal"
      value: "value2"
      effect: "NoExecute"

With this set up I currently get this error:

 error converting YAML to JSON: yaml: line 34: could not find expected ':'

I tried different things but I without toYaml I get:

Error: UPGRADE FAILED: error validating "": error validating data: ValidationError(Deployment.spec.template.spec.tolerations): invalid type for io.k8s.api.core.v1.PodSpec.tolerations: got "string", expected "array"

Solution

  • I see two issues in the code as written. Go templates produce text output, so you don't need to call toYaml on them. Also, when you call indent, it doesn't know about the indentation of the current line, so if you're going to indent something the {{ ... }} template expression usually needs to be on an unindented line.

    The call itself, for example, should look like:

    spec:
      template:
        spec:
          containers:
            - name:  my-container-name
    {{ include "helper.tolerations" (dict "globalTolerations" .Values.global "localTolerations" .Values.ui-log-collector) | indent 6}}
    {{-/* at the first column; without toYaml */}}
    

    Within the helper function, using toYaml is appropriate (.Values is a composite object and not a string) but the indent line again needs to begin at the first column.

    tolerations:
    {{toYaml .globalTolerations.tolerations | indent 2}}
    

    You may find it useful to use helm template to debug issues like this; it will write out the generated YAML file instead of sending it to the cluster. The toYaml form in the question will probably convert the tolerations: block into a YAML string, and that would be pretty visible in the output.