Can we use OR operator in Helm yaml files - kubernetes-helm

Can I do something like this in Helm yamls :
{{- if eq .Values.isCar true }} OR {{- if eq .Values.isBus true }}
# do something
{{- end }}
I understand that we can do a single if check. But how would I check for multiple conditions? Are there some operators equivalent to OR and AND?

As indicated in the Helm documentation on operators:
For templates, the operators (eq, ne, lt, gt, and, or and so on) are all implemented as functions. In pipelines, operations can be grouped with parentheses ((, and )).
It means you could use
{{- if or (eq .Values.isCar true) (eq .Values.isBus true) }}
Furthermore, as noted in the if/else structure:
A pipeline is evaluated as false if the value is:
a boolean false
a numeric zero
an empty string
a nil (empty or null)
an empty collection (map, slice, tuple, dict, array)
Under all other conditions, the condition is true.
If your properties (isCar and isBus) are booleans, you can then skip the equal check:
{{- if or .Values.isCar .Values.isBus }}

Note that or can also be used instead of default like this:
{{ or .Values.someSetting "default_value" }}
This would render to .Values.someSetting if it is set or to
"default_value" otherwise.

Related

Helm equals to one of multiple values

I want to have a condition that will be considered as "true" if .Values.envName is "dev" + the release namespace name is one of a closed list. otherwise, it should be "false".
Tried the following, but seems like it gets unexpected "false" when I ran it with .Values.envName = dev & .Values.envName = ns-1:
env:
- name: MY_ENV
{{- if and (eq .Values.envName "dev") (regexMatch "^(?!^ns-1$)(?!^ns-2$).*$" .Release.Namespace)}}
value: 'true'
{{- else }}
value: 'false'
{{- end }}
A general note - if there is a better way to use eq with multiple possible values please let me know.
You can use has to test if some item is in a list; and then you can use list to construct a list of known items.
So for your example, if the test is that envName is exactly dev, and the namespace is either ns-1 or ns-2, then you can say
env:
{{- $isDev := eq .Values.envName "dev" }}
{{- $isNs := list "ns-1" "ns-2" | has .Release.Namespace }}
- name: MY_ENV
value: {{ and $isDev $isNs | toString | quote }}
You could do this case with a regular expression too. The start-of-string test ^ and the end-of-string test $ generally need to be outside parentheses; I wouldn't worry about "non-greedy matches" if you're just trying to determine whether a string matches without extracting things. Here I might write either of
{{- $isNs := regexMatch "^ns-[12]$" .Release.Namespace }}
{{- $isNs := regexMatch "^ns-1|ns-2$" .Release.Namespace }}
If you've got an enumerated list of values that don't neatly fall into a regex, using has and list is probably clearer.

Go template: is there any item in list of objects with a specific attribute value?

I'm using helm (sprig, go templates). I'm trying to build guards to selectively include stuff in my helm chart, but only if one of the components needs them.
So, I have a list:
- name: foo
flag1: true
flag2: false
flag3: false
- name: bar
flag1: false
flag2: true
flag3: false
I want to do something akin to a (pseudocode) list.any(flag), where over a variable length list, if I passed in flag1 or flag2 I'd get back true, but flag3 would get me false. If possible, I'd like to be able to ask about a different flag without repeating myself each time.
Is there a concise way to accomplish this? Can it be done?
The set of things that are and aren't possible in Go templates can be a little mysterious. A named template always returns a string, but an empty string is logically "false", so it should be possible to write a template call like
{{- if (include "list.any" (list .Values.options "flag2")) }}
...
{{- end }}
A template only takes a single parameter, so in the call we've packed the multiple inputs we need into a list. We've also used the Helm-specific include function to invoke a template and get its output as a string.
How can the template work? Template range loops don't have break or return actions or any other way to stop early. If we only want to output the "success" value once, this means we need to manually iterate through the list. For reasonably short lists, a recursive template call works here.
(For this specific thing, outputting yes or yesyesyes would both be non-empty and therefore logically "true", so you could use a range loop here successfully. This would not work for an equivalent list.all, though.)
In the template definition
{{- define "list.any" -}}
...
{{- end -}}
we need to start by unpacking the parameter list
{{- $list := index . 0 -}}
{{- $search := index . 1 -}}
We only do something if the list is non-empty.
{{- if $list -}}
...
{{- end -}}
If it is non-empty, we can split out its first element. We expect that to be a map, so we can look up the requested key in that with the standard index function. This will return nil if the key is absent and false if it's false, both of which are logically false; if it's true then the if test will pass.
{{- if index (first $list) $search -}}
...
{{- else -}}
...
{{- end -}}
If we do find the item, we can write out a success value and not do anything else
yes
If we don't, then we can recursively call ourselves with the remainder of the list.
{{- include "list.any" (list (rest $list) $search) -}}
Combining that all together gives this template (indented for clarity, the - markers will consume all of the whitespace):
{{- define "list.any" -}}
{{- $list := index . 0 -}}
{{- $search := index . 1 -}}
{{- if index (first $list) $search -}}
yes
{{- else -}}
{{- include "list.any" (list (rest $list) $search) -}}
{{- end -}}
{{- end -}}

