Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions app/handlers/handlers_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,23 @@ def get_ca_cert(tls_secret: kubernetes.client.V1Secret) -> str:
TLS_OBJECT_ANNOTATION = "resource.twingate.com/tlsSecret"


def validate_load_balancer_status(status: Status, service_name: str) -> None:
def get_load_balancer_external_ip(status: Status, service_name: str) -> str:
if not (ingress := status.get("loadBalancer", {}).get("ingress")):
raise kopf.TemporaryError(
f"Kubernetes Service: {service_name} LoadBalancer IP is not ready.",
delay=30,
)

if not ingress[0].get("ip"):
ip = ingress[0].get("ip")
hostname = ingress[0].get("hostname")
if not ip and not hostname:
raise kopf.TemporaryError(
f"Kubernetes Service: {service_name} LoadBalancer IP is not ready.",
delay=30,
)

return ip or hostname


class ServiceType(StrEnum):
CLUSTER_IP = "ClusterIP"
Expand Down Expand Up @@ -128,14 +132,11 @@ def service_to_twingate_resource(service_body: Body, namespace: str) -> dict:
f"Kubernetes Secret object: {tls_secret_name} is missing."
)

if spec["type"] == ServiceType.LOAD_BALANCER:
validate_load_balancer_status(status, service_name)

result["spec"] |= {
"address": "kubernetes.default.svc.cluster.local",
"proxy": {
"address": (
status["loadBalancer"]["ingress"][0]["ip"]
get_load_balancer_external_ip(status, service_name)
if spec["type"] == ServiceType.LOAD_BALANCER
else f"{service_name}.{namespace}.svc.cluster.local"
),
Expand Down
27 changes: 23 additions & 4 deletions app/handlers/tests/test_handlers_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,19 +301,37 @@ def test_kubernetes_resource_type_annotation_with_invalid_ca_cert(
example_cluster_ip_gateway_service_body, "default"
)

@pytest.mark.parametrize(
("status", "expected"),
[
({"loadBalancer": {"ingress": [{"ip": "1.2.3.4"}]}}, "1.2.3.4"),
(
{"loadBalancer": {"ingress": [{"hostname": "gateway.hostname.int"}]}},
"gateway.hostname.int",
),
],
)
def test_kubernetes_resource_with_load_balancer_service_type(
self,
example_load_balancer_gateway_service_body,
k8s_core_client_mock,
k8s_tls_secret_mock,
status,
expected,
):
tls_object_name = "gateway-tls"
namespace = "default"
k8s_core_client_mock.read_namespaced_secret.return_value = k8s_tls_secret_mock

result = service_to_twingate_resource(
example_load_balancer_gateway_service_body, namespace
)
with patch(
"kopf._cogs.structs.bodies.Body.status",
new_callable=PropertyMock,
return_value=status,
):
result = service_to_twingate_resource(
example_load_balancer_gateway_service_body, namespace
)

k8s_core_client_mock.read_namespaced_secret.assert_called_once_with(
namespace=namespace, name=tls_object_name
)
Expand All @@ -323,7 +341,7 @@ def test_kubernetes_resource_with_load_balancer_service_type(
"address": "kubernetes.default.svc.cluster.local",
"alias": "alias.int",
"proxy": {
"address": "10.0.0.1",
"address": expected,
"certificateAuthorityCert": BASE64_OF_VALID_CA_CERT,
},
"protocols": {
Expand All @@ -347,6 +365,7 @@ def test_kubernetes_resource_with_load_balancer_service_type(
{"loadBalancer": {}},
{"loadBalancer": {"ingress": []}},
{"loadBalancer": {"ingress": [{"ip": None}]}},
{"loadBalancer": {"ingress": [{"hostname": None}]}},
],
)
def test_kubernetes_resource_when_load_balancer_ip_is_not_ready(
Expand Down