Add ip mode to external-services for static IP endpoints (#740)
Publish / Gate: k8s deploy e2e (push) Failing after 2s Details
Container Registry Test / Run container registry hosting test on kind/k8s (push) Failing after 0s Details
Publish / Build and publish (push) Has been skipped Details
Database Test / Run database hosting test on kind/k8s (push) Failing after 0s Details
External Stack Test / Run external stack test suite (push) Failing after 0s Details
K8s Deploy Test / Run deploy test suite on kind/k8s (push) Failing after 0s Details
K8s Deployment Control Test / Run deployment control suite on kind/k8s (push) Failing after 0s Details
Webapp Test / Run webapp test suite (push) Failing after 0s Details
Smoke Test / Run basic test suite (push) Failing after 0s Details
Lint Checks / Run linter (push) Failing after 0s Details
Deploy Test / Run deploy test suite (push) Failing after 0s Details

ExternalName services only support DNS names (CNAME records), not
raw IP addresses. Add an ip mode that creates a headless Service +
Endpoints with a static IP, enabling routing to host-network
services like Kind gateway IPs or bare-metal endpoints.

Spec format:
  external-services:
    my-service:
      ip: 172.18.0.1
      port: 8899

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
pull/609/merge v1.1.0-0bf1ea7-202604021227
prathamesh0 2026-04-02 17:53:23 +05:30 committed by GitHub
parent 185ebf17f9
commit 0bf1ea70d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 64 additions and 1 deletions

View File

@ -1094,8 +1094,9 @@ class ClusterInfo:
def get_external_service_resources(self) -> List:
"""Build k8s Services (and Endpoints) for external-services in spec.
Two modes:
Three modes:
- host mode: ExternalName Service (DNS CNAME to external host)
- ip mode: headless Service + Endpoints with a static IP
- selector mode: headless Service + Endpoints (cross-namespace
routing to a mock pod, IP discovered at deploy time)
@ -1124,6 +1125,24 @@ class ClusterInfo:
)
resources.append(svc)
elif "ip" in config:
# Static IP: headless Service + Endpoints with fixed address.
# Useful for routing to host-network services (e.g. Kind
# host gateway) or any endpoint reachable by raw IP.
svc = client.V1Service(
metadata=client.V1ObjectMeta(
name=name,
labels={"app": self.app_name},
),
spec=client.V1ServiceSpec(
cluster_ip="None",
ports=[client.V1ServicePort(port=port, name=f"port-{port}")],
),
)
resources.append(svc)
# Endpoints are created in deploy_k8s.py using the
# static IP — no pod discovery needed.
elif "selector" in config and "namespace" in config:
# Cross-namespace headless Service + Endpoints.
# The Endpoints IP is populated in deploy_k8s.py at deploy

View File

@ -431,6 +431,7 @@ class K8sDeployer(Deployer):
"""Create k8s Services for external-services declared in the spec.
For host mode: ExternalName Service (DNS CNAME).
For ip mode: headless Service + Endpoints with static IP.
For selector mode: headless Service + Endpoints with pod IPs
discovered from the target namespace.
"""
@ -461,6 +462,47 @@ class K8sDeployer(Deployer):
else:
raise
# Create Endpoints for ip-mode services (static IP)
for name, svc_config in ext_services.items():
if "ip" not in svc_config:
continue
if opts.o.dry_run:
continue
ip = svc_config["ip"]
port = svc_config.get("port", 443)
endpoints = client.V1Endpoints(
metadata=client.V1ObjectMeta(
name=name,
labels={"app": self.cluster_info.app_name},
),
subsets=[
client.V1EndpointSubset(
addresses=[client.V1EndpointAddress(ip=ip)],
ports=[
client.CoreV1EndpointPort(port=port, name=f"port-{port}")
],
)
],
)
try:
self.core_api.create_namespaced_endpoints(
body=endpoints, namespace=self.k8s_namespace
)
print(f"Created endpoints for '{name}'{ip}:{port}")
except ApiException as e:
if e.status == 409:
self.core_api.replace_namespaced_endpoints(
name=name,
namespace=self.k8s_namespace,
body=endpoints,
)
print(f"Updated endpoints for '{name}'{ip}:{port}")
else:
raise
# Create Endpoints for selector-mode services
for name, svc_config in ext_services.items():
if "selector" not in svc_config or "namespace" not in svc_config:

View File

@ -319,6 +319,8 @@ class Spec:
Each entry maps a service name to its routing config:
- host mode: {host: "example.com", port: 443}
ExternalName k8s Service (DNS CNAME)
- ip mode: {ip: "172.18.0.1", port: 8899}
Headless Service + Endpoints with static IP address
- selector mode: {selector: {app: "foo"}, namespace: "ns", port: 443}
Headless Service + Endpoints (cross-namespace routing to mock pod)
"""