frank-large has no /metrics to scrape any more, and scraping it would be red by construction: the halogen engine publishes /health, /cache and /v1/* only. That job is removed with the reason written down (the remedy is the blackbox exporter this fleet already wants for the comfyui/services probes), and frank-small's llama.cpp endpoint takes the slot at 192.168.0.143:7771. Homepage: frank-large's card still described the ik runtime, and the new endpoint had no card at all. Both fixed, with a loopback probe -- the checker runs on Dankclaw, which is also what makes the card green-able. Dashboard: the endpoint picker was sourced from a vLLM-only metric, so a llama.cpp job could never be selected, and the prose named frank-large as the llama.cpp endpoint. The picker now matches both runtimes' series and the llama.cpp targets carry the job filter every other panel already used. The seams assert the new contract including the ABSENCE of a 7770 target, so a well-meaning edit cannot re-add a permanently-red probe.
60 lines
2.4 KiB
Bash
60 lines
2.4 KiB
Bash
#!/usr/bin/env bats
|
|
# Seam: shared/homepage/services.json — the service config the checker reads
|
|
# and the pages render. It must be a JSON list; every entry carries name,
|
|
# category, and a host:port probe. category is user or admin. method, when
|
|
# present, is tcp or http.
|
|
|
|
load helpers
|
|
|
|
@test "services.json: valid schema" {
|
|
python3 - "$REPO_ROOT/shared/homepage/services.json" <<'PY'
|
|
import json, sys
|
|
path = sys.argv[1]
|
|
try:
|
|
data = json.load(open(path))
|
|
except FileNotFoundError:
|
|
sys.exit("services.json not found")
|
|
assert isinstance(data, list), "top level must be a list"
|
|
assert data, "the list must not be empty"
|
|
for svc in data:
|
|
for field in ("name", "category", "probe"):
|
|
assert field in svc, f"{svc.get('name', '?')} is missing {field}"
|
|
assert svc["category"] in ("user", "admin"), svc["category"]
|
|
if "method" in svc:
|
|
assert svc["method"] in ("tcp", "http"), svc["method"]
|
|
host, _, port = svc["probe"].rpartition(":")
|
|
assert host and port.isdigit(), f"bad probe {svc['probe']}"
|
|
PY
|
|
}
|
|
|
|
@test "services.json: every user service has an Open link, status-only backends do not" {
|
|
python3 - "$REPO_ROOT/shared/homepage/services.json" <<'PY'
|
|
import json, sys
|
|
data = json.load(open(sys.argv[1]))
|
|
for svc in data:
|
|
show = svc.get("showLink", True)
|
|
if show:
|
|
assert svc.get("url"), f"{svc['name']} shows a link but has no url"
|
|
else:
|
|
assert not svc.get("url"), f"{svc['name']} is status-only but has a url"
|
|
PY
|
|
}
|
|
|
|
@test "the LLM status cards track the fleet, not retired service names" {
|
|
# This drifted twice: the card probing Dankserver's :7770 was still named
|
|
# frank-small long after frank-medium took that endpoint, and Dankclaw's
|
|
# second endpoint now exists again as a real service on its own port. The
|
|
# probe target is what matters; the name has to follow it.
|
|
python3 - "$REPO_ROOT/shared/homepage/services.json" <<'PY'
|
|
import json, sys
|
|
services = json.load(open(sys.argv[1]))
|
|
by_probe = {s["probe"]: s["name"] for s in services}
|
|
medium = by_probe.get("192.168.0.235:7770")
|
|
assert medium == "frank-medium", f"Dankserver :7770 card is named {medium!r}"
|
|
# Dankclaw serves two endpoints on two ports: frank-large (halogen, 7770) and
|
|
# frank-small (llama.cpp on the RTX 3090, 7771).
|
|
assert by_probe.get("127.0.0.1:7770") == "frank-large", by_probe.get("127.0.0.1:7770")
|
|
assert by_probe.get("127.0.0.1:7771") == "frank-small", by_probe.get("127.0.0.1:7771")
|
|
PY
|
|
}
|