4.9 KiB
4.9 KiB
name, description
| name | description |
|---|---|
| audit-never-run-container-deploy | Audit a locally built podman/quadlet deploy path that has never actually run: prove base-image assumptions, env-file reader divergence, and image-hash coverage before the first deploy. |
Audit a never-run container deploy path
A deployable that has never been built or started has never been executed: every
fault in it is invisible to tests that stub the runtime. Audit it as static code
against the tools it calls, then prove each suspicion cheaply against the real
tool. The faults worth hunting are the ones that report success while doing
nothing — those are the ones that survive a green /health and a green e2e run.
The three fault classes (all measured 2026-10-18, frank-tts-engine)
- A command the base image does not ship. e.g.
adduserin aRUNline against a trimmedubuntu:24.04— absent, and not pulled in byca-certificates curl libgomp1. Dies at build time (loud), but only the first time anyone builds it. Prove it in the base, never from memory:podman run --rm <base> sh -c "command -v adduser || echo absent; useradd --system --uid 10000 --home-dir /app --shell /usr/sbin/nologin tts && id tts"Note the accepted cosmetic warning (uid 10000 is greater than SYS_UID_MAX 999) in a comment, or a future agent "fixes" it by dropping--systemand the uid stops matching the sibling container. - Two readers of one env file disagree. systemd units that pass
EnvironmentFile=hand it to podman--env-file, which does no shell parsing — it takes the literal remainder of the line — while bashsourcestrips one quoting layer. SoWARM_TEXT="Warm up, one two three."reaches the container with its quotes, and a script that interpolates it into a JSON body produces invalid JSON: the container boots,active, every voice registered, every voice cold. Fix in the consumer: strip exactly one matched pair only when both ends are the same quote AND the inner content carries another quote or backslash (never mangle a value that legitimately contains quotes), then escape. - A build-skip hash that under-covers the image.
build_compose_image-style helpers skippodman buildwhen a label hash matches; hashing theContainerfilealone means editing aCOPY'd app/script rebuilds nothing — deploy reports success,/healthanswers, container serves old code. Each case arm must name the paths itsContainerfilecopies (extra=(…)) and hash Containerfile+extras. Arms that have no extras keep their old hash, so they do not needlessly rebuild.
Test it as behaviour, never as a grep of the handler
- Drive the real function:
awk '/^build_compose_image\(\) \{/,/^\}/' deploy-remote.sh > bci.sh,sourceit, put a stubpodmanfirst onPATH, assert the observable question — did the deploy ask podman to build, or did it skip? - Stub traps that cost real time: podman is invoked
podman image inspect <image> --format …so the tag is$3, not$2; tags contain/so a state file named after one needs${tag//\//_}or the write silently lands in a nonexistent directory and every image looks permanently unbuilt;--label cfhash=…arrives before-t <tag>, so collect the label and write it after the arg loop. - For an env-file bug, write one test that reads the file both ways —
sourceand the--env-fileway (parseKEY=literal remainder) — and assert the program receives the same value in both shapes, including a value that carries its own quotes and a backslash. A suite that only sources the file is how the bug shipped. - Prove red:
git show HEAD:scripts/deploy-remote.sh > scripts/deploy-remote.sh, run the new test (must fail at the exact intended step), restore. Keep each fix in its own commit and run the seam at each one so the branch bisects.
Base-image and host facts to check while auditing
useraddlives inlogin(1)(base carries it);adduserdoes not.- Rootless podman cannot raise rlimits —
--ulimit nofile=…above the host hard limit dies at container create, not at runtime. - A referenced
Network=.networkfile the generator cannot find means the container unit is not produced at all. - Two containers talk by DNS name only if they attach to a common network; check both unit files rather than trusting a comment that says they share one.
- Container DNS name + the network that owns it, never
host.containers.internalor a published loopback port, for service-to-service traffic.
Do not
- Do not deploy the audited thing because the audit is finished: first build of a heavy image (recursive source clone ~10 min) plus a new unit on a host serving people is the operator's call.
- Do not add a Prometheus target for a container with no
/metrics— say the probe is a decision (blackbox HTTP), do not fake a scrape.