From c554ee816d378dbf5db120e4a3dae3feb6bced1a Mon Sep 17 00:00:00 2001 From: Max Gautier Date: Tue, 8 Sep 2026 09:12:32 +0000 Subject: [PATCH] fix/ansible-core 2.19: etcd: avoid mutating methods in template (#13474) 2.19 disallow methods with side effects in Jinja. Use a simple loop by adding all the SANs source in one list. Also check the sources before hand for failing on CIDR notation. Link: https://github.com/ansible/ansible/issues/85202 --- roles/etcd/templates/openssl.conf.j2 | 43 +++++++++++-------------- roles/validate_inventory/meta/main.yml | 1 + roles/validate_inventory/tasks/main.yml | 1 + 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/roles/etcd/templates/openssl.conf.j2 b/roles/etcd/templates/openssl.conf.j2 index 04965ea81..c0f7cf061 100644 --- a/roles/etcd/templates/openssl.conf.j2 +++ b/roles/etcd/templates/openssl.conf.j2 @@ -1,4 +1,4 @@ -{% set counter = {'dns': 2,'ip': 1,} %}{% macro increment(dct, key, inc=1)%}{% if dct.update({key: dct[key] + inc}) %} {% endif %}{% endmacro %}[req] +[req] req_extensions = v3_req distinguished_name = req_distinguished_name @@ -23,28 +23,21 @@ subjectAltName = @alt_names authorityKeyIdentifier=keyid:always,issuer [alt_names] -DNS.1 = localhost -{% for host in groups['etcd'] %} -{% if hostvars[host]['etcd_access_address'] is defined and not (hostvars[host]['etcd_access_address'] | ansible.utils.ipaddr) %} -{# If defined, the address which etcd uses to access its members must be included in the SAN, otherwise etcd will fail with a TLS error upon startup. #} -DNS.{{ counter["dns"] }} = {{ hostvars[host]['etcd_access_address'] }}{{ increment(counter, 'dns') }} -{% endif %} -{# This will always expand to inventory_hostname, which can be a completely arbitrary name, that etcd will not know or care about, hence this line is (probably) redundant. #} -DNS.{{ counter["dns"] }} = {{ host }}{{ increment(counter, 'dns') }} +{# +List all possible ways etcd instances can be contacted + +main_{access_,}ips: those variables are explicitly set by network_facts role +and thus don't need a defined check +#} +{% for san in ( + groups['etcd'] | map('extract', hostvars, morekeys='etcd_access_address') | select('defined') + + groups['etcd'] + + etcd_cert_alt_names + + groups['etcd'] | map('extract', hostvars, morekeys='main_access_ips') + + groups['etcd'] | map('extract', hostvars, morekeys='main_ips') + + etcd_cert_alt_ips + + ['localhost', '127.0.0.1', '::1'] + ) | flatten | unique + %} +{{ 'IP' if san is ansible.utils.ip_address else 'DNS' }}.{{ loop.index }} = {{ san }} {% endfor %} -{% for etcd_alt_name in etcd_cert_alt_names %} -DNS.{{ counter["dns"] }} = {{ etcd_alt_name }}{{ increment(counter, 'dns') }} -{% endfor %} -{% for host in groups['etcd'] %} -{% for address in hostvars[host]['main_access_ips'] %} -IP.{{ counter["ip"] }} = {{ address }}{{ increment(counter, 'ip') }} -{% endfor %} -{% for address in hostvars[host]['main_ips'] %} -IP.{{ counter["ip"] }} = {{ address }}{{ increment(counter, 'ip') }} -{% endfor %} -{% endfor %} -{% for cert_alt_ip in etcd_cert_alt_ips %} -IP.{{ counter["ip"] }} = {{ cert_alt_ip }}{{ increment(counter, 'ip') }} -{% endfor %} -IP.{{ counter["ip"] }} = 127.0.0.1{{ increment(counter, 'ip') }} -IP.{{ counter["ip"] }} = ::1 diff --git a/roles/validate_inventory/meta/main.yml b/roles/validate_inventory/meta/main.yml index 5b1432e4a..a7ba16859 100644 --- a/roles/validate_inventory/meta/main.yml +++ b/roles/validate_inventory/meta/main.yml @@ -1,3 +1,4 @@ --- dependencies: - role: kubespray_defaults + - role: etcd_defaults diff --git a/roles/validate_inventory/tasks/main.yml b/roles/validate_inventory/tasks/main.yml index 37bdcd6e9..503ee076b 100644 --- a/roles/validate_inventory/tasks/main.yml +++ b/roles/validate_inventory/tasks/main.yml @@ -170,6 +170,7 @@ - etcd_deployment_type in ['host', 'docker', 'kubeadm'] - etcd_deployment_type in ['host', 'kubeadm'] or container_manager == 'docker' - container_manager in ['docker', 'crio', 'containerd'] + - etcd_cert_alt_ips | reject('ansible.utils.ip_address') == [] msg: The selected choice is not supported run_once: true