Reputation: 10771
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 }}
Upvotes: 78
Views: 67979
Reputation: 74630
Most charts will default the parent object to an empty map in values.yaml
so it always exists and first level checks {{ if .Values.foo.bar }}
will work.
foo: {}
Test each key in order with parenthesis (added from Torrey's better solution):
{{ if ((.Values.foo).bar) }}
bar: {{ .Values.foo.bar }}
{{ end }}
Use the and
function (helm 3.10+/go 1.18+ thanks
@nicolauscg)
{{ if (and .Values.foo .Values.foo.bar) }}
bar: {{ .Values.foo.bar }}
{{ end }}
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" }}
Upvotes: 51
Reputation: 1591
Wrap each nullable level with parentheses ()
.
{{ ((.Values.foo).bar) }}
Or
{{ if ((.Values.foo).bar) }}
{{ .Values.foo.bar }}
{{ end }}
Helm uses the go text/template
and inherits the behaviours from there.
Each pair of parentheses ()
can be considered a pipeline
.
From the doc (https://pkg.go.dev/text/template#hdr-Actions)
It is:
The default textual representation (the same as would be printed by fmt.Print)...
With the behaviour:
If the value of the pipeline is empty, no output is generated... The empty values are false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero.
As such, by wrapping each nullable level with parentheses, when they are chained, the predecessor nil pointer gracefully generates no output to the successor and so on, achieving the nested nullable fields workaround.
Upvotes: 149
Reputation: 7560
with
Look at the with
operator. This limits the current scope to the level of .Values.foo
, and the block is silently ignored if .foo
is missing:
{{- with .Values.foo }}
{{- .bar }}
{{- end }}
Upvotes: 24
Reputation: 1344
There is a new function implemented in sprig called dig
that just does fix this issue, see here http://masterminds.github.io/sprig/dicts.html .
Is not yet released so even less likely to be in helm soon.
Meanwhile I have modified @Samuel solution to mimic the new dig function.
{{- define "dig" -}}
{{- $mapToCheck := index . "map" -}}
{{- $keyToFind := index . "key" -}}
{{- $default := index . "default" -}}
{{- $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 */}}
{{- index $mapToCheck $firstKey -}}
{{- else }}{{/* More keys to check, recurse */}}
{{- include "dig" (dict "map" (index $mapToCheck $firstKey) "key" (join "." (rest $keySet)) "default" $default) }}
{{- end }}
{{- else }}{{/* The key was not found */}}
{{- $default -}}
{{- end }}
{{- end }}
and you can call it like this
$regKey := include "dig" (dict "map" .Values "key" "global.registryKey" "default" "")
Upvotes: 9
Reputation: 31
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 }}
Upvotes: 3
Reputation: 158666
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.
Upvotes: 19