feat(ckad): cover the whole curriculum - 37 tasks, 16-task mocks
The CKAD track was a 12-task starter bank whose quota (3/3/2/2/2) drew the whole bank, so every mock was the same 12 tasks reshuffled, and about half of the curriculum had no task at all. Tasks 13-37 (tracks/ckad/bank_extra.py) give every missing competency at least one task: build and export an image with podman (OCI archive), Job completions and parallelism, a DaemonSet on every node, a native sidecar that lets a Job finish, a PVC and an in-memory emptyDir, a canary by replica ratio, rolling-update limits, a Kustomize overlay, a Helm values file and an upgrade that keeps values, manifests on removed APIs, kubectl top, logs of a crashing container, two stacked faults, OOMKilled, a custom resource, RBAC for a ServiceAccount, Pod Security admission, ResourceQuota with LimitRange defaults, Guaranteed QoS, ConfigMaps, Secrets, ServiceAccount tokens, an egress NetworkPolicy tested with real traffic, a Service with two faults and an Ingress with TLS. A mock now draws 16 tasks weighted like the exam (DESIGN 3, DEPLOY 3, OBS 3, ENV 4, NET 3) from a pool of 8/7/7/10/5. New ensure_metrics_server helper so `kubectl top` works on a freshly built cluster. Starter tasks now delete their Pods with --wait=true, so a re-seed cannot apply onto a Pod that is still terminating, and setups that create bare Pods fail loudly if one is missing. README gains a CKAD section. Verified on a throwaway cluster: all 12 starter and 25 new tasks score 0 untouched and full when solved; four full 16-task mocks that together include all 37 tasks scored 122/122, 118/118, 124/124 and 119/119. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mhxm4rsUfuE5HoytYbu2YW
This commit is contained in:
parent
1cd963e8b5
commit
e6ee219dfc
24
README.md
24
README.md
@ -5,8 +5,9 @@ Entry point is `./exam` (renamed from `cka-sim` so it can't collide with the dir
|
|||||||
Hands-on exam tasks graded **per check** against a real cluster, an exam clock, a browser
|
Hands-on exam tasks graded **per check** against a real cluster, an exam clock, a browser
|
||||||
front end modelled on the real PSI exam, attempt history, and drills that rotate through the
|
front end modelled on the real PSI exam, attempt history, and drills that rotate through the
|
||||||
domains before you sit a full exam. Tracks: **CKA** (102 tasks: a random pool of 43 normal +
|
domains before you sit a full exam. Tracks: **CKA** (102 tasks: a random pool of 43 normal +
|
||||||
24 hard, plus two fixed simulator sets, C and D, of 18 and 17 tasks), CKAD and CKS (scaffolded —
|
24 hard, plus two fixed simulator sets, C and D, of 18 and 17 tasks), **CKAD** (37 tasks, at
|
||||||
same format, add questions when you get there).
|
least one for every competency in the curriculum) and CKS (scaffolded — same format, add
|
||||||
|
questions when you get there).
|
||||||
|
|
||||||
## How a session is built
|
## How a session is built
|
||||||
|
|
||||||
@ -73,10 +74,25 @@ The same state is available from the terminal if you prefer:
|
|||||||
./exam list | show N | timer | status # during
|
./exam list | show N | timer | status # during
|
||||||
./exam end # submit
|
./exam end # submit
|
||||||
./exam weak | history # afterwards
|
./exam weak | history # afterwards
|
||||||
./exam --track ckad web # other tracks
|
./exam --track ckad web # other tracks (or CKA_TRACK=ckad)
|
||||||
./teardown.sh # delete cluster + state (history kept)
|
./teardown.sh # delete cluster + state (history kept)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## CKAD track
|
||||||
|
|
||||||
|
`./exam --track ckad ...` (every command takes it). A mock draws **16** tasks in 120 minutes,
|
||||||
|
weighted like the exam: Design and Build 3, Deployment 3, Observability 3, Environment and
|
||||||
|
Security 4, Networking 3, from a pool of 37 (8/7/7/10/5), so no two mocks are the same set. Every
|
||||||
|
line of the curriculum has at least one task: image builds, Jobs, DaemonSets, native sidecars,
|
||||||
|
volumes; blue/green, canary, rolling-update limits, Helm, Kustomize; API deprecations, probes,
|
||||||
|
`kubectl top`, logs, debugging (CrashLoopBackOff, OOMKilled, config errors); CRDs, RBAC, Pod
|
||||||
|
Security admission, quotas and LimitRanges, QoS, ConfigMaps, Secrets, ServiceAccounts;
|
||||||
|
NetworkPolicy (tested with real traffic), Services, Ingress with TLS.
|
||||||
|
|
||||||
|
A few tasks need more than kubectl on this machine: `podman` (Q13 builds an image), `helm`
|
||||||
|
(Q6, Q21), and network access the first time Q23 runs on a fresh cluster (it installs
|
||||||
|
metrics-server). Drill a domain with `./exam --track ckad drill ENV`, as for CKA.
|
||||||
|
|
||||||
## Scoring, history, drills
|
## Scoring, history, drills
|
||||||
|
|
||||||
Each task is a list of named, weighted checks (`tracks/cka/questions.py`). You get the points
|
Each task is a list of named, weighted checks (`tracks/cka/questions.py`). You get the points
|
||||||
@ -158,5 +174,5 @@ Banks live in `tracks/cka/{questions,bank_extra,bank_hard,bank_sim_*}.py`; a tas
|
|||||||
Scripts get `$K` (pinned to the grading context), `$WS`, `$CP/$W1/$W2/$W3`, `nx NODE 'cmd'`,
|
Scripts get `$K` (pinned to the grading context), `$WS`, `$CP/$W1/$W2/$W3`, `nx NODE 'cmd'`,
|
||||||
and helpers `j <get args> '{jsonpath}'` and `eq a b`. Task text may use `{WS}`, `{CP}`, `{W2}`,
|
and helpers `j <get args> '{jsonpath}'` and `eq a b`. Task text may use `{WS}`, `{CP}`, `{W2}`,
|
||||||
`{W3}`, `{SHELL_CP}` etc. `TRACK["quota"]` / `["quota_hard"]` set how many tasks per domain a
|
`{W3}`, `{SHELL_CP}` etc. `TRACK["quota"]` / `["quota_hard"]` set how many tasks per domain a
|
||||||
session draws. For CKAD/CKS, fill in `QUESTIONS` in the
|
session draws. For CKS, fill in `QUESTIONS` in the
|
||||||
existing placeholder track; `TRACK` already carries the domain table and pass mark.
|
existing placeholder track; `TRACK` already carries the domain table and pass mark.
|
||||||
|
|||||||
14
exam
14
exam
@ -136,6 +136,20 @@ ensure_gateway_crds() {
|
|||||||
&& $K wait --for=condition=Established --timeout=60s crd/gatewayclasses.gateway.networking.k8s.io \
|
&& $K wait --for=condition=Established --timeout=60s crd/gatewayclasses.gateway.networking.k8s.io \
|
||||||
crd/gateways.gateway.networking.k8s.io crd/httproutes.gateway.networking.k8s.io >/dev/null 2>&1
|
crd/gateways.gateway.networking.k8s.io crd/httproutes.gateway.networking.k8s.io >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
# ensure_metrics_server -> a working metrics API for `kubectl top` (CKAD Q23). kind ships none, and
|
||||||
|
# kind's kubelet serving certs have no IP SANs, so it only becomes Ready with --kubelet-insecure-tls.
|
||||||
|
# CKA Q23 strips that flag on purpose; the baseline puts it back before every session.
|
||||||
|
ensure_metrics_server() {
|
||||||
|
if ! $K -n kube-system get deploy metrics-server >/dev/null 2>&1; then
|
||||||
|
curl -sfL --max-time 60 https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -o /tmp/ms.yaml \
|
||||||
|
&& $K apply -f /tmp/ms.yaml >/dev/null 2>&1 || return 1
|
||||||
|
fi
|
||||||
|
if ! $K -n kube-system get deploy metrics-server -o jsonpath='{.spec.template.spec.containers[0].args}' | grep -q kubelet-insecure-tls; then
|
||||||
|
$K -n kube-system patch deploy metrics-server --type=json \
|
||||||
|
-p '[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]' >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
$K -n kube-system rollout status deploy metrics-server --timeout=150s >/dev/null 2>&1
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _pick_track_arg(argv):
|
def _pick_track_arg(argv):
|
||||||
|
|||||||
1595
tracks/ckad/bank_extra.py
Normal file
1595
tracks/ckad/bank_extra.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,5 @@
|
|||||||
"""CKAD track. Starter bank - same format as the CKA banks, extend it the same way.
|
"""CKAD track: 37 tasks, at least one per competency in the CKAD curriculum (1-12 here, 13-37 in
|
||||||
|
bank_extra.py). Same format as the CKA banks.
|
||||||
|
|
||||||
CKAD is a build-things exam: less cluster surgery, more "produce the right object, fast".
|
CKAD is a build-things exam: less cluster surgery, more "produce the right object, fast".
|
||||||
The clock is the adversary, so these are mostly single-object tasks graded exactingly.
|
The clock is the adversary, so these are mostly single-object tasks graded exactingly.
|
||||||
@ -17,8 +18,10 @@ TRACK = {
|
|||||||
"ENV": "Application Environment, Configuration and Security (25%)",
|
"ENV": "Application Environment, Configuration and Security (25%)",
|
||||||
"NET": "Services and Networking (20%)",
|
"NET": "Services and Networking (20%)",
|
||||||
},
|
},
|
||||||
"quota": {"DESIGN": 3, "DEPLOY": 3, "OBS": 2, "ENV": 2, "NET": 2},
|
# 16 tasks in 120 minutes, weighted like the exam (20/20/15/25/20 %). The pool per domain is
|
||||||
"quota_hard": {"DESIGN": 3, "DEPLOY": 3, "OBS": 2, "ENV": 2, "NET": 2},
|
# 8/7/7/10/5, so no two mocks are the same set.
|
||||||
|
"quota": {"DESIGN": 3, "DEPLOY": 3, "OBS": 3, "ENV": 4, "NET": 3},
|
||||||
|
"quota_hard": {"DESIGN": 3, "DEPLOY": 3, "OBS": 3, "ENV": 4, "NET": 3},
|
||||||
}
|
}
|
||||||
|
|
||||||
QUESTIONS = [
|
QUESTIONS = [
|
||||||
@ -35,7 +38,7 @@ mounted at `/spool` in both:
|
|||||||
|
|
||||||
`kubectl logs %{pod} -c adapter` must show the writer's lines.
|
`kubectl logs %{pod} -c adapter` must show the writer's lines.
|
||||||
""",
|
""",
|
||||||
"setup": NS + r"""mkns %{ns}; $K -n %{ns} delete pod %{pod} --ignore-not-found --wait=false >/dev/null""",
|
"setup": NS + r"""mkns %{ns}; $K -n %{ns} delete pod %{pod} --ignore-not-found --wait=true --grace-period=1 >/dev/null""",
|
||||||
"checks": [
|
"checks": [
|
||||||
C("pod Running with containers writer and adapter", 2, r"""
|
C("pod Running with containers writer and adapter", 2, r"""
|
||||||
p="$K -n %{ns} get pod %{pod}"
|
p="$K -n %{ns} get pod %{pod}"
|
||||||
@ -86,7 +89,7 @@ connection instead.
|
|||||||
""",
|
""",
|
||||||
"setup": NS + r"""
|
"setup": NS + r"""
|
||||||
mkns %{ns}
|
mkns %{ns}
|
||||||
$K -n %{ns} delete pod %{pod} --ignore-not-found --wait=false >/dev/null
|
$K -n %{ns} delete pod %{pod} --ignore-not-found --wait=true --grace-period=1 >/dev/null
|
||||||
$K -n %{ns} delete deploy backend --ignore-not-found >/dev/null
|
$K -n %{ns} delete deploy backend --ignore-not-found >/dev/null
|
||||||
$K apply -f - >/dev/null <<EOF
|
$K apply -f - >/dev/null <<EOF
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
@ -363,6 +366,7 @@ spec:
|
|||||||
image: busybox:1.99-doesnotexist
|
image: busybox:1.99-doesnotexist
|
||||||
command: [sh, -c, "echo working; sleep 86400"]
|
command: [sh, -c, "echo working; sleep 86400"]
|
||||||
EOF
|
EOF
|
||||||
|
$K -n %{ns} get pod %{pod} >/dev/null
|
||||||
""",
|
""",
|
||||||
"checks": [
|
"checks": [
|
||||||
C("reason.txt records the image pull failure", 3, r"""grep -Eqi 'ImagePull|ErrImagePull|ImagePullBackOff' "$WS/q8/reason.txt" 2>/dev/null"""),
|
C("reason.txt records the image pull failure", 3, r"""grep -Eqi 'ImagePull|ErrImagePull|ImagePullBackOff' "$WS/q8/reason.txt" 2>/dev/null"""),
|
||||||
@ -394,7 +398,7 @@ Create Pod `%{pod}` in `%{ns}` (image `busybox:1.36`, command `sleep 86400`) tha
|
|||||||
- drops **all** capabilities and adds back only `NET_BIND_SERVICE`,
|
- drops **all** capabilities and adds back only `NET_BIND_SERVICE`,
|
||||||
- cannot gain new privileges.
|
- cannot gain new privileges.
|
||||||
""",
|
""",
|
||||||
"setup": NS + r"""mkns %{ns}; $K -n %{ns} delete pod %{pod} --ignore-not-found --wait=false >/dev/null""",
|
"setup": NS + r"""mkns %{ns}; $K -n %{ns} delete pod %{pod} --ignore-not-found --wait=true --grace-period=1 >/dev/null""",
|
||||||
"checks": [
|
"checks": [
|
||||||
C("runs as UID %{uid} with runAsNonRoot", 3, r"""
|
C("runs as UID %{uid} with runAsNonRoot", 3, r"""
|
||||||
p="$K -n %{ns} get pod %{pod}"
|
p="$K -n %{ns} get pod %{pod}"
|
||||||
@ -444,7 +448,7 @@ In `%{ns}`:
|
|||||||
""",
|
""",
|
||||||
"setup": NS + r"""
|
"setup": NS + r"""
|
||||||
mkns %{ns}
|
mkns %{ns}
|
||||||
$K -n %{ns} delete pod %{pod} --ignore-not-found --wait=false >/dev/null
|
$K -n %{ns} delete pod %{pod} --ignore-not-found --wait=true --grace-period=1 >/dev/null
|
||||||
$K -n %{ns} delete cm app-cfg --ignore-not-found >/dev/null; $K -n %{ns} delete secret app-sec --ignore-not-found >/dev/null
|
$K -n %{ns} delete cm app-cfg --ignore-not-found >/dev/null; $K -n %{ns} delete secret app-sec --ignore-not-found >/dev/null
|
||||||
""",
|
""",
|
||||||
"checks": [
|
"checks": [
|
||||||
@ -573,3 +577,8 @@ EOF
|
|||||||
""",
|
""",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# 13-37: the rest of the curriculum, one or more tasks per competency the starter bank left out.
|
||||||
|
from tracks.ckad import bank_extra # noqa: E402
|
||||||
|
|
||||||
|
QUESTIONS = QUESTIONS + bank_extra.QUESTIONS
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user