admi api: remove info about local node from GetClusterStatus and add specific GetNodeInfo endpoint

This commit is contained in:
Alex Auvolat
2025-03-06 10:26:01 +01:00
parent 29ce490dd6
commit 2e03d90585
8 changed files with 85 additions and 85 deletions
+18 -7
View File
@@ -10,10 +10,9 @@ use garage_rpc::*;
use garage_model::garage::Garage;
use garage_api_common::common_error::CommonErrorDerivative;
use garage_api_common::helpers::is_default;
use crate::api_server::{AdminRpc, AdminRpcResponse};
use crate::api_server::{find_matching_nodes, AdminRpc, AdminRpcResponse};
use crate::error::Error;
use crate::macros::*;
use crate::{Admin, RequestHandler};
@@ -77,6 +76,7 @@ admin_endpoints![
RemoveBucketAlias,
// Node operations
GetNodeInfo,
CreateMetadataSnapshot,
GetNodeStatistics,
GetClusterStatistics,
@@ -97,6 +97,7 @@ admin_endpoints![
local_admin_endpoints![
// Node operations
GetNodeInfo,
CreateMetadataSnapshot,
GetNodeStatistics,
LaunchRepairOperation,
@@ -157,11 +158,6 @@ pub struct GetClusterStatusRequest;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetClusterStatusResponse {
pub node: String,
pub garage_version: String,
pub garage_features: Option<Vec<String>>,
pub rust_version: String,
pub db_engine: String,
pub layout_version: u64,
pub nodes: Vec<NodeResp>,
}
@@ -636,6 +632,21 @@ pub struct RemoveBucketAliasResponse(pub GetBucketInfoResponse);
// Node operations
// **********************************************
// ---- GetNodeInfo ----
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct LocalGetNodeInfoRequest;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct LocalGetNodeInfoResponse {
pub node_id: String,
pub garage_version: String,
pub garage_features: Option<Vec<String>>,
pub rust_version: String,
pub db_engine: String,
}
// ---- CreateMetadataSnapshot ----
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+38
View File
@@ -16,6 +16,7 @@ use opentelemetry_prometheus::PrometheusExporter;
use garage_model::garage::Garage;
use garage_rpc::{Endpoint as RpcEndpoint, *};
use garage_util::background::BackgroundRunner;
use garage_util::data::Uuid;
use garage_util::error::Error as GarageError;
use garage_util::socket_address::UnixOrTCPSocketAddress;
@@ -265,3 +266,40 @@ fn verify_bearer_token(token: &hyper::http::HeaderValue, password_hash: &str) ->
Ok(())
}
pub(crate) fn find_matching_nodes(garage: &Garage, spec: &str) -> Result<Vec<Uuid>, Error> {
let mut res = vec![];
if spec == "*" {
res = garage.system.cluster_layout().all_nodes().to_vec();
for node in garage.system.get_known_nodes() {
if node.is_up && !res.contains(&node.id) {
res.push(node.id);
}
}
} else if spec == "self" {
res.push(garage.system.id);
} else {
let layout = garage.system.cluster_layout();
let known_nodes = garage.system.get_known_nodes();
let all_nodes = layout
.all_nodes()
.iter()
.copied()
.chain(known_nodes.iter().filter(|x| x.is_up).map(|x| x.id));
for node in all_nodes {
if !res.contains(&node) && hex::encode(node).starts_with(spec) {
res.push(node);
}
}
if res.is_empty() {
return Err(Error::bad_request(format!("No nodes matching {}", spec)));
}
if res.len() > 1 {
return Err(Error::bad_request(format!(
"Multiple nodes matching {}: {:?}",
spec, res
)));
}
}
Ok(res)
}
-6
View File
@@ -105,12 +105,6 @@ impl RequestHandler for GetClusterStatusRequest {
nodes.sort_by(|x, y| x.id.cmp(&y.id));
Ok(GetClusterStatusResponse {
node: hex::encode(garage.system.id),
garage_version: garage_util::version::garage_version().to_string(),
garage_features: garage_util::version::garage_features()
.map(|features| features.iter().map(ToString::to_string).collect()),
rust_version: garage_util::version::rust_version().to_string(),
db_engine: garage.db.engine(),
layout_version: layout.current().version,
nodes,
})
+1 -14
View File
@@ -136,20 +136,7 @@ macro_rules! local_admin_endpoints {
type Response = [< $endpoint Response >];
async fn handle(self, garage: &Arc<Garage>, admin: &Admin) -> Result<Self::Response, Error> {
let to = match self.node.as_str() {
"*" => garage.system.cluster_layout().all_nodes().to_vec(),
id => {
let nodes = garage.system.cluster_layout().all_nodes()
.iter()
.filter(|x| hex::encode(x).starts_with(id))
.cloned()
.collect::<Vec<_>>();
if nodes.len() != 1 {
return Err(Error::bad_request(format!("Zero or multiple nodes matching {}: {:?}", id, nodes)));
}
nodes
}
};
let to = find_matching_nodes(garage, self.node.as_str())?;
let resps = garage.system.rpc_helper().call_many(&admin.endpoint,
&to,
+19
View File
@@ -18,6 +18,25 @@ use crate::api::*;
use crate::error::Error;
use crate::{Admin, RequestHandler};
impl RequestHandler for LocalGetNodeInfoRequest {
type Response = LocalGetNodeInfoResponse;
async fn handle(
self,
garage: &Arc<Garage>,
_admin: &Admin,
) -> Result<LocalGetNodeInfoResponse, Error> {
Ok(LocalGetNodeInfoResponse {
node_id: hex::encode(garage.system.id),
garage_version: garage_util::version::garage_version().to_string(),
garage_features: garage_util::version::garage_features()
.map(|features| features.iter().map(ToString::to_string).collect()),
rust_version: garage_util::version::rust_version().to_string(),
db_engine: garage.db.engine(),
})
}
}
impl RequestHandler for LocalCreateMetadataSnapshotRequest {
type Response = LocalCreateMetadataSnapshotResponse;
+3 -3
View File
@@ -60,6 +60,7 @@ impl AdminApiRequest {
POST AddBucketAlias (body),
POST RemoveBucketAlias (body),
// Node APIs
GET GetNodeInfo (default::body, query::node),
POST CreateMetadataSnapshot (default::body, query::node),
GET GetNodeStatistics (default::body, query::node),
GET GetClusterStatistics (),
@@ -93,9 +94,8 @@ impl AdminApiRequest {
use router_v1::Endpoint;
match v1_endpoint {
Endpoint::GetClusterStatus => {
Ok(AdminApiRequest::GetClusterStatus(GetClusterStatusRequest))
}
// GetClusterStatus semantics changed:
// info about local node is no longer returned
Endpoint::GetClusterHealth => {
Ok(AdminApiRequest::GetClusterHealth(GetClusterHealthRequest))
}