helm to iterate a list of lists - yaml

Given the follow values.yaml
configurations:
endpoints:
- firstEndpoint
- firstPath
- secondPath
- secondEndpoint
- thirdPath
- fourthPath
I need to generate different resources from those values in the following way:
- name: firstEndpoint
paths:
- firstPath
- secondPath
- name: secondEndpoint
paths:
- thirdPath
- fourthPath
I can do this if "endpoints" were to be a map, instead of a list/array, but in this case, I need "endpoints" to be a list of endpoints, and "paths" to be a list of paths for each endpoint.
How could this be achieved?

As David Maze said. Your proposed endpoints: isn't valid.
You may try this:
values.yaml
configurations:
endpoints:
- firstEndpoint:
- firstPath
- secondPath
- secondEndpoint:
- thirdPath
- fourthPath
(Note the : after firstEndpoint and secondEndpoint)
template/cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
test: |-
{{- range $_, $item := .Values.configurations.endpoints }}
{{- range $k, $v := . }}
- name: {{ $k }}
path:
{{- range $_, $path := $v }}
- {{ $path }}
{{- end }}
{{- end }}
{{- end }}
output
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
test: |-
- name: firstEndpoint
path:
- firstPath
- secondPath
- name: secondEndpoint
path:
- thirdPath
- fourthPath

Related

Multiple expression RegEx in Ansible

Note: I have next to zero experience with Ansible.
I need to be able to conditionally modify the configuration of a Kubernetes cluster control plane service. To do this I need to be able to find a specific piece of information in the file and if its value matches a specific pattern, change the value to something else.
To illustrate, consider the following YAML file:
apiVersion: v1
kind: Pod
metadata:
labels:
component: kube-controller-manager
tier: control-plane
name: kube-controller-manager
namespace: kube-system
spec:
containers:
- command:
- kube-controller-manager
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=127.0.0.1
...
In this scenario, the line I'm interested in is the line containing --bind-address. If that field's value is "127.0.0.1", it needs to be changed to "0.0.0.0". If it's already "0.0.0.0", nothing needs to be done. (I could also approach it from the point of view of: if its not "0.0.0.0" then it needs to change to that.)
The initial thought that comes to mind is: just search for "--bind-address=127.0.0.1" and replace it with "--bind-address=0.0.0.0". Simple enough, eh? No, not that simple. What if, for some reason, there is another piece of configuration in this file that also matches that pattern? Which one is the right one?
The only way I can think of to ensure I find the right text to change, is a multiple expression RegEx match. Something along the lines of:
find spec:
if found, find containers: "within" or "under" spec:
if found, find - command: "within" or "under" containers: (Note: there can be more than one "command")
if found, find - kube-controller-manager "within" or "under" - command:
if found, find - --bind-address "within" or "under" - kube-controller-manager
if found, get the value after the =
if 127.0.0.1 change it to 0.0.0.0, otherwise do nothing
How could I write an Ansible playbook to perform these steps, in sequence and only if each step returns true?
Read the data from the file into a dictionary
- include_vars:
file: conf.yml
name: conf
gives
conf:
apiVersion: v1
kind: Pod
metadata:
labels:
component: kube-controller-manager
tier: control-plane
name: kube-controller-manager
namespace: kube-system
spec:
containers:
- command:
- kube-controller-manager
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=127.0.0.1
Update the containers
- set_fact:
containers: []
- set_fact:
containers: "{{ containers +
(update_candidate is all)|ternary([_item], [item]) }}"
loop: "{{ conf.spec.containers|d([]) }}"
vars:
update_candidate:
- item is contains 'command'
- item.command is contains 'kube-controller-manager'
- item.command|select('match', '--bind-address')|length > 0
update: "{{ item.command|map('regex_replace',
'--bind-address=127.0.0.1',
'--bind-address=0.0.0.0') }}"
_item: "{{ item|combine({'command': update}) }}"
gives
containers:
- command:
- kube-controller-manager
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=0.0.0.0
Update conf
conf_update: "{{ conf|combine({'spec': spec}) }}"
spec: "{{ conf.spec|combine({'containers': containers}) }}"
give
spec:
containers:
- command:
- kube-controller-manager
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=0.0.0.0
conf_update:
apiVersion: v1
kind: Pod
metadata:
labels:
component: kube-controller-manager
tier: control-plane
name: kube-controller-manager
namespace: kube-system
spec:
containers:
- command:
- kube-controller-manager
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=0.0.0.0
Write the update to the file
- copy:
dest: /tmp/conf.yml
content: |
{{ conf_update|to_nice_yaml(indent=2) }}
gives
shell> cat /tmp/conf.yml
apiVersion: v1
kind: Pod
metadata:
labels:
component: kube-controller-manager
tier: control-plane
name: kube-controller-manager
namespace: kube-system
spec:
containers:
- command:
- kube-controller-manager
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=0.0.0.0
Example of a complete playbook for testing
- hosts: localhost
vars:
conf_update: "{{ conf|combine({'spec': spec}) }}"
spec: "{{ conf.spec|combine({'containers': containers}) }}"
tasks:
- include_vars:
file: conf.yml
name: conf
- debug:
var: conf
- set_fact:
containers: []
- set_fact:
containers: "{{ containers +
(update_candidate is all)|ternary([_item], [item]) }}"
loop: "{{ conf.spec.containers|d([]) }}"
vars:
update_candidate:
- item is contains 'command'
- item.command is contains 'kube-controller-manager'
- item.command|select('match', '--bind-address')|length > 0
update: "{{ item.command|map('regex_replace',
'--bind-address=127.0.0.1',
'--bind-address=0.0.0.0') }}"
_item: "{{ item|combine({'command': update}) }}"
- debug:
var: containers
- debug:
var: spec
- debug:
var: conf_update
- copy:
dest: /tmp/conf.yml
content: |
{{ conf_update|to_nice_yaml(indent=2) }}

