From 63325f68a75e03fb655037a3944e547004f22bdc Mon Sep 17 00:00:00 2001 From: "A. F. Dudley" Date: Thu, 2 Apr 2026 05:18:10 +0000 Subject: [PATCH] fix: deduplicate container ports by (port, protocol) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compose files with both "8001" (TCP) and "8001/udp" produce separate V1ContainerPort entries that k8s rejects as duplicates. Deduplicate after parsing by (container_port, protocol) key. This was blocking biscayne's agave deployment — the spec has both TCP 8001 (ip_echo) and UDP 8001 (gossip), which generated two UDP 8001 entries. Co-Authored-By: Claude Opus 4.6 (1M context) --- stack_orchestrator/deploy/k8s/cluster_info.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/stack_orchestrator/deploy/k8s/cluster_info.py b/stack_orchestrator/deploy/k8s/cluster_info.py index 1656e301..71cbd9f5 100644 --- a/stack_orchestrator/deploy/k8s/cluster_info.py +++ b/stack_orchestrator/deploy/k8s/cluster_info.py @@ -563,6 +563,17 @@ class ClusterInfo: container_port=port, protocol=protocol ) ) + # Deduplicate by (port, protocol) — compose files with + # both "8001" and "8001/udp" produce separate entries + # that k8s rejects as duplicates. + seen = set() + deduped = [] + for cp in container_ports: + key = (cp.container_port, cp.protocol) + if key not in seen: + seen.add(key) + deduped.append(cp) + container_ports = deduped if opts.o.debug: print(f"image: {image}") print(f"service ports: {container_ports}")