Search code examples
kubernetes-helm

Use defined variables from parent scope in a named template


I'm trying to provide idiomatic access to a value defined in a template to a named template being included. This much works:

# values.yml
versions:
- 1.2.3.4
- 5.6.7.8
# _helpers.tmpl
{{/*
Get the service path from the version string (passed as ".")
*/}}
{{- define "sample.servicePath" -}}
{{- $pat := "^[0-9]+\\.[0-9]+\\.([0-9]+)\\.[0-9]+$" }}
{{- $version := . | trimAll "\"" }}
{{- if regexMatch $pat $version }}
{{- regexReplaceAll $pat $version "${1}" }}
{{- else }}
{{- printf "Can't extract service number from version string %s" . | fail -}}
{{- end }}
{{- end }}
# sample.yml
{{- range $version := .Values.versions }}
{{- $servicePath := (include "sample.servicePath" $version) }}
# Here is the servicePath: {{ $servicePath }} which works
{{- end }}

But I imagined that this would also work:

# _helpers.tmpl (further down in the file)

{{- define "sample.labels" -}}
servicePath: {{ $servicePath }}
{{- end }}
# sample.yml
{{- range $version := .Values.versions }}
{{- $servicePath := (include "sample.servicePath" $version) }}
# Here is the servicePath: {{ $servicePath }} which still works
# but these labels: {{ include "sample.labels" . }} do not
{{- end }}

Ultimately I don't mind the implementation, I just need to be able to take a single list of values, range over them (to create multiple resources), and parse their value.


Solution

  • The Go text/template language, and by extension Helm charts, doesn't have global variables or dynamic scopes; you can't do this in the way you're suggesting. Each template invocation has its own scope, and the only things it can see are its single parameter . and any variables it declares locally.

    However, Helm includes the Sprig extension library, which includes list and dict functions to create composite values. You can use these to pass multiple values into a template:

    {{- define "sample.labels" -}}
    {{- $top := index . 0 -}}
    {{- $servicePath := index . 1 -}}
    app.kubernetes.io/instance: {{ $top.Release.Name }}
    servicePath: {{ $servicePath }}
    {{ end -}}
    
    {{- range $version := .Values.versions }}
    {{- $servicePath := (include "sample.servicePath" $version) }}
    {{- include "sample.labels" (list . $servicePath) }}
    

    In the last line, we are invoking the template with a list of two parameters, the top-level Helm object (here as .) and the $servicePath local variable. The top of the sample.labels template then unpacks that back into two local variables within its own template, and then uses those to generate a reference set of labels.