Upgrades six PARTIAL modules to the gold-module standard (lesson/demonstration/ knowledge-check/guided-exercise/lab(s)/reflection), preserving every existing lab activity_id and scenario_id: root-06-m03 (jobs/cronjobs/daemonsets), root-07-m04 (ingress/networkpolicy), root-07-m02 (configuration/secrets, converted in place from a bare 5-lab bank), root-09-m02 (serviceaccounts/rbac), root-09-m03 (network/supply-chain security), root-10-m03 (troubleshooting framework). root-05-m01 (20-task kubectl-fluency bank) gets a minimal bookending lesson + reflection rather than a full redesign, since each task already carries its own explanation. root-12-m01, root-capstone-m01, and root-09-m04 are promoted to READY via documented exceptions (no new activities) where adding one would misrepresent their intentional design. Wires a new rootnode-security-assessment (exam, 60min, hint_policy disabled) using the existing Phase 3B assessment engine: 3 required hands-on CORE scenarios + 4 random-draw pools covering SecurityContext, RBAC, network policy, and the 7 previously-unwired CKS MCQs. Wires the previously-unwired readiness-probe-task scenario as an OPTIONAL practice lab in root-10-m01, using the existing is_required model. Adds root-11-m01-devops-platform-thinking — an honest, lesson-only ROOT 11 orientation module naming DevOps/platform engineering practices already exercised in ROOT 05-10/12, explicit that GitOps/CI-CD/IaC/observability need lab infrastructure this platform doesn't yet provide. No fake labs. Adds infra/scripts/validate-course-content.js: cross-checks the DB against scenario/content files for duplicate ids, orphans, invalid references, cycles, sequence collisions, instructor-notes leakage, and (new) self-paced content deferring explanation to a live instructor. 0 errors against the resulting 22 courses/modules, 163 activities, 98 scenario files. No application code changed — every gap was satisfiable with existing tables (course_modules, module_activities, assessment_definitions) and the existing content-delivery pipeline. Co-Authored-By: Claude Sonnet 5 <[email protected]>
2.6 KiB
Demonstration: Watching a Job Actually Complete
Creating the Job
$ kubectl apply -f pi-job.yaml
job.batch/pi-job created
$ kubectl get jobs
NAME COMPLETIONS DURATION AGE
pi-job 0/1 3s 3s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
pi-job-8x2kq 1/1 Running 0 3s
The Job's own status (0/1 completions) is tracked separately from its Pod's status — this
matters because a Job can retry a FAILED Pod attempt without that looking like the healthy
restart behavior you'd see on a Deployment.
Once the Pod's process actually exits successfully
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
pi-job-8x2kq 0/1 Completed 0 8s
$ kubectl get jobs
NAME COMPLETIONS DURATION AGE
pi-job 1/1 7s 8s
STATUS: Completed — not Running, not CrashLoopBackOff. This is the ONE status a
long-running Deployment's Pod is never supposed to reach on its own; for a Job, it's success.
The Job object itself now reports 1/1 and stops — nothing recreates this Pod, because
"finished successfully" is the desired end state here, unlike a Deployment where exiting is
always treated as something to fix.
What a FAILING Job attempt looks like, briefly
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
bad-job-abc12 0/1 Error 0 5s
bad-job-def34 0/1 Error 0 2s
$ kubectl describe job bad-job
...
Events:
Warning BackoffLimitExceeded Job has reached the specified backoff limit
Two DIFFERENT Pods (abc12, def34), not one Pod restarting twice — each failed attempt gets
its own new Pod, up to backoffLimit. Once that limit is hit, the Job stops retrying and
reports the failure via Events rather than trying forever.
Instructor Notes
Demo script: Run the successful case live; showing the FAILING case is optional if time is
short, but the Completed vs Running/Error distinction is worth pausing on regardless.
Approximate timing: 8 minutes live; 5 minutes self-paced.
Common misconception to preempt: students who've only seen Deployments sometimes expect
kubectl get pods to show the Job's Pod disappear once it's done. It doesn't, by default — it
stays as Completed until manually cleaned up or a TTL is configured, which is worth mentioning
briefly (not a hands-on topic here).
Optional extension: ask what kubectl get pods would show for a CronJob's Pods across
several scheduled runs (answer: one Completed Pod per past run, accumulating — the same
"nothing auto-cleans this up" behavior, multiplied by the schedule).