diff options
author | Alex Auvolat <alex@adnab.me> | 2022-06-09 15:43:26 +0200 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2022-06-09 15:43:26 +0200 |
commit | c1baa102029e8706e1a7ffdd58dde2afc2a690d5 (patch) | |
tree | 17123b6568f6f1746f2710bf2d57bb4992931213 /src/api | |
parent | ea1022f8325591b5e6e2addd457eb23a1783dc5f (diff) | |
download | garage-c1baa102029e8706e1a7ffdd58dde2afc2a690d5.tar.gz garage-c1baa102029e8706e1a7ffdd58dde2afc2a690d5.zip |
Add quotas to bucket table and show them in CLI
Diffstat (limited to 'src/api')
-rw-r--r-- | src/api/admin/bucket.rs | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index 7f9a813f..ff0dce54 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -14,6 +14,7 @@ use garage_model::bucket_alias_table::*; use garage_model::bucket_table::*; use garage_model::garage::Garage; use garage_model::permission::*; +use garage_model::s3::object_table::*; use crate::admin::error::*; use crate::admin::key::ApiBucketKeyPerm; @@ -32,10 +33,28 @@ pub async fn handle_list_buckets(garage: &Arc<Garage>) -> Result<Response<Body>, ) .await?; + let ring = garage.system.ring.borrow().clone(); + let counters = garage + .object_counter_table + .table + .get_range( + &EmptyKey, + None, + Some((DeletedFilter::NotDeleted, ring.layout.node_id_vec.clone())), + 15000, + EnumerationOrder::Forward, + ) + .await? + .iter() + .map(|x| (x.sk, x.filtered_values(&ring))) + .collect::<HashMap<_, _>>(); + let res = buckets .into_iter() .map(|b| { let state = b.state.as_option().unwrap(); + let empty_cnts = HashMap::new(); + let cnts = counters.get(&b.id).unwrap_or(&empty_cnts); ListBucketResultItem { id: hex::encode(b.id), global_aliases: state @@ -55,6 +74,9 @@ pub async fn handle_list_buckets(garage: &Arc<Garage>) -> Result<Response<Body>, alias: n.to_string(), }) .collect::<Vec<_>>(), + objects: cnts.get(OBJECTS).cloned().unwrap_or_default(), + bytes: cnts.get(BYTES).cloned().unwrap_or_default(), + unfinshed_uploads: cnts.get(UNFINISHED_UPLOADS).cloned().unwrap_or_default(), } }) .collect::<Vec<_>>(); @@ -68,6 +90,9 @@ struct ListBucketResultItem { id: String, global_aliases: Vec<String>, local_aliases: Vec<BucketLocalAlias>, + objects: i64, + bytes: i64, + unfinshed_uploads: i64, } #[derive(Serialize)] @@ -77,6 +102,13 @@ struct BucketLocalAlias { alias: String, } +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct ApiBucketQuotas { + max_size: Option<u64>, + max_objects: Option<u64>, +} + pub async fn handle_get_bucket_info( garage: &Arc<Garage>, id: Option<String>, @@ -108,6 +140,14 @@ async fn bucket_info_results( .get_existing_bucket(bucket_id) .await?; + let counters = garage + .object_counter_table + .table + .get(&EmptyKey, &bucket_id) + .await? + .map(|x| x.filtered_values(&garage.system.ring.borrow())) + .unwrap_or_default(); + let mut relevant_keys = HashMap::new(); for (k, _) in bucket .state @@ -148,6 +188,7 @@ async fn bucket_info_results( let state = bucket.state.as_option().unwrap(); + let quotas = state.quotas.get(); let res = GetBucketInfoResult { id: hex::encode(&bucket.id), @@ -191,6 +232,16 @@ async fn bucket_info_results( } }) .collect::<Vec<_>>(), + objects: counters.get(OBJECTS).cloned().unwrap_or_default(), + bytes: counters.get(BYTES).cloned().unwrap_or_default(), + unfinshed_uploads: counters + .get(UNFINISHED_UPLOADS) + .cloned() + .unwrap_or_default(), + quotas: ApiBucketQuotas { + max_size: quotas.max_size, + max_objects: quotas.max_objects, + }, }; Ok(json_ok_response(&res)?) @@ -205,6 +256,10 @@ struct GetBucketInfoResult { #[serde(default)] website_config: Option<GetBucketInfoWebsiteResult>, keys: Vec<GetBucketInfoKey>, + objects: i64, + bytes: i64, + unfinshed_uploads: i64, + quotas: ApiBucketQuotas, } #[derive(Serialize)] |