kubernetes-helm

Passing common environment variables to multiple child charts


I have a structure as per below whereby common environment variables across servicea and serviceb are placed in _helpers.tpl in the common folder

screenshot of directory structure

Below is the content of _helpers.tpl of common:

{{/* servicea/templates/_helpers.tpl */}}
{{- define "servicea.env" }}
 - name: KEY
   value: {{ .Values.spring.cloud.kubernetes.enabled }}
 - name: OTHER
   value: test
{{ end -}}

and its values.yaml as per below:

spring:
  cloud:
    kubernetes:
      enabled: true

for example in servicea, if I need to include the common env variables, I will do as per below:

containers:
    - name: "servicea"
      env:
        - name: role
          value: role
{{ include "servicea.env" . | indent 12 }}

The values.yaml of servicea are listed as below:

servicea: 
  replicaCount: 1

and the dev-values.yaml as per below:

servicea: 
  replicaCount: 2
serviceb: 
  replicaCount: 2

Now the issue I am facing is that when I run helm template to generate the manifests, it is giving me the error:

Error: template: <.Values.spring.cloud.kubernetes.enabled>: nil pointer evaluating interface {}

I can understand that when it is trying to run the template locally, the property spring.cloud.kubernetes.enabled and its value are not present in the values.yaml of servicea as it is only present in the values.yaml of common. If I add the property spring.cloud.kubernetes.enabled in values.yaml of servicea, it works fine.

But I do not want to do that as I do not want to repeat the property across all services. Is there a function that can "execute the template and then import it after the execution"?


Solution

  • If you call a named template from an other (dependency) chart, it still runs in the parent chart's context, and sees the parent's .Values. There's not a way to "switch into the child" here; you need to provide the defaults in the template code itself.

    Mechanically: inside servicea.env you call .Values, but in this context . is the parameter to the template. When you include "servicea.env" ., in that context . is the top-level Helm object for the service-A chart, and you're passing that as the parameter. There's never a point where the definition of .Values changes to the common-chart values.

    (I'm not even sure it's possible for the parent chart to see the default values in the common chart; I think e.g. .Values.common.spring will only see what's in the parent chart's values.yaml and deploy-time configuration, but not what's in common/values.yaml. You also can't access these files using .Files.)

    Instead of including this default configuration in the common chart's values.yaml you need to include it in code. Here you need to account for the possibility that none of this configuration will exist at all; if there isn't a .Values.spring then you need to provide a default empty dict instead so lookups at deeper levels continue to work. I'd probably write this as:

    {{- define "servicea.env" }}
    {{- $spring := .Values.spring | default dict }}
    {{- $cloud := $spring.cloud | default dict }}
    {{- $kubernetes := $cloud.kubernetes | default dict }}
     - name: KEY
    {{- if hasKey $kubernetes "enabled" }}
       value: {{ $kubernetes.enabled | toString | quote }}
    {{- else }}
       value: "true"
    {{- end }}
    {{- end }}
    

    You might consider using a shallower values structure, rather than replicating Spring's configuration here. If this is setting the Spring environment variable SPRING_CLOUD_KUBERNETES_ENABLED you might just always force it on, unless you have a specific need to disable it sometimes.