Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions crates/aisix-admin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,12 @@ pub fn build_router(state: AdminState) -> Router {
}

async fn health(
axum::extract::State(state): axum::extract::State<AdminState>,
_state: axum::extract::State<AdminState>,
) -> (StatusCode, Json<serde_json::Value>) {
let snap = state.snapshot.load();
(
StatusCode::OK,
Json(json!({
"status": "ok",
"models": snap.models.len(),
"apikeys": snap.apikeys.len(),
})),
)
}
Expand Down Expand Up @@ -328,7 +325,7 @@ mod tests {
}

#[tokio::test]
async fn health_reports_snapshot_counts() {
async fn health_reports_only_minimal_status() {
let app = build_router(build_state());
let req = Request::builder()
.uri("/health")
Expand All @@ -338,6 +335,21 @@ mod tests {
assert_eq!(resp.status(), StatusCode::OK);
let v = body_json(resp).await;
assert_eq!(v["status"], "ok");
assert_eq!(v.as_object().map(|o| o.len()), Some(1));
assert!(v.get("models").is_none());
assert!(v.get("apikeys").is_none());
}

#[tokio::test]
async fn health_rejects_non_get_requests() {
let app = build_router(build_state());
let req = Request::builder()
.method("POST")
.uri("/health")
.body(Body::empty())
.unwrap();
let resp = run(app, req).await;
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
}

#[tokio::test]
Expand Down
2 changes: 1 addition & 1 deletion crates/aisix-admin/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const OPENAPI_JSON: &str = r##"{
"paths": {
"/health": {
"get": {
"summary": "process health + snapshot counts",
"summary": "minimal process health status",
"security": [],
"responses": {"200": {"description": "OK"}}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/aisix-admin/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Holds:
//! - the bootstrap-config-provided `admin_keys` (auth)
//! - the `ConfigStore` trait object (CRUD backend)
//! - a `SnapshotHandle` for the /health endpoint (snapshot counts)
//! - a `SnapshotHandle` for authenticated operator-health handlers
//! - an optional `Metrics` handle — when present, `/metrics` renders
//! the same Prometheus exposition that the proxy's middleware writes to
//!
Expand Down
25 changes: 20 additions & 5 deletions crates/aisix-proxy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,16 +175,12 @@ async fn drain_body(body: axum::body::Body) {
}

async fn health(
axum::extract::State(state): axum::extract::State<ProxyState>,
_state: axum::extract::State<ProxyState>,
) -> (StatusCode, Json<serde_json::Value>) {
let snap = state.snapshot.load();
(
StatusCode::OK,
Json(json!({
"status": "ok",
"models": snap.models.len(),
"apikeys": snap.apikeys.len(),
"providers": state.hub.len(),
})),
)
}
Expand Down Expand Up @@ -406,6 +402,25 @@ mod tests {
assert_eq!(v["error"]["type"], "invalid_api_key");
}

#[tokio::test]
async fn health_reports_only_minimal_status() {
let hub = Arc::new(Hub::new());
let snap = seed_snapshot("my-gpt4", &["my-gpt4"], "http://unused");
let app = build_router(build_state(snap, hub));

let req = Request::builder()
.method("GET")
.uri("/health")
.body(Body::empty())
.unwrap();

let resp = run(app, req).await;
assert_eq!(resp.status(), StatusCode::OK);
let bytes = to_bytes(resp.into_body(), 1024).await.unwrap();
let v: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
assert_eq!(v, serde_json::json!({"status": "ok"}));
}

#[tokio::test]
async fn unknown_api_key_returns_401() {
let hub = Arc::new(Hub::new());
Expand Down
2 changes: 1 addition & 1 deletion docs/api-admin.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ configured in the bootstrap YAML under `admin.admin_keys` (a list).

The unauthenticated endpoints are:

- `/health` — liveness probe.
- `/health` — minimal unauthenticated status probe.
- `/metrics` — Prometheus scrape (intended to be private).
- `/admin/openapi.json`, `/admin/openapi-scalar` — the OpenAPI spec
and the Scalar UI.
Expand Down
2 changes: 1 addition & 1 deletion docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Lives in `tests/e2e/`. Each test:
(`/aisix-e2e-<uuid>`) so concurrent runs cannot collide.
4. Writes a YAML config to a temp dir.
5. Spawns `target/debug/aisix --config <path>`.
6. Waits up to 10 s for `/health` and `/admin/v1/health` to respond.
6. Waits up to 10 s for the minimal `/health` probe and `/admin/v1/health` to respond.
7. Drives the request scenario via the Admin and Proxy clients.
8. Tears down: SIGTERM + 3 s grace + SIGKILL fallback, plus etcd
prefix cleanup.
Expand Down
Loading