Remove etcd member by peerURLs (#12691)

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
This commit is contained in:
Max Gautier
2025-11-17 10:53:40 +00:00
committed by GitHub
parent c06b669ae6
commit 16e3670dd4
3 changed files with 5 additions and 17 deletions

View File

@@ -1,16 +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
- ip is not defined
- access_ip is not defined
delegate_to: "{{ groups['kube_control_plane'] | first }}"
- name: Remove etcd member from cluster
environment:
ETCDCTL_API: "3"
@@ -21,20 +9,18 @@
delegate_to: "{{ groups['etcd'] | first }}"
block:
- name: Lookup members infos
command: "{{ bin_dir }}/etcdctl member list"
command: "{{ bin_dir }}/etcdctl member list -w json"
register: etcd_members
changed_when: false
check_mode: false
tags:
- facts
- name: Remove member from cluster
vars:
node_ip: "{{ ip if ip is defined else (access_ip if access_ip is defined else (k8s_node_ips.stdout | from_json)[0]) }}"
command:
argv:
- "{{ bin_dir }}/etcdctl"
- member
- 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
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

@@ -130,7 +130,7 @@ run_playbook tests/testcases/100_check-k8s-conformance.yml
# Test node removal procedure
if [ "${REMOVE_NODE_CHECK}" = "true" ]; then
run_playbook remove-node.yml -e skip_confirmation=yes -e node=${REMOVE_NODE_NAME}
run_playbook remove-node.yml -e skip_confirmation=yes -e node="${REMOVE_NODE_NAME}"
fi
# Clean up at the end, this is to allow stage1 tests to include cleanup test