generated from coulomb/repo-seed
Implement SAND-WP-0008: host telemetry and self-canary
Add profile.sandbox-canary, HostSnapshot/inventory/stale schemas, SSH collectors, before/after provision deltas, telemetry export to State Hub and local JSON, default `sandboxer create` self-deploy, inspect/reap-stale CLI, runbook, and CoulombCore verification (26 tests pass).
This commit is contained in:
64
src/sandboxer/telemetry/export.py
Normal file
64
src/sandboxer/telemetry/export.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""Telemetry export sinks."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Protocol
|
||||
|
||||
import httpx
|
||||
|
||||
from sandboxer.lifecycle.state_hub import hub_url
|
||||
from sandboxer.telemetry.models import IntrospectionReport
|
||||
|
||||
|
||||
class TelemetrySink(Protocol):
|
||||
"""Future export target (artifact-store, Prometheus, ClickHouse)."""
|
||||
|
||||
def publish(self, report: IntrospectionReport) -> None: ...
|
||||
|
||||
|
||||
def telemetry_dir() -> Path:
|
||||
base = Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local" / "share"))
|
||||
path = base / "sandboxer" / "telemetry"
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
|
||||
def export_local_artifact(report: IntrospectionReport) -> Path:
|
||||
path = telemetry_dir() / f"{report.sandbox_id}.json"
|
||||
path.write_text(json.dumps(report.model_dump(mode="json"), indent=2, default=str))
|
||||
return path
|
||||
|
||||
|
||||
def export_state_hub(report: IntrospectionReport) -> dict | None:
|
||||
if os.environ.get("SANDBOXER_NO_STATE_HUB", "").lower() in ("1", "true", "yes"):
|
||||
return None
|
||||
payload = {
|
||||
"event_type": "note",
|
||||
"summary": (
|
||||
f"Telemetry {report.sandbox_id}: load Δ "
|
||||
f"{report.provision_delta.load_1m_delta if report.provision_delta else 0}, "
|
||||
f"stale={len(report.stale_candidates)}"
|
||||
),
|
||||
"author": "sandboxer",
|
||||
"detail": report.model_dump(mode="json"),
|
||||
}
|
||||
try:
|
||||
response = httpx.post(f"{hub_url()}/progress/", json=payload, timeout=10.0)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except httpx.HTTPError:
|
||||
return None
|
||||
|
||||
|
||||
def export_telemetry(report: IntrospectionReport) -> Path:
|
||||
path = export_local_artifact(report)
|
||||
export_state_hub(report)
|
||||
return path
|
||||
|
||||
|
||||
class NoopTelemetrySink:
|
||||
def publish(self, report: IntrospectionReport) -> None:
|
||||
export_telemetry(report)
|
||||
Reference in New Issue
Block a user