admi api: remove info about local node from GetClusterStatus and add specific GetNodeInfo endpoint
This commit is contained in:
parent
29ce490dd6
commit
2e03d90585
8 changed files with 85 additions and 85 deletions
|
@ -84,34 +84,12 @@ paths:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
required: [ node, garageVersion, garageFeatures, rustVersion, dbEngine, knownNodes, layout ]
|
required: [ layoutVersion, nodes ]
|
||||||
properties:
|
properties:
|
||||||
node:
|
layoutVersion:
|
||||||
type: string
|
type: integer
|
||||||
example: "ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f"
|
example: 1
|
||||||
garageVersion:
|
nodes:
|
||||||
type: string
|
|
||||||
example: "v2.0.0"
|
|
||||||
garageFeatures:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
example:
|
|
||||||
- "k2v"
|
|
||||||
- "lmdb"
|
|
||||||
- "sqlite"
|
|
||||||
- "consul-discovery"
|
|
||||||
- "kubernetes-discovery"
|
|
||||||
- "metrics"
|
|
||||||
- "telemetry-otlp"
|
|
||||||
- "bundled-libs"
|
|
||||||
rustVersion:
|
|
||||||
type: string
|
|
||||||
example: "1.68.0"
|
|
||||||
dbEngine:
|
|
||||||
type: string
|
|
||||||
example: "LMDB (using Heed crate)"
|
|
||||||
knownNodes:
|
|
||||||
type: array
|
type: array
|
||||||
example:
|
example:
|
||||||
- id: "ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f"
|
- id: "ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f"
|
||||||
|
@ -131,8 +109,6 @@ paths:
|
||||||
hostname: neptune
|
hostname: neptune
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/NodeNetworkInfo'
|
$ref: '#/components/schemas/NodeNetworkInfo'
|
||||||
layout:
|
|
||||||
$ref: '#/components/schemas/ClusterLayout'
|
|
||||||
|
|
||||||
/ConnectClusterNodes:
|
/ConnectClusterNodes:
|
||||||
post:
|
post:
|
||||||
|
|
|
@ -68,26 +68,13 @@ Returns HTTP 200 Ok if yes, or HTTP 4xx if no website is available for this doma
|
||||||
|
|
||||||
Returns the cluster's current status in JSON, including:
|
Returns the cluster's current status in JSON, including:
|
||||||
|
|
||||||
- ID of the node being queried and its version of the Garage daemon
|
|
||||||
- Live nodes
|
- Live nodes
|
||||||
- Currently configured cluster layout
|
- Currently configured cluster layout
|
||||||
- Staged changes to the cluster layout
|
|
||||||
|
|
||||||
Example response body:
|
Example response body:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"node": "b10c110e4e854e5aa3f4637681befac755154b20059ec163254ddbfae86b09df",
|
|
||||||
"garageVersion": "v2.0.0",
|
|
||||||
"garageFeatures": [
|
|
||||||
"k2v",
|
|
||||||
"lmdb",
|
|
||||||
"sqlite",
|
|
||||||
"metrics",
|
|
||||||
"bundled-libs"
|
|
||||||
],
|
|
||||||
"rustVersion": "1.68.0",
|
|
||||||
"dbEngine": "LMDB (using Heed crate)",
|
|
||||||
"layoutVersion": 5,
|
"layoutVersion": 5,
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{
|
{
|
||||||
|
@ -362,19 +349,7 @@ layout, as well as the description of the layout as returned by GetClusterLayout
|
||||||
|
|
||||||
Clears all of the staged layout changes.
|
Clears all of the staged layout changes.
|
||||||
|
|
||||||
Request body format:
|
This requests contains an empty body.
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"version": 13
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Reverting the staged changes is done by incrementing the version number
|
|
||||||
and clearing the contents of the staged change list.
|
|
||||||
Similarly to the CLI, the body must include the incremented
|
|
||||||
version number, which MUST be 1 + the value of the currently
|
|
||||||
existing layout in the cluster.
|
|
||||||
|
|
||||||
This returns the new cluster layout with all changes reverted,
|
This returns the new cluster layout with all changes reverted,
|
||||||
as returned by GetClusterLayout.
|
as returned by GetClusterLayout.
|
||||||
|
|
|
@ -10,10 +10,9 @@ use garage_rpc::*;
|
||||||
|
|
||||||
use garage_model::garage::Garage;
|
use garage_model::garage::Garage;
|
||||||
|
|
||||||
use garage_api_common::common_error::CommonErrorDerivative;
|
|
||||||
use garage_api_common::helpers::is_default;
|
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::error::Error;
|
||||||
use crate::macros::*;
|
use crate::macros::*;
|
||||||
use crate::{Admin, RequestHandler};
|
use crate::{Admin, RequestHandler};
|
||||||
|
@ -77,6 +76,7 @@ admin_endpoints![
|
||||||
RemoveBucketAlias,
|
RemoveBucketAlias,
|
||||||
|
|
||||||
// Node operations
|
// Node operations
|
||||||
|
GetNodeInfo,
|
||||||
CreateMetadataSnapshot,
|
CreateMetadataSnapshot,
|
||||||
GetNodeStatistics,
|
GetNodeStatistics,
|
||||||
GetClusterStatistics,
|
GetClusterStatistics,
|
||||||
|
@ -97,6 +97,7 @@ admin_endpoints![
|
||||||
|
|
||||||
local_admin_endpoints![
|
local_admin_endpoints![
|
||||||
// Node operations
|
// Node operations
|
||||||
|
GetNodeInfo,
|
||||||
CreateMetadataSnapshot,
|
CreateMetadataSnapshot,
|
||||||
GetNodeStatistics,
|
GetNodeStatistics,
|
||||||
LaunchRepairOperation,
|
LaunchRepairOperation,
|
||||||
|
@ -157,11 +158,6 @@ pub struct GetClusterStatusRequest;
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GetClusterStatusResponse {
|
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 layout_version: u64,
|
||||||
pub nodes: Vec<NodeResp>,
|
pub nodes: Vec<NodeResp>,
|
||||||
}
|
}
|
||||||
|
@ -636,6 +632,21 @@ pub struct RemoveBucketAliasResponse(pub GetBucketInfoResponse);
|
||||||
// Node operations
|
// 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 ----
|
// ---- CreateMetadataSnapshot ----
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
|
|
|
@ -16,6 +16,7 @@ use opentelemetry_prometheus::PrometheusExporter;
|
||||||
use garage_model::garage::Garage;
|
use garage_model::garage::Garage;
|
||||||
use garage_rpc::{Endpoint as RpcEndpoint, *};
|
use garage_rpc::{Endpoint as RpcEndpoint, *};
|
||||||
use garage_util::background::BackgroundRunner;
|
use garage_util::background::BackgroundRunner;
|
||||||
|
use garage_util::data::Uuid;
|
||||||
use garage_util::error::Error as GarageError;
|
use garage_util::error::Error as GarageError;
|
||||||
use garage_util::socket_address::UnixOrTCPSocketAddress;
|
use garage_util::socket_address::UnixOrTCPSocketAddress;
|
||||||
|
|
||||||
|
@ -265,3 +266,40 @@ fn verify_bearer_token(token: &hyper::http::HeaderValue, password_hash: &str) ->
|
||||||
|
|
||||||
Ok(())
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -105,12 +105,6 @@ impl RequestHandler for GetClusterStatusRequest {
|
||||||
nodes.sort_by(|x, y| x.id.cmp(&y.id));
|
nodes.sort_by(|x, y| x.id.cmp(&y.id));
|
||||||
|
|
||||||
Ok(GetClusterStatusResponse {
|
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,
|
layout_version: layout.current().version,
|
||||||
nodes,
|
nodes,
|
||||||
})
|
})
|
||||||
|
|
|
@ -136,20 +136,7 @@ macro_rules! local_admin_endpoints {
|
||||||
type Response = [< $endpoint Response >];
|
type Response = [< $endpoint Response >];
|
||||||
|
|
||||||
async fn handle(self, garage: &Arc<Garage>, admin: &Admin) -> Result<Self::Response, Error> {
|
async fn handle(self, garage: &Arc<Garage>, admin: &Admin) -> Result<Self::Response, Error> {
|
||||||
let to = match self.node.as_str() {
|
let to = find_matching_nodes(garage, 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 resps = garage.system.rpc_helper().call_many(&admin.endpoint,
|
let resps = garage.system.rpc_helper().call_many(&admin.endpoint,
|
||||||
&to,
|
&to,
|
||||||
|
|
|
@ -18,6 +18,25 @@ use crate::api::*;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::{Admin, RequestHandler};
|
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 {
|
impl RequestHandler for LocalCreateMetadataSnapshotRequest {
|
||||||
type Response = LocalCreateMetadataSnapshotResponse;
|
type Response = LocalCreateMetadataSnapshotResponse;
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@ impl AdminApiRequest {
|
||||||
POST AddBucketAlias (body),
|
POST AddBucketAlias (body),
|
||||||
POST RemoveBucketAlias (body),
|
POST RemoveBucketAlias (body),
|
||||||
// Node APIs
|
// Node APIs
|
||||||
|
GET GetNodeInfo (default::body, query::node),
|
||||||
POST CreateMetadataSnapshot (default::body, query::node),
|
POST CreateMetadataSnapshot (default::body, query::node),
|
||||||
GET GetNodeStatistics (default::body, query::node),
|
GET GetNodeStatistics (default::body, query::node),
|
||||||
GET GetClusterStatistics (),
|
GET GetClusterStatistics (),
|
||||||
|
@ -93,9 +94,8 @@ impl AdminApiRequest {
|
||||||
use router_v1::Endpoint;
|
use router_v1::Endpoint;
|
||||||
|
|
||||||
match v1_endpoint {
|
match v1_endpoint {
|
||||||
Endpoint::GetClusterStatus => {
|
// GetClusterStatus semantics changed:
|
||||||
Ok(AdminApiRequest::GetClusterStatus(GetClusterStatusRequest))
|
// info about local node is no longer returned
|
||||||
}
|
|
||||||
Endpoint::GetClusterHealth => {
|
Endpoint::GetClusterHealth => {
|
||||||
Ok(AdminApiRequest::GetClusterHealth(GetClusterHealthRequest))
|
Ok(AdminApiRequest::GetClusterHealth(GetClusterHealthRequest))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue