Skip to content

Commit 2c4b8b9

Browse files
refactor: harden monitoring and telemetry handling
- consolidate Docker workload fallback and status handling - improve UTC timestamp and anomaly-zone processing - correct telemetry metrics and health-status precedence - remove dead frontend helpers and expand regression coverage
1 parent 3320e26 commit 2c4b8b9

15 files changed

Lines changed: 1893 additions & 360 deletions

File tree

backend/services/middleware/tactic/src/off_key_tactic_middleware/api/v1/radar.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,18 +232,19 @@ async def stop_radar_service(
232232
)
233233

234234
try:
235+
lookup_target = container_name if container_name else container_id
235236
success = await service.stop_radar_service(container_name, container_id)
236237

237238
if not success:
238239
raise HTTPException(
239240
status_code=404,
240-
detail=f"RADAR service '{container_name}'"
241+
detail=f"RADAR service '{lookup_target}'"
241242
f" not found or could not be stopped",
242243
)
243244

244245
return {
245246
"status": "stopped",
246-
"message": f"RADAR service '{container_name}'"
247+
"message": f"RADAR service '{lookup_target}'"
247248
f" stopped and deleted successfully",
248249
}
249250
except HTTPException:

backend/services/middleware/tactic/src/off_key_tactic_middleware/facades/docker.py

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
11
import asyncio
22
import time
33

4-
import docker.errors
4+
import docker
55
from docker import DockerClient
66
from typing import Callable, Any
77
from off_key_core.config.logs import logger, log_performance
88
from ..config.config import get_tactic_settings
99

1010

11+
_SWARM_FALLBACK_INDICATORS = (
12+
"cannot be used with services",
13+
"only networks scoped to the swarm can be used",
14+
"this node is not a swarm manager",
15+
"swarm mode",
16+
)
17+
18+
19+
def _extract_latest_workload_state(
20+
tasks: list[object], no_tasks_state: str = "no_tasks"
21+
) -> str:
22+
task_items = [task for task in tasks if isinstance(task, dict)]
23+
if not task_items:
24+
return no_tasks_state
25+
latest = max(task_items, key=lambda task: str(task.get("CreatedAt", "")))
26+
status = latest.get("Status")
27+
if not isinstance(status, dict):
28+
status = {}
29+
return str(status.get("State", "unknown")).strip().lower()
30+
31+
1132
class AsyncDocker:
1233
def __init__(self, docker_config=None):
1334
config = docker_config or get_tactic_settings().config.docker
@@ -52,19 +73,11 @@ def close(self) -> None:
5273
logger.warning(f"Error closing Docker client: {e}")
5374

5475

55-
def _should_fallback_to_container(exc: Exception) -> bool:
76+
def should_fallback_to_container(exc: Exception) -> bool:
5677
if isinstance(exc, docker.errors.APIError) and exc.status_code in (400, 406, 503):
5778
return True
5879
text = str(exc).lower()
59-
return any(
60-
indicator in text
61-
for indicator in (
62-
"cannot be used with services",
63-
"only networks scoped to the swarm can be used",
64-
"this node is not a swarm manager",
65-
"swarm mode is not active",
66-
)
67-
)
80+
return any(indicator in text for indicator in _SWARM_FALLBACK_INDICATORS)
6881

6982

7083
async def get_workload_docker_status(
@@ -73,9 +86,9 @@ async def get_workload_docker_status(
7386
"""Return the running status of a Docker workload by ID.
7487
7588
Tries Swarm services first (reads task state), then falls back to plain
76-
containers (reads container status). Both callers RadarOrchestrationService
77-
and RadarStatusReconciliationService delegate here to avoid duplicating the
78-
servicecontainer probe-and-fallback logic.
89+
containers (reads container status). Both callers -- RadarOrchestrationService
90+
and RadarStatusReconciliationService -- delegate here to avoid duplicating the
91+
service->container probe-and-fallback logic.
7992
8093
Returns:
8194
"running" | task state | "no_tasks" | "not_found" | "no_container_id"
@@ -89,14 +102,11 @@ async def get_workload_docker_status(
89102
async_docker.client.services.get, container_id
90103
)
91104
tasks = await async_docker.run(docker_service.tasks)
92-
if tasks:
93-
latest = max(tasks, key=lambda t: t.get("CreatedAt", ""))
94-
return latest.get("Status", {}).get("State", "unknown")
95-
return "no_tasks"
105+
return _extract_latest_workload_state(tasks)
96106
except docker.errors.NotFound:
97107
pass
98108
except Exception as exc:
99-
if not _should_fallback_to_container(exc):
109+
if not should_fallback_to_container(exc):
100110
raise
101111
logger.debug(
102112
"Skipping Swarm service status lookup for workload %s: %s",
@@ -114,3 +124,32 @@ async def get_workload_docker_status(
114124
except Exception as exc:
115125
logger.debug("Error checking Docker status for %s: %s", container_id, exc)
116126
return "error"
127+
128+
129+
async def with_workload_fallback(
130+
async_docker: AsyncDocker,
131+
container_id: str,
132+
on_service: Callable[[Any], Any],
133+
on_container: Callable[[Any], Any],
134+
) -> Any:
135+
"""Resolve a Docker workload as swarm service first, then fallback to container."""
136+
try:
137+
docker_service = await async_docker.run(
138+
async_docker.client.services.get, container_id
139+
)
140+
return await async_docker.run(on_service, docker_service)
141+
except docker.errors.NotFound:
142+
pass
143+
except Exception as exc:
144+
if not should_fallback_to_container(exc):
145+
raise
146+
logger.debug(
147+
"Skipping Swarm service operation for workload %s: %s",
148+
container_id,
149+
exc,
150+
)
151+
152+
docker_container = await async_docker.run(
153+
async_docker.client.containers.get, container_id
154+
)
155+
return await async_docker.run(on_container, docker_container)

0 commit comments

Comments
 (0)