diff options
Diffstat (limited to 'src/api')
-rw-r--r-- | src/api/admin/api_server.rs | 29 | ||||
-rw-r--r-- | src/api/admin/cluster.rs | 5 | ||||
-rw-r--r-- | src/api/admin/router.rs | 7 |
3 files changed, 41 insertions, 0 deletions
diff --git a/src/api/admin/api_server.rs b/src/api/admin/api_server.rs index 2896d058..2d325fb1 100644 --- a/src/api/admin/api_server.rs +++ b/src/api/admin/api_server.rs @@ -15,6 +15,7 @@ use opentelemetry_prometheus::PrometheusExporter; use prometheus::{Encoder, TextEncoder}; use garage_model::garage::Garage; +use garage_rpc::system::ClusterHealthStatus; use garage_util::error::Error as GarageError; use crate::generic_server::*; @@ -76,6 +77,31 @@ impl AdminApiServer { .body(Body::empty())?) } + fn handle_health(&self) -> Result<Response<Body>, Error> { + let health = self.garage.system.health(); + + let (status, status_str) = match health.status { + ClusterHealthStatus::Healthy => (StatusCode::OK, "Garage is fully operational"), + ClusterHealthStatus::Degraded => ( + StatusCode::OK, + "Garage is operational but some storage nodes are unavailable", + ), + ClusterHealthStatus::Unavailable => ( + StatusCode::SERVICE_UNAVAILABLE, + "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 + ); + + Ok(Response::builder() + .status(status) + .header(http::header::CONTENT_TYPE, "text/plain") + .body(Body::from(status_str))?) + } + fn handle_metrics(&self) -> Result<Response<Body>, Error> { #[cfg(feature = "metrics")] { @@ -124,6 +150,7 @@ impl ApiHandler for AdminApiServer { ) -> Result<Response<Body>, Error> { let expected_auth_header = match endpoint.authorization_type() { + Authorization::None => None, Authorization::MetricsToken => self.metrics_token.as_ref(), Authorization::AdminToken => match &self.admin_token { None => return Err(Error::forbidden( @@ -147,8 +174,10 @@ impl ApiHandler for AdminApiServer { match endpoint { Endpoint::Options => self.handle_options(&req), + 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..182a4f6f 100644 --- a/src/api/admin/cluster.rs +++ b/src/api/admin/cluster.rs @@ -43,6 +43,11 @@ pub async fn handle_get_cluster_status(garage: &Arc<Garage>) -> Result<Response< Ok(json_ok_response(&res)?) } +pub async fn handle_get_cluster_health(garage: &Arc<Garage>) -> Result<Response<Body>, Error> { + let health = garage.system.health(); + Ok(json_ok_response(&health)?) +} + pub async fn handle_connect_cluster_nodes( garage: &Arc<Garage>, req: Request<Body>, diff --git a/src/api/admin/router.rs b/src/api/admin/router.rs index 3eee8b67..3fa07b3c 100644 --- a/src/api/admin/router.rs +++ b/src/api/admin/router.rs @@ -6,6 +6,7 @@ use crate::admin::error::*; use crate::router_macros::*; pub enum Authorization { + None, MetricsToken, AdminToken, } @@ -16,8 +17,10 @@ router_match! {@func #[derive(Debug, Clone, PartialEq, Eq)] pub enum Endpoint { Options, + Health, Metrics, GetClusterStatus, + GetClusterHealth, ConnectClusterNodes, // Layout GetClusterLayout, @@ -88,8 +91,10 @@ impl Endpoint { let res = router_match!(@gen_path_parser (req.method(), path, query) [ OPTIONS _ => Options, + GET "/health" => Health, GET "/metrics" => Metrics, GET "/v0/status" => GetClusterStatus, + GET "/v0/health" => GetClusterHealth, POST "/v0/connect" => ConnectClusterNodes, // Layout endpoints GET "/v0/layout" => GetClusterLayout, @@ -130,6 +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::Metrics => Authorization::MetricsToken, _ => Authorization::AdminToken, } @@ -137,6 +143,7 @@ impl Endpoint { } generateQueryParameters! { + "format" => format, "id" => id, "search" => search, "globalAlias" => global_alias, |