Helm optional nested variables

1/17/2020

How do I make an optional block in the values file and then refer to it in the template?

For examples, say I have a values file that looks like the following:

# values.yaml
foo:
   bar: "something"

And then I have a helm template that looks like this:

{{ .Values.foo.bar }}

What if I want to make the foo.bar in the values file optional? An error is raised if the foo key does not exist in the values.

I've tried adding as an if conditional. However, this still fails if the foo key is missing:

{{ if .Values.foo.bar }}
{{ .Values.foo.bar }}
{{ end }}

Any thoughts are much appreciated.

-- Joe J
go-templates
kubernetes
kubernetes-helm

3 Answers

3/8/2020

I searched around for an answer to this same question, and couldn't find anything out there. It seems you have to use a custom function, so I wrote one. Here is what I came up with. It works for my use cases, feedback/improvements are welcome.

_helpers.tpl

{{- define "hasDeepKey" -}}
  {{- $mapToCheck := index . "mapToCheck" -}}
  {{- $keyToFind := index . "keyToFind" -}}
  {{- $keySet := (splitList "." $keyToFind) -}}
  {{- $firstKey := first $keySet -}}
  {{- if index $mapToCheck $firstKey -}}{{*/ The key was found */}}
    {{- if eq 1 (len $keySet) -}}{{*/ The final key in the set implies we're done */}}
true
    {{- else }}{{*/ More keys to check, recurse */}}
      {{- include "hasDeepKey" (dict "mapToCheck" (index $mapToCheck $firstKey) "keyToFind" (join "." (rest $keySet))) }}
    {{- end }}
  {{- else }}{{/* The key was not found */}}
false
  {{- end }}
{{- end }}

values.yaml:

    {{- if eq "true" (include "hasDeepKey" (dict "mapToCheck" .Values "keyToFind" "foo.bar")) }}
      bar: {{- .Values.foo.bar }}
    {{- end }}
-- Samuel
Source: StackOverflow

1/18/2020

A technique I've used successfully is to use a variable to hold the value of the outer block, which then can use templating constructs like default and Sprig's dict helper.

{{- $foo := .Values.foo | default dict -}}
Bar is {{ $foo.bar | default "not in the values file" }}

This provides a fallback dictionary if foo isn't in the file, so then $foo is always defined and you can look up $foo.bar in it.

-- David Maze
Source: StackOverflow

1/18/2020

Most charts will default the parent object to a empty map in values.yaml so it always exists.

foo: {}

Then {{ if .Values.foo.bar }} works.

If that's not possible, test both keys:

{{ if .Values.foo }}
{{ if .Values.foo.bar }}
{{ .Values.foo.bar }}
{{ end }}
{{ end }}

Using the and function doesn't work in this case due to and evaluating all parameters, even if the first is falsey.

There is also the hasKey function included from sprig if you ever need to check the existence of a falsey or empty value:

{{ if hasKey .Values.foo "bar" }}
-- Matt
Source: StackOverflow