If clause in helm chart - if-statement

How can I check if a variable is a boolean value "true" in helm chart?
I have in Values.yaml a parameter set as:
myParameter: true
I don't understand well difference between:
{{- if .Values.service.myParameter }}
and
{{- if eq .Values.service.myParameter "true" }}
I want that flow goes in if clause if the parameter is set as boolean "true"

The documentation for the Go text/template package explains what the if statement considers as "true":
The empty ["false"] values are false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero.
YAML values are typed and the usual rules are that bare true and false are booleans:
boolean: true
string: other text, but not "true", "false", or "null"
also a string: 'true'
In your examples:
{{- if .Values.service.myParameter }}...{{ end }}
will evaluate to true whenever myParameter exists (isn't Go nil) and isn't zero, literal false, or an empty string.
{{- if eq .Values.service.myParameter "true" }}...{{ end }}
will evaluate to true whenever myParameter is exactly the string "true", but not a boolean true. (I think you will get a type error from the template engine if it's a boolean, in fact.)

You can use the following snipped for checking the boolean value
{{if (default .Values.seLinux true)}}
securityContext:
seLinuxOptions:
user: system_u
{{ end }}
Then the Values file will have following snippet
seLinux: true
Please let me know if this helps.

We have some template engine for the values that rendered boolean as strings, but then in other places we were passing boolean.
Because that, our chart had to support both a boolean true|false or strings "true"|"false".
But the helm templating (I guess golang) keeps converting a "true" to boolean, but not "false", meanwhile the boolean operators consider any string as true... Also, the eq fails if you compare a bool and a string, so you must convert it.
The easiest is to set the value to empty string, that evaluates as false in the if https://golang.org/pkg/text/template/#hdr-Actions, so:
rbac:
enabled: ""
After lots of tests, I came to this expression:
value: |
{{ .Values.rbac.enabled }}
toString: |
{{ toString .Values.rbac.enabled }}
evaluated: |
{{ and (not (eq (toString .Values.rbac.enabled) `"false"`)) .Values.rbac.enabled }}
{{- if and (not (eq (toString .Values.rbac.enabled) `"false"`)) .Values.rbac.enabled }}
isTrue: yeah
{{- end }}
So, this will check if the string version of the value is not the string "false", if it is not, then evaluate the value.
It works for all combinations:
for i in true '"true"' false '"false"'; do helm template charts/foo/ --show-only templates/rbac.yaml --set "rbac.enabled=$i"; done
---
# Source: foo/templates/rbac.yaml
value: |
true
toString: |
true
evaluated: |
true
isTrue: yeah
---
# Source: foo/templates/rbac.yaml
value: |
"true"
toString: |
"true"
evaluated: |
"true"
isTrue: yeah
---
# Source: foo/templates/rbac.yaml
value: |
false
toString: |
false
evaluated: |
false
---
# Source: foo/templates/rbac.yaml
value: |
"false"
toString: |
"false"
evaluated: |
false

You can use this notation to judge boolean values
{{- if eq .Values.service.myParameter true }}
{{- end }}

Related

Ansible jinja2 regex search fails to properly find the beginning of string

I'm using
ansible 2.9.27
ansible python module location = /usr/lib/python2.7/site-packages/ansible
executable location = /bin/ansible
python version = 2.7.5 (default, May 27 2022, 11:27:32) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
Pursuant to my question here: Ansible set_fact list using jinja , I am trying to create a list which is derived from a list, but with an element removed: if it contains "default" at the beginning of the list element string, remove it. But why doesn't the regex work properly? The sticking point is the beginning of string operator.
Here is my list:
[ u'default via 10.13.99.1 dev bond0.99',
u'10.13.101.0/23 dev p1p1.101',
u'128.1.66.227 via 10.13.201.1 dev p1p1.201' ]
It's called found_routes. When I try to filter out the first entry, the default route, I attempt:
- name: Populate appended route list
set_fact:
found_added_routes: "{{ found_routes | select('search', ' via ') | reject('regex', '^default') | list }}"
That doesn't work. But this does:
- name: Populate appended route list
set_fact:
found_added_routes: "{{ found_routes | select('search', ' via ') | reject('regex', '^..default') | list }}"
...what are those two invisible characters that my regex is finding?
Here is how I create output:
- name: Debug found_routes
debug:
msg: "Found route list: {{ item }}"
loop: "{{ found_routes }}"
- name: Debug found_added_routes
debug:
msg: "Found: {{ item }}"
loop: "{{ found_added_routes }}"
And here's the output from the successful set_fact:
TASK [networks : Debug found_route_list] **********************************************************************************************************************
ok: [server01.example.com] => (item=default via 10.13.99.1 dev bond0.99) => {
"msg": "Found route list: default via 10.13.99.1 dev bond0.99"
}
ok: [server01.example.com] => (item=10.13.101.0/24 dev p1p1.101) => {
"msg": "Found route list: 10.13.101.0/24 dev p1p1.101"
}
ok: [server01.example.com] => (item=128.1.66.227 via 10.13.101.1 dev p1p1.101) => {
"msg": "Found route list: 128.1.66.227 via 10.13.101.1 dev p1p1.101"
}
TASK [networks : Debug found_added_routes] **********************************************************************************************************************
ok: [server01.example.com] => (item=10.13.101.0/24 dev p1p1.101) => {
"msg": "Found: 10.13.101.0/24 dev p1p1.101"
}
ok: [server01.example.com] => (item=128.1.66.227 via 10.13.101.1 dev p1p1.101) => {
"msg": "Found: 128.1.66.227 via 10.13.101.1 dev p1p1.101"
}
But as you can see, in order to get rid of that first "default" route, I needed to use ^..default instead of ^default and I'm at a loss to understand why.
OK, because of the jinja2 magic that I did in my original question, which was to do this:
- name: Populate appended route list
set_fact:
found_added_routes: |-
{%- set blah = [] %}
{%- for line in found_route_list %}
{%- set _ = blah.append(line) %}
{%- endfor %}
{{ blah }}
...the output was a string that had elements literally like this: u'default via 10.13.99.1 dev bond0.99'
So the string begins not with default... but rather with a literal 'u' and tick mark, sa: u'default...
The first two characters of the string are u and '. Not d and e as I was expecting.
My mistake!

Performing regex_findall and split() on the same line

Here is the output that I'm trying to parse:
hostname#show bgp vrf vrfname summary | i 1.1
BGP Route Distinguisher: 1.1.1.1:0
BGP router identifier 1.1.1.1, local AS number 2222
1.1.1.3 0 64512 349608 316062 896772 0 0 2w4d 1
I have the following regex that succesfully matches just the last line. Now I need to split that line and view the last index. In this case it is "1", but I will want to fail if that value is "0".
- name: debug test
debug:
msg: "{{show_bgp_sessions.data | regex_findall('\\d+\\.\\d+\\.\\d+\\.\\d+\\s\\s.*')}}"
I tried adding a split in a couple different formats at the end of the "msg" line so that I can grab the last index to compare it in the failed_when statement:
msg: "{{show_bgp_sessions.data | regex_findall('\\d+\\.\\d+\\.\\d+\\.\\d+\\s\\s.*') | split(' ')}}"
But I'm getting the following error msg:
"template error while templating string: no filter named 'split'. String:
I've also tried to use a few different forms of "ends_with" to verify the last index in the string as I've used that a lot in my python experience, but I can't get it to work in ansible.
I can't create a new task to parse the data and perform the split seperately because I need to run this verification through a loop.
When you select the line, reverse the string, and split the first item. For example
msg: "{{ (my_line|reverse).split()|first }}"
Possibly the regex provided by #Thefourthbird is a better solution.
But for your issue at hand, this is caused by the fact that there is indeed no filter split in Jinja, see the list there: https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-builtin-filters.
The reason why there is no such a filter is simple: split() is a function of the Python String, and since Jinja is Python, you can just use it as is.
Also mind that, since regex_findall is meant for multiple matches, you'll have to select the first element of the list, for example, with the filter first.
So your message ends up being:
msg: >-
{{
(
show_bgp_sessions.data
| regex_findall('\\d+\\.\\d+\\.\\d+\\.\\d+\\s\\s.*')
| first
).split()
}}
Given the playbook:
- hosts: all
gather_facts: no
vars:
show_bgp_sessions:
data: |
hostname#show bgp vrf vrfname summary | i 1.1
BGP Route Distinguisher: 1.1.1.1:0
BGP router identifier 1.1.1.1, local AS number 2222
1.1.1.3 0 64512 349608 316062 896772 0 0 2w4d 1
tasks:
- debug:
msg: >-
{{
(
show_bgp_sessions.data
| regex_findall('\\d+\\.\\d+\\.\\d+\\.\\d+\\s\\s.*')
| first
).split()
}}
Gives the recap:
TASK [debug] ***************************************************************
ok: [localhost] => {
"msg": [
"1.1.1.3",
"0",
"64512",
"349608",
"316062",
"896772",
"0",
"0",
"2w4d",
"1"
]
}

Extracting all named values from an ansible array

I'm not even quite sure how to describe what I'm trying to do!
So I'll just jump straight in.
I have a hosts file :
[jvbservers]
jvb0 ansible_host=serverA
jvb1 ansible_host=serverB
etc...
And I want a list of the servers in the jvbservers group. What I've come up with so far feels a bit OTT.
set_fact:
jvb_names: "{{ jvb_names|default([]) + [ hostvars[groups.jvbservers[item|int]].ansible_host ] }}"
with_sequence: start=0 end="{{groups.jvbservers|length-1}}"
Gives me a result like I want :
ok: [localhost] => {
"jvb_names": [
"serverA",
"serverB"
]
}
Is this sensible? It feels like there should be a neater way to do something like :
hostvars[groups.jvbservers[*]].ansible_host
(Where "*" would be a pattern matching all valid options. Obvs that particular syntax doesn't exist!)
You may want to check extract filter:
{{ groups['jvbservers'] | map('extract', hostvars, 'ansible_host') | list }}
Have you tried this?:
- name: show all the hosts matching jvbservers
debug:
msg: "{{ item }}"
with_inventory_hostnames:
- all: jvbservers
https://docs.ansible.com/ansible/devel/plugins/lookup/inventory_hostnames.html

Ansible variable manipulation

I have a variable in my playbook that has a number of values separated by commas. At this point I am not sure if the variable is a string or a list. I believe from the output below the "[]" indicate a list.
Variable populated
-set_fact:
snap_master_01: "{{ ec2_snapshot_facts.snapshots |
selectattr(tags.HostName, equalto, ICINGA2_MASTER_1.tag_value) |
sort(attribute=start_time) | reverse | map(attribute=snapshot_id) | list }}"
- name: Print snapshot ID's
debug:
msg:
- "{{ snap_master_01 }}"`
Gives the following output:
`TASK [Print snapshot ID's] ******************************************************************************** ***********************
task path: /home/r_ansible/playbooks/backup_aws.yml:252
ok: [172.16.1.58] => {
"changed": false,
"msg": [
[
"snap-04c88ef6XXXXXXXXX",
"snap-0bd5785fXXXXXXXXX",
"snap-045e0f4bXXXXXXXXX",
"snap-055fda51XXXXXXXXX",
"snap-03759206XXXXXXXXX"
]
]
}`
I would like to delete the last 3 values. What is the best way of achieving this in Ansible?
To manipulate lists in Ansible, you can use Python slices.
In your case snap_master_01[:-3] will give you all but last three elements.

Update value in soy template declared with let

I have one value that is tied to a flag that comes from a config file that I need to show in my soy template. It is either true or false.
If true, the value needs to be "x" (for example, but it is a string)
If false, the value needs to be "" (empty)
Notice that I cannot pass in the true or false value from my config. I also cannot omit the value on false, it has to supply an empty string.
I've tried various forms of if statements using let, but according to my interpretations of the docs, a value declared with let cannot be changed (which doesn't make sense)
This is basically what I need:
{if $inputValue.value == 'true'}
{let $myVar: ($someValueThatExistsInMyTemplate) /}
{else}
{let $myVar: '' /}
{/if}
Then I will use $myVar in my template. However, whenever I try that, I get this error:
Found references to data keys that are not declared in SoyDoc: [myVar]
What can I do!?
Got it working using the ternary operator:
{let $dataParent: ($item.preferences.accordionOnOff.value == 'true') ? ($item.name) : '' /}