From abdf72212b73ad1fc67856e88df97b5173ed1767 Mon Sep 17 00:00:00 2001 From: "Jonas L." Date: Fri, 16 Aug 2024 11:09:20 +0200 Subject: [PATCH] fix: check label_selector child targets with load_balancer_status filter (#552) ##### SUMMARY The previous implementation did not take into consideration label selectors targets, and their child targets. This change implements a recursive function that traverse all the targets. Related to #467 #550 --- plugins/filter/all.py | 43 ++++++++----- .../targets/filter_all/tasks/test.yml | 8 +-- tests/unit/filter/test_all.py | 64 +++++++++---------- 3 files changed, 62 insertions(+), 53 deletions(-) diff --git a/plugins/filter/all.py b/plugins/filter/all.py index b0afd1d..2981311 100644 --- a/plugins/filter/all.py +++ b/plugins/filter/all.py @@ -11,29 +11,38 @@ def load_balancer_status(load_balancer: dict, *args, **kwargs) -> Literal["unkno """ Return the status of a Load Balancer based on its targets. """ - try: + + def targets_status(targets: list) -> Literal["unknown", "unhealthy", "healthy"]: result = "healthy" - for target in load_balancer["targets"]: - target_health_status = target.get("health_status") - - # Report missing health status as unknown - if not target_health_status: - result = "unknown" - continue - - for health_status in target_health_status: - status = health_status.get("status") - if status == "healthy": - continue - - if status in (None, "unknown"): - result = "unknown" - continue + for target in targets: + # Label selector targets have child targets that must be checked + if target["type"] == "label_selector": + status = targets_status(target["targets"]) if status == "unhealthy": return "unhealthy" + if status in (None, "unknown"): + result = "unknown" + + continue + + # Report missing health status as unknown + if not target.get("health_status"): + return "unknown" + + for health_status in target.get("health_status"): + status = health_status.get("status") + if status == "unhealthy": + return "unhealthy" + + if status in (None, "unknown"): + result = "unknown" + return result + + try: + return targets_status(load_balancer["targets"]) except Exception as exc: raise AnsibleFilterError(f"load_balancer_status - {to_native(exc)}", orig_exc=exc) from exc diff --git a/tests/integration/targets/filter_all/tasks/test.yml b/tests/integration/targets/filter_all/tasks/test.yml index 55d8092..ffa826d 100644 --- a/tests/integration/targets/filter_all/tasks/test.yml +++ b/tests/integration/targets/filter_all/tasks/test.yml @@ -4,14 +4,14 @@ ansible.builtin.set_fact: load_balancer_status_healthy: >- {{ { "targets": [ - {"health_status": [{"status": "healthy"}]}, - {"health_status": [{"status": "healthy"}]}, + { "type": "server", "health_status": [{ "status": "healthy" }]}, + { "type": "label_selector", "targets": [{ "type": "server", "health_status": [{ "status": "healthy" }] }]} ]} | hetzner.hcloud.load_balancer_status }} load_balancer_status_healthy_and_unhealthy: >- {{ { "targets": [ - {"health_status": [{"status": "healthy"}, {"status": "unhealthy"}]}, - {"health_status": [{"status": "healthy"}, {"status": "healthy"}]}, + { "type": "server", "health_status": [{ "status": "healthy" }, { "status": "unhealthy" }]}, + { "type": "label_selector", "targets": [{ "type": "server", "health_status": [{ "status": "healthy" }, { "status": "unhealthy" }] }]} ]} | hetzner.hcloud.load_balancer_status }} - name: Verify filter load_balancer_status diff --git a/tests/unit/filter/test_all.py b/tests/unit/filter/test_all.py index 5065c42..1e1be07 100644 --- a/tests/unit/filter/test_all.py +++ b/tests/unit/filter/test_all.py @@ -4,39 +4,39 @@ import pytest from plugins.filter.all import load_balancer_status + +def _lb_target_server(status: str) -> dict: + return {"type": "server", "health_status": [{"status": status}]} + + +def _lb_target_label_selector(status: str) -> dict: + return {"type": "label_selector", "targets": [_lb_target_server(status)]} + + LOAD_BALANCER_STATUS_TEST_CASES = ( - ({"targets": [{"health_status": []}]}, "unknown"), - ({"targets": [{"health_status": [{}]}]}, "unknown"), - ({"targets": [{"health_status": [{"status": "unknown"}]}]}, "unknown"), - ({"targets": [{"health_status": [{"status": "unhealthy"}]}]}, "unhealthy"), - ({"targets": [{"health_status": [{"status": "healthy"}]}]}, "healthy"), - ( - { - "targets": [ - {"health_status": [{"status": "healthy"}]}, - {"health_status": [{"status": "healthy"}]}, - ] - }, - "healthy", - ), - ( - { - "targets": [ - {"health_status": [{"status": "healthy"}, {"status": "unhealthy"}]}, - {"health_status": [{"status": "healthy"}, {"status": "unknown"}]}, - ] - }, - "unhealthy", - ), - ( - { - "targets": [ - {"health_status": [{"status": "healthy"}]}, - {"health_status": [{"status": "unhealthy"}]}, - ] - }, - "unhealthy", - ), + ({"targets": [{"type": "server", "health_status": []}]}, "unknown"), + ({"targets": [{"type": "server", "health_status": [{}]}]}, "unknown"), + ({"targets": [_lb_target_server("healthy")]}, "healthy"), + ({"targets": [_lb_target_server("unhealthy")]}, "unhealthy"), + ({"targets": [_lb_target_server("unknown")]}, "unknown"), + ({"targets": [_lb_target_label_selector("healthy"), _lb_target_server("healthy")]}, "healthy"), + ({"targets": [_lb_target_label_selector("healthy"), _lb_target_server("unhealthy")]}, "unhealthy"), + ({"targets": [_lb_target_label_selector("healthy"), _lb_target_server("unknown")]}, "unknown"), + ({"targets": [_lb_target_label_selector("unhealthy"), _lb_target_server("healthy")]}, "unhealthy"), + ({"targets": [_lb_target_label_selector("unhealthy"), _lb_target_server("unhealthy")]}, "unhealthy"), + ({"targets": [_lb_target_label_selector("unhealthy"), _lb_target_server("unknown")]}, "unhealthy"), + ({"targets": [_lb_target_label_selector("unknown"), _lb_target_server("healthy")]}, "unknown"), + ({"targets": [_lb_target_label_selector("unknown"), _lb_target_server("unhealthy")]}, "unhealthy"), + ({"targets": [_lb_target_label_selector("unknown"), _lb_target_server("unknown")]}, "unknown"), + ({"targets": [_lb_target_server("healthy"), _lb_target_label_selector("healthy")]}, "healthy"), + ({"targets": [_lb_target_server("healthy"), _lb_target_label_selector("unhealthy")]}, "unhealthy"), + ({"targets": [_lb_target_server("healthy"), _lb_target_label_selector("unknown")]}, "unknown"), + ({"targets": [_lb_target_server("unhealthy"), _lb_target_label_selector("healthy")]}, "unhealthy"), + ({"targets": [_lb_target_server("unhealthy"), _lb_target_label_selector("unhealthy")]}, "unhealthy"), + ({"targets": [_lb_target_server("unhealthy"), _lb_target_label_selector("unknown")]}, "unhealthy"), + ({"targets": [_lb_target_server("unknown"), _lb_target_label_selector("healthy")]}, "unknown"), + ({"targets": [_lb_target_server("unknown"), _lb_target_label_selector("unhealthy")]}, "unhealthy"), + ({"targets": [_lb_target_server("unknown"), _lb_target_label_selector("unknown")]}, "unknown"), )