New model for buckets #172
3 changed files with 147 additions and 35 deletions
|
@ -38,9 +38,9 @@ pub enum AdminRpc {
|
||||||
// Replies
|
// Replies
|
||||||
Ok(String),
|
Ok(String),
|
||||||
BucketList(Vec<BucketAlias>),
|
BucketList(Vec<BucketAlias>),
|
||||||
BucketInfo(Bucket),
|
BucketInfo(Bucket, HashMap<String, Key>),
|
||||||
KeyList(Vec<(String, String)>),
|
KeyList(Vec<(String, String)>),
|
||||||
KeyInfo(Key),
|
KeyInfo(Key, HashMap<Uuid, Bucket>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rpc for AdminRpc {
|
impl Rpc for AdminRpc {
|
||||||
|
@ -63,20 +63,7 @@ impl AdminRpcHandler {
|
||||||
async fn handle_bucket_cmd(&self, cmd: &BucketOperation) -> Result<AdminRpc, Error> {
|
async fn handle_bucket_cmd(&self, cmd: &BucketOperation) -> Result<AdminRpc, Error> {
|
||||||
match cmd {
|
match cmd {
|
||||||
BucketOperation::List => self.handle_list_buckets().await,
|
BucketOperation::List => self.handle_list_buckets().await,
|
||||||
BucketOperation::Info(query) => {
|
BucketOperation::Info(query) => self.handle_bucket_info(query).await,
|
||||||
let bucket_id = self
|
|
||||||
.garage
|
|
||||||
.bucket_helper()
|
|
||||||
.resolve_global_bucket_name(&query.name)
|
|
||||||
.await?
|
|
||||||
.ok_or_message("Bucket not found")?;
|
|
||||||
let bucket = self
|
|
||||||
.garage
|
|
||||||
.bucket_helper()
|
|
||||||
.get_existing_bucket(bucket_id)
|
|
||||||
.await?;
|
|
||||||
Ok(AdminRpc::BucketInfo(bucket))
|
|
||||||
}
|
|
||||||
BucketOperation::Create(query) => self.handle_create_bucket(&query.name).await,
|
BucketOperation::Create(query) => self.handle_create_bucket(&query.name).await,
|
||||||
BucketOperation::Delete(query) => self.handle_delete_bucket(query).await,
|
BucketOperation::Delete(query) => self.handle_delete_bucket(query).await,
|
||||||
BucketOperation::Alias(query) => self.handle_alias_bucket(query).await,
|
BucketOperation::Alias(query) => self.handle_alias_bucket(query).await,
|
||||||
|
@ -96,6 +83,52 @@ impl AdminRpcHandler {
|
||||||
Ok(AdminRpc::BucketList(bucket_aliases))
|
Ok(AdminRpc::BucketList(bucket_aliases))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_bucket_info(&self, query: &BucketOpt) -> Result<AdminRpc, Error> {
|
||||||
|
let bucket_id = self
|
||||||
|
.garage
|
||||||
|
.bucket_helper()
|
||||||
|
.resolve_global_bucket_name(&query.name)
|
||||||
|
.await?
|
||||||
|
.ok_or_message("Bucket not found")?;
|
||||||
|
|
||||||
|
let bucket = self
|
||||||
|
.garage
|
||||||
|
.bucket_helper()
|
||||||
|
.get_existing_bucket(bucket_id)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut relevant_keys = HashMap::new();
|
||||||
|
for (k, _) in bucket
|
||||||
|
.state
|
||||||
|
.as_option()
|
||||||
|
.unwrap()
|
||||||
|
.authorized_keys
|
||||||
|
.items()
|
||||||
|
.iter()
|
||||||
|
{
|
||||||
|
if let Some(key) = self.garage.key_table.get(&EmptyKey, k).await? {
|
||||||
|
relevant_keys.insert(k.clone(), key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ((k, _), _, _) in bucket
|
||||||
|
.state
|
||||||
|
.as_option()
|
||||||
|
.unwrap()
|
||||||
|
.local_aliases
|
||||||
|
.items()
|
||||||
|
.iter()
|
||||||
|
{
|
||||||
|
if relevant_keys.contains_key(k) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(key) = self.garage.key_table.get(&EmptyKey, k).await? {
|
||||||
|
relevant_keys.insert(k.clone(), key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(AdminRpc::BucketInfo(bucket, relevant_keys))
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::ptr_arg)]
|
#[allow(clippy::ptr_arg)]
|
||||||
async fn handle_create_bucket(&self, name: &String) -> Result<AdminRpc, Error> {
|
async fn handle_create_bucket(&self, name: &String) -> Result<AdminRpc, Error> {
|
||||||
let mut bucket = Bucket::new();
|
let mut bucket = Bucket::new();
|
||||||
|
@ -476,10 +509,7 @@ impl AdminRpcHandler {
|
||||||
async fn handle_key_cmd(&self, cmd: &KeyOperation) -> Result<AdminRpc, Error> {
|
async fn handle_key_cmd(&self, cmd: &KeyOperation) -> Result<AdminRpc, Error> {
|
||||||
match cmd {
|
match cmd {
|
||||||
KeyOperation::List => self.handle_list_keys().await,
|
KeyOperation::List => self.handle_list_keys().await,
|
||||||
KeyOperation::Info(query) => {
|
KeyOperation::Info(query) => self.handle_key_info(query).await,
|
||||||
let key = self.get_existing_key(&query.key_pattern).await?;
|
|
||||||
Ok(AdminRpc::KeyInfo(key))
|
|
||||||
}
|
|
||||||
KeyOperation::New(query) => self.handle_create_key(query).await,
|
KeyOperation::New(query) => self.handle_create_key(query).await,
|
||||||
KeyOperation::Rename(query) => self.handle_rename_key(query).await,
|
KeyOperation::Rename(query) => self.handle_rename_key(query).await,
|
||||||
KeyOperation::Delete(query) => self.handle_delete_key(query).await,
|
KeyOperation::Delete(query) => self.handle_delete_key(query).await,
|
||||||
|
@ -504,17 +534,22 @@ impl AdminRpcHandler {
|
||||||
Ok(AdminRpc::KeyList(key_ids))
|
Ok(AdminRpc::KeyList(key_ids))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_key_info(&self, query: &KeyOpt) -> Result<AdminRpc, Error> {
|
||||||
|
let key = self.get_existing_key(&query.key_pattern).await?;
|
||||||
|
self.key_info_result(key).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_create_key(&self, query: &KeyNewOpt) -> Result<AdminRpc, Error> {
|
async fn handle_create_key(&self, query: &KeyNewOpt) -> Result<AdminRpc, Error> {
|
||||||
let key = Key::new(query.name.clone());
|
let key = Key::new(query.name.clone());
|
||||||
self.garage.key_table.insert(&key).await?;
|
self.garage.key_table.insert(&key).await?;
|
||||||
Ok(AdminRpc::KeyInfo(key))
|
self.key_info_result(key).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_rename_key(&self, query: &KeyRenameOpt) -> Result<AdminRpc, Error> {
|
async fn handle_rename_key(&self, query: &KeyRenameOpt) -> Result<AdminRpc, Error> {
|
||||||
let mut key = self.get_existing_key(&query.key_pattern).await?;
|
let mut key = self.get_existing_key(&query.key_pattern).await?;
|
||||||
key.name.update(query.new_name.clone());
|
key.name.update(query.new_name.clone());
|
||||||
self.garage.key_table.insert(&key).await?;
|
self.garage.key_table.insert(&key).await?;
|
||||||
Ok(AdminRpc::KeyInfo(key))
|
self.key_info_result(key).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_delete_key(&self, query: &KeyDeleteOpt) -> Result<AdminRpc, Error> {
|
async fn handle_delete_key(&self, query: &KeyDeleteOpt) -> Result<AdminRpc, Error> {
|
||||||
|
@ -577,7 +612,8 @@ impl AdminRpcHandler {
|
||||||
}
|
}
|
||||||
let imported_key = Key::import(&query.key_id, &query.secret_key, &query.name);
|
let imported_key = Key::import(&query.key_id, &query.secret_key, &query.name);
|
||||||
self.garage.key_table.insert(&imported_key).await?;
|
self.garage.key_table.insert(&imported_key).await?;
|
||||||
Ok(AdminRpc::KeyInfo(imported_key))
|
|
||||||
|
self.key_info_result(imported_key).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_existing_key(&self, pattern: &str) -> Result<Key, Error> {
|
async fn get_existing_key(&self, pattern: &str) -> Result<Key, Error> {
|
||||||
|
@ -604,6 +640,25 @@ impl AdminRpcHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(id, &EmptyKey).await? {
|
||||||
|
relevant_buckets.insert(*id, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(AdminRpc::KeyInfo(key, relevant_buckets))
|
||||||
|
}
|
||||||
|
|
||||||
/// Update **key table** to inform of the new linked bucket
|
/// Update **key table** to inform of the new linked bucket
|
||||||
async fn update_key_bucket(
|
async fn update_key_bucket(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -173,8 +173,8 @@ pub async fn cmd_admin(
|
||||||
format_table(table);
|
format_table(table);
|
||||||
println!("Buckets that don't have a global alias (i.e. that only exist in the namespace of an access key) are not shown.");
|
println!("Buckets that don't have a global alias (i.e. that only exist in the namespace of an access key) are not shown.");
|
||||||
}
|
}
|
||||||
AdminRpc::BucketInfo(bucket) => {
|
AdminRpc::BucketInfo(bucket, rk) => {
|
||||||
print_bucket_info(&bucket);
|
print_bucket_info(&bucket, &rk);
|
||||||
}
|
}
|
||||||
AdminRpc::KeyList(kl) => {
|
AdminRpc::KeyList(kl) => {
|
||||||
println!("List of keys:");
|
println!("List of keys:");
|
||||||
|
@ -182,8 +182,8 @@ pub async fn cmd_admin(
|
||||||
println!("{}\t{}", key.0, key.1);
|
println!("{}\t{}", key.0, key.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AdminRpc::KeyInfo(key) => {
|
AdminRpc::KeyInfo(key, rb) => {
|
||||||
print_key_info(&key);
|
print_key_info(&key, &rb);
|
||||||
}
|
}
|
||||||
r => {
|
r => {
|
||||||
error!("Unexpected response: {:?}", r);
|
error!("Unexpected response: {:?}", r);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use garage_util::crdt::*;
|
use garage_util::crdt::*;
|
||||||
use garage_util::data::Uuid;
|
use garage_util::data::Uuid;
|
||||||
use garage_util::error::*;
|
use garage_util::error::*;
|
||||||
|
@ -5,7 +7,24 @@ use garage_util::error::*;
|
||||||
use garage_model::bucket_table::*;
|
use garage_model::bucket_table::*;
|
||||||
use garage_model::key_table::*;
|
use garage_model::key_table::*;
|
||||||
|
|
||||||
pub fn print_key_info(key: &Key) {
|
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()
|
||||||
|
};
|
||||||
|
|
||||||
println!("Key name: {}", key.name.get());
|
println!("Key name: {}", key.name.get());
|
||||||
println!("Key ID: {}", key.key_id);
|
println!("Key ID: {}", key.key_id);
|
||||||
println!("Secret key: {}", key.secret_key);
|
println!("Secret key: {}", key.secret_key);
|
||||||
|
@ -16,18 +35,39 @@ pub fn print_key_info(key: &Key) {
|
||||||
let mut table = vec![];
|
let mut table = vec![];
|
||||||
for (alias_name, _, alias) in p.local_aliases.items().iter() {
|
for (alias_name, _, alias) in p.local_aliases.items().iter() {
|
||||||
if let Some(bucket_id) = alias.as_option() {
|
if let Some(bucket_id) = alias.as_option() {
|
||||||
table.push(format!("\t{}\t{}", alias_name, hex::encode(bucket_id)));
|
table.push(format!(
|
||||||
|
"\t{}\t{}\t{}",
|
||||||
|
alias_name,
|
||||||
|
bucket_global_aliases(bucket_id),
|
||||||
|
hex::encode(bucket_id)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
format_table(table);
|
format_table(table);
|
||||||
|
|
||||||
println!("\nAuthorized buckets:");
|
println!("\nAuthorized buckets:");
|
||||||
let mut table = vec![];
|
let mut table = vec![];
|
||||||
for (b, perm) in p.authorized_buckets.items().iter() {
|
for (bucket_id, perm) in p.authorized_buckets.items().iter() {
|
||||||
let rflag = if perm.allow_read { "R" } else { " " };
|
let rflag = if perm.allow_read { "R" } else { " " };
|
||||||
let wflag = if perm.allow_write { "W" } else { " " };
|
let wflag = if perm.allow_write { "W" } else { " " };
|
||||||
let oflag = if perm.allow_owner { "O" } else { " " };
|
let oflag = if perm.allow_owner { "O" } else { " " };
|
||||||
table.push(format!("\t{}{}{}\t{:?}", rflag, wflag, oflag, b));
|
let local_aliases = p
|
||||||
|
.local_aliases
|
||||||
|
.items()
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, _, a)| a.as_option() == 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);
|
format_table(table);
|
||||||
}
|
}
|
||||||
|
@ -37,32 +77,49 @@ pub fn print_key_info(key: &Key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_bucket_info(bucket: &Bucket) {
|
pub fn print_bucket_info(bucket: &Bucket, relevant_keys: &HashMap<String, Key>) {
|
||||||
println!("Bucket: {}", hex::encode(bucket.id));
|
println!("Bucket: {}", hex::encode(bucket.id));
|
||||||
match &bucket.state {
|
match &bucket.state {
|
||||||
Deletable::Deleted => println!("Bucket is deleted."),
|
Deletable::Deleted => println!("Bucket is deleted."),
|
||||||
Deletable::Present(p) => {
|
Deletable::Present(p) => {
|
||||||
|
println!("Website access: {}", p.website_access.get());
|
||||||
|
|
||||||
println!("\nGlobal aliases:");
|
println!("\nGlobal aliases:");
|
||||||
for (alias, _, active) in p.aliases.items().iter() {
|
for (alias, _, active) in p.aliases.items().iter() {
|
||||||
if *active {
|
if *active {
|
||||||
println!("- {}", alias);
|
println!(" {}", alias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("\nKey-specific aliases:");
|
println!("\nKey-specific aliases:");
|
||||||
|
let mut table = vec![];
|
||||||
for ((key_id, alias), _, active) in p.local_aliases.items().iter() {
|
for ((key_id, alias), _, active) in p.local_aliases.items().iter() {
|
||||||
if *active {
|
if *active {
|
||||||
println!("- {} {}", key_id, alias);
|
let key_name = relevant_keys
|
||||||
|
.get(key_id)
|
||||||
|
.map(|k| k.name.get().as_str())
|
||||||
|
.unwrap_or("");
|
||||||
|
table.push(format!("\t{}\t{} ({})", alias, key_id, key_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
format_table(table);
|
||||||
|
|
||||||
println!("\nAuthorized keys:");
|
println!("\nAuthorized keys:");
|
||||||
|
let mut table = vec![];
|
||||||
for (k, perm) in p.authorized_keys.items().iter() {
|
for (k, perm) in p.authorized_keys.items().iter() {
|
||||||
let rflag = if perm.allow_read { "R" } else { " " };
|
let rflag = if perm.allow_read { "R" } else { " " };
|
||||||
let wflag = if perm.allow_write { "W" } else { " " };
|
let wflag = if perm.allow_write { "W" } else { " " };
|
||||||
let oflag = if perm.allow_owner { "O" } else { " " };
|
let oflag = if perm.allow_owner { "O" } else { " " };
|
||||||
println!("- {}{}{} {}", rflag, wflag, oflag, k);
|
let key_name = relevant_keys
|
||||||
|
.get(k)
|
||||||
|
.map(|k| k.name.get().as_str())
|
||||||
|
.unwrap_or("");
|
||||||
|
table.push(format!(
|
||||||
|
"\t{}{}{}\t{} ({})",
|
||||||
|
rflag, wflag, oflag, k, key_name
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
format_table(table);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue