cli_v2: implement CreateMetadataSnapshot
All checks were successful
ci/woodpecker/pr/debug Pipeline was successful
ci/woodpecker/push/debug Pipeline was successful

This commit is contained in:
Alex 2025-02-05 14:22:10 +01:00
parent 97be7b38fa
commit 9f468b4439
9 changed files with 94 additions and 68 deletions

View file

@ -77,6 +77,9 @@ admin_endpoints![
AddBucketAlias, AddBucketAlias,
RemoveBucketAlias, RemoveBucketAlias,
// Node operations
CreateMetadataSnapshot,
// Worker operations // Worker operations
ListWorkers, ListWorkers,
GetWorkerInfo, GetWorkerInfo,
@ -91,6 +94,8 @@ admin_endpoints![
]; ];
local_admin_endpoints![ local_admin_endpoints![
// Node operations
CreateMetadataSnapshot,
// Background workers // Background workers
ListWorkers, ListWorkers,
GetWorkerInfo, GetWorkerInfo,
@ -623,6 +628,18 @@ pub struct RemoveBucketAliasRequest {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RemoveBucketAliasResponse(pub GetBucketInfoResponse); pub struct RemoveBucketAliasResponse(pub GetBucketInfoResponse);
// **********************************************
// Node operations
// **********************************************
// ---- CreateMetadataSnapshot ----
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct LocalCreateMetadataSnapshotRequest;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocalCreateMetadataSnapshotResponse;
// ********************************************** // **********************************************
// Worker operations // Worker operations
// ********************************************** // **********************************************

View file

@ -16,6 +16,7 @@ mod key;
mod special; mod special;
mod block; mod block;
mod node;
mod worker; mod worker;
use std::sync::Arc; use std::sync::Arc;

23
src/api/admin/node.rs Normal file
View file

@ -0,0 +1,23 @@
use std::sync::Arc;
use async_trait::async_trait;
use garage_model::garage::Garage;
use crate::api::*;
use crate::error::Error;
use crate::{Admin, RequestHandler};
#[async_trait]
impl RequestHandler for LocalCreateMetadataSnapshotRequest {
type Response = LocalCreateMetadataSnapshotResponse;
async fn handle(
self,
garage: &Arc<Garage>,
_admin: &Admin,
) -> Result<LocalCreateMetadataSnapshotResponse, Error> {
garage_model::snapshot::async_snapshot_metadata(garage).await?;
Ok(LocalCreateMetadataSnapshotResponse)
}
}

View file

@ -59,6 +59,8 @@ impl AdminApiRequest {
// Bucket aliases // Bucket aliases
POST AddBucketAlias (body), POST AddBucketAlias (body),
POST RemoveBucketAlias (body), POST RemoveBucketAlias (body),
// Node APIs
POST CreateMetadataSnapshot (default::body, query::node),
// Worker APIs // Worker APIs
POST ListWorkers (body_field, query::node), POST ListWorkers (body_field, query::node),
POST GetWorkerInfo (body_field, query::node), POST GetWorkerInfo (body_field, query::node),

View file

@ -20,10 +20,6 @@ use garage_rpc::*;
use garage_model::garage::Garage; use garage_model::garage::Garage;
use garage_model::helper::error::Error; use garage_model::helper::error::Error;
use garage_api_admin::api::{AdminApiRequest, TaggedAdminApiResponse};
use garage_api_admin::RequestHandler as AdminApiEndpoint;
use garage_api_common::generic_server::ApiError;
use crate::cli::*; use crate::cli::*;
use crate::repair::online::launch_online_repair; use crate::repair::online::launch_online_repair;
@ -34,7 +30,6 @@ pub const ADMIN_RPC_PATH: &str = "garage/admin_rpc.rs/Rpc";
pub enum AdminRpc { pub enum AdminRpc {
LaunchRepair(RepairOpt), LaunchRepair(RepairOpt),
Stats(StatsOpt), Stats(StatsOpt),
MetaOperation(MetaOperation),
// Replies // Replies
Ok(String), Ok(String),
@ -319,43 +314,6 @@ impl AdminRpcHandler {
t.data.gc_todo_len()? t.data.gc_todo_len()?
)) ))
} }
// ================ META DB COMMANDS ====================
async fn handle_meta_cmd(self: &Arc<Self>, mo: &MetaOperation) -> Result<AdminRpc, Error> {
match mo {
MetaOperation::Snapshot { all: true } => {
let to = self.garage.system.cluster_layout().all_nodes().to_vec();
let resps = futures::future::join_all(to.iter().map(|to| async move {
let to = (*to).into();
self.endpoint
.call(
&to,
AdminRpc::MetaOperation(MetaOperation::Snapshot { all: false }),
PRIO_NORMAL,
)
.await
}))
.await;
let mut ret = vec![];
for (to, resp) in to.iter().zip(resps.iter()) {
let res_str = match resp {
Ok(_) => "ok".to_string(),
Err(e) => format!("error: {}", e),
};
ret.push(format!("{:?}\t{}", to, res_str));
}
Ok(AdminRpc::Ok(format_table_to_string(ret)))
}
MetaOperation::Snapshot { all: false } => {
garage_model::snapshot::async_snapshot_metadata(&self.garage).await?;
Ok(AdminRpc::Ok("Snapshot has been saved.".into()))
}
}
}
} }
#[async_trait] #[async_trait]
@ -368,7 +326,6 @@ impl EndpointHandler<AdminRpc> for AdminRpcHandler {
match message { match message {
AdminRpc::LaunchRepair(opt) => self.handle_launch_repair(opt.clone()).await, AdminRpc::LaunchRepair(opt) => self.handle_launch_repair(opt.clone()).await,
AdminRpc::Stats(opt) => self.handle_stats(opt.clone()).await, AdminRpc::Stats(opt) => self.handle_stats(opt.clone()).await,
AdminRpc::MetaOperation(mo) => self.handle_meta_cmd(mo).await,
m => Err(GarageError::unexpected_rpc_message(m).into()), m => Err(GarageError::unexpected_rpc_message(m).into()),
} }
} }

