convert cli key operations to admin rpc
This commit is contained in:
parent
076ce04fe5
commit
f8c6a8373d
7 changed files with 247 additions and 307 deletions
|
@ -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))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
mod block;
|
mod block;
|
||||||
mod bucket;
|
mod bucket;
|
||||||
mod key;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
@ -23,10 +22,8 @@ use garage_rpc::*;
|
||||||
|
|
||||||
use garage_block::manager::BlockResyncErrorInfo;
|
use garage_block::manager::BlockResyncErrorInfo;
|
||||||
|
|
||||||
use garage_model::bucket_table::*;
|
|
||||||
use garage_model::garage::Garage;
|
use garage_model::garage::Garage;
|
||||||
use garage_model::helper::error::{Error, OkOrBadRequest};
|
use garage_model::helper::error::{Error, OkOrBadRequest};
|
||||||
use garage_model::key_table::*;
|
|
||||||
use garage_model::s3::mpu_table::MultipartUpload;
|
use garage_model::s3::mpu_table::MultipartUpload;
|
||||||
use garage_model::s3::version_table::Version;
|
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)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum AdminRpc {
|
pub enum AdminRpc {
|
||||||
BucketOperation(BucketOperation),
|
BucketOperation(BucketOperation),
|
||||||
KeyOperation(KeyOperation),
|
|
||||||
LaunchRepair(RepairOpt),
|
LaunchRepair(RepairOpt),
|
||||||
Stats(StatsOpt),
|
Stats(StatsOpt),
|
||||||
Worker(WorkerOperation),
|
Worker(WorkerOperation),
|
||||||
|
@ -52,15 +48,6 @@ pub enum AdminRpc {
|
||||||
|
|
||||||
// Replies
|
// Replies
|
||||||
Ok(String),
|
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(
|
WorkerList(
|
||||||
HashMap<usize, garage_util::background::WorkerInfo>,
|
HashMap<usize, garage_util::background::WorkerInfo>,
|
||||||
WorkerListOpt,
|
WorkerListOpt,
|
||||||
|
@ -546,7 +533,6 @@ impl EndpointHandler<AdminRpc> for AdminRpcHandler {
|
||||||
) -> Result<AdminRpc, Error> {
|
) -> Result<AdminRpc, Error> {
|
||||||
match message {
|
match message {
|
||||||
AdminRpc::BucketOperation(bo) => self.handle_bucket_cmd(bo).await,
|
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::LaunchRepair(opt) => self.handle_launch_repair(opt.clone()).await,
|
||||||
AdminRpc::Stats(opt) => self.handle_stats(opt.clone()).await,
|
AdminRpc::Stats(opt) => self.handle_stats(opt.clone()).await,
|
||||||
AdminRpc::Worker(wo) => self.handle_worker_cmd(wo).await,
|
AdminRpc::Worker(wo) => self.handle_worker_cmd(wo).await,
|
||||||
|
|
|
@ -17,12 +17,6 @@ pub async fn cmd_admin(
|
||||||
AdminRpc::Ok(msg) => {
|
AdminRpc::Ok(msg) => {
|
||||||
println!("{}", msg);
|
println!("{}", msg);
|
||||||
}
|
}
|
||||||
AdminRpc::KeyList(kl) => {
|
|
||||||
print_key_list(kl);
|
|
||||||
}
|
|
||||||
AdminRpc::KeyInfo(key, rb) => {
|
|
||||||
print_key_info(&key, &rb);
|
|
||||||
}
|
|
||||||
AdminRpc::WorkerList(wi, wlo) => {
|
AdminRpc::WorkerList(wi, wlo) => {
|
||||||
print_worker_list(wi, wlo);
|
print_worker_list(wi, wlo);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,101 +3,16 @@ use std::time::Duration;
|
||||||
|
|
||||||
use format_table::format_table;
|
use format_table::format_table;
|
||||||
use garage_util::background::*;
|
use garage_util::background::*;
|
||||||
use garage_util::crdt::*;
|
|
||||||
use garage_util::data::*;
|
use garage_util::data::*;
|
||||||
use garage_util::time::*;
|
use garage_util::time::*;
|
||||||
|
|
||||||
use garage_block::manager::BlockResyncErrorInfo;
|
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::mpu_table::MultipartUpload;
|
||||||
use garage_model::s3::version_table::*;
|
use garage_model::s3::version_table::*;
|
||||||
|
|
||||||
use crate::cli::structs::WorkerListOpt;
|
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) {
|
pub fn print_worker_list(wi: HashMap<usize, WorkerInfo>, wlo: WorkerListOpt) {
|
||||||
let mut wi = wi.into_iter().collect::<Vec<_>>();
|
let mut wi = wi.into_iter().collect::<Vec<_>>();
|
||||||
wi.sort_by_key(|(tid, info)| {
|
wi.sort_by_key(|(tid, info)| {
|
||||||
|
|
|
@ -43,41 +43,25 @@ impl Cli {
|
||||||
capacity = capacity_string(cfg.capacity),
|
capacity = capacity_string(cfg.capacity),
|
||||||
data_avail = data_avail,
|
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 {
|
} else {
|
||||||
/*
|
let new_role = match layout.staged_role_changes.iter().find(|x| x.id == adv.id) {
|
||||||
let prev_role = layout
|
Some(_) => "pending...",
|
||||||
.versions
|
_ => "NO ROLE ASSIGNED",
|
||||||
.iter()
|
};
|
||||||
.rev()
|
healthy_nodes.push(format!(
|
||||||
.find_map(|x| match x.roles.get(&adv.id) {
|
"{id:.16}\t{h}\t{addr}\t\t\t{new_role}",
|
||||||
Some(NodeRoleV(Some(cfg))) => Some(cfg),
|
id = adv.id,
|
||||||
_ => None,
|
h = host,
|
||||||
});
|
addr = addr,
|
||||||
*/
|
new_role = new_role,
|
||||||
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,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
format_table(healthy_nodes);
|
format_table(healthy_nodes);
|
||||||
|
|
227
src/garage/cli_v2/key.rs
Normal file
227
src/garage/cli_v2/key.rs
Normal file
|
@ -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);
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ pub mod util;
|
||||||
|
|
||||||
pub mod bucket;
|
pub mod bucket;
|
||||||
pub mod cluster;
|
pub mod cluster;
|
||||||
|
pub mod key;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
@ -37,15 +38,9 @@ impl Cli {
|
||||||
}
|
}
|
||||||
Command::Layout(layout_opt) => self.layout_command_dispatch(layout_opt).await,
|
Command::Layout(layout_opt) => self.layout_command_dispatch(layout_opt).await,
|
||||||
Command::Bucket(bo) => self.cmd_bucket(bo).await,
|
Command::Bucket(bo) => self.cmd_bucket(bo).await,
|
||||||
|
Command::Key(ko) => self.cmd_key(ko).await,
|
||||||
|
|
||||||
// TODO
|
// 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(
|
Command::Repair(ro) => cli_v1::cmd_admin(
|
||||||
&self.admin_rpc_endpoint,
|
&self.admin_rpc_endpoint,
|
||||||
self.rpc_host,
|
self.rpc_host,
|
||||||
|
|
Loading…
Add table
Reference in a new issue