2D array in YAML - arrays

I've a helm chart with attribute 2darrayIPs. This attribute takes value from values.yaml file which inturn is given via helm installation command
helm-chart/templates/main.yaml
2darrayIPs: {{ .Values.2darrayIPs }}
helm-chart/values.yaml
2darrayIPs: [[]] -- empty array, this value is given via installation command
I'm passing 2d array via helm command while installing helm chart.
helm install ..... -f val.yaml
val.yaml
2darrayIPs:
- - 1.1.1.1
- 2.2.2.2
- - 3.3.3.3
- 4.4.4.4
I'm getting this error while installing helm chart:
Error: YAML parse error on templates/main.yaml: error converting YAML to JSON: yaml: did not find expected ',' or ']'
If I give one array as given below, the installation is successful but in my logs I get a single array with only one value instead of two:
[[1.1.1.1 2.2.2.2]]
val.yaml
2darrayIPs:
- - 1.1.1.1
- 2.2.2.2
Where am I going wrong?

If your templates are trying to write out something more complex than a simple string, the default {{ .Values.name }} serialization is something Go-native that's not especially useful. Helm includes a toJson template function, and also an undocumented toYaml, which can write these out in more useful formats.
# as an array of arrays, in JSON syntax
2darrayIPs: {{ .Values.2darrayIPs | toJson }}
# as an array of arrays, in expanded YAML syntax
# (identical to the previous, but `helm template` output will be
# easier to read)
2darrayIPs: {{- .Values.2darrayIPs | toYaml | nindent 2 }}
# as a YAML-encoded string; for example in a ConfigMap
2darrayIPs: |-
{{ .Values.2darrayIPs | toYaml | indent 2 }}

This is valid yaml. If possible, you should raise an issue on the repository of the yaml parser used here. In the meantime, Yaml is a superset of JSON, so you can use plain JSON 2D arrays:
2darrayIPs: [["1.1.1.1","2.2.2.2"]]

Related

helm : error converting YAML to JSON: yaml: line xx: did not find expected key

I have a json encoded string in my values.yaml file ->
values.yaml
network:
cidrs : "[\"123.123.123.123/32\",\"123.124.125.125/32\"]"
Now , I want to use this value as a list of string in my network policy egress ipblock. But I'm not able to convert it to list.
Currently, I'm following this to achieve the requirement , but it fails -
error converting YAML to JSON: yaml: line xx : did not find expected key
netpol.yaml
spec:
podSelector:
matchLabels:
name: log-forwarder
policyTypes:
- Egress
egress:
{{- $json := .Values.network.cidrs | fromJson -}}
{{- range $json }}
- to:
- ipBlock:
cidr: {{- . }}
{{- end }}
ports:
- protocol: TCP
port: 443
Any idea , how to convert the encoded string to list of string and use it in my network policy ?
Use mustFromJson instead of fromJson, had the same problem recently and that fixed it, gonna have a look in the docs to see if I find out why.
edit: for some reason fromJson cant handle top level lists, but mustFromJson can, looks like a bug as the only difference listed in the docs is that mustFromJson returns an error in case the JSON is invalid.
source

ansible apt-key module with loop

I'm provisioning a system that requires multiple GPG keys to be added. I'm attempting to streamline the process and follow DRY principals.
I have apt packages installing from a vars list like so:
- name: Install packages
apt: name={{ apt_packages }}
Where my vars.yml looks like this:
apt_packages:
- tilix
- terraform
- ansible
- opera
This works because the apt module accepts comma separated inputs and parses accordingly.
So I'm trying to achieve a similar process when using the apt_key module but I can't seem to get it to work. Here are a couple of attempts I've made:
- name Add keys
apt_key:
url: url="{{ items }}"
loop: "{{ gpg_keys }}"
state: present
and
- name: Add GPG Keys
apt_key:
url: url="{{ gpg_keys }}"
state: present
Both throw different errors.
Is it possible to do something like this using the apt-key module? Obviously I'm trying to avoid having a separate caller for each key I want to add as there will be many keys and I'd like to be able to add additional keys later on by simply appending the list in vars.yml.
You have a few small mistakes in your task.
The right way is this:
- name: Add keys
apt_key:
url: "{{ item }}"
state: present
loop: "{{ gpg_keys }}"
you already have the key url, so prepending url= is incorrect
loop is an argument to the task and not to the apt_key module, so it needs to be indented to the level of apt_key (unlike url which is an argument to the model)
Sidenotes:
You also need to make sure that gpg_keys contains a list, similar to apt_packages.
The name parameter of apt accepts a list, as you define correctly in your vars.yml, no comma-separated string. (You are already doing it right)
Documentation:
apt
apt_key

Asciidoc archetype for Hugo

I just started to use Hugo and I want to use it with Asciidoc.
The default "archetype" (i.e. template) is in markdown (default.md). Anyone knows how to get an archetype for Asciidoc?
In particular, shall I rename to default.adoc?
This is how the default archetype looks like.
default.md
---
title: "{{ replace .TranslationBaseName "-" " " | title }}"
date: {{ .Date }}
draft: true
---
It shall be kept as default.md.
This only represents the font-matter that will be on top of the files and is not linked to any type (i.e. MarkDown or AsciiDoc, it will be the same).

How can I get a list of child groups in Ansible?

