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 <noreply@anthropic.com>
fix/k8s-port-mappings-hostnetwork
A. F. Dudley 2026-03-03 05:28:52 +00:00
parent 4a1b5d86fd
commit fb69cc58ff
2 changed files with 37 additions and 0 deletions

View File

@ -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(

View File

@ -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