Kubernetes Helm chart if condition inside an array - kubernetes

I am trying to update this chart :
k8s.v1.cni.cncf.io/networks: '[
{ "name" : "ext", "ips": {{ .Values.global.extoamlancas0 | quote }}, "interface": "e1"},
{ "name" : "ext-app", "ips": {{ .Values.global.extapplancas0 | quote }}, "interface": "e3"},
{ "name" : "int-", "ips": {{ .Values.global.intoamlancas0 | quote }}, "interface": "e2"}
]'
Here
if {{- if Values.a }} then I want "ips" to be in an array i.e
{ "name" : "ext-", "ips": [ {{ .Values.global.extoamlancas0 | quote }} ], "interface": "e1"}
else
{ "name" : "ext", "ips": {{ .Values.global.extoamlancas0 | quote }}, "interface": "e1"}
I want this to be done for all other 2 ips too.

In the values.yaml file you need to specify an array ips like this:
ips:
- address: 192.168.1.1
name: no1
- address: 192.168.1.2
name: no2
And in the templates file you can loop like this:
{{- range .Values.ips }}
- name: {{ .name }}
address: {{ .address }}
{{- end }}
Below is the snippet from golang docs: template - Go | range
{{range pipeline}} T1 {{end}} The value of the pipeline must be an
array, slice, map, or channel. If the value of the pipeline has
length zero, nothing is output; otherwise, dot is set to the
successive elements of the array, slice, or map and T1 is executed.
If the value is a map and the keys are of basic type with a defined
order, the elements will be visited in sorted key order.
{{range pipeline}} T1 {{else}} T0 {{end}} The value of the pipeline
must be an array, slice, map, or channel. If the value of the
pipeline has length zero, dot is unaffected and T0 is executed;
otherwise, dot is set to the successive elements of the array, slice,
or map and T1 is executed.

Related

helm or operator with required function

I have to check for mandatory values based on some condition.
My values.yaml is as below
id: 3
test:
id: 2
test1:
id: 1
In my template, I need to check if id is present at .Values.test.test1.id and assign that value. If not fall back to .Values.test.id and at last .Values.id. But id has to be mandatory and I need to use required function.
My template is as below
{{- if .Values.test.test1.id }}
<assign> {{ .Values.test.test1.id }}
{{- else }}
{{- $id := .Values.test.id }}
{{- $id2 := .Values.id }}
<assign> <need to check required with or of $id $id2> </assign>
{{- end }}
I know this can be solved with one elseif in between if and else. But I need to repeat the same logic for many ids.
What is the best way to achieve this?
It seems like a perfect fit for the coalesce function, which
takes a list of values and returns the first non-empty one.
Source: https://helm.sh/docs/chart_template_guide/function_list/#coalesce
You will also need to use the default function to convert the cases when the test or test.test1 dictionaries are not defined.
All this together, gives:
{{- $test := .Values.test | default dict -}}
{{- $test1 := $test.test1 | default dict -}}
{{- $id := required "Please provide an ID" (
coalesce $test1.id $test.id .Values.id
) -}}
id: {{ $id }}
And here are the test cases and results:
Gives:
id: 1
when values.yaml is
id: 3
test:
id: 2
test1:
id: 1
Gives:
id: 2
when values.yaml is
id: 3
test:
id: 2
Gives:
id: 3
when values.yaml is
id: 3
Gives:
Error: execution error at (demo/templates/test.yaml:3:11):
Please provide an ID
when values.yaml is an empty file

Helm upgrade - preserve value of an environment variable when if condition is not met

I am doing helm upgrade --install with the --reuse-values option. I want to conditionally update the value of an environment variable if a certain condition is met and if the condition is not met preserve the already existing value. I've tried to achieve that this way:
env:
- name: RELEASE_DATE
value: "{{ if eq .Values.config.myCondition "testValue" }}{{ date "2006-01-02T15:04:05" .Release.Time }}{{ end }}"
or
env:
- name: RELEASE_DATE
value: "{{ if eq .Values.config.myCondition "testValue" }}{{ date "2006-01-02T15:04:05" .Release.Time }}{{ else }}{{ .Values.someOtherValue }}{{ end }}"
where .Values.someOtherValue is never really existing, or even doing it this way
env:
{{- if eq .Values.config.deployedService "ftmFrontend" }}
- name: RELEASE_DATE
value: "{{ if eq .Values.config.myCondition "testValue" }}{{ date "2006-01-02T15:04:05" .Release.Time }}{{ end }}"
{{- end }}
I was expecting that if I use a --reuse-values option that the values are going to be kept/preserved if the condition is not met, but instead it always deletes it and leaves it empty.
The option does work however, when there is no condition clause and only the value from the chart is referenced. It's only not working if the if statement is used.
How can I use if statement and still preserve the value if the condition is not met?

Is it possible to neatly flatten a list of maps from values.yaml by selecting a key present in each map?

