Inheritance helm template

12/1/2019

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?

-- pythonhmmm
kubernetes-helm

1 Answer

12/2/2019

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.

-- David Maze
Source: StackOverflow