#!/usr/bin/env bash # Install etcdctl/etcdutl on the control-plane node (the real exam has them on the node, and # Q02 (snapshot) / Q102 (restore) need them there). They cannot be copied out of the running # etcd pod: registry.k8s.io/etcd is distroless, so there is no cat, sh or tar inside it. # Re-run this after ./setup.sh if the install was skipped or the network was down. # ./etcd-tools.sh install them on the control-plane node (podman/docker clusters) # ./etcd-tools.sh --emit print the node-side script; ./exam pipes it through CKA_NODE_ACCESS, # so ssh/vagrant clusters get the same install when a task is seeded set -euo pipefail cd "$(dirname "$0")" # Runs on the node, as root, POSIX sh only. Three sources, best first: # 1. the etcd image already in the node's containerd - offline, exactly the server version # 2. the matching upstream release tarball - needs network, exactly the server version # 3. the distro's etcd-client package - needs network, etcdctl only, version skew NODE_SCRIPT=$(cat <<'EOS' set -u PATH="/usr/local/bin:$PATH"; export PATH BIN=/usr/local/bin have() { command -v etcdctl >/dev/null 2>&1 && command -v etcdutl >/dev/null 2>&1; } have && { echo "already installed: $(etcdctl version | head -1)"; exit 0; } img=$(ctr -n k8s.io images ls -q 2>/dev/null | grep -m1 -E '(^|/)etcd:[0-9]' || true) ver=$(echo "$img" | sed 's/.*etcd://; s/-[0-9]*$//') if [ -n "$img" ]; then mnt=$(mktemp -d) if ctr -n k8s.io images mount --rw "$img" "$mnt" >/dev/null 2>&1; then cp "$mnt/usr/local/bin/etcdctl" "$mnt/usr/local/bin/etcdutl" "$BIN/" 2>/dev/null || true ctr -n k8s.io images unmount --rm "$mnt" >/dev/null 2>&1 || true fi rmdir "$mnt" 2>/dev/null || true chmod +x "$BIN/etcdctl" "$BIN/etcdutl" 2>/dev/null || true have && { echo "installed $ver from $img"; exit 0; } fi if [ -n "$ver" ] && command -v curl >/dev/null 2>&1; then arch=$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/') d="etcd-v$ver-linux-$arch" curl -sfL --max-time 180 "https://github.com/etcd-io/etcd/releases/download/v$ver/$d.tar.gz" \ | tar -xzf - -C "$BIN" --strip-components=1 "$d/etcdctl" "$d/etcdutl" 2>/dev/null || true chmod +x "$BIN/etcdctl" "$BIN/etcdutl" 2>/dev/null || true have && { echo "installed $ver from the upstream release"; exit 0; } fi if command -v apt-get >/dev/null 2>&1; then DEBIAN_FRONTEND=noninteractive apt-get update -qq >/dev/null 2>&1 || true DEBIAN_FRONTEND=noninteractive apt-get install -y -qq etcd-client >/dev/null 2>&1 || true command -v etcdctl >/dev/null 2>&1 && { echo "installed $(etcdctl version | head -1 | awk '{print $3}') from apt: no etcdutl (use" echo "'etcdctl snapshot restore') and it may not match the ${ver:-running} server" exit 0 } fi echo "ERROR: could not install etcdctl/etcdutl. Tried the etcd image in containerd" echo "(${img:-none found}), the upstream release and apt-get (needs outbound network)." exit 1 EOS ) if [ "${1:-}" = --emit ]; then printf '%s\n' "$NODE_SCRIPT" exit 0 fi RT="${CONTAINER_RUNTIME:-$(cat .state/runtime 2>/dev/null || echo podman)}" [ "$RT" = podman ] && export KIND_EXPERIMENTAL_PROVIDER=podman CP="${CKA_CP:-cka-sim-control-plane}" printf '%s: ' "$CP" printf '%s\n' "$NODE_SCRIPT" | "$RT" exec -i "$CP" sh -s