1

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?

asked Jan 10, 2023 at 21:11

1 Answer 1

1

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) }}
answered Jan 10, 2023 at 23:32
Sign up to request clarification or add additional context in comments.

5 Comments

Wow. Thank you very much. It will take some time for me to analyze and understand this. I do see bits and pieces of what you're doing, though.
It seems like some phrases are out of order? For example, in the second set_fact an item named update_candidate appears to be used/specified before the vars phrase that defines it. Is this just the Ansible syntax that I don't yet understand?
Do you have any recommendations on good sources of tutorials for Ansible?
vars in a task means these variables exist in the scope of this task only. There is no order. A variable is evaluated at the last moment when it is used. See Using filters to manipulate data.
See Tests.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.