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
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
parent
185ebf17f9
commit
0bf1ea70d5
|
|
@ -1094,8 +1094,9 @@ class ClusterInfo:
|
||||||
def get_external_service_resources(self) -> List:
|
def get_external_service_resources(self) -> List:
|
||||||
"""Build k8s Services (and Endpoints) for external-services in spec.
|
"""Build k8s Services (and Endpoints) for external-services in spec.
|
||||||
|
|
||||||
Two modes:
|
Three modes:
|
||||||
- host mode: ExternalName Service (DNS CNAME to external host)
|
- 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
|
- selector mode: headless Service + Endpoints (cross-namespace
|
||||||
routing to a mock pod, IP discovered at deploy time)
|
routing to a mock pod, IP discovered at deploy time)
|
||||||
|
|
||||||
|
|
@ -1124,6 +1125,24 @@ class ClusterInfo:
|
||||||
)
|
)
|
||||||
resources.append(svc)
|
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:
|
elif "selector" in config and "namespace" in config:
|
||||||
# Cross-namespace headless Service + Endpoints.
|
# Cross-namespace headless Service + Endpoints.
|
||||||
# The Endpoints IP is populated in deploy_k8s.py at deploy
|
# 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.
|
"""Create k8s Services for external-services declared in the spec.
|
||||||
|
|
||||||
For host mode: ExternalName Service (DNS CNAME).
|
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
|
For selector mode: headless Service + Endpoints with pod IPs
|
||||||
discovered from the target namespace.
|
discovered from the target namespace.
|
||||||
"""
|
"""
|
||||||
|
|
@ -461,6 +462,47 @@ class K8sDeployer(Deployer):
|
||||||
else:
|
else:
|
||||||
raise
|
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
|
# Create Endpoints for selector-mode services
|
||||||
for name, svc_config in ext_services.items():
|
for name, svc_config in ext_services.items():
|
||||||
if "selector" not in svc_config or "namespace" not in svc_config:
|
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:
|
Each entry maps a service name to its routing config:
|
||||||
- host mode: {host: "example.com", port: 443}
|
- host mode: {host: "example.com", port: 443}
|
||||||
→ ExternalName k8s Service (DNS CNAME)
|
→ 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}
|
- selector mode: {selector: {app: "foo"}, namespace: "ns", port: 443}
|
||||||
→ Headless Service + Endpoints (cross-namespace routing to mock pod)
|
→ Headless Service + Endpoints (cross-namespace routing to mock pod)
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue