I have a block of code I'm trying to re-use across several yaml manifest files.
How can I build a function from this that I can re-use across files? I have tried adding a template inside _helpers.tpl
like below, but no luck!
Does anyone have any tips on how to template this code for re-use?
The template:
{{- define "xx" -}}
{{ range $model := .Values.appConfig.models }}
{{ range $modelMode := $.Values.appConfig.models }}
{{- end -}}
The code to wrap:
{{ range $model := .Values.appConfig.models }}
{{ range $modelMode := $.Values.appConfig.models }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $modelMode }}
labels:
{{- end -}}
{{- end -}}
My attempt:
{{- template "xx" }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $modelMode }}
labels:
{{- end -}}
{{- end -}}
There's a couple of rules around template definitions.
First off: within a define
...end
block, all of the inner blocks need to match up; there need to be as many range
directives as matching end
s. In your case, I'd put the range
...end
block in the top-level templates/*.yaml
file and invoke the template inside that.
{{- range $model := .Values.appConfig.models -}}
{{- range $modelMode := $.Values.appConfig.models -}}
{{- template "xx" . -}}
{{- end -}}
{{- end -}}
{{- define "xx" -}}
---
apiVersion: policy/v1
...
{{ end -}}
The Go text/template
language doesn't have global variables, and a template can't access variables in its caller. Anything you use in a define
d template needs to be passed in the template parameter. Templates only take a single parameter; in Helm, you can pack multiple parameters into a dict
or a list
.
The other important detail here is that, normally in Helm, .
refers to a specific object with Values
, Release
, and other properties, but in the Go template language .
has a couple of other uses as well. In a defined template, .
is the parameter. If you need access to the Helm .
, maybe to invoke a subtemplate that defines labels, you need to explicitly pass it as a parameter.
This all combines into:
{{- range $model := .Values.appConfig.models -}}
{{- range $modelMode := $.Values.appConfig.models -}}
{{- template "xx" (dict "top" $ "modelMode" $modelMode) -}}
{{- end -}}
{{- end -}}
{{- define "xx" -}}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ .modelMode }}
labels:
{{ include "mychart.labels" .top | indent 4 }}
spec:
minAvailable: {{ .top.Values.minAvailable }}
{{ end -}}
Take note of a couple of things in the last example. Where we invoke the template
, we now pass a dict
as the single parameter. We can then get the parameters out inside the invoked template with .modelMode
and .top
. If we need the top-level values, those are inside the .top
object; if we need to invoke another template that needs the top-level object, we need to pass that object, and not the template-specific .
.
I've also called the label template with include
rather than template
. include
is a Helm extension that returns the template result as a string, so in this case it can be re-indented. The two invocation paths are very similar and I might default to using include
in most cases.