diff --git a/src/api/admin/api_server.rs b/src/api/admin/api_server.rs index f86ed599..2d325fb1 100644 --- a/src/api/admin/api_server.rs +++ b/src/api/admin/api_server.rs @@ -1,4 +1,3 @@ -use std::fmt::Write; use std::net::SocketAddr; use std::sync::Arc; @@ -78,7 +77,7 @@ impl AdminApiServer { .body(Body::empty())?) } - fn handle_health(&self, format: Option<&str>) -> Result, Error> { + fn handle_health(&self) -> Result, Error> { let health = self.garage.system.health(); let (status, status_str) = match health.status { @@ -92,47 +91,15 @@ impl AdminApiServer { "Quorum is not available for some/all partitions, reads and writes will fail", ), }; + let status_str = format!( + "{}\nConsult the full health check API endpoint at /v0/health for more details\n", + status_str + ); - let resp = Response::builder().status(status); - - if matches!(format, Some("json")) { - let resp_json = - serde_json::to_string_pretty(&health).map_err(garage_util::error::Error::from)?; - Ok(resp - .header(http::header::CONTENT_TYPE, "application/json") - .body(Body::from(resp_json))?) - } else { - let mut buf = status_str.to_string(); - writeln!( - &mut buf, - "\nAll nodes: {} connected, {} known", - health.connected_nodes, health.known_nodes, - ) - .unwrap(); - writeln!( - &mut buf, - "Storage nodes: {} connected, {} in layout", - health.storage_nodes_ok, health.storage_nodes - ) - .unwrap(); - writeln!(&mut buf, "Number of partitions: {}", health.partitions).unwrap(); - writeln!( - &mut buf, - "Partitions with quorum: {}", - health.partitions_quorum - ) - .unwrap(); - writeln!( - &mut buf, - "Partitions with all nodes available: {}", - health.partitions_all_ok - ) - .unwrap(); - - Ok(resp - .header(http::header::CONTENT_TYPE, "text/plain") - .body(Body::from(buf))?) - } + Ok(Response::builder() + .status(status) + .header(http::header::CONTENT_TYPE, "text/plain") + .body(Body::from(status_str))?) } fn handle_metrics(&self) -> Result, Error> { @@ -207,9 +174,10 @@ impl ApiHandler for AdminApiServer { match endpoint { Endpoint::Options => self.handle_options(&req), - Endpoint::Health { format } => self.handle_health(format.as_deref()), + Endpoint::Health => self.handle_health(), Endpoint::Metrics => self.handle_metrics(), Endpoint::GetClusterStatus => handle_get_cluster_status(&self.garage).await, + Endpoint::GetClusterHealth => handle_get_cluster_health(&self.garage).await, Endpoint::ConnectClusterNodes => handle_connect_cluster_nodes(&self.garage, req).await, // Layout Endpoint::GetClusterLayout => handle_get_cluster_layout(&self.garage).await, diff --git a/src/api/admin/cluster.rs b/src/api/admin/cluster.rs index 706db727..a773e27a 100644 --- a/src/api/admin/cluster.rs +++ b/src/api/admin/cluster.rs @@ -9,6 +9,7 @@ use garage_util::crdt::*; use garage_util::data::*; use garage_rpc::layout::*; +use garage_rpc::system::ClusterHealthStatus; use garage_model::garage::Garage; @@ -43,6 +44,22 @@ pub async fn handle_get_cluster_status(garage: &Arc) -> Result) -> Result, Error> { + let health = garage.system.health(); + + let status = match health.status { + ClusterHealthStatus::Unavailable => StatusCode::SERVICE_UNAVAILABLE, + _ => StatusCode::OK, + }; + + let resp_json = + serde_json::to_string_pretty(&health).map_err(garage_util::error::Error::from)?; + Ok(Response::builder() + .status(status) + .header(http::header::CONTENT_TYPE, "application/json") + .body(Body::from(resp_json))?) +} + pub async fn handle_connect_cluster_nodes( garage: &Arc, req: Request, diff --git a/src/api/admin/router.rs b/src/api/admin/router.rs index 6ffcc131..3fa07b3c 100644 --- a/src/api/admin/router.rs +++ b/src/api/admin/router.rs @@ -17,11 +17,10 @@ router_match! {@func #[derive(Debug, Clone, PartialEq, Eq)] pub enum Endpoint { Options, - Health { - format: Option, - }, + Health, Metrics, GetClusterStatus, + GetClusterHealth, ConnectClusterNodes, // Layout GetClusterLayout, @@ -92,9 +91,10 @@ impl Endpoint { let res = router_match!(@gen_path_parser (req.method(), path, query) [ OPTIONS _ => Options, - GET "/health" => Health (query_opt::format), + GET "/health" => Health, GET "/metrics" => Metrics, GET "/v0/status" => GetClusterStatus, + GET "/v0/health" => GetClusterHealth, POST "/v0/connect" => ConnectClusterNodes, // Layout endpoints GET "/v0/layout" => GetClusterLayout, @@ -135,7 +135,7 @@ impl Endpoint { /// Get the kind of authorization which is required to perform the operation. pub fn authorization_type(&self) -> Authorization { match self { - Self::Health { .. } => Authorization::None, + Self::Health => Authorization::None, Self::Metrics => Authorization::MetricsToken, _ => Authorization::AdminToken, }