feat(k8s): map compose service ports to Kind extraPortMappings and support hostNetwork
Some checks failed
Lint Checks / Run linter (pull_request) Successful in 2m18s
Deploy Test / Run deploy test suite (pull_request) Successful in 5m42s
Webapp Test / Run webapp test suite (pull_request) Failing after 6m28s
Smoke Test / Run basic test suite (pull_request) Successful in 7m33s
K8s Deploy Test / Run deploy test suite on kind/k8s (pull_request) Successful in 7m44s

_generate_kind_port_mappings now reads ports from compose services and
adds them to Kind's extraPortMappings with correct protocol handling
(TCP/UDP). Previously only port 80 for the ingress controller was mapped,
so application ports like Solana gossip/RPC were unreachable from the host.

Also adds hostNetwork support: when any compose service declares
network_mode: host, the K8s pod spec gets hostNetwork: true and
dnsPolicy: ClusterFirstWithHostNet. Required for workloads like Solana
validators that need direct host networking for UDP gossip.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
A. F. Dudley 2026-03-03 04:39:07 +00:00
parent e7aa2e944f
commit fbaa2eaa71
2 changed files with 49 additions and 4 deletions

View File

@ -300,6 +300,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: str = None):
containers = []
@ -362,12 +370,22 @@ class ClusterInfo:
for service_name in services:
labels[key.replace("{name}", service_name)] = value
use_host_network = self._any_service_has_host_network()
template = client.V1PodTemplateSpec(
metadata=client.V1ObjectMeta(
annotations=annotations,
labels=labels
),
spec=client.V1PodSpec(containers=containers, image_pull_secrets=image_pull_secrets, volumes=volumes),
spec=client.V1PodSpec(
containers=containers,
image_pull_secrets=image_pull_secrets,
volumes=volumes,
host_network=use_host_network or None,
dns_policy=(
"ClusterFirstWithHostNet" if use_host_network else None
),
),
)
spec = client.V1DeploymentSpec(
replicas=1, template=template, selector={

View File

@ -251,9 +251,36 @@ def _generate_kind_port_mappings_from_services(parsed_pod_files):
def _generate_kind_port_mappings(parsed_pod_files):
port_definitions = []
# For now we just map port 80 for the nginx ingress controller we install in kind
port_string = "80"
port_definitions.append(f" - containerPort: {port_string}\n hostPort: {port_string}\n")
seen = set()
# Map port 80 for the ingress controller
for port_string in ["80"]:
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()
# Handle host:container port mapping - use container port
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 else (
" extraPortMappings:\n"