83 lines
3.1 KiB
Python
83 lines
3.1 KiB
Python
"""Compose bounded Gitea and controller runner-fleet telemetry.
|
|
|
|
AppOps is intentionally a consumer of this projection. The root-owned
|
|
controller remains the only component that may perform an allowlisted recovery
|
|
or cleanup inside the isolated CI Docker daemon.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from ..connectors.gitea_actions import GiteaActionsConnector
|
|
from .deployment_broker import DeploymentBrokerClient
|
|
|
|
|
|
_CAPABILITIES = (
|
|
"linux-validation",
|
|
"docker-validation",
|
|
"browser-validation",
|
|
"security-validation",
|
|
"windows-native",
|
|
)
|
|
_SEVERITY_RANK = {"healthy": 0, "warning": 1, "critical": 2, "unknown": 3}
|
|
|
|
|
|
def _severity(*states: object) -> str:
|
|
normalized = [str(state) for state in states]
|
|
return max(normalized, key=lambda value: _SEVERITY_RANK.get(value, 3), default="unknown")
|
|
|
|
|
|
def _capabilities(runners: list[dict[str, Any]]) -> list[dict[str, object]]:
|
|
rows = []
|
|
for capability in _CAPABILITIES:
|
|
compatible = [runner for runner in runners if capability in runner.get("labels", [])]
|
|
online = [
|
|
runner
|
|
for runner in compatible
|
|
if runner.get("status") == "online" and not runner.get("disabled")
|
|
]
|
|
rows.append(
|
|
{
|
|
"name": capability,
|
|
"total": len(compatible),
|
|
"online": len(online),
|
|
"busy": sum(bool(runner.get("busy")) for runner in online),
|
|
"status": "healthy" if online else "critical",
|
|
}
|
|
)
|
|
return rows
|
|
|
|
|
|
class RunnerFleetService:
|
|
"""Provide a read-only, explainable CI fleet projection."""
|
|
|
|
def snapshot(self) -> dict[str, object]:
|
|
actions = GiteaActionsConnector().snapshot()
|
|
controller = DeploymentBrokerClient().runner_fleet_status()
|
|
runners = actions.get("runners") if isinstance(actions.get("runners"), list) else []
|
|
typed_runners = [runner for runner in runners if isinstance(runner, dict)]
|
|
capabilities = _capabilities(typed_runners)
|
|
controller_status = str(controller.get("status") or "unavailable")
|
|
controller_severity = (
|
|
controller_status
|
|
if controller_status in _SEVERITY_RANK
|
|
else "warning" if controller_status != "unavailable" else "unknown"
|
|
)
|
|
action_severity = str(actions.get("severity") or "unknown")
|
|
capability_severity = _severity(*(row["status"] for row in capabilities))
|
|
return {
|
|
"status": _severity(action_severity, controller_severity, capability_severity),
|
|
"generatedAt": actions.get("generatedAt"),
|
|
"runners": typed_runners,
|
|
"capabilities": capabilities,
|
|
"queue": actions.get("queue") if isinstance(actions.get("queue"), dict) else {},
|
|
"controller": controller,
|
|
"actions": [
|
|
{"intent": "runner.inspect", "mode": "read_only"},
|
|
{"intent": "runner.cleanup.preview", "mode": "read_only"},
|
|
{"intent": "runner.restart", "mode": "root_allowlisted_and_bounded"},
|
|
{"intent": "runner.drain", "mode": "operator_controlled"},
|
|
],
|
|
}
|