The Caddy ingress image was hardcoded in the component manifest and
had no update path shy of cluster recreate or manual kubectl patch.
That forced woodburn to run an out-of-band ansible playbook to bump
Caddy, and broke the "spec.yml is source of truth" model.
Changes:
- spec.yml: new `caddy-ingress-image` key (default
`ghcr.io/laconicnetwork/caddy-ingress:latest`).
- Deployment manifest: `strategy: Recreate` on the Caddy Deployment
— required because the pod binds hostPort 80/443, which prevents
any rolling update from completing (new pod hangs Pending forever
waiting for old pod to release the ports).
- install_ingress_for_kind: accepts caddy_image and templates the
manifest before applying, same pattern as the existing acme-email
templating.
- update_caddy_ingress_image: patches the running Caddy Deployment
when the spec image differs from the live image. No-op if they
match. Returns True if a patch was applied so the caller can wait
for the rollout.
- deploy_k8s._setup_cluster: on cluster reuse (ingress already up),
reconcile the running image against the spec. Installs path
unchanged; only the "already running, maybe needs update" branch
is new.
Cluster-scoped caveat: caddy-system is shared by every deployment on
the cluster, so the spec value in any one deployment rolls Caddy for
all of them — last `deployment start` wins. Documented in
deployment_patterns.md alongside the other cluster-scoped concerns
(kind-mount-root, namespace ownership).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>