1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//! Subscription health reporting (issue #318).
//!
//! Separate from `proxy::health`, which answers liveness only: `/health` drives
//! both Kubernetes probes and a restart cannot mint an OAuth token.
use axum::extract::State;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use crate::app_state::AppState;
/// `GET /health/subscriptions` — can this router serve what it advertises?
///
/// The signal that did not exist. A revoked subscription left `/health` at
/// `ok`, `degraded_providers` empty and no counter anywhere, so the operator
/// learned about it from a client hours later via a message that named neither
/// the subscription nor the credential (issue #318).
///
/// Answers `503` when a *configured* subscription is degraded, which is what
/// lets a stock uptime check fire without knowing anything about router
/// internals. A readable credential still awaiting its first live catalog is
/// listed as starting and answers `200`; a deployment with no credential also
/// answers `200` with empty provider lists.
pub async fn subscription_health(State(state): State<AppState>) -> impl IntoResponse {
let providers = crate::model_routing::configured_provider_health_report(&state).await;
let degraded = providers
.iter()
.filter(|health| health.is_degraded())
.map(|health| {
serde_json::json!({
"provider": health.provider.as_str(),
"reason": health.summary,
})
})
.collect::<Vec<_>>();
let healthy = providers
.iter()
.filter(|health| health.state == crate::model_routing::ProviderHealthState::Healthy)
.map(|health| health.provider.as_str())
.collect::<Vec<_>>();
let starting = providers
.iter()
.filter(|health| health.state == crate::model_routing::ProviderHealthState::Starting)
.map(|health| health.provider.as_str())
.collect::<Vec<_>>();
let status = if degraded.is_empty() {
StatusCode::OK
} else {
StatusCode::SERVICE_UNAVAILABLE
};
(
status,
axum::Json(serde_json::json!({
"status": if degraded.is_empty() { "ok" } else { "degraded" },
"starting_providers": starting,
"healthy_providers": healthy,
"degraded_providers": degraded,
})),
)
.into_response()
}