From 8712a5ea62a8b5b115af3c40053d9968da16beb1 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Tue, 21 Apr 2026 08:38:29 +0000 Subject: [PATCH] fix(k8s): template Caddy image by container name, not string match Replacing the string `constants.default_caddy_ingress_image` in the raw YAML missed the intended override when the manifest on disk had a different hardcoded image than the constant (e.g. shiv built from an older main where the default was still `caddy/ingress:latest`). The spec's override silently lost to whatever the manifest happened to ship with. Parse the YAML first, find the Deployment named `caddy-ingress-controller` and its container by the same name, set image directly. Makes the spec the source of truth regardless of what's hardcoded in the manifest. Co-Authored-By: Claude Opus 4.7 (1M context) --- stack_orchestrator/deploy/k8s/helpers.py | 30 +++++++++++++++++------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/stack_orchestrator/deploy/k8s/helpers.py b/stack_orchestrator/deploy/k8s/helpers.py index fc12cbef..4cbf3270 100644 --- a/stack_orchestrator/deploy/k8s/helpers.py +++ b/stack_orchestrator/deploy/k8s/helpers.py @@ -488,17 +488,29 @@ def install_ingress_for_kind( if opts.o.debug: print(f"Configured Caddy with ACME email: {acme_email}") - # Substitute image only when an override is requested; otherwise - # leave the hardcoded default in the manifest. - if caddy_image and caddy_image != constants.default_caddy_ingress_image: - yaml_content = yaml_content.replace( - constants.default_caddy_ingress_image, caddy_image - ) - if opts.o.debug: - print(f"Configured Caddy image: {caddy_image}") - yaml_objects = list(yaml.safe_load_all(yaml_content)) + # Override the Caddy container's image when a spec value is set. + # Works regardless of what's hardcoded in the manifest — we locate + # the container by name and overwrite its image field, rather than + # relying on a string match of the default. + if caddy_image: + for obj in yaml_objects: + if not obj: + continue + if ( + obj.get("kind") == "Deployment" + and obj.get("metadata", {}).get("name") + == "caddy-ingress-controller" + ): + for c in ( + obj["spec"]["template"]["spec"].get("containers") or [] + ): + if c.get("name") == "caddy-ingress-controller": + c["image"] = caddy_image + if opts.o.debug: + print(f"Configured Caddy image: {caddy_image}") + # Split: apply everything except the Caddy controller Deployment first, # so the namespace + secrets exist before the pod can start and read its # secret_store. Race-free: Caddy has no way to see the cluster until