kubernetes-helm

Using Helm helper.tpl to set repository and image from the values.yaml or Chart.yaml


I a have a couple of charts for different products. In the first one a helper was written to build out the repo, image name and tag/version. This works but as the other Chart is quite different I've gone through a simpler approach but it does not work. I get the error,

error calling include: template: MYChart/templates/_helpers.tpl:94:28: executing "getImageName" at <.Values.registryName>: nil pointer evaluating interface {}.registryName

This is the helper.

{{/*
This allows us to not have image: .Values.xxxx.ssss/.Values.xxx.xxx:.Values.ssss
in every single template.
*/}}
{{- define "imageName" -}}
{{- $registryName := .Values.registryName -}}
{{- $imageName := .Values.imageName -}}
{{- $tag := .Chart.AppVersion -}}
{{- printf "%s/%s:%s" $registryName $imageName $tag -}}
{{- end -}}

These are the values

registry:
  registryName: "index.docker.io/myrepo"
  image_Name: "myimage"

Calling a value like the above in a _helper.tpl should work, there are plenty of examples that use this approach. What am I missing?

The template file :

{{- $root := . -}}
{{- $FullChartName := include "myapp.fullname" . -}}
{{- $ChartName := include "myapp.name" . -}}
{{- range $worker, $parameter := .Values.workerPods }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ $parameter.name }}-worker
spec:
  replicas: {{ $parameter.replicas }}
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ $parameter.name }}-worker
      app.kubernetes.io/instance: {{ $root.Release.Name }}
  template:
    metadata:
      labels:
        app.kubernetes.io/name: {{ $parameter.name }}-worker
        app.kubernetes.io/instance: {{ $root.Release.Name }}
        autoscale: "true"
      annotations:
      {{- if $root.Values.worker.annotations }}
      {{ toYaml $root.Values.worker.annotations | indent 8 }}
      {{- end }}
    spec:
      imagePullSecrets:
        - name: myapp-registry-credentials
      containers:
        - name: {{ $parameter.name }}-worker
          image: {{ template "imageName" . }}
          imagePullPolicy: {{ $root.Values.worker.image.pullPolicy }}
          command: ["/bin/sh"]
          args: ["-c", "until /usr/bin/pg_isready -h $DATABASE_HOST; do sleep 2; done; bundle exec rake jobs:work"]
          {{- range $container, $containerResources := $root.Values.containers }}
          {{- if eq $container $parameter.size }}
          resources:
            {{- toYaml $containerResources.resources | nindent 12 }}
          {{- end }}
          {{- end }}
          envFrom:
            - configMapRef:
                name: common-env
            - secretRef:
                name: myapp-secrets          

          volumeMounts:
          - name: mnt-data
            mountPath: "/mnt/data"
      volumes:
        - name: mnt-data
          persistentVolumeClaim:
            claimName: myapp-pvc
      {{- with $root.Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
    {{- with $root.Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
    {{- end }}
    {{- with $root.Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
    {{- end }}
{{- end }}

I also tried this approach and added the following to the Chart.yaml but got a similar error, I'll be honest and wasn't sure this would even work but would be interested to hear other's thoughts.

annotations:
  image: "myimage"
  registry: "index.docker.io/myrepo"

And the helper looked like this.

{{/*
This allows us to not have image: .Values.xxxx.ssss/.Values.xxx.xxx:.Values.ssss
in every single template.
*/}}
{{- define "imageName" -}}
{{- $registryName := .Chart.Annotations.registry -}}
{{- $imageName := .Chart.Annotations.image -}}
{{- $tag := .Chart.AppVersion -}}
{{- printf "%s/%s:%s" $registryName $imageName $tag -}}
{{- end -}}

Solution

  • You're calling the template with the wrong parameter. Reducing the Helm template file to the bare minimum to demonstrate this:

    {{- $root := . -}}
    {{- range $worker, $parameter := .Values.workerPods }}
    image: {{ template "imageName" . }}
    imagePullPolicy: {{ $root.Values.worker.image.pullPolicy }}
    {{- end }}
    

    The standard Go text/template range statement rebinds the . variable (I believe to the same thing as $parameter). So then when you call the imageName template, its parameter isn't the Helm root value but rather the block from the values file; .Values is undefined and returns nil; and then .Values.registryName is a lookup on nil which produces the error you see.

    One standard workaround to this is to save . to a variable outside the range loop and use that variable everywhere you would have used .. And in fact you already do this, the $root.Values.worker... reference in the following line should work correctly. You just need to change this at the point of call:

    image: {{ template "imageName" $root }}