From fb69cc58ffd015e187d208910b233c140128046c Mon Sep 17 00:00:00 2001 From: "A. F. Dudley" Date: Tue, 3 Mar 2026 05:28:52 +0000 Subject: [PATCH 1/2] feat(k8s): map compose service ports to Kind extraPortMappings and support hostNetwork Kind's extraPortMappings only included ports 80/443 for Caddy. Compose service ports (RPC, gossip, UDP) were never forwarded, making them unreachable from the host. Also adds hostNetwork/dnsPolicy to the k8s pod spec when any compose service uses network_mode: host. Co-Authored-By: Claude Opus 4.6 --- stack_orchestrator/deploy/k8s/cluster_info.py | 13 ++++++++++ stack_orchestrator/deploy/k8s/helpers.py | 24 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/stack_orchestrator/deploy/k8s/cluster_info.py b/stack_orchestrator/deploy/k8s/cluster_info.py index da24bdc2..161fbd03 100644 --- a/stack_orchestrator/deploy/k8s/cluster_info.py +++ b/stack_orchestrator/deploy/k8s/cluster_info.py @@ -394,6 +394,14 @@ class ClusterInfo: result.append(pv) return result + def _any_service_has_host_network(self): + for pod_name in self.parsed_pod_yaml_map: + pod = self.parsed_pod_yaml_map[pod_name] + for svc in pod.get("services", {}).values(): + if svc.get("network_mode") == "host": + return True + return False + # TODO: put things like image pull policy into an object-scope struct def get_deployment(self, image_pull_policy: Optional[str] = None): containers = [] @@ -568,6 +576,7 @@ class ClusterInfo: ) ) + use_host_network = self._any_service_has_host_network() template = client.V1PodTemplateSpec( metadata=client.V1ObjectMeta(annotations=annotations, labels=labels), spec=client.V1PodSpec( @@ -577,6 +586,10 @@ class ClusterInfo: affinity=affinity, tolerations=tolerations, runtime_class_name=self.spec.get_runtime_class(), + host_network=use_host_network or None, + dns_policy=( + "ClusterFirstWithHostNet" if use_host_network else None + ), ), ) spec = client.V1DeploymentSpec( diff --git a/stack_orchestrator/deploy/k8s/helpers.py b/stack_orchestrator/deploy/k8s/helpers.py index 8b367f86..4d9cbe3b 100644 --- a/stack_orchestrator/deploy/k8s/helpers.py +++ b/stack_orchestrator/deploy/k8s/helpers.py @@ -683,11 +683,35 @@ def _generate_kind_port_mappings_from_services(parsed_pod_files): def _generate_kind_port_mappings(parsed_pod_files): port_definitions = [] + seen = set() # Map port 80 and 443 for the Caddy ingress controller (HTTPS support) for port_string in ["80", "443"]: port_definitions.append( f" - containerPort: {port_string}\n hostPort: {port_string}\n" ) + seen.add((port_string, "TCP")) + # Map ports declared in compose services + for pod in parsed_pod_files: + parsed_pod_file = parsed_pod_files[pod] + if "services" in parsed_pod_file: + for service_name in parsed_pod_file["services"]: + service_obj = parsed_pod_file["services"][service_name] + for port_entry in service_obj.get("ports", []): + port_str = str(port_entry) + protocol = "TCP" + if "/" in port_str: + port_str, proto = port_str.split("/", 1) + protocol = proto.upper() + if ":" in port_str: + port_str = port_str.split(":")[-1] + port_num = port_str.strip("'\"") + if (port_num, protocol) not in seen: + seen.add((port_num, protocol)) + port_definitions.append( + f" - containerPort: {port_num}\n" + f" hostPort: {port_num}\n" + f" protocol: {protocol}\n" + ) return ( "" if len(port_definitions) == 0 -- 2.45.2 From f305214ce1b6a545c2d5c1533e7a3b5e6b391f01 Mon Sep 17 00:00:00 2001 From: "A. F. Dudley" Date: Tue, 3 Mar 2026 05:28:55 +0000 Subject: [PATCH 2/2] add local test runner script Co-Authored-By: Claude Opus 4.6 --- tests/scripts/run-test-local.sh | 53 +++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 tests/scripts/run-test-local.sh diff --git a/tests/scripts/run-test-local.sh b/tests/scripts/run-test-local.sh new file mode 100755 index 00000000..f6f32346 --- /dev/null +++ b/tests/scripts/run-test-local.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# Run a test suite locally in an isolated venv. +# +# Usage: +# ./tests/scripts/run-test-local.sh +# +# Examples: +# ./tests/scripts/run-test-local.sh tests/webapp-test/run-webapp-test.sh +# ./tests/scripts/run-test-local.sh tests/smoke-test/run-smoke-test.sh +# ./tests/scripts/run-test-local.sh tests/k8s-deploy/run-deploy-test.sh +# +# The script creates a temporary venv, installs shiv, builds the laconic-so +# package, runs the requested test, then cleans up. + +set -euo pipefail + +if [ $# -lt 1 ]; then + echo "Usage: $0 [args...]" + exit 1 +fi + +TEST_SCRIPT="$1" +shift + +if [ ! -f "$TEST_SCRIPT" ]; then + echo "Error: $TEST_SCRIPT not found" + exit 1 +fi + +REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +VENV_DIR=$(mktemp -d /tmp/so-test-XXXXXX) + +cleanup() { + echo "Cleaning up venv: $VENV_DIR" + rm -rf "$VENV_DIR" +} +trap cleanup EXIT + +cd "$REPO_DIR" + +echo "==> Creating venv in $VENV_DIR" +python3 -m venv "$VENV_DIR" +source "$VENV_DIR/bin/activate" + +echo "==> Installing shiv" +pip install -q shiv + +echo "==> Building laconic-so package" +./scripts/create_build_tag_file.sh +./scripts/build_shiv_package.sh + +echo "==> Running: $TEST_SCRIPT $*" +exec "./$TEST_SCRIPT" "$@" -- 2.45.2