From 819f4f00509a57097d0ee8291e1556829e982e14 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Thu, 30 Jan 2025 12:19:23 +0100 Subject: [PATCH] cli: migrate layout remove, apply, revert --- src/garage/cli/layout.rs | 69 --------------------------------- src/garage/cli/util.rs | 21 ---------- src/garage/cli_v2/layout.rs | 77 +++++++++++++++++++++++++++++++------ 3 files changed, 65 insertions(+), 102 deletions(-) diff --git a/src/garage/cli/layout.rs b/src/garage/cli/layout.rs index d0b62fc7..bb81d144 100644 --- a/src/garage/cli/layout.rs +++ b/src/garage/cli/layout.rs @@ -1,7 +1,6 @@ use bytesize::ByteSize; use format_table::format_table; -use garage_util::crdt::Crdt; use garage_util::error::*; use garage_rpc::layout::*; @@ -10,33 +9,6 @@ use garage_rpc::*; use crate::cli::*; -pub async fn cmd_remove_role( - rpc_cli: &Endpoint, - rpc_host: NodeID, - args: RemoveRoleOpt, -) -> Result<(), Error> { - let mut layout = fetch_layout(rpc_cli, rpc_host).await?; - - let mut roles = layout.current().roles.clone(); - roles.merge(&layout.staging.get().roles); - - let deleted_node = - find_matching_node(roles.items().iter().map(|(id, _, _)| *id), &args.node_id)?; - - layout - .staging - .get_mut() - .roles - .merge(&roles.update_mutator(deleted_node, NodeRoleV(None))); - - send_layout(rpc_cli, rpc_host, layout).await?; - - println!("Role removal is staged but not yet committed."); - println!("Use `garage layout show` to view staged role changes,"); - println!("and `garage layout apply` to enact staged changes."); - Ok(()) -} - pub async fn cmd_show_layout( rpc_cli: &Endpoint, rpc_host: NodeID, @@ -85,47 +57,6 @@ pub async fn cmd_show_layout( Ok(()) } -pub async fn cmd_apply_layout( - rpc_cli: &Endpoint, - rpc_host: NodeID, - apply_opt: ApplyLayoutOpt, -) -> Result<(), Error> { - let layout = fetch_layout(rpc_cli, rpc_host).await?; - - let (layout, msg) = layout.apply_staged_changes(apply_opt.version)?; - for line in msg.iter() { - println!("{}", line); - } - - send_layout(rpc_cli, rpc_host, layout).await?; - - println!("New cluster layout with updated role assignment has been applied in cluster."); - println!("Data will now be moved around between nodes accordingly."); - - Ok(()) -} - -pub async fn cmd_revert_layout( - rpc_cli: &Endpoint, - rpc_host: NodeID, - revert_opt: RevertLayoutOpt, -) -> Result<(), Error> { - if !revert_opt.yes { - return Err(Error::Message( - "Please add the --yes flag to run the layout revert operation".into(), - )); - } - - let layout = fetch_layout(rpc_cli, rpc_host).await?; - - let layout = layout.revert_staged_changes()?; - - send_layout(rpc_cli, rpc_host, layout).await?; - - println!("All proposed role changes in cluster layout have been canceled."); - Ok(()) -} - pub async fn cmd_config_layout( rpc_cli: &Endpoint, rpc_host: NodeID, diff --git a/src/garage/cli/util.rs b/src/garage/cli/util.rs index 21c14f42..c591cadd 100644 --- a/src/garage/cli/util.rs +++ b/src/garage/cli/util.rs @@ -233,27 +233,6 @@ pub fn print_bucket_info( }; } -pub fn find_matching_node( - cand: impl std::iter::Iterator, - pattern: &str, -) -> Result { - let mut candidates = vec![]; - for c in cand { - if hex::encode(c).starts_with(pattern) && !candidates.contains(&c) { - candidates.push(c); - } - } - if candidates.len() != 1 { - Err(Error::Message(format!( - "{} nodes match '{}'", - candidates.len(), - pattern, - ))) - } else { - Ok(candidates[0]) - } -} - pub fn print_worker_list(wi: HashMap, wlo: WorkerListOpt) { let mut wi = wi.into_iter().collect::>(); wi.sort_by_key(|(tid, info)| { diff --git a/src/garage/cli_v2/layout.rs b/src/garage/cli_v2/layout.rs index ccd1886f..8088f019 100644 --- a/src/garage/cli_v2/layout.rs +++ b/src/garage/cli_v2/layout.rs @@ -1,5 +1,5 @@ -use bytesize::ByteSize; -use format_table::format_table; +//use bytesize::ByteSize; +//use format_table::format_table; use garage_util::error::*; @@ -14,21 +14,14 @@ impl Cli { pub async fn layout_command_dispatch(&self, cmd: LayoutOperation) -> Result<(), Error> { match cmd { LayoutOperation::Assign(assign_opt) => self.cmd_assign_role(assign_opt).await, + LayoutOperation::Remove(remove_opt) => self.cmd_remove_role(remove_opt).await, + LayoutOperation::Apply(apply_opt) => self.cmd_apply_layout(apply_opt).await, + LayoutOperation::Revert(revert_opt) => self.cmd_revert_layout(revert_opt).await, // TODO - LayoutOperation::Remove(remove_opt) => { - cli_v1::cmd_remove_role(&self.system_rpc_endpoint, self.rpc_host, remove_opt).await - } LayoutOperation::Show => { cli_v1::cmd_show_layout(&self.system_rpc_endpoint, self.rpc_host).await } - LayoutOperation::Apply(apply_opt) => { - cli_v1::cmd_apply_layout(&self.system_rpc_endpoint, self.rpc_host, apply_opt).await - } - LayoutOperation::Revert(revert_opt) => { - cli_v1::cmd_revert_layout(&self.system_rpc_endpoint, self.rpc_host, revert_opt) - .await - } LayoutOperation::Config(config_opt) => { cli_v1::cmd_config_layout(&self.system_rpc_endpoint, self.rpc_host, config_opt) .await @@ -116,4 +109,64 @@ impl Cli { println!("and `garage layout apply` to enact staged changes."); Ok(()) } + + pub async fn cmd_remove_role(&self, opt: RemoveRoleOpt) -> Result<(), Error> { + let status = self.api_request(GetClusterStatusRequest).await?; + let layout = self.api_request(GetClusterLayoutRequest).await?; + + let all_node_ids_iter = status + .nodes + .iter() + .map(|x| x.id.as_str()) + .chain(layout.roles.iter().map(|x| x.id.as_str())); + + let id = find_matching_node(all_node_ids_iter.clone(), &opt.node_id)?; + + let actions = vec![NodeRoleChange { + id, + action: NodeRoleChangeEnum::Remove { remove: true }, + }]; + + self.api_request(UpdateClusterLayoutRequest(actions)) + .await?; + + println!("Role removal is staged but not yet committed."); + println!("Use `garage layout show` to view staged role changes,"); + println!("and `garage layout apply` to enact staged changes."); + Ok(()) + } + + pub async fn cmd_apply_layout(&self, apply_opt: ApplyLayoutOpt) -> Result<(), Error> { + let missing_version_error = r#" +Please pass the new layout version number to ensure that you are writing the correct version of the cluster layout. +To know the correct value of the new layout version, invoke `garage layout show` and review the proposed changes. + "#; + + let req = ApplyClusterLayoutRequest { + version: apply_opt.version.ok_or_message(missing_version_error)?, + }; + let res = self.api_request(req).await?; + + for line in res.message.iter() { + println!("{}", line); + } + + println!("New cluster layout with updated role assignment has been applied in cluster."); + println!("Data will now be moved around between nodes accordingly."); + + Ok(()) + } + + pub async fn cmd_revert_layout(&self, revert_opt: RevertLayoutOpt) -> Result<(), Error> { + if !revert_opt.yes { + return Err(Error::Message( + "Please add the --yes flag to run the layout revert operation".into(), + )); + } + + self.api_request(RevertClusterLayoutRequest).await?; + + println!("All proposed role changes in cluster layout have been canceled."); + Ok(()) + } }