aboutsummaryrefslogtreecommitdiff
path: root/src/garage/cli_v2
diff options
context:
space:
mode:
Diffstat (limited to 'src/garage/cli_v2')
-rw-r--r--src/garage/cli_v2/cluster.rs52
-rw-r--r--src/garage/cli_v2/key.rs227
-rw-r--r--src/garage/cli_v2/mod.rs9
3 files changed, 247 insertions, 41 deletions
diff --git a/src/garage/cli_v2/cluster.rs b/src/garage/cli_v2/cluster.rs
index fa63960d..adaf9a25 100644
--- a/src/garage/cli_v2/cluster.rs
+++ b/src/garage/cli_v2/cluster.rs
@@ -43,41 +43,25 @@ impl Cli {
capacity = capacity_string(cfg.capacity),
data_avail = data_avail,
));
+ } else if adv.draining {
+ healthy_nodes.push(format!(
+ "{id:.16}\t{host}\t{addr}\t\t\tdraining metadata...",
+ id = adv.id,
+ host = host,
+ addr = addr,
+ ));
} else {
- /*
- let prev_role = layout
- .versions
- .iter()
- .rev()
- .find_map(|x| match x.roles.get(&adv.id) {
- Some(NodeRoleV(Some(cfg))) => Some(cfg),
- _ => None,
- });
- */
- let prev_role = Option::<NodeRoleResp>::None; //TODO
- if let Some(cfg) = prev_role {
- healthy_nodes.push(format!(
- "{id:.16}\t{host}\t{addr}\t[{tags}]\t{zone}\tdraining metadata...",
- id = adv.id,
- host = host,
- addr = addr,
- tags = cfg.tags.join(","),
- zone = cfg.zone,
- ));
- } else {
- let new_role = match layout.staged_role_changes.iter().find(|x| x.id == adv.id)
- {
- Some(_) => "pending...",
- _ => "NO ROLE ASSIGNED",
- };
- healthy_nodes.push(format!(
- "{id:.16}\t{h}\t{addr}\t\t\t{new_role}",
- id = adv.id,
- h = host,
- addr = addr,
- new_role = new_role,
- ));
- }
+ let new_role = match layout.staged_role_changes.iter().find(|x| x.id == adv.id) {
+ Some(_) => "pending...",
+ _ => "NO ROLE ASSIGNED",
+ };
+ healthy_nodes.push(format!(
+ "{id:.16}\t{h}\t{addr}\t\t\t{new_role}",
+ id = adv.id,
+ h = host,
+ addr = addr,
+ new_role = new_role,
+ ));
}
}
format_table(healthy_nodes);
diff --git a/src/garage/cli_v2/key.rs b/src/garage/cli_v2/key.rs
new file mode 100644
index 00000000..ff403a9a
--- /dev/null
+++ b/src/garage/cli_v2/key.rs
@@ -0,0 +1,227 @@
+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_key(&self, cmd: KeyOperation) -> Result<(), Error> {
+ match cmd {
+ KeyOperation::List => self.cmd_list_keys().await,
+ KeyOperation::Info(query) => self.cmd_key_info(query).await,
+ KeyOperation::Create(query) => self.cmd_create_key(query).await,
+ KeyOperation::Rename(query) => self.cmd_rename_key(query).await,
+ KeyOperation::Delete(query) => self.cmd_delete_key(query).await,
+ KeyOperation::Allow(query) => self.cmd_allow_key(query).await,
+ KeyOperation::Deny(query) => self.cmd_deny_key(query).await,
+ KeyOperation::Import(query) => self.cmd_import_key(query).await,
+ }
+ }
+
+ pub async fn cmd_list_keys(&self) -> Result<(), Error> {
+ let keys = self.api_request(ListKeysRequest).await?;
+
+ println!("List of keys:");
+ let mut table = vec![];
+ for key in keys.0.iter() {
+ table.push(format!("\t{}\t{}", key.id, key.name));
+ }
+ format_table(table);
+
+ Ok(())
+ }
+
+ pub async fn cmd_key_info(&self, opt: KeyInfoOpt) -> Result<(), Error> {
+ let key = self
+ .api_request(GetKeyInfoRequest {
+ id: None,
+ search: Some(opt.key_pattern),
+ show_secret_key: opt.show_secret,
+ })
+ .await?;
+
+ print_key_info(&key);
+
+ Ok(())
+ }
+
+ pub async fn cmd_create_key(&self, opt: KeyNewOpt) -> Result<(), Error> {
+ let key = self
+ .api_request(CreateKeyRequest {
+ name: Some(opt.name),
+ })
+ .await?;
+
+ print_key_info(&key.0);
+
+ Ok(())
+ }
+
+ pub async fn cmd_rename_key(&self, opt: KeyRenameOpt) -> Result<(), Error> {
+ let key = self
+ .api_request(GetKeyInfoRequest {
+ id: None,
+ search: Some(opt.key_pattern),
+ show_secret_key: false,
+ })
+ .await?;
+
+ let new_key = self
+ .api_request(UpdateKeyRequest {
+ id: key.access_key_id,
+ body: UpdateKeyRequestBody {
+ name: Some(opt.new_name),
+ allow: None,
+ deny: None,
+ },
+ })
+ .await?;
+
+ print_key_info(&new_key.0);
+
+ Ok(())
+ }
+
+ pub async fn cmd_delete_key(&self, opt: KeyDeleteOpt) -> Result<(), Error> {
+ let key = self
+ .api_request(GetKeyInfoRequest {
+ id: None,
+ search: Some(opt.key_pattern),
+ show_secret_key: false,
+ })
+ .await?;
+
+ if !opt.yes {
+ println!("About to delete key {}...", key.access_key_id);
+ return Err(Error::Message(
+ "Add --yes flag to really perform this operation".to_string(),
+ ));
+ }
+
+ self.api_request(DeleteKeyRequest {
+ id: key.access_key_id.clone(),
+ })
+ .await?;
+
+ println!("Access key {} has been deleted.", key.access_key_id);
+
+ Ok(())
+ }
+
+ pub async fn cmd_allow_key(&self, opt: KeyPermOpt) -> Result<(), Error> {
+ let key = self
+ .api_request(GetKeyInfoRequest {
+ id: None,
+ search: Some(opt.key_pattern),
+ show_secret_key: false,
+ })
+ .await?;
+
+ let new_key = self
+ .api_request(UpdateKeyRequest {
+ id: key.access_key_id,
+ body: UpdateKeyRequestBody {
+ name: None,
+ allow: Some(KeyPerm {
+ create_bucket: opt.create_bucket,
+ }),
+ deny: None,
+ },
+ })
+ .await?;
+
+ print_key_info(&new_key.0);
+
+ Ok(())
+ }
+
+ pub async fn cmd_deny_key(&self, opt: KeyPermOpt) -> Result<(), Error> {
+ let key = self
+ .api_request(GetKeyInfoRequest {
+ id: None,
+ search: Some(opt.key_pattern),
+ show_secret_key: false,
+ })
+ .await?;
+
+ let new_key = self
+ .api_request(UpdateKeyRequest {
+ id: key.access_key_id,
+ body: UpdateKeyRequestBody {
+ name: None,
+ allow: None,
+ deny: Some(KeyPerm {
+ create_bucket: opt.create_bucket,
+ }),
+ },
+ })
+ .await?;
+
+ print_key_info(&new_key.0);
+
+ Ok(())
+ }
+
+ pub async fn cmd_import_key(&self, opt: KeyImportOpt) -> Result<(), Error> {
+ if !opt.yes {
+ return Err(Error::Message("This command is intended to re-import keys that were previously generated by Garage. If you want to create a new key, use `garage key new` instead. Add the --yes flag if you really want to re-import a key.".to_string()));
+ }
+
+ let new_key = self
+ .api_request(ImportKeyRequest {
+ name: Some(opt.name),
+ access_key_id: opt.key_id,
+ secret_access_key: opt.secret_key,
+ })
+ .await?;
+
+ print_key_info(&new_key.0);
+
+ Ok(())
+ }
+}
+
+fn print_key_info(key: &GetKeyInfoResponse) {
+ println!("Key name: {}", key.name);
+ println!("Key ID: {}", key.access_key_id);
+ println!(
+ "Secret key: {}",
+ key.secret_access_key.as_deref().unwrap_or("(redacted)")
+ );
+ println!("Can create buckets: {}", key.permissions.create_bucket);
+
+ println!("\nKey-specific bucket aliases:");
+ let mut table = vec![];
+ for bucket in key.buckets.iter() {
+ for la in bucket.local_aliases.iter() {
+ table.push(format!(
+ "\t{}\t{}\t{}",
+ la,
+ bucket.global_aliases.join(","),
+ bucket.id
+ ));
+ }
+ }
+ format_table(table);
+
+ println!("\nAuthorized buckets:");
+ let mut table = vec![];
+ for bucket in key.buckets.iter() {
+ let rflag = if bucket.permissions.read { "R" } else { " " };
+ let wflag = if bucket.permissions.write { "W" } else { " " };
+ let oflag = if bucket.permissions.owner { "O" } else { " " };
+ table.push(format!(
+ "\t{}{}{}\t{}\t{}\t{:.16}",
+ rflag,
+ wflag,
+ oflag,
+ bucket.global_aliases.join(","),
+ bucket.local_aliases.join(","),
+ bucket.id
+ ));
+ }
+ format_table(table);
+}
diff --git a/src/garage/cli_v2/mod.rs b/src/garage/cli_v2/mod.rs
index 24ff6f72..e6d2d8c6 100644
--- a/src/garage/cli_v2/mod.rs
+++ b/src/garage/cli_v2/mod.rs
@@ -2,6 +2,7 @@ pub mod util;
pub mod bucket;
pub mod cluster;
+pub mod key;
pub mod layout;
use std::collections::{HashMap, HashSet};
@@ -37,15 +38,9 @@ impl Cli {
}
Command::Layout(layout_opt) => self.layout_command_dispatch(layout_opt).await,
Command::Bucket(bo) => self.cmd_bucket(bo).await,
+ Command::Key(ko) => self.cmd_key(ko).await,
// TODO
- Command::Key(ko) => cli_v1::cmd_admin(
- &self.admin_rpc_endpoint,
- self.rpc_host,
- AdminRpc::KeyOperation(ko),
- )
- .await
- .ok_or_message("xoxo"),
Command::Repair(ro) => cli_v1::cmd_admin(
&self.admin_rpc_endpoint,
self.rpc_host,