61 lines
1.6 KiB
Python
61 lines
1.6 KiB
Python
"""Uptime probe — the homelab healthcheck runner.
|
|
|
|
Polls every service listed in ``CHECKS`` every 5 minutes and posts a
|
|
failure to the ntfy topic ``homelab-alerts``.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
|
|
#: (name, health URL) for every long-running service.
|
|
CHECKS: list[tuple[str, str]] = [
|
|
("k3s", "https://10.0.1.10:6443/healthz"),
|
|
("gitea", "https://gitea.reeseapps.com/api/healthz"),
|
|
("ntfy", "https://ntfy.reeseapps.com/health"),
|
|
("gitlab", "https://gitlab.reeseapps.com/-/health_check"),
|
|
]
|
|
|
|
|
|
def probe(name: str, url: str) -> bool:
|
|
"""One HTTP check; returns True when the service answered 200."""
|
|
result = subprocess.run(
|
|
["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "10", url],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
return result.stdout.strip() == "200"
|
|
|
|
|
|
def notify_failure(name: str) -> None:
|
|
"""Push an alert to ntfy (best effort — alerting must not crash the probe)."""
|
|
subprocess.run(
|
|
[
|
|
"curl",
|
|
"-s",
|
|
"-X",
|
|
"POST",
|
|
"https://ntfy.reeseapps.com/homelab-alerts",
|
|
"-H",
|
|
"Title: homelab check failed",
|
|
"-d",
|
|
f"{name} is down",
|
|
],
|
|
capture_output=True,
|
|
)
|
|
|
|
|
|
def run_round() -> int:
|
|
"""Probe everything once; returns the number of failing services."""
|
|
failed = 0
|
|
for name, url in CHECKS:
|
|
if not probe(name, url):
|
|
failed += 1
|
|
notify_failure(name)
|
|
return failed
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
sys.exit(run_round())
|