#!/usr/bin/env python3 """homepage-status.py — the homepage status checker. Reads a service config (a JSON array of service entries), probes each backend, and writes a status.json atomically. A wip service is not probed; its state is "wip-open" or "wip-closed" following the wip value in the config. Usage: homepage-status.py """ import json import os import socket import sys import tempfile import urllib.error import urllib.request from datetime import datetime, timezone TIMEOUT = 5 # seconds, per probe (Q11 / Q40) def probe(host, port, method="tcp"): """Return 'up' if the backend answers, else 'down'.""" if method == "http": try: urllib.request.urlopen( urllib.request.Request("http://%s:%d/" % (host, port)), timeout=TIMEOUT, ) return "up" except urllib.error.HTTPError: return "up" # an HTTP response of any status means the backend is up except Exception: return "down" try: with socket.create_connection((host, port), timeout=TIMEOUT): return "up" except Exception: return "down" def main(argv): if len(argv) != 3: sys.stderr.write("usage: homepage-status.py \n") return 2 services_path, out_path = argv[1], argv[2] with open(services_path) as f: services = json.load(f) results = [] for svc in services: wip = svc.get("wip") if wip: state = "wip-%s" % wip else: host, _, port = svc["probe"].rpartition(":") state = probe(host, int(port), svc.get("method", "tcp")) results.append({ "name": svc.get("name"), "category": svc.get("category", "user"), "state": state, }) doc = { "generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), "services": results, } # Atomic write: a temp file in the same directory, then rename over the target. out_dir = os.path.dirname(os.path.abspath(out_path)) fd, tmp = tempfile.mkstemp(dir=out_dir, prefix=".status.", suffix=".tmp") try: with os.fdopen(fd, "w") as f: json.dump(doc, f) os.replace(tmp, out_path) os.chmod(out_path, 0o644) # world-readable so Caddy can serve it except Exception: if os.path.exists(tmp): os.unlink(tmp) raise return 0 if __name__ == "__main__": sys.exit(main(sys.argv))