The Q02/Q102 seeds tried to copy etcdctl out of the running etcd pod with `kubectl exec -- cat`, which cannot work: registry.k8s.io/etcd is distroless (no cat, no sh, no tar). The copy failed, stderr went to /dev/null and seeding still printed a green tick, so the snapshot task had no etcdctl on the node. Q102 was worse: it also ran `exec -- sh -c 'etcdctl ... snapshot save'` inside that container, so its /opt/cka/backup.db was never created at all. - etcd-tools.sh installs both binaries on the control-plane node, taking them from the etcd image already in the node's containerd (offline, exactly the server version), falling back to the upstream release tarball, then apt. - setup.sh runs it at bootstrap; ensure_etcd_tools in the exam prelude installs them through CKA_NODE_ACCESS when a task is seeded, so ssh/vagrant clusters get them too, and both etcd setups now fail with a reason instead of hiding it. - Q102 takes its snapshot with the node's etcdctl; Q02 no longer documents a kubectl cp fallback that cannot work on a distroless image. Co-authored-by: Junie <junie@jetbrains.com>
93 lines
4.2 KiB
Bash
Executable File
93 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Bootstraps the cka-sim kind cluster (1 control-plane + 3 workers) on podman and preloads images.
|
|
# Override with CONTAINER_RUNTIME=docker if you ever want docker instead.
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")"
|
|
|
|
RT="${CONTAINER_RUNTIME:-podman}"
|
|
|
|
need() {
|
|
command -v "$1" >/dev/null 2>&1 || { echo "ERROR: '$1' is not installed. $2"; exit 1; }
|
|
}
|
|
need "$RT" "https://podman.io/docs/installation"
|
|
need kind "https://kind.sigs.k8s.io/docs/user/quick-start/#installation"
|
|
need kubectl "https://kubernetes.io/docs/tasks/tools/"
|
|
need python3 ""
|
|
|
|
if [ "$RT" = podman ]; then
|
|
export KIND_EXPERIMENTAL_PROVIDER=podman
|
|
podman info >/dev/null 2>&1 || { echo "ERROR: podman is not working (try: podman info)."; exit 1; }
|
|
# kind needs cgroup v2 with controller delegation when podman is rootless.
|
|
if [ "$(id -u)" != 0 ] && [ "$(podman info --format '{{.Host.CgroupsVersion}}' 2>/dev/null)" != "v2" ]; then
|
|
echo "ERROR: rootless podman needs cgroup v2 for kind. Either enable cgroup v2 or run setup as root (sudo -E ./setup.sh)."
|
|
exit 1
|
|
fi
|
|
# inotify limits are the #1 cause of pods stuck in ContainerCreating on multi-node kind
|
|
for k in fs.inotify.max_user_watches:524288 fs.inotify.max_user_instances:512; do
|
|
key=${k%%:*}; want=${k##*:}; have=$(sysctl -n "$key" 2>/dev/null || echo 0)
|
|
[ "$have" -ge "$want" ] || echo "warn: $key=$have (recommend >= $want: sudo sysctl $key=$want)"
|
|
done
|
|
else
|
|
docker info >/dev/null 2>&1 || { echo "ERROR: docker daemon is not running/reachable."; exit 1; }
|
|
fi
|
|
|
|
if kind get clusters 2>/dev/null | grep -qx cka-sim; then
|
|
echo "==> kind cluster 'cka-sim' already exists, reusing it."
|
|
else
|
|
echo "==> Creating kind cluster 'cka-sim' on $RT (1 control-plane + 3 workers)..."
|
|
kind create cluster --config kind-config.yaml ${KIND_NODE_IMAGE:+--image "$KIND_NODE_IMAGE"} --wait 180s
|
|
fi
|
|
|
|
kubectl config use-context kind-cka-sim >/dev/null
|
|
echo "==> Waiting for nodes..."
|
|
kubectl wait --for=condition=Ready nodes --all --timeout=180s
|
|
|
|
echo "==> Preloading images into the cluster (non-fatal if a pull fails)..."
|
|
for img in nginx:1.25 nginx:1.27 busybox:1.36 redis:7-alpine; do
|
|
if "$RT" pull -q "docker.io/library/$img" >/dev/null 2>&1; then
|
|
# podman stores the fully-qualified name; kind load accepts either
|
|
kind load docker-image --name cka-sim "docker.io/library/$img" >/dev/null 2>&1 \
|
|
&& echo " loaded $img" || echo " warn: could not load $img"
|
|
else
|
|
echo " warn: could not pull $img (tasks will pull at runtime)"
|
|
fi
|
|
done
|
|
|
|
echo "==> Making sure the exam workdir exists on the control-plane node..."
|
|
"$RT" exec cka-sim-control-plane mkdir -p /opt/cka
|
|
|
|
# The real exam gives you vim (and less/jq) on every node; the kind node image is minimal, so
|
|
# install them once here. Skip with CKA_SKIP_NODE_TOOLS=1.
|
|
if [ -z "${CKA_SKIP_NODE_TOOLS:-}" ]; then
|
|
echo "==> Installing editors and tools on the nodes (vim, nano, less, jq)..."
|
|
for n in $(kind get nodes --name cka-sim); do
|
|
if "$RT" exec "$n" sh -c 'command -v vim >/dev/null'; then
|
|
echo " $n already has vim"
|
|
continue
|
|
fi
|
|
if "$RT" exec "$n" sh -c 'DEBIAN_FRONTEND=noninteractive apt-get update -qq >/dev/null 2>&1 && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq vim nano less jq >/dev/null 2>&1'; then
|
|
"$RT" exec "$n" sh -c 'printf "set tabstop=2\nset expandtab\nset shiftwidth=2\n" > /root/.vimrc'
|
|
echo " $n ok"
|
|
else
|
|
echo " warn: could not install tools on $n (no outbound network?). Fall back to:"
|
|
echo " $RT cp $n:/path/file /tmp/f && \$EDITOR /tmp/f && $RT cp /tmp/f $n:/path/file"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# The exam expects etcdctl/etcdutl on the control-plane node, like the real one. They cannot be
|
|
# copied out of the running etcd pod (that image is distroless), so install them properly.
|
|
echo "==> Installing etcdctl/etcdutl on the control-plane node..."
|
|
printf ' '
|
|
CONTAINER_RUNTIME="$RT" bash ./etcd-tools.sh || echo " warn: no etcd client on the control-plane node; Q02/Q102 will say so when they seed"
|
|
|
|
# remember the runtime so the CLI uses the matching exec command
|
|
mkdir -p .state && echo "$RT" > .state/runtime
|
|
|
|
chmod +x exam
|
|
echo
|
|
echo "Cluster ready. Nodes:"
|
|
kubectl get nodes -o wide
|
|
echo
|
|
echo "Next: ./exam start (seeds all 20 tasks and starts the 120-minute clock)"
|