Commit Graph

4 Commits

Author SHA1 Message Date
49fcb76166 fix: taint cleanup removed nothing, Q23 leaked a broken metrics-server, CKS lost 40% of its blueprint
Audit of the bank and harness after a 23% mock result. Two of these were
silently corrupting every practice session on this cluster.

restore_baseline:

  - The w1-taint cleanup removed nothing at all. `kubectl taint node N a- b- c-`
    aborts on the first key that is not present and applies none of the removals,
    so with only `workload` set the command reported five "not found" lines,
    exited 1, and left the taint in place - the precise failure the block's own
    comment says it exists to prevent. W1 then rejected pods in every later
    session. Now one call per key. Verified by re-seeding workload=gpu:NoSchedule
    and confirming a single restore_baseline run clears it.
  - metrics-server was never repaired. Q23 seeds its scenario by stripping
    --kubelet-insecure-tls, but carried no "breaks" key, so nothing tracked it
    and nothing undid it. On kind the kubelet serving certs have no IP SANs, so
    without that flag metrics-server never goes Ready, v1beta1.metrics.k8s.io
    has no endpoints, aggregated discovery goes stale, and EVERY namespace
    deletion in the cluster hangs on NamespaceDeletionDiscoveryFailure - which
    is where the ~40 stuck namespaces came from, plus a dead `kubectl top`.
    Q23 now declares breaks: metrics-server and the baseline restores the flag.
    Baseline runs before seeding, so a session containing Q23 re-breaks it a
    moment later as intended. Verified by stripping the flag and confirming one
    restore_baseline run puts it back.

Session composition:

  - pick() dropped any domain whose tasks are all "hard" from a normal session.
    CKS SUPPLY and RUNTIME hold one hard task each, so `./exam start` returned
    6 of 9 tasks with 40% of the blueprint silently absent. It now tops up from
    the hard pool when a domain's normal pool is short.
  - compose_exam() iterated QUOTA in declaration order, so a one-task domain
    lost every "breaks" tie to a bigger domain that claimed the shared slot
    first: CKS RUNTIME (only Q9, apiserver-flag) was never drawn in ANY mode
    because SETUP went first and took that slot with Q2. Now walks domains
    scarcest-first, and warns on a shortfall instead of truncating in silence.
    CKS now yields 8/9 covering all six domains, warning SETUP 1/2.
  - session_questions() rebuilt the list by filtering QUESTIONS (sorted by id),
    discarding the session's own order - so compose_exam's rng.shuffle never
    reached the candidate and every exam arrived grouped by domain and
    difficulty, base tasks then hard then sim sets. Fixed to preserve the stored
    order, and cmd_drill now shuffles explicit-id drills after conflict
    resolution, so which task gets dropped stays deterministic while the order
    does not.

Bank correctness (verified by grading each reference solution):

  - CKAD Q2 was unpassable, 5/8. Its init container waited on `until nslookup
    backend`, but busybox nslookup exits 1 even on a successful lookup because
    it walks the whole search list and the trailing NXDOMAINs set the status -
    the loop never terminates. The premise was wrong too: a ClusterIP Service
    resolves from creation, endpoints or not, so DNS could never have gated it.
    Now waits on a real connection; 8/8, and the pod genuinely holds at Init:0/1
    until the backend Deployment exists. Same bug class as e3f5ecd.
  - CKS Q1 was unsatisfiable as written, 6/10. The text asked for three
    NetworkPolicies but the grader required a fourth: one podSelector cannot
    cover both the web egress half and the db ingress half. Task text now asks
    for four and says why.
  - CKS Q4 check 4 was a false positive. `... || r=0` read a failed exec as
    "no token mounted", awarding 3 points when the candidate had done nothing
    and the probe pod simply could not start - which happens often on this
    cluster. Now requires the pod to reach Running and exec to work. Verified
    it awards the points before the fix and fails after, with pod creation
    blocked by a quota.
  - CKAD Q8's solution asserted a Pod's image cannot be changed in place.
    It can - spec.containers[].image is mutable; `kubectl set image pod/...`
    works and grades 7/7. Rewritten, noting which fields really are immutable.

Also: the `sim` usage and help strings hardcoded "a|b" and a "17-task" count,
wrong for a third set and wrong for CKAD/CKS, which define no sets at all. Both
are now generated from SETS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NziZdvBvy7CpEAAbZNgp8i
2026-09-18 09:05:03 -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
595003e377 fix: repair stale reference solutions and Q33's grading check
tracks/cka/solutions.py had drifted out of sync with the actual current
task text for several questions (Q06, 11, 12, 13, 14, 22, 32, 40, 109,
110, 120, 122) - wrong namespaces/resource names, missing required steps,
or output files that were never actually written. Rewrote each to match
its task's real requirements.

tracks/cka/bank_extra.py's Q33 grading check queried
`.spec.aggregationRule` on a ClusterRole, but ClusterRole has no `.spec`
field - `aggregationRule` and `rules` are top-level. Fixed the jsonpath
so a correct answer actually passes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fqcio9TXkEXH35UHebTgoL
2026-09-14 19:23:54 -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