From bbc36d57c442df7a50b67c47dd9af0d2125363f9 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 28 Jan 2026 20:34:58 -0300 Subject: [PATCH] snapshot: add namespace pod rollup --- ariadne/services/cluster_state.py | 37 +++++++++++++++++++++++++++++++ tests/test_cluster_state.py | 2 ++ 2 files changed, 39 insertions(+) diff --git a/ariadne/services/cluster_state.py b/ariadne/services/cluster_state.py index 8683221..8f75cc4 100644 --- a/ariadne/services/cluster_state.py +++ b/ariadne/services/cluster_state.py @@ -363,6 +363,40 @@ def _summarize_workloads(payload: dict[str, Any]) -> list[dict[str, Any]]: return output +def _summarize_namespace_pods(payload: dict[str, Any]) -> list[dict[str, Any]]: + namespaces: dict[str, dict[str, Any]] = {} + for pod in _items(payload): + metadata = pod.get("metadata") if isinstance(pod.get("metadata"), dict) else {} + status = pod.get("status") if isinstance(pod.get("status"), dict) else {} + namespace = metadata.get("namespace") if isinstance(metadata.get("namespace"), str) else "" + if not _namespace_allowed(namespace): + continue + phase = status.get("phase") if isinstance(status.get("phase"), str) else "" + entry = namespaces.setdefault( + namespace, + { + "namespace": namespace, + "pods_total": 0, + "pods_running": 0, + "pods_pending": 0, + "pods_failed": 0, + "pods_succeeded": 0, + }, + ) + entry["pods_total"] += 1 + if phase == "Running": + entry["pods_running"] += 1 + elif phase == "Pending": + entry["pods_pending"] += 1 + elif phase == "Failed": + entry["pods_failed"] += 1 + elif phase == "Succeeded": + entry["pods_succeeded"] += 1 + output = list(namespaces.values()) + output.sort(key=lambda item: (-item.get("pods_total", 0), item.get("namespace") or "")) + return output + + def _vm_query(expr: str) -> list[dict[str, Any]] | None: base = settings.vm_url if not base: @@ -562,9 +596,11 @@ def collect_cluster_state() -> tuple[dict[str, Any], ClusterStateSummary]: errors.append(f"flux: {exc}") workloads: list[dict[str, Any]] = [] + namespace_pods: list[dict[str, Any]] = [] try: pods_payload = get_json("/api/v1/pods?limit=5000") workloads = _summarize_workloads(pods_payload) + namespace_pods = _summarize_namespace_pods(pods_payload) except Exception as exc: errors.append(f"pods: {exc}") @@ -577,6 +613,7 @@ def collect_cluster_state() -> tuple[dict[str, Any], ClusterStateSummary]: "nodes_detail": node_details, "flux": kustomizations or {}, "workloads": workloads, + "namespace_pods": namespace_pods, "metrics": metrics, "errors": errors, } diff --git a/tests/test_cluster_state.py b/tests/test_cluster_state.py index 534447a..86bcc18 100644 --- a/tests/test_cluster_state.py +++ b/tests/test_cluster_state.py @@ -80,6 +80,8 @@ def test_collect_cluster_state(monkeypatch) -> None: assert snapshot["nodes_summary"]["ready"] == 1 assert snapshot["nodes_detail"] assert snapshot["workloads"] + assert snapshot["namespace_pods"] + assert snapshot["namespace_pods"][0]["namespace"] == "media" assert summary.nodes_total == 2 assert summary.nodes_ready == 1 assert summary.pods_running == 5.0