From fe399a326506a9d8870cb7783a57495849793d2c Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Thu, 12 May 2022 11:02:36 +0200 Subject: [PATCH] DeleteBucket --- Makefile | 2 +- doc/drafts/admin-api.md | 2 ++ src/api/admin/api_server.rs | 1 + src/api/admin/bucket.rs | 62 +++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c70be9da..eeeffedb 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: doc all release shell all: - clear; cargo build --features k2v + clear; cargo build --all-features doc: cd doc/book; mdbook build diff --git a/doc/drafts/admin-api.md b/doc/drafts/admin-api.md index b24e4cf4..048b77fb 100644 --- a/doc/drafts/admin-api.md +++ b/doc/drafts/admin-api.md @@ -454,3 +454,5 @@ or no alias at all. ### DeleteBucket `DELETE /bucket?id=` Deletes a storage bucket. A bucket cannot be deleted if it is not empty. + +Warning: this will delete all aliases associated with the bucket! diff --git a/src/api/admin/api_server.rs b/src/api/admin/api_server.rs index 783ecfcb..4366cbd6 100644 --- a/src/api/admin/api_server.rs +++ b/src/api/admin/api_server.rs @@ -146,6 +146,7 @@ impl ApiHandler for AdminApiServer { handle_get_bucket_info(&self.garage, id, global_alias).await } Endpoint::CreateBucket => handle_create_bucket(&self.garage, req).await, + Endpoint::DeleteBucket { id } => handle_delete_bucket(&self.garage, id).await, _ => Err(Error::NotImplemented(format!( "Admin endpoint {} not implemented yet", endpoint.name() diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index 723391c5..8e6cc067 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use hyper::{Body, Request, Response, StatusCode}; use serde::{Deserialize, Serialize}; +use garage_util::crdt::*; use garage_util::data::*; use garage_util::error::Error as GarageError; @@ -13,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::ObjectFilter; use crate::admin::key::KeyBucketPermResult; use crate::error::*; @@ -306,3 +308,63 @@ struct CreateBucketLocalAlias { #[serde(rename = "allPermissions", default)] all_permissions: bool, } + +pub async fn handle_delete_bucket( + garage: &Arc, + id: String, +) -> Result, Error> { + let helper = garage.bucket_helper(); + + let id_hex = hex::decode(&id).ok_or_bad_request("Invalid bucket id")?; + let bucket_id = Uuid::try_from(&id_hex).ok_or_bad_request("Invalid bucket id")?; + + let mut bucket = helper.get_existing_bucket(bucket_id).await?; + let state = bucket.state.as_option().unwrap(); + + // Check bucket is empty + let objects = garage + .object_table + .get_range( + &bucket_id, + None, + Some(ObjectFilter::IsData), + 10, + EnumerationOrder::Forward, + ) + .await?; + if !objects.is_empty() { + return Err(Error::BadRequest("Bucket is not empty".into())); + } + + // --- done checking, now commit --- + // 1. delete authorization from keys that had access + for (key_id, perm) in bucket.authorized_keys() { + if perm.is_any() { + helper + .set_bucket_key_permissions(bucket.id, key_id, BucketKeyPerm::NO_PERMISSIONS) + .await?; + } + } + // 2. delete all local aliases + for ((key_id, alias), _, active) in state.local_aliases.items().iter() { + if *active { + helper + .unset_local_bucket_alias(bucket.id, &key_id, &alias) + .await?; + } + } + // 3. delete all global aliases + for (alias, _, active) in state.aliases.items().iter() { + if *active { + helper.purge_global_bucket_alias(bucket.id, &alias).await?; + } + } + + // 4. delete bucket + bucket.state = Deletable::delete(); + garage.bucket_table.insert(&bucket).await?; + + Ok(Response::builder() + .status(StatusCode::NO_CONTENT) + .body(Body::empty())?) +}