Files
ced4568andClaude Sonnet 5 7f6e1857c0 feat(curriculum): Phase 6C module upgrades, security assessment bank, ROOT 11 orientation
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]>
2026-08-26 11:20:17 -05:00

73 lines
2.6 KiB
Markdown

# 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).