aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2022-12-05 15:38:32 +0100
committerAlex Auvolat <alex@adnab.me>2022-12-05 15:38:32 +0100
commitd7868c48a4d8d5831051a0be088fe7bbec259bca (patch)
tree27e5b5d5050a73e159f716884f69c2fec5a8fbb6
parent280d1be7b1fde13d23e47f75aa8acd2f90efb81f (diff)
downloadgarage-d7868c48a4d8d5831051a0be088fe7bbec259bca.tar.gz
garage-d7868c48a4d8d5831051a0be088fe7bbec259bca.zip
Separate /health (simple text answer) and /v0/health (full json answer, authenticated)
-rw-r--r--src/api/admin/api_server.rs54
-rw-r--r--src/api/admin/cluster.rs17
-rw-r--r--src/api/admin/router.rs10
3 files changed, 33 insertions, 48 deletions
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<Response<Body>, Error> {
+ fn handle_health(&self) -> Result<Response<Body>, 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<Response<Body>, 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<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();
+
+ 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<Garage>,
req: Request<Body>,
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<String>,
- },
+ 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,
}