Add ip mode to external-services for static IP endpoints
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/740/head
parent
185ebf17f9
commit
ff32c4350d
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
"""
|
||||
|
|
|
|||
Loading…
Reference in New Issue