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