Three dashboards served by one FastAPI app with a SQLite history store
(raw / 1m / 1h) and hand-written SVG charts: no Prometheus, no Grafana,
no CDN, no build step. Nothing is fetched from the internet at runtime.
* system: a stdlib-only agent (agent/host_agent.py) reports /proc and
/sys counters; the dashboard turns them into rates over a labelled
window, so every number says how it was measured.
* llm: gufo/llama.cpp and vLLM /metrics are parsed by an in-house
exposition parser. Measured delta/elapsed throughput beats a native
per-request gauge, and each value carries its provenance.
* ups: a NUT wire-protocol client plus health checks that name what
they looked at. Commands are gated by NUT_ALLOW_ACTIONS,
DASH_READ_ONLY and a DANGER confirmation, re-checked server-side.
Missing values render as n/a rather than 0, and an unconfigured source
answers 404 {"configured": false} without dialling anything, so "not
deployed here" never looks like an outage.
253 tests at 94% coverage against captured real fixtures; ruff and
pyright clean.
LLM-Generated: true
101 lines
3.8 KiB
Bash
Executable File
101 lines
3.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Re-record ground-truth captures into tests/fixtures/.
|
|
#
|
|
# ./scripts/capture-fixtures.sh # needs the tunnels up: scripts/dev-tunnels.sh start
|
|
# ./scripts/capture-fixtures.sh --with-llm # also scrape the live engines' /metrics
|
|
#
|
|
# Fixtures are verbatim, not hand-written: a fixture you typed is a test of your memory.
|
|
# When a host's kernel or an engine's metric names change, re-run this and read the diff.
|
|
set -uo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
FIX="$ROOT/tests/fixtures"
|
|
mkdir -p "$FIX"
|
|
|
|
HOSTS=(driveripper:9121 deskwork:9122 gateway:9123)
|
|
# Engine URLs come from the same env the app uses, so this script carries no deployment
|
|
# detail of its own — and never records a tokenised URL into a fixture by accident.
|
|
if [ -f "$ROOT/.env.dev" ]; then
|
|
set -a; . "$ROOT/.env.dev"; set +a
|
|
fi
|
|
ENGINES=()
|
|
[ -n "${GUFO_BASE_URL:-}" ] && ENGINES+=("gufo:${GUFO_BASE_URL%/}/metrics")
|
|
[ -n "${VLLM_BASE_URL:-}" ] && ENGINES+=("vllm:${VLLM_BASE_URL%/}/metrics")
|
|
|
|
failed=0
|
|
|
|
for entry in "${HOSTS[@]}"; do
|
|
name=${entry%%:*}; port=${entry##*:}
|
|
url="http://127.0.0.1:${port}/snapshot"
|
|
if curl -fsS --max-time 8 "$url" -o "$FIX/agent_${name}.json"; then
|
|
echo " ✓ agent_${name}.json ($(wc -c <"$FIX/agent_${name}.json") bytes)"
|
|
else
|
|
echo " ✗ agent_${name}.json — no agent on $url (scripts/dev-tunnels.sh start?)"
|
|
failed=1
|
|
fi
|
|
done
|
|
|
|
if [ "${1:-}" = "--with-llm" ]; then
|
|
if [ ${#ENGINES[@]} -eq 0 ]; then
|
|
echo " ! no GUFO_BASE_URL / VLLM_BASE_URL set (see .env.dev) — nothing to capture"
|
|
failed=1
|
|
fi
|
|
for entry in "${ENGINES[@]}"; do
|
|
name=${entry%%:*}; url=${entry#*:}
|
|
if curl -fsS --max-time 20 "$url" -o "$FIX/metrics_${name}.txt"; then
|
|
echo " ✓ metrics_${name}.txt ($(wc -l <"$FIX/metrics_${name}.txt") lines)"
|
|
else
|
|
echo " ✗ metrics_${name}.txt — $url unreachable (VPN? engine down?)"
|
|
failed=1
|
|
fi
|
|
done
|
|
else
|
|
echo " (skipped engine /metrics captures — pass --with-llm when the engines are reachable)"
|
|
fi
|
|
|
|
# Secrets never belong in a fixture: engines can expose /metrics behind a token in the URL,
|
|
# and a hostname you use at home is a hostname in a public repo.
|
|
if grep -rlE "Authorization|Bearer |://[^/]*:[^/@]*@" "$FIX" >/dev/null 2>&1; then
|
|
echo " ! fixture contains what looks like a credential — scrub it before committing"
|
|
failed=1
|
|
fi
|
|
|
|
# Two things leak out of a real capture that nobody notices: LUKS/partition UUIDs (which
|
|
# identify *your* disks, and appear in `device` strings as /dev/mapper/luks-<uuid>) and the
|
|
# FQDN a host thinks it has. They are replaced deterministically so re-capturing does not
|
|
# churn the fixture, and the tests keep the same shapes to assert against.
|
|
python3 - "$FIX" <<'PY'
|
|
import pathlib
|
|
import re
|
|
import sys
|
|
|
|
uuid_re = re.compile(r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
|
|
SYNTHETIC = "00000000-0000-4000-8000-" # already-scrubbed markers: re-running must not churn
|
|
fix = pathlib.Path(sys.argv[1])
|
|
changed = []
|
|
for path in sorted(fix.iterdir()):
|
|
if path.suffix not in (".json", ".txt"):
|
|
continue
|
|
text = path.read_text()
|
|
seen: dict[str, str] = {}
|
|
|
|
def swap(match: re.Match[str]) -> str:
|
|
value = match.group(0)
|
|
if value.startswith(SYNTHETIC):
|
|
return value
|
|
if value not in seen:
|
|
seen[value] = f"00000000-0000-4000-8000-{len(seen) + 1:012d}"
|
|
return seen[value]
|
|
|
|
text = uuid_re.sub(swap, text)
|
|
# "hostname": "gw.lab.example.com" -> "gw": the dashboard's host key is the target name
|
|
# from HOST_TARGETS, never whatever the box calls itself
|
|
text = re.sub(r'("hostname": "[^"]*?)\.[^"]*(")', r"\1\2", text)
|
|
if text != path.read_text():
|
|
path.write_text(text)
|
|
changed.append(path.name)
|
|
print(f" scrubbed identifiers in: {', '.join(changed)}" if changed else " fixtures carry no identifiers")
|
|
PY
|
|
|
|
exit "$failed"
|