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 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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)| {
|
||||
|
|
|
@ -43,30 +43,15 @@ impl Cli {
|
|||
capacity = capacity_string(cfg.capacity),
|
||||
data_avail = data_avail,
|
||||
));
|
||||
} 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 {
|
||||
} else if adv.draining {
|
||||
healthy_nodes.push(format!(
|
||||
"{id:.16}\t{host}\t{addr}\t[{tags}]\t{zone}\tdraining metadata...",
|
||||
"{id:.16}\t{host}\t{addr}\t\t\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)
|
||||
{
|
||||
let new_role = match layout.staged_role_changes.iter().find(|x| x.id == adv.id) {
|
||||
Some(_) => "pending...",
|
||||
_ => "NO ROLE ASSIGNED",
|
||||
};
|
||||
|
@ -79,7 +64,6 @@ impl Cli {
|
|||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
format_table(healthy_nodes);
|
||||
|
||||
// Determine which nodes are unhealthy and print that to stdout
|
||||
|
|
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 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,
|
||||
|
|
Loading…
Add table
Reference in a new issue