feat(system): add compute contour telemetry APIs

This commit is contained in:
DCCONSTRUCTIONS
2026-07-28 00:54:18 +03:00
parent b8a008b50e
commit 1f4c960cc4
5 changed files with 909 additions and 23 deletions
+127
View File
@@ -0,0 +1,127 @@
from __future__ import annotations
import stat
from collections.abc import Callable
from pathlib import Path
from typing import Any
import pytest
from fastapi import APIRouter, HTTPException
from fastapi.routing import APIRoute
from k1link.web.compute_contour_api import (
CATALOG_SCHEMA,
ComputeContourCreate,
ComputeContourPut,
ComputeContourStore,
build_compute_contour_router,
)
def _endpoint(router: APIRouter, path: str, method: str) -> Callable[..., Any]:
for route in router.routes:
if (
isinstance(route, APIRoute)
and route.path == path
and route.methods is not None
and method in route.methods
):
return route.endpoint
raise AssertionError(f"{method} {path} route is missing")
def test_contour_store_migrates_worker_006_as_first_configuration(
tmp_path: Path,
) -> None:
store = ComputeContourStore(tmp_path / "system")
contours = store.list_contours()
assert len(contours) == 1
assert contours[0].contour_id == "worker-006"
assert contours[0].telemetry_mode == "agent-mqtt"
assert not store.path.exists()
def test_contour_store_creates_and_updates_private_catalog(tmp_path: Path) -> None:
store = ComputeContourStore(tmp_path / "system")
created = store.create(
ComputeContourCreate(
display_name="Field Worker",
expected_node_id="FIELD-01",
platform="linux",
address="192.0.2.25",
mqtt_host="192.0.2.5",
)
)
updated = store.update(
created.contour_id,
ComputeContourPut(
revision=created.revision,
display_name="Field Worker 01",
expected_node_id="FIELD-01",
platform="linux",
telemetry_mode="agent-mqtt",
address="192.0.2.25",
ssh_port=22,
mqtt_host="192.0.2.5",
mqtt_port=1883,
),
)
assert updated.display_name == "Field Worker 01"
assert updated.revision == 1
assert stat.S_IMODE(store.path.stat().st_mode) == 0o600
assert len(store.list_contours()) == 2
with pytest.raises(RuntimeError, match="revision changed"):
store.update(
created.contour_id,
ComputeContourPut(
revision=0,
display_name="stale",
expected_node_id="FIELD-01",
platform="linux",
telemetry_mode="agent-mqtt",
address="",
ssh_port=22,
mqtt_host="127.0.0.1",
mqtt_port=1883,
),
)
def test_contour_router_exposes_catalog_and_safe_install_contract(
tmp_path: Path,
) -> None:
router = build_compute_contour_router(
root_provider=lambda: tmp_path / "system"
)
list_contours = _endpoint(router, "/api/v1/system/contours", "GET")
install = _endpoint(
router,
"/api/v1/system/contours/{contour_id}/agent-install",
"GET",
)
catalog = list_contours()
assert catalog["schema_version"] == CATALOG_SCHEMA
assert catalog["contours"][0]["contour_id"] == "worker-006"
document = install("worker-006")
assert document["agent"]["distribution"] == "Telegraf"
assert "MQTT password" in document["command"]
assert "password" not in document["agent"]["environment"]
assert document["ready"] is False
def test_contour_router_returns_404_for_unknown_contour(tmp_path: Path) -> None:
router = build_compute_contour_router(
root_provider=lambda: tmp_path / "system"
)
install = _endpoint(
router,
"/api/v1/system/contours/{contour_id}/agent-install",
"GET",
)
with pytest.raises(HTTPException) as error:
install("missing")
assert error.value.status_code == 404
+103
View File
@@ -16,6 +16,7 @@ from k1link.web.system_telemetry_api import (
WorkerConnectionProfilePut,
WorkerProfileStore,
WorkerTelemetryService,
_agent_raw_document,
_ssh_arguments,
build_system_telemetry_router,
)
@@ -42,6 +43,7 @@ def _probe(
return {
"reachable": True,
"identity_matches": node_id == EXPECTED_NODE_ID,
"source": "agent-mqtt",
"node_id": node_id,
"latency_ms": 12.5,
"observed_at_utc": "2026-07-27T12:00:00Z",
@@ -208,6 +210,7 @@ def test_worker_telemetry_separates_mission_core_and_external_load(
runtimes = {runtime["name"]: runtime for runtime in document["runtimes"]}
assert document["connection"]["identity_matches"] is True
assert document["connection"]["source"] == "agent-mqtt"
assert document["node"]["memory"]["used_percent"] == 60
assert document["node"]["gpu"]["memory_used_percent"] == 50
assert document["node"]["triton"]["requests_succeeded"] == 12
@@ -237,6 +240,106 @@ def test_worker_telemetry_separates_mission_core_and_external_load(
)["share_percent"] is None
def test_worker_telemetry_prefers_ndc_container_names_during_migration(
tmp_path: Path,
) -> None:
probe = _probe()
raw = probe["raw"]
triton_stats = raw["docker_stats"].pop("mission-core-triton")
triton_stats["Name"] = "ndc-mission-core-triton"
raw["docker_stats"]["ndc-mission-core-triton"] = triton_stats
raw["container_states"]["ndc-mission-core-triton"] = raw[
"container_states"
].pop("mission-core-triton")
service = WorkerTelemetryService(
WorkerProfileStore(tmp_path / "system"),
lambda _: probe,
cache_seconds=0,
)
document = service.snapshot(10)
mission_core_runtimes = [
runtime for runtime in document["runtimes"] if not runtime["external"]
]
assert len(mission_core_runtimes) == 2
triton = next(
runtime
for runtime in mission_core_runtimes
if runtime["role"] == "Inference Runtime"
)
assert triton["name"] == "ndc-mission-core-triton"
assert triton["canonical_name"] == "ndc-mission-core-triton"
def test_agent_metrics_are_mapped_to_the_existing_product_contract() -> None:
document = _agent_raw_document(
{
"samples": [
{
"node_id": EXPECTED_NODE_ID,
"observed_at_utc": "2026-07-27T12:00:00Z",
"kind": "host",
"measurement": "cpu",
"payload": {
"fields": {"usage_active": 12.5},
"tags": {"cpu": "cpu-total"},
},
},
{
"node_id": EXPECTED_NODE_ID,
"observed_at_utc": "2026-07-27T12:00:00Z",
"kind": "host",
"measurement": "mem",
"payload": {
"fields": {
"total": 1_000,
"available": 400,
},
"tags": {},
},
},
{
"node_id": EXPECTED_NODE_ID,
"observed_at_utc": "2026-07-27T12:00:00Z",
"kind": "host",
"measurement": "net",
"payload": {
"fields": {
"bytes_recv": 2_000,
"bytes_sent": 1_000,
},
"tags": {"interface": "Ethernet"},
},
},
{
"node_id": EXPECTED_NODE_ID,
"observed_at_utc": "2026-07-27T12:00:00Z",
"kind": "host",
"measurement": "docker_container_cpu",
"payload": {
"fields": {"usage_percent": 2.5},
"tags": {
"container_name": "ndc-mission-core-triton",
"container_image": "triton@sha256:accepted",
},
},
},
]
}
)
assert document["node_id"] == EXPECTED_NODE_ID
assert document["cpu"]["load_percent"] == 12.5
assert document["memory"] == {"total_bytes": 1_000.0, "free_bytes": 400.0}
assert document["network"][0]["name"] == "Ethernet"
assert document["docker_stats"]["ndc-mission-core-triton"]["CPUPerc"] == "2.500%"
assert (
document["container_states"]["ndc-mission-core-triton"]["image"]
== "triton@sha256:accepted"
)
def test_profile_apply_fails_closed_on_wrong_node_and_keeps_old_profile(
tmp_path: Path,
) -> None: