Remove etcd member by peerURLs (#12682)

* Remove etcd member by peerURLs

The way to obtain the IP of a particular member is convoluted and depend
on multiple variables. The match is also textual and it's not clear
against what we're matching

It's also broken for etcd member which are not also Kubernetes nodes,
because the "Lookup node IP in kubernetes" task will fail and abort the
play.

Instead, match against 'peerURLs', which does not need new variable, and
use json output.

* Add testcase for etcd removal on external etcd

* do not merge

* fixup! Remove etcd member by peerURLs

* fixup! Remove etcd member by peerURLs
This commit is contained in:
Max Gautier
2025-11-10 11:52:56 +00:00
committed by GitHub
parent 990695de7b
commit 97a3776d8e
4 changed files with 6 additions and 21 deletions

View File

@@ -24,7 +24,7 @@ variables:
ANSIBLE_REMOTE_USER: kubespray ANSIBLE_REMOTE_USER: kubespray
ANSIBLE_PRIVATE_KEY_FILE: /tmp/id_rsa ANSIBLE_PRIVATE_KEY_FILE: /tmp/id_rsa
ANSIBLE_INVENTORY: /tmp/inventory ANSIBLE_INVENTORY: /tmp/inventory
ANSIBLE_STDOUT_CALLBACK: "debug" ANSIBLE_STDOUT_CALLBACK: "default"
RESET_CHECK: "false" RESET_CHECK: "false"
REMOVE_NODE_CHECK: "false" REMOVE_NODE_CHECK: "false"
UPGRADE_TEST: "false" UPGRADE_TEST: "false"

View File

@@ -1,14 +1,4 @@
--- ---
- name: Lookup node IP in kubernetes
command: >
{{ kubectl }} get nodes {{ node }}
-o jsonpath-as-json='{.status.addresses[?(@.type=="InternalIP")].address}'
register: k8s_node_ips
changed_when: false
when:
- groups['kube_control_plane'] | length > 0
delegate_to: "{{ groups['kube_control_plane'] | first }}"
- name: Remove etcd member from cluster - name: Remove etcd member from cluster
environment: environment:
ETCDCTL_API: "3" ETCDCTL_API: "3"
@@ -19,25 +9,18 @@
delegate_to: "{{ groups['etcd'] | first }}" delegate_to: "{{ groups['etcd'] | first }}"
block: block:
- name: Lookup members infos - name: Lookup members infos
command: "{{ bin_dir }}/etcdctl member list" command: "{{ bin_dir }}/etcdctl member list -w json"
register: etcd_members register: etcd_members
changed_when: false changed_when: false
check_mode: false check_mode: false
tags: tags:
- facts - facts
- name: Remove member from cluster - name: Remove member from cluster
vars:
node_ip: >-
{%- if not ipv4_stack -%}
{{ ip6 if ip6 is defined else (access_ip6 if access_ip6 is defined else (k8s_node_ips.stdout | from_json)[0]) | ansible.utils.ipwrap }}
{%- else -%}
{{ ip if ip is defined else (access_ip if access_ip is defined else (k8s_node_ips.stdout | from_json)[0]) | ansible.utils.ipwrap }}
{%- endif -%}
command: command:
argv: argv:
- "{{ bin_dir }}/etcdctl" - "{{ bin_dir }}/etcdctl"
- member - member
- remove - remove
- "{{ ((etcd_members.stdout_lines | select('contains', '//' + node_ip + ':'))[0] | split(','))[0] }}" - "{{ '%x' | format(((etcd_members.stdout | from_json).members | selectattr('peerURLs.0', '==', etcd_peer_url))[0].ID) }}"
register: etcd_removal_output register: etcd_removal_output
changed_when: "'Removed member' in etcd_removal_output.stdout" changed_when: "'Removed member' in etcd_removal_output.stdout"

View File

@@ -0,0 +1,2 @@
REMOVE_NODE_CHECK=true
REMOVE_NODE_NAME=etcd[2]

View File

@@ -92,7 +92,7 @@ ansible-playbook \
# Test node removal procedure # Test node removal procedure
if [ "${REMOVE_NODE_CHECK}" = "true" ]; then if [ "${REMOVE_NODE_CHECK}" = "true" ]; then
run_playbook remove-node -e skip_confirmation=yes -e node=${REMOVE_NODE_NAME} run_playbook remove-node -e skip_confirmation=yes -e node="${REMOVE_NODE_NAME}"
fi fi
# Clean up at the end, this is to allow stage1 tests to include cleanup test # Clean up at the end, this is to allow stage1 tests to include cleanup test