Considering the following test playbook:
---
- name: Node Tolerations
hosts: localhost
gather_facts: false
vars:
tolerations:
- key: node.cilium.io/agent-not-ready
operator: Exists
effect: NoExecute
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
tasks:
- name: Set node tolerations fact
ansible.builtin.set_fact:
node_tolerations: "{{ (node_tolerations | default([]) | union([':'.join((item.key, item.effect))])) }}"
with_items: '{{ tolerations }}'
- name: Set node taint fact
ansible.builtin.set_fact:
node_taint: "{{ node_tolerations | select('search', 'control-plane') }}"
- name: Variable output
ansible.builtin.debug:
var: node_taint
Which produces the expected result:
ok: [localhost] =>
node_taint:
- node-role.kubernetes.io/control-plane:NoSchedule
While using the node_taint
fact into a Jinja2 template:
node-taint:
{{ node_taint | indent(2) }}
The following error is generated:
AttributeError: 'list' object has no attribute 'splitlines'
As temporary workaround, I used:
node-taint:
- {{ node_taint | join }}
But I prefer the initial Jinja2 format, allowing me to define the proper indentation. I was wondering if you can provide some insight, what would be the proper fix.
Edit: A second usage I will have for the above detailed playbook is:
tolerations:
{{ tolerations | indent(2) }}
Which produces the same error. I would prefer to avoid the use of for
loops inside Jinja template and manipulate the data at fact level, allowing me to use a proper indent
inside template.
Given the mre list
node_taint:
- node-role.0
- node-role.1
- node-role.2
the below debug
- debug:
var: node_taint
gives in YAML format
node_taint:
- node-role.0
- node-role.1
- node-role.2
If you want to indent the lines
- debug:
msg: |
node_taint:
{{ node_taint | indent(2) }}
you get the below error because the value of node_taint isn't a string
... The error was: AttributeError: 'list' object has no attribute 'splitlines'
Quoting from the function indent:
jinja-filters.indent(s: str, width: int | str = 4, ...
Return a copy of the string with each line indented by 4 spaces. The first line and blank lines are not indented by default.
The solution is to convert the value of node_taint to a string. Use the filter to_nice_yaml
- debug:
msg: |
node_taint:
{{ node_taint | to_nice_yaml | indent(2) }}
gives
msg: |-
node_taint:
- node-role.0
- node-role.1
- node-role.2
Example of a complete playbook for testing
- hosts: localhost
vars:
node_taint:
- node-role.0
- node-role.1
- node-role.2
tasks:
- debug:
var: node_taint
- debug:
msg: |
node_taint:
{{ node_taint | to_yaml | indent(2) }}
- debug:
msg: |
node_taint:
{{ node_taint | to_nice_yaml | indent(2) }}