-
Notifications
You must be signed in to change notification settings - Fork 323
/
rm.rs
168 lines (136 loc) · 4.68 KB
/
rm.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
use std::{io, io::Write, ops::Deref};
use crate::prelude::*;
use abscissa_core::{application::fatal_error, error::BoxError, Command, Options, Runnable};
use ibc::ics24_host::identifier::ChainId;
use relayer::config::PeersConfig;
use tendermint_light_client::types::PeerId;
#[derive(Command, Debug, Options)]
pub struct RmCmd {
/// identifiers of peers to remove
#[options(free)]
peer_id: Vec<PeerId>,
/// identifier of the chain to remove peers from
#[options(short = "c")]
chain_id: Option<ChainId>,
/// force removal of primary peer
#[options(short = "f")]
force: bool,
/// remove all peers, implies --force
#[options(no_short)]
all: bool,
/// skip confirmation
yes: bool,
}
#[derive(Clone, Debug)]
struct RmOptions {
/// identifier of the chain to remove peers from
chain_id: ChainId,
/// identifiers of peers to remove
peer_ids: Vec<PeerId>,
/// remove all peers, implies --force
all: bool,
/// force removal of primary peer
force: bool,
/// skip confirmation
yes: bool,
}
impl RmOptions {
fn from_cmd(cmd: &RmCmd) -> Result<RmOptions, BoxError> {
let chain_id = cmd.chain_id.clone().ok_or("missing chain identifier")?;
let peer_ids = if !cmd.all && cmd.peer_id.is_empty() {
return Err("missing peer identifier".into());
} else {
cmd.peer_id.clone()
};
Ok(RmOptions {
chain_id,
peer_ids,
all: cmd.all,
yes: cmd.yes,
force: cmd.force,
})
}
}
impl Runnable for RmCmd {
fn run(&self) {
self.cmd()
.unwrap_or_else(|e| fatal_error(app_reader().deref(), &*e))
}
}
impl RmCmd {
fn cmd(&self) -> Result<(), BoxError> {
let options = RmOptions::from_cmd(self).map_err(|e| format!("invalid options: {}", e))?;
let mut config = (*app_config()).clone();
let chain_config = config
.chains
.iter_mut()
.find(|c| c.id == options.chain_id)
.ok_or_else(|| format!("could not find config for chain: {}", options.chain_id))?;
let mut peers_config = chain_config
.peers
.as_mut()
.ok_or_else(|| format!("no peers configured for chain: {}", options.chain_id))?;
if options.all && (options.yes || confirm(&options.chain_id)?) {
let removed_peers = get_all_peer_ids(&peers_config);
chain_config.peers = None;
status_ok!("Removed", "light client peers {:?}", removed_peers);
} else {
for peer_id in options.peer_ids {
let removed_peer = remove_peer(&mut peers_config, peer_id, options.force)?;
status_ok!("Removed", "light client peer '{}'", removed_peer);
}
};
let config_path = crate::config::config_path()?;
relayer::config::store(&config, config_path)?;
Ok(())
}
}
fn remove_peer(
peers_config: &mut PeersConfig,
peer_id: PeerId,
force: bool,
) -> Result<PeerId, BoxError> {
// Check if the given peer actually exists already, if not throw an error.
let peer_exists = peers_config.light_client(peer_id).is_some();
if !peer_exists {
return Err(format!("cannot find peer: {}", peer_id).into());
}
// Only allow remove the primary peer if the --force option is set
let removed_primary = peers_config.primary == peer_id;
if removed_primary && !force {
return Err("cannot remove primary peer, pass --force flag to force removal".into());
}
// Filter out the light client config with the specified peer id
peers_config.light_clients.retain(|p| p.peer_id != peer_id);
// If the peer we removed was the primary peer, use the next available peer as the primary,
// if any.
if removed_primary {
if let Some(new_primary) = peers_config.light_clients.first() {
peers_config.primary = new_primary.peer_id;
}
}
Ok(peer_id)
}
fn get_all_peer_ids(peers_config: &PeersConfig) -> Vec<String> {
peers_config
.light_clients
.iter()
.map(|c| c.peer_id.to_string())
.collect()
}
fn confirm(chain_id: &ChainId) -> Result<bool, BoxError> {
loop {
print!(
"\n? Do you really want to remove all peers for chain '{}'? (y/n) > ",
chain_id
);
io::stdout().flush()?; // need to flush stdout since stdout is often line-buffered
let mut choice = String::new();
io::stdin().read_line(&mut choice)?;
match choice.trim_end() {
"y" | "yes" => return Ok(true),
"n" | "no" => return Ok(false),
_ => continue,
}
}
}