Commit Graph

6 Commits

Author SHA1 Message Date
111a6c63a0 fix: repair apiserver/staticPodPath leaks, 3 drifted solutions, workspace tracking
Remaining findings from the repo audit (1, 2 and 4 landed in df04c56).

restore_baseline now undoes two more leaks, both of which cost real debugging
time this week:

  - apiserver (Q123 renames the command to "kube-apiserverr"). This runs FIRST
    and over podman/ssh rather than kubectl, because it is exactly the failure
    that makes kubectl unusable - every later step in the block depends on the
    API being up. It waits for /readyz before continuing.
  - staticPodPath (Q103 relocates it to /etc/kubernetes/static on the tier=edge
    node). The existing kubelet repair only fired when
    /var/lib/kubelet/config.yaml.orig existed, and nothing ever creates that
    backup, so a relocated path survived every later session and silently broke
    Q05, whose check hardcodes /etc/kubernetes/manifests. Normalised
    unconditionally, restarting the kubelet only when the file actually changed.

Verified against the live cluster: with both breaks applied the API was
unreachable, and one restore_baseline run brought all four nodes Ready, restored
the manifest, reset staticPodPath and let the kubelet re-adopt the Q05 mirror
pod. Re-running takes 1.1s versus 14.6s when repairing, confirming it is
idempotent and does not restart anything it has not changed.

etcd (Q102) and certs (Q122) are deliberately left alone, now documented in the
block: neither setup damages anything, so any damage comes from a half-finished
candidate attempt, and "mid-attempt" is indistinguishable from "done correctly"
without guessing - guessing wrong rewrites etcd's data-dir or re-issues certs on
a healthy cluster. proxy-rules (Q302) needs no repair at all; its "breaks" key
exists for no_conflicts() bookkeeping, not because it touches shared state.

Also:
  - S[30], S[42], S[108] referenced params that do not exist (%{svc}/%{app},
    %{np}/%{port}, %{rev}), so they rendered placeholders verbatim. Rewritten
    against the real task text - S[42] was missing the DNS egress rule entirely
    and S[108] stopped before the scale/pause/set-image steps, so both would
    have failed 2 of 3 checks if followed literally. A param-reference scan over
    the whole bank is now clean.
  - Q124's check and S[124] used the deprecated v1 Endpoints API; switched to
    EndpointSlice, verified both forms return 10.89.1.3:6443 on this cluster.
  - q_by_id printed "Valid: 1-101" for non-contiguous ids; it now derives the
    real ranges (1-43, 101-124, 201-217, 301-317).
  - Untracked workspace/q03. Both q03 and q21 setups start with rm -rf and
    rewrite every file from heredocs, so they are generated output, not seed
    templates: tracking them only produced phantom diffs, since a re-seed
    reverted edits to base/ and deleted anything left in overlays/. With nothing
    tracked there, teardown.sh can go back to wiping workspace/ wholesale.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fqcio9TXkEXH35UHebTgoL
2026-09-14 22:09:26 -06:00
df04c56533 fix: create exam contexts when needed, widen baseline reset, bind ttyd to loopback
Three fixes from an audit of the repo:

1. setup_contexts() deleted all three k8s-c?-? contexts and recreated none
   unless the session was started with --hard. But './exam drill <ids>' and
   './exam drill weak' don't filter by difficulty, so hard tasks land in
   hard=False sessions - and 24 of them open with "kubectl config use-context
   ...", which then fails outright. Q121 was unpassable that way: it says to
   deploy into the context's namespace without -n, and its check requires that
   namespace, so the context was the only way to learn the name. Contexts are
   now created whenever a task in the session references one.

   Also fixes how k8s-c2-AC's namespace was chosen: it took the first ns in the
   whole session, which is arbitrary when several tasks share a context. A
   context has one default namespace, so it belongs to the task that actually
   reads it - Q121, now marked "ctx_ns": True.

2. restore_baseline() cleared taint keys dedicated/workload/team, but Q107
   draws from pool/lane and Q307 pins maintenance=inprogress, and nothing
   uncordoned a node. A leaked cordon or taint silently fails every later task
   that counts pods per node (Q26/Q211 DaemonSet coverage, Q212's spread) -
   W1 had been cordoned and tainted across sessions for two days. Clears all
   six keys now and uncordons all three workers.

3. ttyd was launched with -W (writable) and no interface binding, so it listened
   on 0.0.0.0 while the API server correctly bound localhost. That exposed an
   unauthenticated root-equivalent shell with the cluster kubeconfig to the whole
   network. Pinned to loopback, overridable via CKA_TTYD_INTERFACE.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fqcio9TXkEXH35UHebTgoL
2026-09-14 19:43:15 -06:00
d755c4017c fix: reset control-plane/node state to baseline before every session
Disruptive tasks corrupt shared state (static pod manifests, kubelet
config, CoreDNS) keyed by a "breaks" target, but a session's setup only
touches the questions it actually contains. If a task isn't re-picked in
a later session, its damage lingers indefinitely - e.g. Q39 wrecking W1's
kubelet config persisted across two unrelated TSHOOT drills that never
included Q39 again.

Add restore_baseline(), run unconditionally in begin_session() before
seeding, that repairs every known "breaks" target (scheduler,
controller-manager, CoreDNS, all three worker kubelets, W1's demo
taints) regardless of which questions are in the new session. Hard-mode-
only breaks (etcd, certs, apiserver) aren't covered yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 15:50:30 -06:00
23a9774a9f fix: install etcdctl/etcdutl on the control-plane node for the etcd tasks
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>
2026-09-07 13:51:37 -06:00
40dca1dab1 fix: output single checkmark line when seeding scenarios
Co-authored-by: Junie <junie@jetbrains.com>
2026-09-06 22:06:55 -06:00
dece60fd8a feat: add CKA/CKAD/CKS exam simulator and question banks
Co-authored-by: Junie <junie@jetbrains.com>
2026-09-06 19:06:10 -06:00