Suppose I have a values.yaml that contains the following arbitrary-length list of maps:
values.yaml
-----------
people:
- name: "alice"
age: 56
- name: "bob"
age: 42
One of the containers in my stack will need access to a list of just the names. (NAMES="alice,bob")
So far, I have come up with the following solution:
templates/_helpers.tpl
----------------------
{{- define "list.listNames" -}}
{{ $listStarted := false }}
{{- range .Values.people }}
{{- if $listStarted }},{{- end }}{{ $listStarted = true }}{{ .name }}
{{- end }}
{{- end }}
templates/configmap.yaml
------------------------
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
NAMES: '{{- include "list.listNames" . }}'
This solution works, but it feels inelegant. Looking through the Helm documentation, it seems like this would be the perfect use case for pluck in combination with join, but I haven't found a way to use .Values.people as an argument to that function.
Is there a cleaner way to do this?
There's a join function.
I've two different solutions to utilize join:
Solution 1
I can't collect the names in a list. Therefore I use a dict and extract the keys later:
{{- define "list.listNames" -}}
{{- $map := dict }}
{{- range .Values.people }}
{{- $_ := set $map .name "" }}
{{- end }}
{{- keys $map | join "," }}
{{- end }}
This solution might change the order. Perhaps sortAlpha could fix this.
Solution 2
Because function must return a map, I need to add a key. Otherwise you can't convert to a dict by fromYaml:
templates/_helpers.tpl
----------------------
{{- define "list.listNames" -}}
items:
{{- range .Values.people }}
- {{ .name }}
{{- end }}
{{- end }}
Then I convert to a dict object by fromYaml and join the list:
templates/configmap.yaml
------------------------
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
{{- $items := include "list.listNames" . | fromYaml }}
NAMES: {{ $items.items | join "," | quote }}

Create entry in helm template only if an entry exists in a map

Assuming this map exists in my values.yaml
something:
somethingElse:
variable1: value1
variable2: value2
variable3: value3
I want to create a helm template for a kubernetes Secret resource (although this is not of primary importance) if and only if say the key - value pair variable2: value2 exists. (I am only actually interest to match the variable2's existence, not what is the value of value2)
I know how to range to include all entries
{{- range $name, $value := .Values.something.somethingElse }}
{{indent 4 $name }}: {{ $value }}
{{- end }}
but in pseudocode, what I want is
if variable2 in .Values.something.somethingElse
variable2: value2
Is this somehow feasible using the helm templating language?
You can do:
{{if (index .Values.something.somethingElse "variable2")}}
Helm contain a flow control and you can create if / else conditional blocks, for your case this example can be helpful.
In your values.yaml
foo:
enabled: true
So, in your template:
{{- if .Values.foo.enabled }}
--- TEMPLATE CODE --
{{- end }}
Ref: https://helm.sh/docs/chart_template_guide/control_structures/

How to combine multiple dict variables into an array based on regex of dict variable names

I have multiple dict variables in my inventories that start with 'my_var_*'. I would like to combine these into an array of dicts named 'my_var'
In my playbook, I'm using 'set_fact:' to create the 'my_var' variable by attempting to pull the matching variables from "hostvars['localhost']" with a select filter and match regex, but join only works on strings.
variables.yml
my_var_1:
element1: value11
element2: value12
my_var_2:
element1: value21
element2: value22
playbook.yml
- hosts: localhost
connection: local
gather_facts: False
tasks:
- set_fact:
my_var: "{{ hostvars['localhost'] | select('match', '^my_var_*') | join(', ' }}"
- debug:
msg: "{{ my_var }}"
is it possible to join these 'dict' variables into an 'array' like this?
my_var:
- element1: value11
element2: value12
- element1: value21
element2: value22
or possibly even
my_var:
- name: 1
element1: value11
element2: value12
- name: 2
element1: value21
element2: value22
You're very close, but as you point out, the join method on a string is for joining strings. You want to append lists, which you accomplish with the + operator.
There are also a few other issues:
The expression:
hostvars['localhost'] | select('match', '^my_var_*')
Will produce a list that looks like:
[
"my_var_1",
"my_var_2"
]
...which isn't what you want. You want the values of these variables, not the key names. We can use the dict2items filter and the selectattr filter to generate the data we want:
---
- hosts: localhost
gather_facts: false
tasks:
- name: set facts on localhost
set_fact:
my_var_1:
element1: value11
element2: value12
my_var_2:
element1: value21
element2: value22
- hosts: localhost
gather_facts: false
tasks:
- name: merge vars into my_var
set_fact:
my_var: "{{ hostvars['localhost']|dict2items|selectattr('key', 'match', '^my_var_')|map(attribute='value')|list }}"
- name: show content of my_var
debug:
var: my_var
This will produce the following output:
TASK [show content of my_var] ************************************************************************************
ok: [localhost] => {
"my_var": [
{
"element1": "value11",
"element2": "value12"
},
{
"element1": "value21",
"element2": "value22"
}
]
}
If you get rid of the map(attribute='value') filter, you get:
TASK [show content of my_var] *****************************************************************************************
ok: [localhost] => {
"my_var": [
{
"key": "my_var_1",
"value": {
"element1": "value11",
"element2": "value12"
}
},
{
"key": "my_var_2",
"value": {
"element1": "value21",
"element2": "value22"
}
}
]
}
This isn't exactly what you ask for as the second option, but it does include both the key name and values.
Additional notes:
In the above, I've used a separate play running set_fact to set the values of these variables, because this solution will only work if the variables are host vars (aka "facts") rather than global variables. You don't show in your question how you're setting these variables so I don't know if this will all work as written.
In a regular expression, * means "the preceding character zero or more times", so the expression ^my_var_* would match my_var, my_var_1, my_var______________, my_varfoo, and so forth. You can simply write ^my_var_ to select the variable names in which you're interested (this will select anything that begins with the text my_var_).