Checkpoint Lab — Containers, Reproducible Runtimes, and Distributed Test Environments
Containerize a deterministic Robot Framework suite, prove host/container semantic equivalence, inject and diagnose a result-mount path failure, preserve evidence, and document the filesystem/network ownership contract.
Checkpoint outcomes
- Build a deterministic non-root Robot image and record image/base/dependency provenance.
- Predict and verify source, process-user, result-volume and output-state changes before execution.
- Run the same suite on host and in the container and prove semantic equivalence.
- Inject a wrong result-mount destination, preserve the stopped container, recover its evidence, and repair the runtime contract.
- Produce an operator-ready evidence ledger describing filesystem, network, user and cleanup boundaries.
Current compatibility baseline — verified 2026-08-31.
Robot Framework 7.4.2 is the stable course baseline
and requires Python 3.8+. The mandatory container lab uses the
Docker Official Image python:3.12.14-slim-bookworm and
installs the Robot Framework 7.4.2 universal wheel by exact version
and SHA-256 hash. Docker image tags are mutable, so the lesson
records the resolved image digest and explains full digest pinning
for controlled pipelines. Pabot 5.2.2 is discussed
as the current stable optional parallel executor; 5.3.0b1 is
prerelease and is not required. No browser library, external
API/database/SSH service, CI provider, Kubernetes cluster, paid
platform, real credential, or production target is required.
1. Checkpoint scenario and safety boundary
You will reuse the rf26-container-lab project from
Lesson 2. Everything stays local: one Python/Robot image, one
deterministic Robot suite, Docker-managed named volumes and host
evidence directories. No public ports, production systems,
credentials, browsers, databases or external services are involved.
flowchart TD
A[Host source] --> B[rf26 image]
B --> C[Host baseline PASS]
B --> D[Container PASS]
D --> E[Correct result volume]
B --> F[Injected wrong mount target]
F --> G[Robot PASS but volume empty]
G --> H[Inspect + recover stopped-container results]
H --> I[Fix destination + rerun]
I --> J[Evidence packet + cleanup]
The injected failure is deliberately about evidence persistence, not the Robot assertions. This demonstrates an important production lesson: application/test success and operational evidence success are separate contracts.
2. Exact preflight and version manifest
python --version
python -m robot --version
docker version
docker info
docker image inspect python:3.12.14-slim-bookworm --format '{{json .RepoDigests}}' > evidence/checkpoint-base-digest.json
docker image inspect rf26-robot:7.4.2 --format '{{.Id}} {{.Config.User}}' > evidence/checkpoint-image.txt
Expected course assumptions: Robot 7.4.2, Python 3.12.14 inside the
image, non-root image user 10001:10001. If your local
Docker Engine/Desktop differs, record it; do not pretend the engine
itself is part of the image.
3. Predict before mutation
| Prediction | Before action | Expected after action | Independent verification |
|---|---|---|---|
| Image build does not create host Robot result files | results directories empty | image exists; result dirs still empty |
docker image inspect + host directory listing
|
| Correct named volume captures Robot outputs | new volume empty | output.xml/log/report/proof exist in mounted destination | inspect mounts + copy/list results |
| Wrong mount target does not fail test assertions | wrong volume empty | Robot exits 0 but writes to image-owned /workspace/results | container exit + mount inspection + internal file listing |
| Removing container before recovery would destroy internal-only evidence | stopped broken container exists | evidence recoverable until rm | docker cp before docker rm |
Write your own expected image ID/user, mount destination and
artifact list into evidence/predictions.md before
running the mutation steps.
4. Establish the host baseline and retain its exit code
rm -rf results-host/* results-container/* 2>/dev/null || true
python -m robot --variable RUNTIME_LABEL:host-checkpoint --outputdir results-host tests/runtime_contract.robot
printf 'host_exit=%s
' "$?" > evidence/checkpoint-host-exit.txt
PowerShell users should preserve $LASTEXITCODE instead.
Verify four host artifacts: output.xml,
log.html, report.html, and
proof.txt.
5. Correct container run: explicit named volume, retained container, exported evidence
docker volume create rf26-checkpoint-results
docker create --name rf26-checkpoint --mount type=volume,src=rf26-checkpoint-results,dst=/workspace/results rf26-robot:7.4.2 --variable RUNTIME_LABEL:container-checkpoint --outputdir /workspace/results /workspace/tests/runtime_contract.robot
docker start --attach rf26-checkpoint
docker inspect rf26-checkpoint --format '{{.State.ExitCode}}' > evidence/checkpoint-container-exit.txt
docker inspect rf26-checkpoint --format '{{json .Mounts}}' > evidence/checkpoint-container-mounts.json
docker cp rf26-checkpoint:/workspace/results/. results-container/
Verify the mount destination is exactly
/workspace/results. The container's Robot exit code
should be 0 and the exported host directory should contain the same
four artifact types as the host baseline.
6. Prove semantic equivalence
python tools/compare_results.py
sha256sum results-host/proof.txt results-container/proof.txt 2>/dev/null || true
The XML comparison script proves matching test names/statuses; the proof files demonstrate matching deterministic domain evidence. The HTML/XML result files themselves need not be byte-identical because runtime metadata differs.
7. Inject the mount-path failure without deleting the failed runtime
docker volume create rf26-broken-results
docker create --name rf26-broken --mount type=volume,src=rf26-broken-results,dst=/workspace/wrong-results rf26-robot:7.4.2 --variable RUNTIME_LABEL:broken-mount --outputdir /workspace/results /workspace/tests/runtime_contract.robot
docker start --attach rf26-broken
docker inspect rf26-broken --format '{{.State.ExitCode}}' > evidence/broken-exit.txt
docker inspect rf26-broken --format '{{json .Mounts}}' > evidence/broken-mounts.json
Expected surprise: Robot can still PASS because
/workspace/results exists and is writable inside the
image, but the named volume is attached to
/workspace/wrong-results. If the container were started
with --rm, the test evidence at the real output
directory would disappear on removal.
8. Diagnose independently: prove the volume is empty and the stopped container still owns the missing evidence
# List the wrongly mounted volume using the same image as a harmless inspector.
docker run --rm --entrypoint python --mount type=volume,src=rf26-broken-results,dst=/check rf26-robot:7.4.2 -c 'import os; print(os.listdir("/check"))' > evidence/broken-volume-list.txt
# Recover the actual Robot result directory before deleting the stopped container.
mkdir -p recovered-broken
docker cp rf26-broken:/workspace/results/. recovered-broken/
find recovered-broken -maxdepth 1 -type f -printf "%f\n" > evidence/broken-internal-results.txt
docker cp works from a stopped container, so the exact
first-run files can be recovered without restarting the process. The
authoritative diagnosis is the mount destination from
docker inspect plus the recovered files that actually
existed at /workspace/results.
This is why the diagnostic sequence preserves the stopped container: you can recover first-failure evidence without rerunning and changing timestamps/state.
9. Apply the least destructive correction and rerun only the broken contract
docker rm rf26-broken
# Keep rf26-broken-results until after diagnosis; it proves the wrong mount was empty.
docker create --name rf26-repaired --mount type=volume,src=rf26-broken-results,dst=/workspace/results rf26-robot:7.4.2 --variable RUNTIME_LABEL:repaired-mount --outputdir /workspace/results /workspace/tests/runtime_contract.robot
docker start --attach rf26-repaired
docker inspect rf26-repaired --format '{{json .Mounts}}' > evidence/repaired-mounts.json
mkdir -p repaired-results
docker cp rf26-repaired:/workspace/results/. repaired-results/
The only material change is the mount destination. No test code, timeout, retry, privilege or assertion was changed because none of those layers caused the defect.
10. Build the evidence ledger/operator runbook
RF26 checkpoint evidence ledger
-------------------------------
Source revision / git status:
Base image tag + resolved digest:
Built image ID:
Container configured user:
Robot version / Python version:
Host command + exit code:
Correct container command + exit code:
Correct volume destination:
Wrong volume destination:
Recovered internal result path:
Repaired volume destination:
Host/container semantic comparison:
Network exposure: none (no published ports)
Secrets used: none
CPU/memory limits: record if configured
Cleanup owner/date:
Artifacts retained:
- host output.xml/log.html/report.html/proof.txt
- container output.xml/log.html/report.html/proof.txt
- base/image provenance
- correct/broken/repaired mount inspections
- broken-volume listing
- recovered first-failure evidence
This ledger is deliberately operational. A reviewer can see what changed, which layer failed, how evidence was preserved, and why the repair was limited to a mount destination.
11. Verification checklist
-
The image is built from
python:3.12.14-slim-bookworm; its resolved base digest is recorded. - Robot 7.4.2 is hash-pinned in the image.
-
The image runs as numeric non-root user
10001:10001. - Host and correct-container runs have the same Robot test names and PASS statuses.
-
Both retained result sets contain
proof.txtwithrf26-container-contract. -
The intentionally wrong volume destination is visible in
broken-mounts.json. - The wrong volume does not contain the Robot result files.
- First-failure results were recovered from the stopped broken container before deletion.
- The repaired run changes the mount destination, not the Robot assertions/timeouts.
- No ports, production endpoints, credentials, privileged mode or Docker socket mounts were used.
12. Cleanup/rollback after evidence is accepted
docker rm -f rf26-checkpoint rf26-repaired 2>/dev/null || true
docker volume rm rf26-checkpoint-results rf26-broken-results 2>/dev/null || true
# Optional after review:
docker image rm rf26-robot:7.4.2
# Do NOT run docker system prune as part of this lab.
# Retain evidence/, results-host/, results-container/, recovered-broken/, repaired-results/
# until your review/retention policy says they can be removed.
Cleanup is narrowly scoped to named resources created by the lab. A global prune could destroy unrelated developer or CI evidence and is intentionally excluded.
13. What Chapter 26 adds to a production Robot operating model
You can now treat the container runtime as a governed execution boundary rather than a black box. The production model records image/dependency provenance, non-root identity, runtime mounts and networks, capacity, Robot exit status and durable evidence. It also distinguishes Pabot worker isolation from container/CI isolation and avoids assuming that a fresh container resets external state.
Chapter 27 moves from runtime reproducibility to source maintainability: the Robot Framework Style Guide, current Robocop linting/formatting, documentation and naming conventions become enforceable review/CI contracts.
Knowledge check
Why is the injected failure valuable even though the Robot tests still PASS?
It proves that test status and evidence persistence are separate contracts. A green assertion result can still be operationally unusable if output.xml/log/report disappear with the container.
Why keep the broken container stopped instead of immediately rerunning with the correct mount?
The stopped instance preserves the exact first-failure filesystem. Recovering it proves the diagnosis without replacing evidence with a second run.
Which state changed in the repair: Robot model, image, or container runtime?
Only container runtime configuration—the mount destination. The Robot suite and image remain unchanged.
If this lab later used Pabot inside four CI jobs, what additional evidence would be required?
Record Pabot version/process count, worker/output isolation and capacity math, then prove external files/ports/accounts/records do not collide across the multiplied workers.
Why is “no published ports” part of the evidence ledger?
It proves the mandatory lab did not create an unnecessary network exposure boundary. Networking was not needed to satisfy the checkpoint.
14. Checkpoint complete
The checkpoint demonstrates deterministic host/container behavior, explicit image and package provenance, non-root runtime identity, durable result handling, a recoverable mount-path failure and narrowly scoped cleanup. You have a repeatable pattern for containerizing Robot Framework without confusing containerization with isolation or losing the evidence needed to debug it.
References and version anchors
- Robot Framework 7.4.2 User Guide — execution/result semantics used inside the container
- Robot Framework 7.4.2 PyPI — stable version, Python requirement and distribution hashes
- Docker build best practices — reproducible image and non-root guidance
- Docker run reference — runtime mount/read-only behavior
- Docker resource constraints — capacity evidence for future parallel/container runs
- Pabot releases — optional current parallel-executor version anchor
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.