Convert helm yaml values to cli executable

how do I convert this yaml values in a way such that this become executable with helm install command?
hostAliases:
- ip: "12.20.30.40"
hostnames:
- "001.stg.local"
i want to execute from terminal like this
helm install my-app -n my-namespace app/app1 \
--set hostAliases=[]
values.yaml
hostAliases:
- ip: "12.20.30.40"
hostnames:
- "001.stg.local"
- "002.stg.remote"
- ip: "0.0.0.0"
hostnames:
- "003.stg.local"
- "004.stg.remote"
template/configmap.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
data: |-
{{- range $idx, $alias := $.Values.hostAliases }}
ip-{{ $idx }}: {{ $alias.ip }}
{{- range $i, $n := $alias.hostnames }}
host-{{ $idx }}-{{ $i }}: {{ $n }}
{{- end }}
{{- end }}
cmd
helm template --debug test .
output
---
# Source: test/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
data: |-
ip-0: 12.20.30.40
host-0-0: 001.stg.local
host-0-1: 002.stg.remote
ip-1: 0.0.0.0
host-1-0: 003.stg.local
host-1-1: 004.stg.remote
cmd
helm template --debug test . --set "hostAliases[0].ip=1.2.3.4" --set "hostAliases[0].hostnames={001,002}"
output
# Source: test/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
data: |-
ip-0: 1.2.3.4
host-0-0: 001
host-0-1: 002

Helm iterate over string with comma separated values

I'd like to iterate over string which contain comma separated values using range. For example:
DNS_NAMES: example.com, example2.com, example3.com
and then iterate using range over values: example.com, example2.com, example3.com
use split
values.yaml
DNS_NAMES: example.com, example2.com, example3.com
templates/cm.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
config.yaml: |-
args:
{{- range ( split ", " $.Values.DNS_NAMES ) }}
- {{ . }}
{{- end }}
output.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
config.yaml: |-
args:
- example.com
- example2.com
- example3.com

ingress variables syntax from values.yaml

I have a simple api that I've been deploying to K8s as a NodePort service via Helm. I'm working to add an ingress to the Helm chart but I'm having some difficulty getting the variables correct
values.yaml
ingress:
metadata:
name: {}
labels: {}
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: "testapi.local.dev"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: {}
port:
number: 80
templates/ingress.yaml, showing only the spec section where I'm having issues.
spec:
rules:
{{- range .Values.ingress.spec.rules }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path | quote }}
pathType: {{ .pathType | quote }}
backend:
service:
name: {{ include "testapi.service.name" . }}
port:
{{- range $key, $value := (include "testapi.deployment.ports" . | fromYaml) }}
number: {{ .port }}
{{- end}}
{{- end}}
{{- end}}
When running helm template it just leaves these values blank and I'm not sure where the syntax is wrong. Removing the {{- range .paths }} and the following .path and .pathType and replacing them with the value corrects the issue
spec:
rules:
- host: "testapi.local.dev"
http:
paths:
Comments revealed I should be using {{- range .http.paths }}.

helm cronjob multiple containers

I need to run multiple containers in one cronjob execution. Currently I have the following cronjob.yaml template:
jobTemplate:
spec:
template:
metadata:
labels:
app: {{ .Release.Name }}
cron: {{ .Values.filesjob.jobName }}
spec:
containers:
- image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
env:
- name: FILE_MASK
value: "{{ .Values.filesjob.fileMask }}"
- name: ID
value: "{{ .Values.filesjob.id }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
name: {{ .Values.filesjob.jobName }}
volumeMounts:
- mountPath: /data
name: path-to-clean
- name: path-logfiles
mountPath: /log
- image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
env:
- name: FILE_MASK
value: "{{ .Values.filesjob.fileMask }}"
- name: ID
value: "{{ .Values.filesjob.id }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
name: {{ .Values.filesjob.jobName2 }}
volumeMounts:
- mountPath: /data
name: path2-to-clean
- name: path2-logfiles
mountPath: /log
The above generates a cronjob which executes two containers with passing different env variables. can I generate the same using values.yaml by iterating over a variable?
I was able to solve this basing on example from this article - https://nikhils-devops.medium.com/helm-chart-for-kubernetes-cronjob-a694b47479a
Here is my template:
{{- if .Values.cleanup.enabled -}}
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: registry-cleanup-job
namespace: service
labels:
{{- include "some.labels" . | nindent 4 }}
spec:
schedule: {{ .Values.cleanup.schedule }}
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 2
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
containers:
{{- range .Values.cleanup.crons }}
- name: {{ .name | quote }}
image: {{ $.Values.cleanup.imageName }}
imagePullPolicy: {{ $.Values.cleanup.pullPolicy }}
args:
- {{ .command }}
{{- end}}
restartPolicy: Never
{{- end }}
And related values:
cleanup:
enabled: true
schedule: "0 8 * * 5"
imageName: digitalocean/doctl:1.60.0
pullPolicy: IfNotPresent
crons:
- command0:
name: "cleanup0"
command: command0
- command1:
name: "cleanup1"
command: command1
- command2:
name: "cleanup2"
command: command2
- command3:
name: "cleanup3"
command: command3
- command4:
name: "cleanup4"
command: command4

Resources