Skip to main content

greentic_runner_host/http/
admin.rs

1use axum::Json;
2use axum::extract::State;
3use axum::http::StatusCode;
4use axum::response::IntoResponse;
5use serde_json::json;
6use time::format_description::well_known::Rfc3339;
7
8use crate::http::auth::AdminGuard;
9use crate::runner::ServerState;
10
11pub async fn status(AdminGuard: AdminGuard, State(state): State<ServerState>) -> impl IntoResponse {
12    let snapshot = state.active.snapshot();
13    let tenants = snapshot
14        .iter()
15        .map(|(tenant, runtime)| {
16            let pack = runtime.pack();
17            let metadata = pack.metadata();
18            let required_secrets = runtime.required_secrets();
19            let missing_secrets = runtime.missing_secrets();
20            let overlays = runtime
21                .overlays()
22                .into_iter()
23                .zip(runtime.overlay_digests().into_iter())
24                .map(|(overlay, digest)| {
25                    let meta = overlay.metadata();
26                    json!({
27                        "pack_id": meta.pack_id,
28                        "version": meta.version,
29                        "digest": digest,
30                    })
31                })
32                .collect::<Vec<_>>();
33            json!({
34                "tenant": tenant,
35                "pack_id": metadata.pack_id,
36                "version": metadata.version,
37                "digest": runtime.digest(),
38                "overlays": overlays,
39                "required_secrets": required_secrets,
40                "missing_secrets": missing_secrets,
41            })
42        })
43        .collect::<Vec<_>>();
44
45    let health = state.health.snapshot();
46    let last_reload = health.last_reload.and_then(|ts| ts.format(&Rfc3339).ok());
47
48    Json(json!({
49        "tenants": tenants,
50        "active": snapshot.len(),
51        "last_reload": last_reload,
52        "last_error": health.last_error,
53    }))
54}
55
56pub async fn reload(AdminGuard: AdminGuard, State(state): State<ServerState>) -> impl IntoResponse {
57    if let Some(handle) = &state.reload {
58        match handle.trigger().await {
59            Ok(()) => {
60                tracing::info!("pack.reload.requested");
61                (
62                    StatusCode::ACCEPTED,
63                    Json(json!({ "status": "reload requested" })),
64                )
65            }
66            Err(err) => {
67                tracing::warn!(error = %err, "reload trigger failed");
68                (
69                    StatusCode::INTERNAL_SERVER_ERROR,
70                    Json(json!({ "error": err.to_string() })),
71                )
72            }
73        }
74    } else {
75        (
76            StatusCode::NOT_IMPLEMENTED,
77            Json(json!({ "error": "reload handle unavailable" })),
78        )
79    }
80}