feat(deploy): kind validation of the three-tier topology with NetworkPolicy - #465
Merged
Minipada merged 10 commits intoSep 2, 2026
Merged
Conversation
…Policy Adds tools/kind/, a kind cluster with Calico (a real, policy-enforcing CNI, unlike k3d's default Flannel) that proves the fleet's security claims by attempted connection rather than by rendering: the robot tier has no internet route, sites cannot reach each other, and robot -> edge -> hub carries real Records collected by dc-ros, including through an induced edge-tier outage the robot buffers through and recovers from without loss. Wired into ci.yaml as a pre-merge check (verify-kind-networkpolicy), separate from the fast colcon test/prek loop. Live-verified against a local kind+Calico cluster: cluster bring-up, all NetworkPolicy enforcement checks, and the edge aggregator's Vector config against a real hub Postgres all pass. The dc-ros/Records-flow path is exercised the same way verify-robot-manifests already does (CI's build-dc-ros-image image), not re-built locally. Closes #452 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDTVLJ2NZ2fa3EKgdRCFDj Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## jazzy #465 +/- ##
=======================================
Coverage 71.44% 71.44%
=======================================
Files 124 124
Lines 7563 7563
=======================================
Hits 5403 5403
Misses 2160 2160
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…hem inline
verify-kind-networkpolicy previously curl-downloaded kind and kubectl on
every run. Adds containers/kind-tools/Containerfile (pinned versions) and a
build-kind-tools-image job; verify-kind-networkpolicy now pulls that image
and podman-cp's the binaries out instead of reinstalling them itself, with
--cache-from/--cache-to making an unchanged pin a cache hit rather than a
rebuild. Records this as a general convention in CLAUDE.md ("Podman-built
images for CI tools, not inline installs") for future CI tooling.
Verified locally: podman build + hadolint clean, and both binaries
extracted from the built image run and report the pinned versions.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDTVLJ2NZ2fa3EKgdRCFDj
Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
…ookworm bookworm is the previous stable; trixie is current. Rebuilt and re-verified locally (podman build, hadolint, and both extracted binaries reporting the pinned versions) before pushing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDTVLJ2NZ2fa3EKgdRCFDj Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
…cument in mdbook
Per feedback: CI, local dev, and any future reproduction of this validation
should all run the exact same script, never a dev-only convenience path that
can drift from what CI actually asserts. Removes
tools/kind/scripts/{up,down,load_images}.sh and inlines their podman
build/save, kind create/delete, and kubectl commands directly into run.sh —
one script, top to bottom (bring up, verify, tear down), same as
tools/e2e/scripts/run.sh already does for the E2E harness.
Adds doc/src/dc/deploy_kind_networkpolicy.md (mdbook), walking through the
exact same commands run.sh runs, for reading or running by hand without the
script — verified by building the docs site locally (mdbook + linkcheck,
tools/ci/pre-commit/build_doc.sh) and confirming the page renders with no
broken links.
Records the "one script for CI and dev, no up/down pairs" rule in CLAUDE.md
for future harnesses.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDTVLJ2NZ2fa3EKgdRCFDj
Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
Per feedback: run.sh had become a second place (alongside the mdbook page) maintaining the same command sequence. Deletes it and inlines every step directly into ci.yaml's verify-kind-networkpolicy job as its own named step — cluster create, Calico install, image load, manifest applies, readiness waits, the NetworkPolicy check, and the induced-outage/record count check are now literal podman/kind/kubectl commands, not a script CI happens to call. verify_network_policy.sh stays a script: it has real control flow (retry loops, pass/fail assertions across six checks) that duplicating across CI and docs would actually risk drifting, unlike a fixed one-shot command sequence with no branching. doc/src/dc/deploy_kind_networkpolicy.md is updated to match exactly — every command on that page is now the literal ci.yaml step, no more references to a script that no longer exists. Verified: extracted every ci.yaml step's bash into temp files and ran bash -n + shellcheck on each (clean); rebuilt the mdbook site locally (no broken links); and re-ran the full command sequence (cluster create through NetworkPolicy verification) against a live local kind+Calico cluster — all six checks pass again. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDTVLJ2NZ2fa3EKgdRCFDj Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
…harness-only tag
kubernetes/robot-a.yaml and edge-a.yaml previously required ${DC_ROS_IMAGE}/
${VECTOR_IMAGE} to always be substituted, and the docs illustrated that
with an invented local tag (dc-ros:kind) built on the spot — a second,
harness-only way to get dc-ros that diverged from how every other
rendering in this repo gets it.
Both manifests now commit real, working defaults instead, same convention
deploy/robot/scripts/verify_kube_play.sh already uses: dc-ros defaults to
ghcr.io/minipada/ros2_data_collection/dc-ros:jazzy (the floating ref
build-dc-ros-image actually pushes and a real deployment actually pins to
— not :latest, which this repo doesn't push), and vector defaults to the
same literal version robot-pod.yaml pins. CI substitutes DC_ROS_IMAGE only
to test the PR's own just-built image; a plain local run needs no
substitution at all and pulls the exact image a real robot would run.
Verified: podman pull of the real ghcr.io/.../dc-ros:jazzy ref succeeds
(public, no auth needed); kubeconform still validates all manifests; every
ci.yaml step's bash re-checked with bash -n + shellcheck; mdbook rebuilt
locally with no broken links.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDTVLJ2NZ2fa3EKgdRCFDj
Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
This repo doesn't push a :latest tag for dc-ros/dc-uploader — :jazzy (the
branch ref build-dc-ros-image/build-dc-uploader-image push on every merge)
is the one a real deployment pins to. Several files still assumed :latest
existed:
- ci.yaml's build-dc-ros-image/build-dc-uploader-image jobs were pushing a
:latest tag alongside :<sha>/:jazzy on every push to jazzy — removed, so
CI stops producing a tag this repo doesn't want.
- deploy/robot/{compose.yaml,kubernetes/robot-pod.yaml,quadlet/*.container}
defaulted to :latest — now default to :jazzy, matching what CI actually
publishes.
- deploy/robot/scripts/verify_kube_play.sh's sed substitution and docs
updated to match.
- .github/actions/{image-ref,podman-push}/action.yaml's own descriptions
corrected — they described the now-removed :latest push.
Verified: podman pull of ghcr.io/.../dc-ros:jazzy succeeds; kubeconform
still validates robot-pod.yaml and every tools/kind manifest; ci.yaml's
every job (not just verify-kind-networkpolicy) re-checked with bash -n;
full prek suite and reuse lint pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDTVLJ2NZ2fa3EKgdRCFDj
Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
…s image Replaces the hand-rolled containers/kind-tools/Containerfile + build-kind-tools-image job + podman-cp extraction with one step: the Helm-maintained, widely-used helm/kind-action (pinned to commit ef37e7f390d99f746eb8b610417061a60e82a6cc / v1.14.0), install_only: true. It puts pinned kind/kubectl on PATH with sha256 checksum verification against their own published sums — something the old Containerfile never did — for two binaries a maintained action already installs correctly, no custom image to build/push/maintain. install_only is required, not optional: the action otherwise always runs `kind create cluster --wait=<duration>`, which blocks for node Readiness. That can't succeed here before Calico is installed, since kind-config.yaml's disableDefaultCNI leaves every node NotReady until then. Cluster creation stays exactly as it was: a plain `kind create cluster` with no --wait, immediately followed by the Calico apply. Updates CLAUDE.md's CI-tools rule to prefer a maintained, SHA-pinned installer action first, falling back to a Podman-built image only when no such installer exists for the tool. Verified before landing: read helm/kind-action's actual kind.sh/main.sh source (not just its README) to confirm the --wait behavior and that install_only skips cluster creation; resolved the v1 tag to v1.14.0's commit SHA via the GitHub API; bash -n on every ci.yaml job's embedded script (not just this one); kubeconform on every touched manifest; mdbook rebuilt locally with no broken links; full prek + reuse lint. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDTVLJ2NZ2fa3EKgdRCFDj Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
…omize Replaces 7 separate kubectl apply/create-configmap steps (namespaces, 3 imperative ConfigMaps, networkpolicies, hub, edge, probes, robot) with tools/kind/kustomization.yaml + one `kubectl apply -k tools/kind/` — kustomize ships in kubectl, no new binary. Its configMapGenerator replaces two of the three imperative `kubectl create configmap --dry-run=client | apply` commands; the third (hub-init-sql, sourced from tools/e2e/sql/init.sql, genuinely outside this tree) stays as-is, since kustomize refuses by design to read a file outside its own root. Considered Helm too (raised directly): not a fit here — this harness deploys one fixed topology every run, nothing to parameterize across environments, which is Helm's actual value proposition. Documented as tools/kind/README.md's new "Why not Helm" section, and noted that the real fleet deployment tooling (deploy/robot/ and beyond) is a different problem where that combination genuinely fits — tracked separately as its own architecture question (#466), not folded into this harness. Verified against a live local kind+Calico cluster: `kubectl kustomize` renders 19 valid resources (kubeconform-clean), `kubectl apply -k` creates everything, namespace-first ordering happens automatically, and critically the ConfigMap hash-suffix reference rewrite actually took effect at runtime — dc-robot's mounted robot-a-params ConfigMap volume resolved to the generated robot-a-params-<hash> name, not the plain one. (An unrelated environmental issue — this long-lived sandbox's accumulated fs.inotify.max_user_instances exhaustion — kept dc-ros's vector container from starting cleanly on this run; that is unchanged pre-existing behavior, not something this refactor touches, and CI's GitHub-hosted runners start fresh with no such history.) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDTVLJ2NZ2fa3EKgdRCFDj Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
… too Raises the kustomization root from tools/kind/ to tools/ (the actual common ancestor of tools/kind/ and tools/e2e/, whose init.sql the hub-init-sql ConfigMap needs) so kustomize's own load-restriction — it refuses to read a file outside its root — no longer forces one ConfigMap to be created imperatively while the other two are generated. All three are now configMapGenerator entries, and the CI job collapses from three steps (apply namespaces, create hub-init-sql, apply everything else) to one: `kubectl apply -k tools/`. Namespace objects apply before namespaced resources that need them (kubectl's own apply-time Kind ordering) even when the Namespace and the ConfigMap targeting it are both generated in the same kustomize build — verified live, not assumed. The induced-outage restore step now reuses that same `apply -k tools/` command too, instead of a narrower `apply -f networkpolicies.yaml` — the command that brings the topology up is also the one that puts it back. Verified against a live local kind+Calico cluster: a single `kubectl apply -k tools/` with no separate namespace step created all 20 resources (kubeconform-clean) with zero ordering errors, and hub-postgres/edge-vector came up functional (dc_records table present, reachable). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDTVLJ2NZ2fa3EKgdRCFDj Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
github-actions
Bot
deleted the
feature/452-kind-validation-of-the-three-tier-topolo
branch
September 2, 2026 01:07
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
tools/kind/: a kubeadm cluster (kind) with Calico — a real, policy-enforcing CNI, unlike k3d's default Flannel (k3d cluster for local development iteration #451) — that proves the fleet's security claims by attempted connection rather than by rendering.dc-ros+ its local Vector Shipper), an edge aggregator (site A), a stand-in second site (dc-edge-b), and a hub (Postgres), and asserts viascripts/verify_network_policy.shandscripts/run.sh:dc-edge-b);dc-ros, arriving at the hub through the edge aggregator;NetworkPolicyswap, not a stopped container — and loses nothing once it's restored..github/workflows/ci.yamlasverify-kind-networkpolicy, a pre-merge check separate from the fastcolcon test/prekloop, needing onlybuild-dc-ros-image's published image.tools/kind/README.md; building and shipping DC stays on Podman (scripts/load_images.shroutes thedc-rosimage throughpodman save/kind load image-archive, neverkind load docker-image).check-yamlexclude fortools/kind/kubernetes/(.pre-commit-config.yaml) since those manifests are intentionally multi-document and already schema-validated by the existingkubeconformhook.Verification
prek run --all-files --skip build-doc: all hooks pass, includingkubeconformagainst every new manifest.reuse lint: compliant.shellcheck/bash -nclean on every new script.up.sh), all manifests applied and became Ready, and everyverify_network_policy.shcheck passed for real — no-internet, both site-to-site directions, and both positive controls. The edge aggregator's Vector config was confirmed valid and its healthcheck against a real hub Postgres passed;tools/e2e/sql/init.sql'sdc_recordstable was confirmed present.dc-ros/Records-flow and induced-outage portions ofrun.shneed a realdc-rosimage, which is out of scope to build in this environment (a full ROS workspace build) — CI'sbuild-dc-ros-imagejob supplies that image the same wayverify-robot-manifestsalready consumes it.Closes #452