I have close to 25 deployments and services. Each Helm template has the following:
apiVersion: v1
kind: Deployment
metadata:
name: aa1
spec:
replicas: 1
strategy: {}
template:
metadata:
labels:
app: aa1
spec:
containers:
- env:
- name: {{ .Values.env1 }}
value: "yes"
volumeMounts:
- name: {{ .Values.v1 }}
mountPath: {{ .Values.v1monthPath}}
subPath: {{ .Values.v1subpath }}
Similarly I have same env
and volumeMount
defined for all 25 templates.
Is there any way I can use template inheritance in Helm?
A single Helm .tpl
file can contain multiple YAML documents, separated with the ---
start-of-document separator. Also, you can use all of the standard Go text/template capabilities, including its looping constructs.
That means that you could write a template like this:
{{- $top := . -}}
{{- range .Values.names -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ . }}
spec:
...
template:
spec:
containers:
- env:
- name: {{ $top.Values.env1 }}
value: "yes"
---
apiVersion: v1
kind: Service
metadata:
name: {{ . }}
spec: { ... }
{{ end -}}
What this does is first to save away the current context; since range
resets the current template default context .
to the iterator value, we need to remember what its current value is. We then iterate through every value in a list from the values file. (The Sprig list
function could create the list in the template.) For each item, we create a Deployment and a Service, each starting with a ---
on its own line. When we need the current name, it is .
; when we need something from the Helm values, we need to explicitly look it up in $top.Values
.
Another possible approach is to write separate templates for the deployment and service, and then have files invoke each of them. Technically a template only takes one parameter, but you can use list
and index
to have that one parameter be a list.
{{/* _deployment.tpl */}}
{{- define "deployment" -}}
{{- $name := index 0 . -}}
{{- $top := index 1 . -}}
---
apiVersion: apps/v1
kind: Deployment
...
name: {{ $name }}
...
name: {{ $top.Values.env1 }}
...
{{ end -}}
{{/* aa1.yaml */}}
{{- template "deployment" (list "aa1" .) -}}
{{- template "service" (list "aa1" .) -}}
This can get almost arbitrarily complex (you can have conditionals based on the current object name; you can use the template index
function to look up values in the values object; and so on). helm template
will do some validation and write the rendered template to stdout without invoking Kubernetes, which is helpful for eyeballing it and can be a basis for automated tests.
The text/template "inheritance" capabilities (using e.g. block
) aren't a good match here. The documentation for (*text/template.Template).Parse
notes that, if Parse
is called multiple times, the last define
found wins, which gives you a room to Parse
a base template with a placeholder block
and then Parse
a refined template that redefines that. Helm doesn't really use this functionality; while it loads all of the YAML and supporting template files into the same template instance, it renders all of the top-level files separately instead of rendering some root template that allows for overrides.