Helm templating for 2 sets of annotations

2/25/2021

I currently have a helm template for a deployment defined as

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
  labels:
    {{- include "demo.labels" . | nindent 4 }}
    app.kubernetes.io/component: "server"
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app.kubernetes.io/name: demo
      app.kubernetes.io/instance: {{ .Release.Name }}
      app.kubernetes.io/component: "server"
  template:
    metadata:
      {{- with .Values.deployment.annotations }}
      annotations:
        {{- toYaml . | nindent 8 }}
      {{- end }}

For the annotations, it works fine as we can pass in annotations from our values.yml. However, now I also want to add in a set of vault annotations that have predefined values into the template:

{{- if .Values.vault.enabled -}}
vault.hashicorp.com/agent-inject: {{ .Values.vault.enabled | quote }}
vault.hashicorp.com/agent-cache-enable: "true"
vault.hashicorp.com/agent-cache-use-auto-auth-token: "force"
vault.hashicorp.com/role: {{ .Values.vault.role | quote }}
vault.hashicorp.com/ca-cert: "/run/secrets/kubernetes.io/serviceaccount/ca.crt"
vault.hashicorp.com/agent-init-first: "true"
traffic.sidecar.istio.io/excludeOutboundPorts: "8200"
{{- $systemcontext := .Values.vault.systemcontext -}}
{{- $releasename := .Release.Name -}}
{{- range .Values.vault.secretkeys}}
{{- $secretpath := printf "kv/%s/restricted/%s/%s" $systemcontext $releasename . }}
{{- $annotatefilename := printf "vault.hashicorp.com/agent-inject-secret-%s.yaml" . }}
{{ $annotatefilename }}: {{ $secretpath }}
{{ $annotatefilename }}: |
  {{ printf "%s%s%s" `{{- with secret ` ($secretpath | quote) ` -}}{{ range $k, $v := .Data.data }}{{ $k }}: {{ $v }}
  {{ end }}{{- end -}}`}}
{{- end -}}

How can I define the template so that it can render both sets of annotations, even if vault.enabled=false, or deployment.annotations is of empty value?

Example our values.yml:

deployment:
  annotations:
    test-annotation: "hello world"
    test2-annotations: "foo"

vault:
  enabled: true
  role: myrole
  systemcontext: "foo"

Thanks

-- popopanda
hashicorp-vault
kubernetes
kubernetes-helm

1 Answer

2/26/2021

You can define the additional set of annotations as a named template, that emits key: value pairs, aligned at the first column.

<!-- language: lang-yaml -->
{{- define "annotations.vault" }}
{{- if .Values.vault.enabled -}}
vault.hashicorp.com/agent-inject: {{ .Values.vault.enabled | quote }}
...
{{ end -}}
{{ end -}}

Then when you need to use it, you can use the Helm include extension to invoke it. This returns a string, and so you can combine it with indent to indent it appropriately.

Your original template code uses with to skip the annotations: block entirely if nothing is present, so you can use this same technique at the top level. (You need to be careful that the template emits nothing, not even a new line, if the control is disabled.)

<!-- language: lang-yaml -->
metadata:
  labels: { as: above }
{{- with include "annotations.vault . }}
{{- . | indent 4 }}
{{- end }}

Inside the pod spec, there are two possibly places the annotations could come from. The easiest way to create a syntactically valid annotations: block is to include an artificial key: value pair:

spec:
  template:
    metadata:
      annotations:
        _: '' # meaningless, but forces a YAML dictionary
{{- with .Values.deployment.annotations }}
{{- toYaml . | indent 8 }}
{{- end }}
{{- with include "annotations.vault" . }}
{{- indent 8 . }}
{{- end }}

Or, you could capture both annotation sets into variables, and do logic based on that.

spec:
  template:
    metadata:
{{- $a := .Values.deployment.annotations }}
{{/*           if $a    then   (toYaml $a) else "" end */}}
{{- $manual :=    $a | ternary (toYaml $a)      "" }}
{{- $vault := include "annotations.vault" . }}
{{- $annotations := printf "%s%s" $manual $vault }}
{{- with $annotations }}
      annotations: {{- nindent 8 . }}
{{- end }}
-- David Maze
Source: StackOverflow