Skip to content

Commit 59a1343

Browse files
committed
Fix: Parse nested targets within label_selector load balancer targets
When a load balancer has a label_selector type target, the API returns a nested "targets" array containing the resolved individual server targets with their health statuses. This data was previously discarded. - Add `targets` parameter to `LoadBalancerTarget.__init__` in domain.py - Parse nested targets in `BoundLoadBalancer.__init__` for label_selector targets, creating `LoadBalancerTarget` objects with server, health_status, type, and use_private_ip fields - Add test fixture and test case for nested target parsing https://claude.ai/code/session_018oDf81V2LsgKcFheq7KSRp
1 parent 7aaaa72 commit 59a1343

File tree

4 files changed

+70
-1
lines changed

4 files changed

+70
-1
lines changed

hcloud/load_balancers/client.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,33 @@ def __init__(
108108
selector=target["label_selector"]["selector"]
109109
)
110110
tmp_target.use_private_ip = target["use_private_ip"]
111+
nested_targets = target.get("targets", [])
112+
if nested_targets:
113+
tmp_nested = []
114+
for nested in nested_targets:
115+
nested_target = LoadBalancerTarget(type=nested["type"])
116+
if nested["type"] == "server":
117+
nested_target.server = BoundServer(
118+
client._parent.servers,
119+
data=nested["server"],
120+
complete=False,
121+
)
122+
elif nested["type"] == "ip":
123+
nested_target.ip = LoadBalancerTargetIP(
124+
ip=nested["ip"]["ip"]
125+
)
126+
nested_target.use_private_ip = nested.get("use_private_ip")
127+
nested_health_status = nested.get("health_status")
128+
if nested_health_status is not None:
129+
nested_target.health_status = [
130+
LoadBalancerTargetHealthStatus(
131+
listen_port=hs["listen_port"],
132+
status=hs["status"],
133+
)
134+
for hs in nested_health_status
135+
]
136+
tmp_nested.append(nested_target)
137+
tmp_target.targets = tmp_nested
111138
elif target["type"] == "ip":
112139
tmp_target.ip = LoadBalancerTargetIP(ip=target["ip"]["ip"])
113140

hcloud/load_balancers/domain.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ class LoadBalancerTarget(BaseDomain):
420420
"ip",
421421
"use_private_ip",
422422
"health_status",
423+
"targets",
423424
)
424425
__slots__ = __api_properties__
425426

@@ -431,13 +432,15 @@ def __init__(
431432
ip: LoadBalancerTargetIP | None = None,
432433
use_private_ip: bool | None = None,
433434
health_status: list[LoadBalancerTargetHealthStatus] | None = None,
435+
targets: list[LoadBalancerTarget] | None = None,
434436
):
435437
self.type = type
436438
self.server = server
437439
self.label_selector = label_selector
438440
self.ip = ip
439441
self.use_private_ip = use_private_ip
440442
self.health_status = health_status
443+
self.targets = targets
441444

442445
def to_payload(self) -> dict[str, Any]:
443446
"""

tests/unit/load_balancers/conftest.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,23 @@ def response_load_balancer():
8686
"health_status": [{"listen_port": 443, "status": "healthy"}],
8787
"label_selector": None,
8888
"use_private_ip": False,
89-
}
89+
},
90+
{
91+
"type": "label_selector",
92+
"label_selector": {"selector": "eu"},
93+
"use_private_ip": True,
94+
"targets": [
95+
{
96+
"type": "server",
97+
"server": {"id": 105054278},
98+
"use_private_ip": True,
99+
"health_status": [
100+
{"listen_port": 443, "status": "healthy"},
101+
{"listen_port": 3000, "status": "healthy"},
102+
],
103+
}
104+
],
105+
},
90106
],
91107
"algorithm": {"type": "round_robin"},
92108
}

tests/unit/load_balancers/test_client.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,29 @@ def test_init(self, response_load_balancer):
6060
assert bound_load_balancer.id == 4711
6161
assert bound_load_balancer.name == "Web Frontend"
6262

63+
def test_init_label_selector_nested_targets(self, response_load_balancer):
64+
bound_load_balancer = BoundLoadBalancer(
65+
client=mock.MagicMock(), data=response_load_balancer["load_balancer"]
66+
)
67+
68+
label_selector_target = bound_load_balancer.targets[1]
69+
assert label_selector_target.type == "label_selector"
70+
assert label_selector_target.label_selector.selector == "eu"
71+
assert label_selector_target.use_private_ip is True
72+
assert label_selector_target.targets is not None
73+
assert len(label_selector_target.targets) == 1
74+
75+
nested = label_selector_target.targets[0]
76+
assert nested.type == "server"
77+
assert nested.server.id == 105054278
78+
assert nested.use_private_ip is True
79+
assert nested.health_status is not None
80+
assert len(nested.health_status) == 2
81+
assert nested.health_status[0].listen_port == 443
82+
assert nested.health_status[0].status == "healthy"
83+
assert nested.health_status[1].listen_port == 3000
84+
assert nested.health_status[1].status == "healthy"
85+
6386

6487
class TestLoadBalancerslient:
6588
@pytest.fixture()

0 commit comments

Comments
 (0)