aboutsummaryrefslogtreecommitdiff
path: root/src/garage/cli_v2/layout.rs
blob: ccd1886f0d4b53aa570df619cc9bb608f219cbc1 (plain) (blame)
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
use bytesize::ByteSize;
use format_table::format_table;

use garage_util::error::*;

use garage_api::admin::api::*;

use crate::cli::layout as cli_v1;
use crate::cli::structs::*;
use crate::cli_v2::util::*;
use crate::cli_v2::*;

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,

			// 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
			}
			LayoutOperation::History => {
				cli_v1::cmd_layout_history(&self.system_rpc_endpoint, self.rpc_host).await
			}
			LayoutOperation::SkipDeadNodes(assume_sync_opt) => {
				cli_v1::cmd_layout_skip_dead_nodes(
					&self.system_rpc_endpoint,
					self.rpc_host,
					assume_sync_opt,
				)
				.await
			}
		}
	}

	pub async fn cmd_assign_role(&self, opt: AssignRoleOpt) -> 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 mut actions = vec![];

		for node in opt.replace.iter() {
			let id = find_matching_node(all_node_ids_iter.clone(), &node)?;

			actions.push(NodeRoleChange {
				id,
				action: NodeRoleChangeEnum::Remove { remove: true },
			});
		}

		for node in opt.node_ids.iter() {
			let id = find_matching_node(all_node_ids_iter.clone(), &node)?;

			let current = get_staged_or_current_role(&id, &layout);

			let zone = opt
				.zone
				.clone()
				.or_else(|| current.as_ref().map(|c| c.zone.clone()))
				.ok_or_message("Please specify a zone with the -z flag")?;

			let capacity = if opt.gateway {
				if opt.capacity.is_some() {
					return Err(Error::Message("Please specify only -c or -g".into()));
				}
				None
			} else if let Some(cap) = opt.capacity {
				Some(cap.as_u64())
			} else {
				current.as_ref().ok_or_message("Please specify a capacity with the -c flag, or set node explicitly as gateway with -g")?.capacity
			};

			let tags = if !opt.tags.is_empty() {
				opt.tags.clone()
			} else if let Some(cur) = current.as_ref() {
				cur.tags.clone()
			} else {
				vec![]
			};

			actions.push(NodeRoleChange {
				id,
				action: NodeRoleChangeEnum::Update {
					zone,
					capacity,
					tags,
				},
			});
		}

		self.api_request(UpdateClusterLayoutRequest(actions))
			.await?;

		println!("Role changes are 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(())
	}
}