Commit Graph

7 Commits

Author SHA1 Message Date
55ab18f814 fix: repair the CKA bank and harness after a live audit; add Simulator Set D
Every changed task was seeded, graded untouched, solved and re-graded on a
separate kind cluster. The four sim sets were rehearsed as full sittings (real
baseline, seed everything, solve in order, grade at the end): A 150/150,
B 176/176, C 163/163, D 122/122. Every random-pool task was either passed in a
real session or cycled the same way.

Graders that could never pass, or passed for free:
  Q306 (local-path cannot resize; now graded as an in-place resize), Q314 (GNU
  grep prefixed file names), Q305 (plain `get events` has no SOURCE column and
  events expire; now managedFields), Q410 (kindnet DOES enforce NetworkPolicy;
  the expected answer is now derived from the CNI), Q35, Q212 (missing `]`),
  Q101 (`can-i create pods/exec` asks about a pod NAMED exec; multi-type get),
  Q109 (an emptyDir over nginx's /var/log crash-loops it), Q40 (TTL was
  measured at grading time), Q121, Q106, Q104, Q41, Q26, Q403, Q409, Q417,
  Q113, Q22, Q24/Q108 (flaky right after a rollout), and free points on
  untouched Q24/Q102/Q108/Q119/Q122.

Reference solutions: S[43], S[107], S[121], S[106] rewritten; S[116] (a
Completed Job pod still pins its PVC), S[119] (a pods quota equal to replicas
deadlocks the rollout); DNS checks use FQDNs; the explainer and exam guide
are corrected (etcd restore is etcdutl's job on 3.6+).

Cross-session leaks: the pre-session baseline now also repairs Q114 (the
kube-system NetworkPolicy and CoreDNS replicas), Q117/Q39 kubelet CA paths,
Q317, kubelet drop-in binary paths, a moved scheduler manifest, leftover
static pods and the default StorageClass. Q115 releases its fixed PVs from
claims left by earlier sessions.

Conflicts: `breaks` may be a list and `solo` keeps Q102's etcd restore out of
mixed sessions. Q118, Q5/Q103, Q26 vs the W1 taints, set A 203/204/208/212 and
set B 305/309 no longer sabotage each other. Q317 now disables only CSR
signing (the old break killed the whole controller-manager). Q110/Q311 no
longer compare against the whole cluster at submit time. Two delete/recreate
races in setups are fixed.

Harness: the weak list clears after two clean passes in a row, weak entries
show the latest attempt's failures, domain scores use the exam's own tasks,
history entries can be marked synthetic (entry 11 is), the web UI lists
every sim set, `./exam grade` refuses mid-exam (--force), preflight checks W1.

Set D (`./exam sim d`): 17 original killer.sh-style tasks, ids 501-517,
122 points - control-plane inventory, event forensics with crictl, a broken
kubelet unit, kubelet certificates, manual scheduling with the scheduler
stopped, Helm housekeeping with a stuck release, an enforced AND-vs-OR
NetworkPolicy, dnsPolicy and access-mode troubleshooting, and more.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-22 21:59:35 -06:00
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
e3f5ecd8a6 fix: Q37/Q114 DNS checks were unpassable - busybox ignores search domains
Both checks ran `nslookup kubernetes.default` inside a busybox:1.36 pod and
required it to succeed. busybox's nslookup does not apply the pod's resolv.conf
`search` list - it queries the name literally - so that lookup returns NXDOMAIN
no matter how correctly CoreDNS is configured. Verified in-cluster: from a pod
with a correct `search default.svc.cluster.local svc.cluster.local cluster.local`
and a healthy Corefile, `kubernetes`, `kubernetes.default` and
`kubernetes.default.svc` all return NXDOMAIN while the full
`kubernetes.default.svc.cluster.local` resolves to 10.96.0.1.

The result was a task that could never score more than 3/6: the local drill
history shows this check failing on 4 of 4 attempts with the Corefile fully
repaired.

Use the FQDN in both checks, and say so in the task text so the candidate
verifies the same way the grader does. Q216 in bank_sim_a already used an FQDN;
these two were the outliers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fqcio9TXkEXH35UHebTgoL
2026-09-14 20:54:25 -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
58a357d7d2 feat: add kube-apiserver failure and node-join-verification tasks
Close two of the four CKA-relevant gaps identified against Killercoda's
scenario catalog:

- Q123 (TSHOOT, hard): kube-apiserver itself crash-loops on a static-pod
  manifest typo, same pattern as the existing scheduler/controller-manager
  failures (Q20/Q118) but with the added twist that kubectl is completely
  unusable until it's fixed, forcing node-level diagnosis (crictl,
  journalctl).
- Q124 (ARCH, hard): produce a real node-join handoff file by
  independently computing the CA discovery hash kubeadm join verifies
  against, and finding the API server's actual in-cluster address rather
  than the local kubeconfig's port-forwarded one.

Cluster-upgrade and operator-authoring remain unimplemented and are
documented as genuine infrastructure gaps in CKA_SOLUTIONS_EXPLAINED.md
(kind's node images have no apt-installable multi-version kubelet/kubeadm,
and operator authoring needs a real built/pushed image) rather than faked.

Also updates the Killercoda cross-reference table with the gaps closed,
and logs an ended drill session to history-cka.jsonl/weak-spots-cka.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 21:25:18 -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
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