Aller au contenu

Référence endpoint santé

Repod expose trois endpoints de santé compatibles Kubernetes (backend/routers/health_router.py). Aucun ne requiert d'authentification.

Endpoint Objet Codes de statut
GET /health Rapport de santé complet — chaque sonde, groupée par criticité. 200 (healthy/degraded), 503 (unhealthy)
GET /health/live Sonde de liveness — le processus tourne. Intentionnellement minimale, aucune E/S ni verrou. 200 toujours
GET /health/ready Sonde de readiness — le service peut accepter du trafic. 200 (ready), 503 (une sonde critique a échoué)

Sémantique des statuts

Statut Signification Code HTTP sur /health
healthy Chaque sonde critique et non-critique a réussi. 200
degraded Toutes les sondes critiques ont réussi ; au moins une sonde non-critique a échoué. 200
unhealthy Au moins une sonde critique a échoué. 503

Réponse GET /health

{
  "status": "healthy | degraded | unhealthy",
  "timestamp": "ISO 8601 UTC",
  "version": "string (variable d'env APP_VERSION, défaut \"dev\")",
  "checks": {
    "critical": { "...": "..." },
    "non_critical": { "...": "..." },
    "info": { "...": "..." }
  }
}

checks comporte trois groupes. Chaque résultat de sonde est un objet avec au moins un champ "ok": bool.


checks.critical

Un échec sur l'une de ces quatre sondes fixe status = "unhealthy" et GET /health / GET /health/ready retournent tous deux 503.

Champ Sonde Forme de la réponse
manifests Le répertoire /repos/manifests existe et est accessible. {ok, path, free_gb, total_gb, used_pct}
pool Le répertoire /repos/pool existe et est accessible. {ok, path, free_gb, total_gb, used_pct}
auth_db SELECT COUNT(*) FROM users réussit sur PostgreSQL. {ok, count} ou {ok: false, error}
manifest_db SELECT COUNT(*) FROM manifests réussit sur PostgreSQL. {ok, count} ou {ok: false, error}

GET /health/ready rapporte exactement ces quatre mêmes sondes sous checks, plus un "ready": bool de premier niveau et, si non prêt, un tableau "failing": [...] listant les noms des sondes en échec.


checks.non_critical

Un échec ici fixe status = "degraded" (jamais unhealthy à lui seul) — /health retourne quand même 200.

Champ Sonde Forme de la réponse
audit Le répertoire /repos/audit existe et est accessible. {ok, path, free_gb, total_gb, used_pct}
clamav clamscan --version s'exécute avec succès. {ok, version} ou {ok: false, version: null, error}
reprepro Le binaire reprepro --version est dans le PATH. {ok: true, version} ou {ok: false, version: null, error}
gpg Au moins une clé privée existe dans le trousseau GPG (GNUPGHOME). {ok, fingerprint} ou {ok: false, error}
scheduler APScheduler tourne avec des jobs actifs. {ok, jobs: [...]} — voir ci-dessous
alembic La table alembic_version est peuplée (non vide alors que les tables applicatives existent déjà). {ok, version} ou {ok: false, version: null, error}

Entrées de job scheduler

Chaque entrée dans jobs (quand le scheduler tourne) :

{"id": "string", "name": "string", "next_run": "ISO 8601 ou null", "paused": "bool"}

paused vaut true quand next_run_time est null.

Sur une réplique HA passive (voir Variables d'environnement et les sections HA de la documentation d'architecture), aucun scheduler ne tourne du tout — c'est attendu et ne dégrade pas le statut :

{"ok": true, "jobs": [], "note": "réplique passive — scheduler sur l'instance leader"}

Si l'instance courante est le leader et que le scheduler n'a pas réussi à démarrer, ceci devient {"ok": false, "jobs": [], "error": "scheduler non démarré"}.


checks.info

Données informatives en lecture seule. N'affecte jamais status.

Champ Objet Forme de la réponse
packages Nombre de fichiers du pool et taille, par format. voir ci-dessous
storage Utilisation du système de fichiers sur /repos plus une répartition de taille par répertoire. voir ci-dessous
license Édition de licence active. {ok, edition, active, issued_to}
setup Si l'assistant de configuration du premier lancement s'est terminé. {ok: bool, setup_done: bool}
ha Statut actif-passif HA et backend d'état de job par flux. voir ci-dessous
cache_backend Backend de cache de réponses actif. {ok: true, backend: "memory" \| "redis"}

packages

{
  "ok": true,
  "total_manifests": 0,
  "pool_files": 0,
  "pool_size_mb": 0.0,
  "by_format": {"deb": 0, "rpm": 0, "apk": 0}
}

storage

{
  "ok": true,
  "free_gb": 0.0,
  "total_gb": 0.0,
  "used_pct": 0.0,
  "dirs": {
    "pool":      {"path": "/repos/pool", "size_mb": 0.0},
    "manifests": {"path": "/repos/manifests", "size_mb": 0.0},
    "audit":     {"path": "/repos/audit", "size_mb": 0.0},
    "grype_db":  {"path": "/repos/grype-db", "size_mb": 0.0},
    "clamav_db": {"path": "/var/lib/clamav", "size_mb": 0.0}
  }
}

size_mb vaut null quand le répertoire n'existe pas.

ha

{
  "ok": true,
  "is_leader": "bool",
  "instance_id": "string",
  "scheduler_active": "bool",
  "job_state_backend": {
    "scan":    "redis | local",
    "install": "redis | local",
    "mirror":  "redis | local",
    "sync":    "redis | local",
    "sse":     "redis | local",
    "logs":    "redis | local"
  }
}
  • is_leader — si cette instance détient le verrou consultatif ("advisory lock") PostgreSQL (services/leader_election.py). Seul le leader exécute les jobs cron APScheduler.
  • job_state_backend.{scan,install,mirror,sync}"redis" signifie que l'état de ce flux est réellement distribué entre les répliques en ce moment même (donc require_leader_for(flow) devient un no-op pour lui) ; "local" signifie le comportement historique mono-processus, soit par configuration par défaut, soit comme repli fail-soft (JOB_STATE_BACKEND=redis configuré mais Redis injoignable au démarrage) — les deux cas sont indiscernables à partir de ce seul champ, par conception.
  • job_state_backend.sse / .logs"redis" signifie que les événements live (GET /dashboard/events) ou les entrées de log (GET /logs, GET /logs/stream) sont diffusés à travers toutes les répliques via Redis pub/sub ; "local" signifie une livraison mono-réplique uniquement. Ni sse ni logs n'ont de verrou require_leader"local" signifie ici « diffusion mono-réplique uniquement », pas « réplique bloquée ».

cache_backend

{"ok": true, "backend": "memory"}

ou, en cas d'erreur de résolution du module de cache :

{"ok": true, "backend": "memory", "note": "error string"}

Réponse GET /health/live

{"alive": true, "timestamp": "ISO 8601 UTC"}

Toujours 200. Aucune E/S, aucun accès base de données — confirme uniquement que le processus FastAPI tourne.


Réponse GET /health/ready

{
  "ready": "bool",
  "timestamp": "ISO 8601 UTC",
  "checks": {
    "manifests":   {"ok": "bool", "...": "..."},
    "pool":        {"ok": "bool", "...": "..."},
    "auth_db":     {"ok": "bool", "...": "..."},
    "manifest_db": {"ok": "bool", "...": "..."}
  },
  "failing": ["présent uniquement quand ready vaut false — liste des noms de sondes en échec"]
}