feat(supervision): add choice watcher daemon, HTTPS spec docs, and test suites
- bin/muse_choice_watcher.py + systemd/muse-choices-reconcile.*: automatic choice answering and timer reconciliation - bin/digest.py: fleet log and health summarization - docs/BOX-*-HTTPS.md: comprehensive HTTPS execution contracts and API documentation - docs/MUSE-CHOICES-POLICY.md & docs/SUPERVISION-SPEC.md: autonomous execution specs - tests/test_*.py: unit test suites for HTTPS API, choice watcher, fleet heal, and swarm pruning
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
"""Watchdog coverage: every registry node is supervised.
|
||||
|
||||
Regression test for the dev/def outage (2026-10-06): both watchdogs
|
||||
hardcoded the original four nodes (muse/pip/646/opm), so dev/def had no
|
||||
browser supervision, no Warp-tunnel auto-recovery, and no relay
|
||||
supervision. A dead tunnel paged CRITICAL partition alerts forever with
|
||||
nothing acting on it.
|
||||
|
||||
Both scripts now resolve nodes/ports from the fleet registry
|
||||
(bin/netvm-registry.py). These tests drive the real shell code sourced
|
||||
with a LIB_ONLY guard (same pattern as test_agent_health.py).
|
||||
"""
|
||||
import importlib.util
|
||||
import subprocess
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
CHROMEBOX_WD = REPO_ROOT / "bin" / "chromebox-watchdog.sh"
|
||||
RELAY_WD = REPO_ROOT / "bin" / "cdp-relay-watchdog.sh"
|
||||
|
||||
# The fleet's pinned contract (also pinned in bin/netvm-names.sh).
|
||||
EXPECTED_PORTS = {
|
||||
"muse": 9410, "pip": 9420, "646": 9430,
|
||||
"opm": 9440, "def": 9450, "dev": 9455,
|
||||
}
|
||||
EXPECTED_PEERS = {
|
||||
"muse": "10.201.35.2", "pip": "10.201.87.2", "646": "10.201.202.2",
|
||||
"opm": "10.201.157.2", "def": "10.201.66.2", "dev": "10.201.36.2",
|
||||
}
|
||||
|
||||
|
||||
def _load(mod_name, rel_path):
|
||||
spec = importlib.util.spec_from_file_location(mod_name, REPO_ROOT / rel_path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
|
||||
|
||||
def _chromebox(profile, snippet):
|
||||
prog = "set -- '%s'\nsource '%s'\n%s\n" % (profile, CHROMEBOX_WD, snippet)
|
||||
env = {"PATH": "/usr/bin:/bin", "CHROMEBOX_WATCHDOG_LIB_ONLY": "1"}
|
||||
return subprocess.run(["bash", "-c", prog], capture_output=True,
|
||||
text=True, env=env, timeout=30)
|
||||
|
||||
|
||||
def _relay(snippet):
|
||||
prog = "source '%s'\n%s\n" % (RELAY_WD, snippet)
|
||||
env = {"PATH": "/usr/bin:/bin", "CDP_RELAY_WATCHDOG_LIB_ONLY": "1"}
|
||||
return subprocess.run(["bash", "-c", prog], capture_output=True,
|
||||
text=True, env=env, timeout=30)
|
||||
|
||||
|
||||
class RegistryContract(unittest.TestCase):
|
||||
def test_registry_matches_pinned_ports(self):
|
||||
reg = _load("netvm_registry_cov", "bin/netvm-registry.py")
|
||||
self.assertEqual(reg.active_nodes(), EXPECTED_PORTS)
|
||||
for node, peer in EXPECTED_PEERS.items():
|
||||
self.assertEqual(reg.peer_ip_for(node), peer)
|
||||
|
||||
|
||||
class ChromeboxWatchdogPorts(unittest.TestCase):
|
||||
def test_all_registry_nodes_resolve_to_pinned_ports(self):
|
||||
for node, port in sorted(EXPECTED_PORTS.items()):
|
||||
with self.subTest(node=node):
|
||||
r = _chromebox(node, "echo \"PORT=$CDP_PORT\"")
|
||||
self.assertEqual(r.returncode, 0, r.stderr)
|
||||
line = next((ln for ln in r.stdout.splitlines()
|
||||
if ln.startswith("PORT=")), None)
|
||||
self.assertIsNotNone(
|
||||
line, "no PORT line: stdout=%r stderr=%r"
|
||||
% (r.stdout, r.stderr))
|
||||
self.assertEqual(int(line.split("=", 1)[1]), port)
|
||||
|
||||
def test_unknown_profile_rejected(self):
|
||||
r = _chromebox("ghost", "echo UNREACHABLE")
|
||||
self.assertNotEqual(r.returncode, 0)
|
||||
self.assertIn("unknown profile", r.stderr)
|
||||
self.assertNotIn("UNREACHABLE", r.stdout)
|
||||
|
||||
|
||||
class RelayWatchdogCoverage(unittest.TestCase):
|
||||
def test_watched_nodes_covers_registry(self):
|
||||
r = _relay("watched_nodes")
|
||||
self.assertEqual(r.returncode, 0, r.stderr)
|
||||
nodes = set(r.stdout.split())
|
||||
for node in EXPECTED_PORTS:
|
||||
self.assertIn(node, nodes)
|
||||
|
||||
def test_relay_targets_use_pinned_ports(self):
|
||||
for node, port in sorted(EXPECTED_PORTS.items()):
|
||||
with self.subTest(node=node):
|
||||
r = _relay("relay_target %s" % node)
|
||||
self.assertEqual(r.returncode, 0, r.stderr)
|
||||
self.assertEqual(r.stdout.strip(),
|
||||
"%s:%d" % (EXPECTED_PEERS[node], port))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user