Chapter 9 — Programming II
Lesson map
Lesson 09 published. Map, example, EX025.
flowchart TD
N0["Learning outcomes"]
N1["9.1 Modules"]
N2["9.2 Parsing"]
N3["9.3 Error handling"]
N4["9.4 Testing"]
N5["9.5 Code review"]
N6["9.6 Performance"]
N7["Packaging the checker"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
N7 --> EX["Worked example"]
EX --> Q["Exercise EX025"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["9.1 Modules"]
N2["9.2 Parsing"]
N3["9.3 Error handling"]
N4["9.4 Testing"]
N5["9.5 Code review"]
N6["9.6 Performance"]
N7["Packaging the checker"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX025"]
[Learning outcomes]
|
v
[9.1 Modules]
|
v
[9.2 Parsing]
|
v
[9.3 Error handling]
|
v
[9.4 Testing]
|
v
[9.5 Code review]
|
v
[9.6 Performance]
|
v
[Packaging the checker]
|
v
[Worked example] --> [Exercise EX025]
Learning outcomes
You will (1) structure larger programs into modules; (2) parse YAML-like and JSON configuration; (3) handle errors robustly; (4) write tests and review code.
9.1 Modules
Elena organises her configuration checker into modules: loading, checks, reporting. Each check is a function with one responsibility, so new checks can be added without changing others.
Separating loading from checks means a new export format needs only a new loader. Separating reporting means output can change from text to CSV without touching the checks. One function per check keeps each easy to test and read, and lets the team turn individual checks on or off for different clusters. Rosa adds one check herself.
9.2 Parsing
Kubernetes manifests are YAML; Python's standard library has no YAML parser, so labs use JSON exports, which Kubernetes tools can produce. Parsing errors are reported with the file and line.
Using JSON keeps the labs within the standard library while still working with real Kubernetes data. Reporting the file and line of a parse error lets the user find and fix the problem quickly. Elena's loader also checks that each document has the expected top-level fields before passing it to the checks.
9.3 Error handling
Missing fields default to the least safe interpretation for reporting: a missing runAsNonRoot is reported as "not set", because Kubernetes would not enforce it.
Treating a missing setting as safe would hide real exposure, since Kubernetes applies its own default, which may be less strict. Reporting it as not set makes the gap visible. The report distinguishes 'explicitly false' from 'not set', because the fixes and the reasons differ slightly. Elena documents each default the checker assumes, citing the Kubernetes behaviour behind it, so reviewers can confirm the assumptions are correct.
9.4 Testing
Each check has tests with compliant and non-compliant synthetic manifests. Tests run on every change in the course repository.
Each test pairs a synthetic manifest with the expected result, such as one issue for a pod that runs as root. Testing both compliant and non-compliant cases checks that the function flags problems and does not raise false alarms. Running tests on every change catches mistakes before they reach users. A new check cannot be merged without both kinds of test, and the test data live beside the check in the repository.
9.5 Code review
A reviewer checks correctness, clarity and that the program only reads data.
Correctness asks whether the checks match the policy; clarity asks whether others can maintain them. Confirming the program only reads data protects production systems from accidental changes. The reviewer also checks that test cases cover the edge cases Elena described in her design note. Rosa reviews Elena's first three checks herself. Her comments become a short checklist that Elena applies to every later check before asking for review, so reviews get quicker as the project goes on.
9.6 Performance
The checker processes 3,000 manifests in under two seconds, fast enough to run in a build pipeline.
Fast checks can run on every change without slowing developers. If the checker took minutes, teams would be tempted to skip it. Elena measures run time on the full set of exported manifests and records it, so later changes that slow the checker are noticed. The time is printed at the end of each run. If the manifest count doubles as meterhub grows, the checker should still finish quickly, since each manifest is checked independently and the work grows only in proportion to the count.
Packaging the checker
The checker is packaged with a short README, example inputs and expected outputs, so others can run it and confirm it works. Version numbers are recorded in each report, which matters when results change after an update. Elena also writes a list of checks with the reason and source for each one, linking to the Kubernetes Pod Security Standards and NIST SP 800-190. That list becomes the basis of later control design, so each check traces back to published guidance.
Logging decisions
Each check logs why it flagged a resource, making false alarms easier to trace.
Worked example
A review finds Elena's checker treats a container-level setting as overriding the pod-level one in the wrong direction. A new test reproduces it, the fix is one line, and the test stays to prevent regression.
Synthetic case. Code and counts are synthetic.
Errors and limits
Defaults matter: know how the platform interprets missing settings.
Exercises
- EX025 — Missing fields. How are missing security fields reported? Rubric: 3 = as not set, least safe; 2 = partial; 1 = vague; 0 = none.
- EX026 — JSON choice. Why use JSON exports in labs? Rubric: 3 = stdlib lacks YAML parser; 2 = partial; 1 = vague; 0 = none.
- EX027 — Review catch. What did review catch? Rubric: 3 = override direction wrong; 2 = partial; 1 = vague; 0 = none.
Synthesis
Structured, tested programs scale to real configuration sets.
Continue studying — chapter 09 of 50
← Chapter 08 (draft) · Course chapter index · Chapter 10 (draft) →