109 lines
3.4 KiB
Python

from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
import httpx
from ..settings import settings
from ..utils.logging import get_logger
logger = get_logger(__name__)
_WATCH_PATH = "/internal/sentinel/watch"
@dataclass(frozen=True)
class MetisSentinelWatchSummary:
status: str
watch_url: str
detail: str = ""
result: dict[str, Any] = field(default_factory=dict)
def _watch_url() -> str:
if settings.metis_watch_url:
return settings.metis_watch_url
if settings.metis_base_url:
return f"{settings.metis_base_url}{_WATCH_PATH}"
return ""
def _normalize_payload(payload: Any) -> dict[str, Any]:
if isinstance(payload, dict):
return payload
if payload is None:
return {}
return {"result": payload}
class MetisService:
"""Trigger Metis sentinel watch runs and normalize their response."""
def ready(self) -> bool:
return bool(_watch_url())
def _finish(self, status: str, watch_url: str, detail: str = "", result: dict[str, Any] | None = None) -> MetisSentinelWatchSummary:
summary = MetisSentinelWatchSummary(
status=status,
watch_url=watch_url,
detail=detail,
result=result or {},
)
logger.info(
"metis sentinel watch finished",
extra={
"event": "metis_sentinel_watch",
"status": summary.status,
"watch_url": summary.watch_url,
"detail": summary.detail,
},
)
return summary
def watch_sentinel(self) -> MetisSentinelWatchSummary:
watch_url = _watch_url()
if not watch_url:
return self._finish("skipped", "", "metis watch url not configured")
try:
with httpx.Client(timeout=settings.metis_timeout_sec, follow_redirects=True) as client:
response = client.post(watch_url)
response.raise_for_status()
try:
payload = response.json()
except Exception:
payload = {}
except httpx.HTTPStatusError as exc:
response = exc.response
detail = f"metis watch failed with HTTP {response.status_code}"
try:
payload = response.json()
except Exception:
payload = {}
payload = _normalize_payload(payload)
if isinstance(payload.get("detail"), str) and payload["detail"].strip():
detail = payload["detail"].strip()
return self._finish("error", watch_url, detail, payload)
except Exception as exc: # noqa: BLE001
return self._finish("error", watch_url, str(exc).strip() or "metis watch failed")
payload = _normalize_payload(payload)
status = payload.get("status") if isinstance(payload.get("status"), str) else "ok"
detail = ""
if isinstance(payload.get("detail"), str):
detail = payload["detail"].strip()
elif isinstance(payload.get("message"), str):
detail = payload["message"].strip()
elif status != "ok":
detail = f"metis watch returned {status}"
if status not in {"ok", "skipped", "error"}:
status = "ok"
return self._finish(status, watch_url, detail, payload)
metis = MetisService()