I have an inventory file that looks like this:
[master]
host01
[nl]
host02
[us]
host03
[satellites:children]
nl
us
How can I get a list of groups that have satellites as their parent?
I am looking for a solution that works similar to this:
- debug: msg="{{ item }}"
with_items: "{{ groups['satellites:children'] }}"
Update:
The only solution I was able to come with is this:
- debug: {{ item }}
with_items: "{{ groups }}"
when: item != "master" and item != "satellites" and item != "all" and item != "ungrouped"
But that is not very flexible.
You can try the following approaches:
vars:
parent: 'satellites'
tasks:
# functional style
- debug: msg="{{hostvars[item].group_names | select('ne', parent) | first}}"
with_items: "{{groups[parent]}}"
# set style
- debug: msg="{{hostvars[item].group_names | difference(parent) | first}}"
with_items: "{{groups[parent]}}"
Also select('ne', parent) is interchangeable with reject('equalto', parent) depending on what is more readable to you.
Links:
set theory operator
select and reject filters
Updated answer based on comments. inspiration from this thread.
vars:
parent: 'satellites'
tasks:
- name: build a subgroups list
set_fact: subgroups="{{ ((subgroups | default([])) + hostvars[item].group_names) | difference(parent) }}"
with_items: "{{groups[parent]}}"
- debug: var=subgroups
output:
"subgroups": [
"nl",
"us"
]
There is also another way where you process the file as text file with bash commands like awk
If file is with contents like
cat /tmp/test.ini
[group1]
host1
host2
[group2]
host3
host4
[first_two_groups:children]
group1
group2
[group3]
host5
host6
[group4]
host7
host8
Then you can use one command like this for when file has Linux EOL:
awk "/first_two_groups\:children/,/^$/" /tmp/test.ini | grep -v children
group1
group2
Where group to find children of is called first_two_groups and it can be anywhere in the file. Command works as long as after the group definition there is empty line which is used as anchor to end the awk stream. I think most people add empty line in an ansible inventory file for readability and we can leverage that fact.
If the file happens to have Windows EOL then command is
awk "/first_two_groups\:children/,/^[\\r]$/" /tmp/test.ini | grep -v children
with ansible example something like this:
- name: get group children
shell: awk "/first_two_groups\:children/,/^$/" /tmp/test.ini | grep -v children
register: chlldren
#returns list of the children groups to loop through later
- debug: var=children.stdout_lines
Above is not fully tested but if an issue then it could be at the most with the escaping of special chars in the shell module. REMEMBER - when you pass special chars in ansible shell module like colon use jinja expansion - instead of : use {{ ':' }}
Also for consistent bash behavior is recommended to pass explicitly the executable by tacking at the end of the task
args:
executable: /bin/bash

Ansible create directories from a list

I want to create some directories from a list I have in my vars/main.yml.
- app_root:
network_prod: "/var/www/prod/network/app"
database_prod: "/var/www/prod/db/app"
My tasks/main.yml so far has this:
- name: Create application directory structure
file:
path: "{{ item }}"
state: directory
mode: 755
with_items:
- app_root
but doesn't work. I thought this could be achieved using with_dict and I also tried:
- name: Create application directory structure
file:
path: "{{ item.value }}"
state: directory
mode: 755
with_dict:
- app_root
but I got: fatal: [vagrant.dev] => with_dict expects a dict.
I've read all about looping-over-hashes, but this doesn't seem to work.
The reason I'm using this notation is because I use these variables elsewhere as well and I need to know how to call them.
I personally find it a bit easier to convert yaml to json to make sure I'm understanding it properly. Take your example:
- app_root:
network_prod: "/var/www/prod/network/app"
database_prod: "/var/www/prod/db/app"
What you have here is not a list, but a nested dictionary. If you converted this to json it would look like this:
[
{
"app_root": {
"network_prod": "/var/www/prod/network/app",
"database_prod": "/var/www/prod/db/app"
}
}
]
In order to loop through this in Ansible you would need to dereference two levels of a dictionary, the first being app_root and the second being the path elements. Unfortunately I don't think Ansible supports looping through nested dictionaries, only through nested loops.
Your best bet is probably to redo the way you're defining your paths so that you're not creating as complex a data structure. If all you're doing in this case is iterating over a list of paths in order to ensure the directories exist then I'd suggest something like this in your vars/main.yml file:
network_prod: "/var/www/prod/network/app"
database_prod: "/var/www/prod/db/app"
app_root:
- network_prod
- database_prod
Then you can have a task like this:
file: path={{ item }}
state=directory
with_items: app_root
In vars/main.yml, try removing the dash in front of app_root.
app_root:
network_prod: "/var/www/prod/network/app"
database_prod: "/var/www/prod/db/app"
I think that the approach with with_dict was correct and I believe that the only issue here is the dash - in front of app_root variable. Instead of:
- name: Create application directory structure
file:
path: "{{ item.value }}"
state: directory
mode: 755
with_dict:
- app_root
It should be:
- name: Create application directory structure
file:
path: "{{ item.value }}"
state: directory
mode: 755
with_dict: app_root
See the difference on the way the variable app_root is passed to with_dict.
A dash in YAML starts a list and the elements are not treated as variables but as literals, think of it as if you were passing an immutable string 'app_root' to with_dict (not exactly true but it helps me to think it this way) so when_dict fails to parse it because it is given a list instead of the expected dictionary. However, without a dash, with_dict is populated with the variable app_root instead and it will parse it without issues.

Resources