View file

@ -1,6 +1,3 @@
use garage_util::error::*;
use garage_rpc::system::*;
use garage_rpc::*; use garage_rpc::*;
use garage_model::helper::error::Error as HelperError; use garage_model::helper::error::Error as HelperError;
@ -22,18 +19,3 @@ pub async fn cmd_admin(
} }
Ok(()) Ok(())
} }
// ---- utility ----
pub async fn fetch_status(
rpc_cli: &Endpoint<SystemRpc, ()>,
rpc_host: NodeID,
) -> Result<Vec<KnownNodeInfo>, Error> {
match rpc_cli
.call(&rpc_host, SystemRpc::GetKnownNodes, PRIO_NORMAL)
.await??
{
SystemRpc::ReturnKnownNodes(nodes) => Ok(nodes),
resp => Err(Error::unexpected_rpc_message(resp)),
}
}

View file

@ -260,6 +260,19 @@ pub async fn cmd_layout_skip_dead_nodes(
// --- utility --- // --- utility ---
pub async fn fetch_status(
rpc_cli: &Endpoint<SystemRpc, ()>,
rpc_host: NodeID,
) -> Result<Vec<KnownNodeInfo>, Error> {
match rpc_cli
.call(&rpc_host, SystemRpc::GetKnownNodes, PRIO_NORMAL)
.await??
{
SystemRpc::ReturnKnownNodes(nodes) => Ok(nodes),
resp => Err(Error::unexpected_rpc_message(resp)),
}
}
pub async fn fetch_layout( pub async fn fetch_layout(
rpc_cli: &Endpoint<SystemRpc, ()>, rpc_cli: &Endpoint<SystemRpc, ()>,
rpc_host: NodeID, rpc_host: NodeID,

View file

@ -4,6 +4,7 @@ pub mod key;
pub mod layout; pub mod layout;
pub mod block; pub mod block;
pub mod node;
pub mod worker; pub mod worker;
use std::convert::TryFrom; use std::convert::TryFrom;
@ -43,6 +44,7 @@ impl Cli {
Command::Key(ko) => self.cmd_key(ko).await, Command::Key(ko) => self.cmd_key(ko).await,
Command::Worker(wo) => self.cmd_worker(wo).await, Command::Worker(wo) => self.cmd_worker(wo).await,
Command::Block(bo) => self.cmd_block(bo).await, Command::Block(bo) => self.cmd_block(bo).await,
Command::Meta(mo) => self.cmd_meta(mo).await,
// TODO // TODO
Command::Repair(ro) => cli_v1::cmd_admin( Command::Repair(ro) => cli_v1::cmd_admin(
@ -57,13 +59,6 @@ impl Cli {
.await .await
.ok_or_message("cli_v1") .ok_or_message("cli_v1")
} }
Command::Meta(mo) => cli_v1::cmd_admin(
&self.admin_rpc_endpoint,
self.rpc_host,
AdminRpc::MetaOperation(mo),
)
.await
.ok_or_message("cli_v1"),
_ => unreachable!(), _ => unreachable!(),
} }

36
src/garage/cli_v2/node.rs Normal file
View file

@ -0,0 +1,36 @@
use format_table::format_table;
use garage_util::error::*;
use garage_api_admin::api::*;
use crate::cli::structs::*;
use crate::cli_v2::*;
impl Cli {
pub async fn cmd_meta(&self, cmd: MetaOperation) -> Result<(), Error> {
let MetaOperation::Snapshot { all } = cmd;
let res = self
.api_request(CreateMetadataSnapshotRequest {
node: if all {
"*".to_string()
} else {
hex::encode(self.rpc_host)
},
body: LocalCreateMetadataSnapshotRequest,
})
.await?;
let mut table = vec![];
for (node, err) in res.error.iter() {
table.push(format!("{:.16}\tError: {}", node, err));
}
for (node, _) in res.success.iter() {
table.push(format!("{:.16}\tOk", node));
}
format_table(table);
Ok(())
}
}