garage node configure --replace <old_node_id> <new_node_id>
This commit is contained in:
parent
e19e267b7e
commit
a1014224d3
@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use garage_util::error::Error;
|
use garage_util::error::Error;
|
||||||
|
use garage_util::data::UUID;
|
||||||
use garage_util::time::*;
|
use garage_util::time::*;
|
||||||
|
|
||||||
use garage_rpc::membership::*;
|
use garage_rpc::membership::*;
|
||||||
@ -82,6 +83,10 @@ pub struct ConfigureNodeOpt {
|
|||||||
/// Optionnal node tag
|
/// Optionnal node tag
|
||||||
#[structopt(short = "t", long = "tag")]
|
#[structopt(short = "t", long = "tag")]
|
||||||
tag: Option<String>,
|
tag: Option<String>,
|
||||||
|
|
||||||
|
/// Replaced node(s): list of node IDs that will be removed from the current cluster
|
||||||
|
#[structopt(long = "replace")]
|
||||||
|
replace: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(StructOpt, Debug)]
|
#[derive(StructOpt, Debug)]
|
||||||
@ -379,6 +384,24 @@ pub async fn cmd_status(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_matching_node(cand: impl std::iter::Iterator<Item=UUID>, pattern: &str) -> Result<UUID, Error> {
|
||||||
|
let mut candidates = vec![];
|
||||||
|
for c in cand {
|
||||||
|
if hex::encode(&c).starts_with(&pattern) {
|
||||||
|
candidates.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if candidates.len() != 1 {
|
||||||
|
Err(Error::Message(format!(
|
||||||
|
"{} nodes match '{}'",
|
||||||
|
candidates.len(),
|
||||||
|
pattern,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(candidates[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn cmd_configure(
|
pub async fn cmd_configure(
|
||||||
rpc_cli: RpcAddrClient<Message>,
|
rpc_cli: RpcAddrClient<Message>,
|
||||||
rpc_host: SocketAddr,
|
rpc_host: SocketAddr,
|
||||||
@ -392,18 +415,7 @@ pub async fn cmd_configure(
|
|||||||
resp => return Err(Error::Message(format!("Invalid RPC response: {:?}", resp))),
|
resp => return Err(Error::Message(format!("Invalid RPC response: {:?}", resp))),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut candidates = vec![];
|
let added_node = find_matching_node(status.iter().map(|x| x.id), &args.node_id)?;
|
||||||
for adv in status.iter() {
|
|
||||||
if hex::encode(&adv.id).starts_with(&args.node_id) {
|
|
||||||
candidates.push(adv.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if candidates.len() != 1 {
|
|
||||||
return Err(Error::Message(format!(
|
|
||||||
"{} matching nodes",
|
|
||||||
candidates.len()
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut config = match rpc_cli
|
let mut config = match rpc_cli
|
||||||
.call(&rpc_host, &Message::PullConfig, ADMIN_RPC_TIMEOUT)
|
.call(&rpc_host, &Message::PullConfig, ADMIN_RPC_TIMEOUT)
|
||||||
@ -413,7 +425,14 @@ pub async fn cmd_configure(
|
|||||||
resp => return Err(Error::Message(format!("Invalid RPC response: {:?}", resp))),
|
resp => return Err(Error::Message(format!("Invalid RPC response: {:?}", resp))),
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_entry = match config.members.get(&candidates[0]) {
|
for replaced in args.replace.iter() {
|
||||||
|
let replaced_node = find_matching_node(config.members.keys().cloned(), replaced)?;
|
||||||
|
if config.members.remove(&replaced_node).is_none() {
|
||||||
|
return Err(Error::Message(format!("Cannot replace node {:?} as it is not in current configuration", replaced_node)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_entry = match config.members.get(&added_node) {
|
||||||
None => NetworkConfigEntry {
|
None => NetworkConfigEntry {
|
||||||
datacenter: args
|
datacenter: args
|
||||||
.datacenter
|
.datacenter
|
||||||
@ -430,7 +449,7 @@ pub async fn cmd_configure(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
config.members.insert(candidates[0].clone(), new_entry);
|
config.members.insert(added_node, new_entry);
|
||||||
config.version += 1;
|
config.version += 1;
|
||||||
|
|
||||||
rpc_cli
|
rpc_cli
|
||||||
@ -456,27 +475,16 @@ pub async fn cmd_remove(
|
|||||||
resp => return Err(Error::Message(format!("Invalid RPC response: {:?}", resp))),
|
resp => return Err(Error::Message(format!("Invalid RPC response: {:?}", resp))),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut candidates = vec![];
|
let deleted_node = find_matching_node(config.members.keys().cloned(), &args.node_id)?;
|
||||||
for (key, _) in config.members.iter() {
|
|
||||||
if hex::encode(key).starts_with(&args.node_id) {
|
|
||||||
candidates.push(*key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if candidates.len() != 1 {
|
|
||||||
return Err(Error::Message(format!(
|
|
||||||
"{} matching nodes",
|
|
||||||
candidates.len()
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !args.yes {
|
if !args.yes {
|
||||||
return Err(Error::Message(format!(
|
return Err(Error::Message(format!(
|
||||||
"Add the flag --yes to really remove {:?} from the cluster",
|
"Add the flag --yes to really remove {:?} from the cluster",
|
||||||
candidates[0]
|
deleted_node
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
config.members.remove(&candidates[0]);
|
config.members.remove(&deleted_node);
|
||||||
config.version += 1;
|
config.version += 1;
|
||||||
|
|
||||||
rpc_cli
|
rpc_cli
|
||||||
|
Loading…
Reference in New Issue
Block a user