Files
windy-git/api/app/routes/health.py
Grant Whitmer ce54d488f2 G1: stop probing the tunnel from inside a container
cloudflared binds 127.0.0.1:2000 on the HOST. This process runs in a container
whose only route to the host is the bridge gateway (172.17.0.1), where nothing
is listening — so the check was permanently red regardless of what the tunnel
was actually doing.

Binding the metrics endpoint wider would have fixed the probe and made a
metrics bind failure capable of taking down ingress. That is a worse trade than
losing one row on a dashboard.

The check is not silently dropped: /health/full now carries a 'not_checked_here'
map naming the tunnel and where its health actually lives (systemd
windygit-tunnel). An observer should never have to wonder whether a missing
check means healthy or means forgotten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 14:37:53 -04:00

84 lines
2.7 KiB
Python

"""G0.2 / G0.3 — /version and /health/full.
The two endpoints this ecosystem most needs to be honest, because both audits
found them lying elsewhere: nine of twelve services cannot name their commit, and
a sibling cell reported healthy while serving from a mock.
`/health/full` returns `ok` ONLY when every provider it depends on proved itself
against the real dependency. Anything else is `degraded`. There is no code path
that returns `ok` from an unconfigured provider (I-8).
"""
from __future__ import annotations
from fastapi import APIRouter, Request, Response
from api.app.buildinfo import get_build_info
from api.app.config import get_settings
router = APIRouter(tags=["system"])
@router.get("/version")
async def version() -> dict:
"""I-12. A runtime COMMIT_SHA override is ignored; see buildinfo.py."""
info = get_build_info()
s = get_settings()
return {
"service": s.service_name,
"version": info.version,
"commit_sha": info.commit_sha,
"built_at": info.built_at,
"source": info.source,
"environment": s.environment,
"repo_types_enabled": list(s.repo_types_enabled),
}
@router.get("/health")
async def health() -> dict:
"""Cheap liveness. Says nothing about dependencies — /health/full does that."""
return {"status": "ok"}
@router.get("/health/full")
async def health_full(request: Request, response: Response) -> dict:
providers = request.app.state.providers
checks: dict[str, dict] = {}
for provider in providers:
result = await provider.healthy()
checks[provider.name] = {
"ok": result.ok,
"configured": provider.configured,
"reachable": result.reachable,
"detail": result.detail,
}
all_ok = all(c["ok"] for c in checks.values())
status = "ok" if all_ok else "degraded"
if not all_ok:
# Degraded is a real answer, not a 500. But it must never read as ok.
response.status_code = 503
info = get_build_info()
return {
"status": status,
"commit_sha": info.commit_sha,
"checks": checks,
# Named, not hidden. An observer should never have to wonder whether a
# missing check means healthy or means forgotten.
"not_checked_here": {
"tunnel": (
"host-scoped: cloudflared binds host loopback and is supervised "
"by systemd (windygit-tunnel). Verify with `systemctl status`."
)
},
# Grandma-words, and the D-9 vocabulary law binds this string.
"speak": (
"Everything is working."
if all_ok
else "Some parts of Windy Git aren't switched on. Nothing you have is lost."
),
}