Helm 'if' condition in one line

I was trying to put the if condition in a single line of the helm template:
- name: ENV_VARIABLE
value: {{- if .Values.condition }}"Value1"{{- else }}"Value2"{{- end }}
However, I'm getting error:
Error: YAML parse error on chart/templates/deployment.yaml: error
converting YAML to JSON: yaml: line NN: could not find expected ':'
So I've ended up with multi-line condition:
- name: ENV_VARIABLE
{{- if .Values.condition }}
value: "Value1"
{{- else }}
value: "Value2"
{{- end }}
which is working fine, but is very uncompact.
Is there a way to use one-liner if condition in helm?
What you do works, but you use the leading hyphen, which removes all preceding whitespace.
The below will render as value:foo due to the hyphen.
value: {{- "foo" }}
If you remove the hyphen, it should work.
That said, there is also the ternary function which could fit here.
ternary
The ternary function takes two values, and a test value. If the test value is true, the first value will be returned. If the test value is empty, the second value will be returned. This is similar to the c ternary operator.
true test value
ternary "foo" "bar" true
or
true | ternary "foo" "bar"
- name: ENV_VARIABLE
value: {{ .Values.condition | ternary "value1" "value2" | quote }}

Usage of variables in Helm

I'm using a Helm chart and I was wondering how I can define a value by default. In my case, I wanna define a date when it isn't defined in values.yaml and I have the following code:
{{- if ne .Value.persistence.home.restoreBackup.date "" }}
{{- $bkDate := .Value.persistence.home.restoreBackup.date }}
{{- else }}
{{- $bkDate := "2022-01-01" }}
{{- end }}
I wanna set $bkDate to an specific date if it is not defined in .Value.persistence.home.restoreBackup.date but when I try to print $bkDate it is empty.
Do you know what is wrong here?
The Go text/template documentation notes (under "Variables"):
A variable's scope extends to the "end" action of the control structure ("if", "with", or "range") in which it is declared....
This essentially means you can't define a variable inside an if block and have it visible outside the block.
For what you want to do, the Helm default function provides a straightforward workaround. You can unconditionally define the variable, but its value is the Helm value or else some default if that's not defined.
{{- $bkDate := .Value.persistence.home.restoreBackup.date | default "2022-01-01" -}}
(Note that a couple of things other than empty-string are "false" for purposes of default, including "unset", nil, and empty-list, but practically this won't matter to you.)
If you need more complex logic than this then the ternary function could meet your needs {{- $var := $someCondition | ternary "trueValue" "falseValue" -}} but this leads to hard-to-read expressions, and it might be better to refactor to avoid this.
Try
{{- if ((.Value.persistence.home.restoreBackup).date) }}
{{- $bkDate := .Value.persistence.home.restoreBackup.date }}
{{- else }}
{{- $bkDate := "2022-01-01" }}
{{- end }}
You can check the with option : https://helm.sh/docs/chart_template_guide/control_structures/#modifying-scope-using-with
{{ with PIPELINE }}
# restricted scope
{{ end }}

How to fetch a value inside an array using helm template

I have the following structure:
label:
foo:
bar:
- x: hello
y: hallo
z: hola
In order to reach the value of z, I am currently doing:
{{ $bar := pick .Values.label.foo "bar" }}
{{ $firstItem := first $bar }}
{{ $myValue := get $firstItem "z" }}
Is there a more concise way to do this? I tried something like pick .Values.label.foo[0].z but that does not work
Since the values structure you show is just simple string-keyed dictionaries and lists, you don't need functions like pick or get; you can just use the . operator to retrieve a specific key from the containing dictionary.
{{ $bar := .Values.label.foo.bar }}
{{ $firstItem := first $bar }}
{{ $myValue := $firstItem.z }}
Then you can replace the variable references with their expressions, using (...) parenthesis grouping if needed.
{{ $myValue := (first .Values.label.foo.bar).z }}
You can also use the standard index function here; I believe it is legal to mix array and map keys. This will give you a single call, though a mix of indexing syntax.
{{ $myValue := index .Values.label.foo.bar 0 "z" }}
{{ $myValue := index .Values "label" "foo" "bar" 0 "z" }}