aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/garage/admin/key.rs161
-rw-r--r--src/garage/admin/mod.rs14
-rw-r--r--src/garage/cli/cmd.rs6
-rw-r--r--src/garage/cli/util.rs85
-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
7 files changed, 247 insertions, 307 deletions
diff --git a/src/garage/admin/key.rs b/src/garage/admin/key.rs
deleted file mode 100644
index bd010d2c..00000000
--- a/src/garage/admin/key.rs
+++ /dev/null
@@ -1,161 +0,0 @@
-use std::collections::HashMap;
-
-use garage_table::*;
-
-use garage_model::helper::error::*;
-use garage_model::key_table::*;
-
-use crate::cli::*;
-
-use super::*;
-
-impl AdminRpcHandler {
- pub(super) async fn handle_key_cmd(&self, cmd: &KeyOperation) -> Result<AdminRpc, Error> {
- match cmd {
- KeyOperation::List => self.handle_list_keys().await,
- KeyOperation::Info(query) => self.handle_key_info(query).await,
- KeyOperation::Create(query) => self.handle_create_key(query).await,
- KeyOperation::Rename(query) => self.handle_rename_key(query).await,
- KeyOperation::Delete(query) => self.handle_delete_key(query).await,
- KeyOperation::Allow(query) => self.handle_allow_key(query).await,
- KeyOperation::Deny(query) => self.handle_deny_key(query).await,
- KeyOperation::Import(query) => self.handle_import_key(query).await,
- }
- }
-
- async fn handle_list_keys(&self) -> Result<AdminRpc, Error> {
- let key_ids = self
- .garage
- .key_table
- .get_range(
- &EmptyKey,
- None,
- Some(KeyFilter::Deleted(DeletedFilter::NotDeleted)),
- 10000,
- EnumerationOrder::Forward,
- )
- .await?
- .iter()
- .map(|k| (k.key_id.to_string(), k.params().unwrap().name.get().clone()))
- .collect::<Vec<_>>();
- Ok(AdminRpc::KeyList(key_ids))
- }
-
- async fn handle_key_info(&self, query: &KeyInfoOpt) -> Result<AdminRpc, Error> {
- let mut key = self
- .garage
- .key_helper()
- .get_existing_matching_key(&query.key_pattern)
- .await?;
-
- if !query.show_secret {
- key.state.as_option_mut().unwrap().secret_key = "(redacted)".into();
- }
-
- self.key_info_result(key).await
- }
-
- async fn handle_create_key(&self, query: &KeyNewOpt) -> Result<AdminRpc, Error> {
- let key = Key::new(&query.name);
- self.garage.key_table.insert(&key).await?;
- self.key_info_result(key).await
- }
-
- async fn handle_rename_key(&self, query: &KeyRenameOpt) -> Result<AdminRpc, Error> {
- let mut key = self
- .garage
- .key_helper()
- .get_existing_matching_key(&query.key_pattern)
- .await?;
- key.params_mut()
- .unwrap()
- .name
- .update(query.new_name.clone());
- self.garage.key_table.insert(&key).await?;
- self.key_info_result(key).await
- }
-
- async fn handle_delete_key(&self, query: &KeyDeleteOpt) -> Result<AdminRpc, Error> {
- let helper = self.garage.locked_helper().await;
-
- let mut key = helper
- .key()
- .get_existing_matching_key(&query.key_pattern)
- .await?;
-
- if !query.yes {
- return Err(Error::BadRequest(
- "Add --yes flag to really perform this operation".to_string(),
- ));
- }
-
- helper.delete_key(&mut key).await?;
-
- Ok(AdminRpc::Ok(format!(
- "Key {} was deleted successfully.",
- key.key_id
- )))
- }
-
- async fn handle_allow_key(&self, query: &KeyPermOpt) -> Result<AdminRpc, Error> {
- let mut key = self
- .garage
- .key_helper()
- .get_existing_matching_key(&query.key_pattern)
- .await?;
- if query.create_bucket {
- key.params_mut().unwrap().allow_create_bucket.update(true);
- }
- self.garage.key_table.insert(&key).await?;
- self.key_info_result(key).await
- }
-
- async fn handle_deny_key(&self, query: &KeyPermOpt) -> Result<AdminRpc, Error> {
- let mut key = self
- .garage
- .key_helper()
- .get_existing_matching_key(&query.key_pattern)
- .await?;
- if query.create_bucket {
- key.params_mut().unwrap().allow_create_bucket.update(false);
- }
- self.garage.key_table.insert(&key).await?;
- self.key_info_result(key).await
- }
-
- async fn handle_import_key(&self, query: &KeyImportOpt) -> Result<AdminRpc, Error> {
- if !query.yes {
- return Err(Error::BadRequest("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 prev_key = self.garage.key_table.get(&EmptyKey, &query.key_id).await?;
- if prev_key.is_some() {
- return Err(Error::BadRequest(format!("Key {} already exists in data store. Even if it is deleted, we can't let you create a new key with the same ID. Sorry.", query.key_id)));
- }
-
- let imported_key = Key::import(&query.key_id, &query.secret_key, &query.name)
- .ok_or_bad_request("Invalid key format")?;
- self.garage.key_table.insert(&imported_key).await?;
-
- self.key_info_result(imported_key).await
- }
-
- async fn key_info_result(&self, key: Key) -> Result<AdminRpc, Error> {
- let mut relevant_buckets = HashMap::new();
-
- for (id, _) in key
- .state
- .as_option()
- .unwrap()
- .authorized_buckets
- .items()
- .iter()
- {
- if let Some(b) = self.garage.bucket_table.get(&EmptyKey, id).await? {
- relevant_buckets.insert(*id, b);
- }
- }
-
- Ok(AdminRpc::KeyInfo(key, relevant_buckets))
- }
-}
diff --git a/src/garage/admin/mod.rs b/src/garage/admin/mod.rs
index aa528965..1888a208 100644
--- a/src/garage/admin/mod.rs
+++ b/src/garage/admin/mod.rs
@@ -1,6 +1,5 @@
mod block;
mod bucket;
-mod key;
use std::collections::HashMap;
use std::fmt::Write;
@@ -23,10 +22,8 @@ use garage_rpc::*;
use garage_block::manager::BlockResyncErrorInfo;
-use garage_model::bucket_table::*;
use garage_model::garage::Garage;
use garage_model::helper::error::{Error, OkOrBadRequest};
-use garage_model::key_table::*;
use garage_model::s3::mpu_table::MultipartUpload;
use garage_model::s3::version_table::Version;
@@ -43,7 +40,6 @@ pub const ADMIN_RPC_PATH: &str = "garage/admin_rpc.rs/Rpc";
#[allow(clippy::large_enum_variant)]
pub enum AdminRpc {
BucketOperation(BucketOperation),
- KeyOperation(KeyOperation),
LaunchRepair(RepairOpt),
Stats(StatsOpt),
Worker(WorkerOperation),
@@ -52,15 +48,6 @@ pub enum AdminRpc {
// Replies
Ok(String),
- BucketList(Vec<Bucket>),
- BucketInfo {
- bucket: Bucket,
- relevant_keys: HashMap<String, Key>,
- counters: HashMap<String, i64>,
- mpu_counters: HashMap<String, i64>,
- },
- KeyList(Vec<(String, String)>),
- KeyInfo(Key, HashMap<Uuid, Bucket>),
WorkerList(
HashMap<usize, garage_util::background::WorkerInfo>,
WorkerListOpt,
@@ -546,7 +533,6 @@ impl EndpointHandler<AdminRpc> for AdminRpcHandler {
) -> Result<AdminRpc, Error> {
match message {
AdminRpc::BucketOperation(bo) => self.handle_bucket_cmd(bo).await,
- AdminRpc::KeyOperation(ko) => self.handle_key_cmd(ko).await,
AdminRpc::LaunchRepair(opt) => self.handle_launch_repair(opt.clone()).await,
AdminRpc::Stats(opt) => self.handle_stats(opt.clone()).await,
AdminRpc::Worker(wo) => self.handle_worker_cmd(wo).await,
diff --git a/src/garage/cli/cmd.rs b/src/garage/cli/cmd.rs
index debe7dec..a6540c65 100644
--- a/src/garage/cli/cmd.rs
+++ b/src/garage/cli/cmd.rs
@@ -17,12 +17,6 @@ pub async fn cmd_admin(
AdminRpc::Ok(msg) => {
println!("{}", msg);
}
- AdminRpc::KeyList(kl) => {
- print_key_list(kl);
- }
- AdminRpc::KeyInfo(key, rb) => {
- print_key_info(&key, &rb);
- }
AdminRpc::WorkerList(wi, wlo) => {
print_worker_list(wi, wlo);
}
diff --git a/src/garage/cli/util.rs b/src/garage/cli/util.rs
index acf7923e..a3a1480e 100644
--- a/src/garage/cli/util.rs
+++ b/src/garage/cli/util.rs
@@ -3,101 +3,16 @@ use std::time::Duration;
use format_table::format_table;
use garage_util::background::*;
-use garage_util::crdt::*;
use garage_util::data::*;
use garage_util::time::*;
use garage_block::manager::BlockResyncErrorInfo;
-use garage_model::bucket_table::*;
-use garage_model::key_table::*;
use garage_model::s3::mpu_table::MultipartUpload;
use garage_model::s3::version_table::*;
use crate::cli::structs::WorkerListOpt;
-pub fn print_key_list(kl: Vec<(String, String)>) {
- println!("List of keys:");
- let mut table = vec![];
- for key in kl {
- table.push(format!("\t{}\t{}", key.0, key.1));
- }
- format_table(table);
-}
-
-pub fn print_key_info(key: &Key, relevant_buckets: &HashMap<Uuid, Bucket>) {
- let bucket_global_aliases = |b: &Uuid| {
- if let Some(bucket) = relevant_buckets.get(b) {
- if let Some(p) = bucket.state.as_option() {
- return p
- .aliases
- .items()
- .iter()
- .filter(|(_, _, active)| *active)
- .map(|(a, _, _)| a.clone())
- .collect::<Vec<_>>()
- .join(", ");
- }
- }
-
- "".to_string()
- };
-
- match &key.state {
- Deletable::Present(p) => {
- println!("Key name: {}", p.name.get());
- println!("Key ID: {}", key.key_id);
- println!("Secret key: {}", p.secret_key);
- println!("Can create buckets: {}", p.allow_create_bucket.get());
- println!("\nKey-specific bucket aliases:");
- let mut table = vec![];
- for (alias_name, _, alias) in p.local_aliases.items().iter() {
- if let Some(bucket_id) = alias {
- table.push(format!(
- "\t{}\t{}\t{}",
- alias_name,
- bucket_global_aliases(bucket_id),
- hex::encode(bucket_id)
- ));
- }
- }
- format_table(table);
-
- println!("\nAuthorized buckets:");
- let mut table = vec![];
- for (bucket_id, perm) in p.authorized_buckets.items().iter() {
- if !perm.is_any() {
- continue;
- }
- let rflag = if perm.allow_read { "R" } else { " " };
- let wflag = if perm.allow_write { "W" } else { " " };
- let oflag = if perm.allow_owner { "O" } else { " " };
- let local_aliases = p
- .local_aliases
- .items()
- .iter()
- .filter(|(_, _, a)| *a == Some(*bucket_id))
- .map(|(a, _, _)| a.clone())
- .collect::<Vec<_>>()
- .join(", ");
- table.push(format!(
- "\t{}{}{}\t{}\t{}\t{:?}",
- rflag,
- wflag,
- oflag,
- bucket_global_aliases(bucket_id),
- local_aliases,
- bucket_id
- ));
- }
- format_table(table);
- }
- Deletable::Deleted => {
- println!("Key {} is deleted.", key.key_id);
- }
- }
-}
-
pub fn print_worker_list(wi: HashMap<usize, WorkerInfo>, wlo: WorkerListOpt) {
let mut wi = wi.into_iter().collect::<Vec<_>>();
wi.sort_by_key(|(tid, info)| {
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,