diff options
author | Alex Auvolat <alex@adnab.me> | 2021-12-14 13:55:11 +0100 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2022-01-04 12:45:46 +0100 |
commit | 5b1117e582db16cc5aa50840a685875cbd5501f4 (patch) | |
tree | 06fec47bf56cb08cb51334454dc15f98352c98f2 /src/api | |
parent | 8f6026de5ecd44cbe0fc0bcd47638a1ece860439 (diff) | |
download | garage-5b1117e582db16cc5aa50840a685875cbd5501f4.tar.gz garage-5b1117e582db16cc5aa50840a685875cbd5501f4.zip |
New model for buckets
Diffstat (limited to 'src/api')
-rw-r--r-- | src/api/Cargo.toml | 8 | ||||
-rw-r--r-- | src/api/api_server.rs | 109 | ||||
-rw-r--r-- | src/api/s3_bucket.rs | 62 | ||||
-rw-r--r-- | src/api/s3_copy.rs | 20 | ||||
-rw-r--r-- | src/api/s3_delete.rs | 14 | ||||
-rw-r--r-- | src/api/s3_get.rs | 9 | ||||
-rw-r--r-- | src/api/s3_list.rs | 8 | ||||
-rw-r--r-- | src/api/s3_put.rs | 44 | ||||
-rw-r--r-- | src/api/s3_website.rs | 27 | ||||
-rw-r--r-- | src/api/signature.rs | 2 |
10 files changed, 208 insertions, 95 deletions
diff --git a/src/api/Cargo.toml b/src/api/Cargo.toml index 3ca46764..de58f78b 100644 --- a/src/api/Cargo.toml +++ b/src/api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_api" -version = "0.5.0" +version = "0.6.0" authors = ["Alex Auvolat <alex@adnab.me>"] edition = "2018" license = "AGPL-3.0" @@ -14,9 +14,9 @@ path = "lib.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -garage_model = { version = "0.5.0", path = "../model" } -garage_table = { version = "0.5.0", path = "../table" } -garage_util = { version = "0.5.0", path = "../util" } +garage_model = { version = "0.6.0", path = "../model" } +garage_table = { version = "0.6.0", path = "../table" } +garage_util = { version = "0.6.0", path = "../util" } base64 = "0.13" bytes = "1.0" diff --git a/src/api/api_server.rs b/src/api/api_server.rs index 2de86233..cc9b9c38 100644 --- a/src/api/api_server.rs +++ b/src/api/api_server.rs @@ -7,9 +7,12 @@ use hyper::server::conn::AddrStream; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Request, Response, Server}; +use garage_util::crdt; +use garage_util::data::*; use garage_util::error::Error as GarageError; use garage_model::garage::Garage; +use garage_model::key_table::Key; use crate::error::*; use crate::signature::check_signature; @@ -105,10 +108,20 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon .and_then(|root_domain| host_to_bucket(&host, root_domain)); let endpoint = Endpoint::from_request(&req, bucket.map(ToOwned::to_owned))?; + + let bucket_name = match endpoint.authorization_type() { + Authorization::None => { + return handle_request_without_bucket(garage, req, api_key, endpoint).await + } + Authorization::Read(bucket) | Authorization::Write(bucket) => bucket.to_string(), + }; + + let bucket_id = resolve_bucket(&garage, &bucket_name, &api_key).await?; + let allowed = match endpoint.authorization_type() { - Authorization::None => true, - Authorization::Read(bucket) => api_key.allow_read(bucket), - Authorization::Write(bucket) => api_key.allow_write(bucket), + Authorization::Read(_) => api_key.allow_read(&bucket_id), + Authorization::Write(_) => api_key.allow_write(&bucket_id), + _ => unreachable!(), }; if !allowed { @@ -118,19 +131,18 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon } match endpoint { - Endpoint::ListBuckets => handle_list_buckets(&api_key), - Endpoint::HeadObject { bucket, key, .. } => handle_head(garage, &req, &bucket, &key).await, - Endpoint::GetObject { bucket, key, .. } => handle_get(garage, &req, &bucket, &key).await, + Endpoint::HeadObject { key, .. } => handle_head(garage, &req, bucket_id, &key).await, + Endpoint::GetObject { key, .. } => handle_get(garage, &req, bucket_id, &key).await, Endpoint::UploadPart { - bucket, key, part_number, upload_id, + .. } => { handle_put_part( garage, req, - &bucket, + bucket_id, &key, part_number, &upload_id, @@ -138,38 +150,46 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon ) .await } - Endpoint::CopyObject { bucket, key } => { + Endpoint::CopyObject { key, .. } => { let copy_source = req.headers().get("x-amz-copy-source").unwrap().to_str()?; let copy_source = percent_encoding::percent_decode_str(copy_source).decode_utf8()?; let (source_bucket, source_key) = parse_bucket_key(©_source, None)?; - if !api_key.allow_read(source_bucket) { + let source_bucket_id = + resolve_bucket(&garage, &source_bucket.to_string(), &api_key).await?; + if !api_key.allow_read(&source_bucket_id) { return Err(Error::Forbidden(format!( "Reading from bucket {} not allowed for this key", source_bucket ))); } let source_key = source_key.ok_or_bad_request("No source key specified")?; - handle_copy(garage, &req, &bucket, &key, source_bucket, source_key).await + handle_copy(garage, &req, bucket_id, &key, source_bucket_id, source_key).await } - Endpoint::PutObject { bucket, key } => { - handle_put(garage, req, &bucket, &key, content_sha256).await + Endpoint::PutObject { key, .. } => { + handle_put(garage, req, bucket_id, &key, content_sha256).await } - Endpoint::AbortMultipartUpload { - bucket, - key, - upload_id, - } => handle_abort_multipart_upload(garage, &bucket, &key, &upload_id).await, - Endpoint::DeleteObject { bucket, key, .. } => handle_delete(garage, &bucket, &key).await, + Endpoint::AbortMultipartUpload { key, upload_id, .. } => { + handle_abort_multipart_upload(garage, bucket_id, &key, &upload_id).await + } + Endpoint::DeleteObject { key, .. } => handle_delete(garage, bucket_id, &key).await, Endpoint::CreateMultipartUpload { bucket, key } => { - handle_create_multipart_upload(garage, &req, &bucket, &key).await + handle_create_multipart_upload(garage, &req, &bucket, bucket_id, &key).await } Endpoint::CompleteMultipartUpload { bucket, key, upload_id, } => { - handle_complete_multipart_upload(garage, req, &bucket, &key, &upload_id, content_sha256) - .await + handle_complete_multipart_upload( + garage, + req, + &bucket, + bucket_id, + &key, + &upload_id, + content_sha256, + ) + .await } Endpoint::CreateBucket { bucket } => { debug!( @@ -206,7 +226,8 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon garage, &ListObjectsQuery { is_v2: false, - bucket, + bucket_name: bucket, + bucket_id, delimiter: delimiter.map(|d| d.to_string()), max_keys: max_keys.unwrap_or(1000), prefix: prefix.unwrap_or_default(), @@ -234,7 +255,8 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon garage, &ListObjectsQuery { is_v2: true, - bucket, + bucket_name: bucket, + bucket_id, delimiter: delimiter.map(|d| d.to_string()), max_keys: max_keys.unwrap_or(1000), prefix: prefix.unwrap_or_default(), @@ -252,8 +274,8 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon ))) } } - Endpoint::DeleteObjects { bucket } => { - handle_delete_objects(garage, &bucket, req, content_sha256).await + Endpoint::DeleteObjects { .. } => { + handle_delete_objects(garage, bucket_id, req, content_sha256).await } Endpoint::PutBucketWebsite { bucket } => { handle_put_website(garage, bucket, req, content_sha256).await @@ -263,6 +285,41 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon } } +async fn handle_request_without_bucket( + garage: Arc<Garage>, + _req: Request<Body>, + api_key: Key, + endpoint: Endpoint, +) -> Result<Response<Body>, Error> { + match endpoint { + Endpoint::ListBuckets => handle_list_buckets(&garage, &api_key).await, + endpoint => Err(Error::NotImplemented(endpoint.name().to_owned())), + } +} + +#[allow(clippy::ptr_arg)] +async fn resolve_bucket( + garage: &Garage, + bucket_name: &String, + api_key: &Key, +) -> Result<Uuid, Error> { + let api_key_params = api_key + .state + .as_option() + .ok_or_else(|| Error::Forbidden("Operation is not allowed for this key.".to_string()))?; + + if let Some(crdt::Deletable::Present(bucket_id)) = api_key_params.local_aliases.get(bucket_name) + { + Ok(*bucket_id) + } else { + Ok(garage + .bucket_helper() + .resolve_global_bucket_name(bucket_name) + .await? + .ok_or(Error::NotFound)?) + } +} + /// Extract the bucket name and the key name from an HTTP path and possibly a bucket provided in /// the host header of the request /// diff --git a/src/api/s3_bucket.rs b/src/api/s3_bucket.rs index 2be0a818..dc131a31 100644 --- a/src/api/s3_bucket.rs +++ b/src/api/s3_bucket.rs @@ -1,9 +1,12 @@ +use std::collections::HashMap; use std::sync::Arc; use hyper::{Body, Response}; use garage_model::garage::Garage; use garage_model::key_table::Key; +use garage_table::util::EmptyKey; +use garage_util::crdt::*; use garage_util::time::*; use crate::error::*; @@ -34,20 +37,65 @@ pub fn handle_get_bucket_versioning() -> Result<Response<Body>, Error> { .body(Body::from(xml.into_bytes()))?) } -pub fn handle_list_buckets(api_key: &Key) -> Result<Response<Body>, Error> { +pub async fn handle_list_buckets(garage: &Garage, api_key: &Key) -> Result<Response<Body>, Error> { + let key_state = api_key.state.as_option().ok_or_internal_error( + "Key should not be in deleted state at this point (internal error)", + )?; + + // Collect buckets user has access to + let ids = api_key + .state + .as_option() + .unwrap() + .authorized_buckets + .items() + .iter() + .filter(|(_, perms)| perms.allow_read || perms.allow_write) + .map(|(id, _)| *id) + .collect::<Vec<_>>(); + + let mut buckets_by_id = HashMap::new(); + let mut aliases = HashMap::new(); + + for bucket_id in ids.iter() { + let bucket = garage.bucket_table.get(bucket_id, &EmptyKey).await?; + if let Some(bucket) = bucket { + if let Deletable::Present(param) = bucket.state { + for (alias, _, active) in param.aliases.items() { + if *active { + let alias_ent = garage.bucket_alias_table.get(&EmptyKey, alias).await?; + if let Some(alias_ent) = alias_ent { + if let Some(alias_p) = alias_ent.state.get().as_option() { + if alias_p.bucket_id == *bucket_id { + aliases.insert(alias_ent.name.clone(), *bucket_id); + } + } + } + } + } + buckets_by_id.insert(bucket_id, param); + } + } + } + + for (alias, _, id) in key_state.local_aliases.items() { + if let Some(id) = id.as_option() { + aliases.insert(alias.clone(), *id); + } + } + + // Generate response let list_buckets = s3_xml::ListAllMyBucketsResult { owner: s3_xml::Owner { display_name: s3_xml::Value(api_key.name.get().to_string()), id: s3_xml::Value(api_key.key_id.to_string()), }, buckets: s3_xml::BucketList { - entries: api_key - .authorized_buckets - .items() + entries: aliases .iter() - .filter(|(_, _, perms)| perms.allow_read || perms.allow_write) - .map(|(name, ts, _)| s3_xml::Bucket { - creation_date: s3_xml::Value(msec_to_rfc3339(*ts)), + .filter_map(|(name, id)| buckets_by_id.get(id).map(|p| (name, id, p))) + .map(|(name, _id, param)| s3_xml::Bucket { + creation_date: s3_xml::Value(msec_to_rfc3339(param.creation_date)), name: s3_xml::Value(name.to_string()), }) .collect(), diff --git a/src/api/s3_copy.rs b/src/api/s3_copy.rs index 9ade6985..4ede8230 100644 --- a/src/api/s3_copy.rs +++ b/src/api/s3_copy.rs @@ -18,14 +18,14 @@ use crate::s3_xml; pub async fn handle_copy( garage: Arc<Garage>, req: &Request<Body>, - dest_bucket: &str, + dest_bucket_id: Uuid, dest_key: &str, - source_bucket: &str, + source_bucket_id: Uuid, source_key: &str, ) -> Result<Response<Body>, Error> { let source_object = garage .object_table - .get(&source_bucket.to_string(), &source_key.to_string()) + .get(&source_bucket_id, &source_key.to_string()) .await? .ok_or(Error::NotFound)?; @@ -76,7 +76,7 @@ pub async fn handle_copy( )), }; let dest_object = Object::new( - dest_bucket.to_string(), + dest_bucket_id, dest_key.to_string(), vec![dest_object_version], ); @@ -99,7 +99,7 @@ pub async fn handle_copy( state: ObjectVersionState::Uploading(new_meta.headers.clone()), }; let tmp_dest_object = Object::new( - dest_bucket.to_string(), + dest_bucket_id, dest_key.to_string(), vec![tmp_dest_object_version], ); @@ -109,12 +109,8 @@ pub async fn handle_copy( // this means that the BlockRef entries linked to this version cannot be // marked as deleted (they are marked as deleted only if the Version // doesn't exist or is marked as deleted). - let mut dest_version = Version::new( - new_uuid, - dest_bucket.to_string(), - dest_key.to_string(), - false, - ); + let mut dest_version = + Version::new(new_uuid, dest_bucket_id, dest_key.to_string(), false); garage.version_table.insert(&dest_version).await?; // Fill in block list for version and insert block refs @@ -151,7 +147,7 @@ pub async fn handle_copy( )), }; let dest_object = Object::new( - dest_bucket.to_string(), + dest_bucket_id, dest_key.to_string(), vec![dest_object_version], ); diff --git a/src/api/s3_delete.rs b/src/api/s3_delete.rs index 425f86d7..1976139b 100644 --- a/src/api/s3_delete.rs +++ b/src/api/s3_delete.rs @@ -14,12 +14,12 @@ use crate::signature::verify_signed_content; async fn handle_delete_internal( garage: &Garage, - bucket: &str, + bucket_id: Uuid, key: &str, ) -> Result<(Uuid, Uuid), Error> { let object = garage .object_table - .get(&bucket.to_string(), &key.to_string()) + .get(&bucket_id, &key.to_string()) .await? .ok_or(Error::NotFound)?; // No need to delete @@ -45,7 +45,7 @@ async fn handle_delete_internal( let version_uuid = gen_uuid(); let object = Object::new( - bucket.into(), + bucket_id, key.into(), vec![ObjectVersion { uuid: version_uuid, @@ -61,11 +61,11 @@ async fn handle_delete_internal( pub async fn handle_delete( garage: Arc<Garage>, - bucket: &str, + bucket_id: Uuid, key: &str, ) -> Result<Response<Body>, Error> { let (_deleted_version, delete_marker_version) = - handle_delete_internal(&garage, bucket, key).await?; + handle_delete_internal(&garage, bucket_id, key).await?; Ok(Response::builder() .header("x-amz-version-id", hex::encode(delete_marker_version)) @@ -76,7 +76,7 @@ pub async fn handle_delete( pub async fn handle_delete_objects( garage: Arc<Garage>, - bucket: &str, + bucket_id: Uuid, req: Request<Body>, content_sha256: Option<Hash>, ) -> Result<Response<Body>, Error> { @@ -90,7 +90,7 @@ pub async fn handle_delete_objects( let mut ret_errors = Vec::new(); for obj in cmd.objects.iter() { - match handle_delete_internal(&garage, bucket, &obj.key).await { + match handle_delete_internal(&garage, bucket_id, &obj.key).await { Ok((deleted_version, delete_marker_version)) => { if cmd.quiet { continue; diff --git a/src/api/s3_get.rs b/src/api/s3_get.rs index 428bbf34..269a3fa8 100644 --- a/src/api/s3_get.rs +++ b/src/api/s3_get.rs @@ -7,6 +7,7 @@ use hyper::body::Bytes; use hyper::{Body, Request, Response, StatusCode}; use garage_table::EmptyKey; +use garage_util::data::*; use garage_model::garage::Garage; use garage_model::object_table::*; @@ -84,12 +85,12 @@ fn try_answer_cached( pub async fn handle_head( garage: Arc<Garage>, req: &Request<Body>, - bucket: &str, + bucket_id: Uuid, key: &str, ) -> Result<Response<Body>, Error> { let object = garage .object_table - .get(&bucket.to_string(), &key.to_string()) + .get(&bucket_id, &key.to_string()) .await? .ok_or(Error::NotFound)?; @@ -123,12 +124,12 @@ pub async fn handle_head( pub async fn handle_get( garage: Arc<Garage>, req: &Request<Body>, - bucket: &str, + bucket_id: Uuid, key: &str, ) -> Result<Response<Body>, Error> { let object = garage .object_table - .get(&bucket.to_string(), &key.to_string()) + .get(&bucket_id, &key.to_string()) .await? .ok_or(Error::NotFound)?; diff --git a/src/api/s3_list.rs b/src/api/s3_list.rs index df9c3e6b..07efb02d 100644 --- a/src/api/s3_list.rs +++ b/src/api/s3_list.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use hyper::{Body, Response}; +use garage_util::data::*; use garage_util::error::Error as GarageError; use garage_util::time::*; @@ -18,7 +19,8 @@ use crate::s3_xml; #[derive(Debug)] pub struct ListObjectsQuery { pub is_v2: bool, - pub bucket: String, + pub bucket_name: String, + pub bucket_id: Uuid, pub delimiter: Option<String>, pub max_keys: usize, pub prefix: String, @@ -102,7 +104,7 @@ pub async fn handle_list( let objects = garage .object_table .get_range( - &query.bucket, + &query.bucket_id, Some(next_chunk_start.clone()), Some(DeletedFilter::NotDeleted), query.max_keys + 1, @@ -232,7 +234,7 @@ pub async fn handle_list( let mut result = s3_xml::ListBucketResult { xmlns: (), - name: s3_xml::Value(query.bucket.to_string()), + name: s3_xml::Value(query.bucket_name.to_string()), prefix: uriencode_maybe(&query.prefix, query.urlencode_resp), marker: None, next_marker: None, diff --git a/src/api/s3_put.rs b/src/api/s3_put.rs index f63e8307..152e59b4 100644 --- a/src/api/s3_put.rs +++ b/src/api/s3_put.rs @@ -24,7 +24,7 @@ use crate::signature::verify_signed_content; pub async fn handle_put( garage: Arc<Garage>, req: Request<Body>, - bucket: &str, + bucket_id: Uuid, key: &str, content_sha256: Option<Hash>, ) -> Result<Response<Body>, Error> { @@ -77,7 +77,7 @@ pub async fn handle_put( )), }; - let object = Object::new(bucket.into(), key.into(), vec![object_version]); + let object = Object::new(bucket_id, key.into(), vec![object_version]); garage.object_table.insert(&object).await?; return Ok(put_response(version_uuid, data_md5sum_hex)); @@ -90,14 +90,14 @@ pub async fn handle_put( timestamp: version_timestamp, state: ObjectVersionState::Uploading(headers.clone()), }; - let object = Object::new(bucket.into(), key.into(), vec![object_version.clone()]); + let object = Object::new(bucket_id, key.into(), vec![object_version.clone()]); garage.object_table.insert(&object).await?; // Initialize corresponding entry in version table // Write this entry now, even with empty block list, // to prevent block_ref entries from being deleted (they can be deleted // if the reference a version that isn't found in the version table) - let version = Version::new(version_uuid, bucket.into(), key.into(), false); + let version = Version::new(version_uuid, bucket_id, key.into(), false); garage.version_table.insert(&version).await?; // Transfer data and verify checksum @@ -127,7 +127,7 @@ pub async fn handle_put( Err(e) => { // Mark object as aborted, this will free the blocks further down object_version.state = ObjectVersionState::Aborted; - let object = Object::new(bucket.into(), key.into(), vec![object_version.clone()]); + let object = Object::new(bucket_id, key.into(), vec![object_version.clone()]); garage.object_table.insert(&object).await?; return Err(e); } @@ -143,7 +143,7 @@ pub async fn handle_put( }, first_block_hash, )); - let object = Object::new(bucket.into(), key.into(), vec![object_version]); + let object = Object::new(bucket_id, key.into(), vec![object_version]); garage.object_table.insert(&object).await?; Ok(put_response(version_uuid, md5sum_hex)) @@ -315,7 +315,8 @@ pub fn put_response(version_uuid: Uuid, md5sum_hex: String) -> Response<Body> { pub async fn handle_create_multipart_upload( garage: Arc<Garage>, req: &Request<Body>, - bucket: &str, + bucket_name: &str, + bucket_id: Uuid, key: &str, ) -> Result<Response<Body>, Error> { let version_uuid = gen_uuid(); @@ -327,20 +328,20 @@ pub async fn handle_create_multipart_upload( timestamp: now_msec(), state: ObjectVersionState::Uploading(headers), }; - let object = Object::new(bucket.to_string(), key.to_string(), vec![object_version]); + let object = Object::new(bucket_id, key.to_string(), vec![object_version]); garage.object_table.insert(&object).await?; // Insert empty version so that block_ref entries refer to something // (they are inserted concurrently with blocks in the version table, so // there is the possibility that they are inserted before the version table // is created, in which case it is allowed to delete them, e.g. in repair_*) - let version = Version::new(version_uuid, bucket.into(), key.into(), false); + let version = Version::new(version_uuid, bucket_id, key.into(), false); garage.version_table.insert(&version).await?; // Send success response let result = s3_xml::InitiateMultipartUploadResult { xmlns: (), - bucket: s3_xml::Value(bucket.to_string()), + bucket: s3_xml::Value(bucket_name.to_string()), key: s3_xml::Value(key.to_string()), upload_id: s3_xml::Value(hex::encode(version_uuid)), }; @@ -352,7 +353,7 @@ pub async fn handle_create_multipart_upload( pub async fn handle_put_part( garage: Arc<Garage>, req: Request<Body>, - bucket: &str, + bucket_id: Uuid, key: &str, part_number: u64, upload_id: &str, @@ -366,12 +367,11 @@ pub async fn handle_put_part( }; // Read first chuck, and at the same time try to get object to see if it exists - let bucket = bucket.to_string(); let key = key.to_string(); let mut chunker = BodyChunker::new(req.into_body(), garage.config.block_size); let (object, first_block) = - futures::try_join!(garage.object_table.get(&bucket, &key), chunker.next(),)?; + futures::try_join!(garage.object_table.get(&bucket_id, &key), chunker.next(),)?; // Check object is valid and multipart block can be accepted let first_block = first_block.ok_or_else(|| Error::BadRequest("Empty body".to_string()))?; @@ -386,7 +386,7 @@ pub async fn handle_put_part( } // Copy block to store - let version = Version::new(version_uuid, bucket, key, false); + let version = Version::new(version_uuid, bucket_id, key, false); let first_block_hash = blake2sum(&first_block[..]); let (_, data_md5sum, data_sha256sum) = read_and_put_blocks( &garage, @@ -424,7 +424,8 @@ pub async fn handle_put_part( pub async fn handle_complete_multipart_upload( garage: Arc<Garage>, req: Request<Body>, - bucket: &str, + bucket_name: &str, + bucket_id: Uuid, key: &str, upload_id: &str, content_sha256: Option<Hash>, @@ -442,10 +443,9 @@ pub async fn handle_complete_multipart_upload( let version_uuid = decode_upload_id(upload_id)?; - let bucket = bucket.to_string(); let key = key.to_string(); let (object, version) = futures::try_join!( - garage.object_table.get(&bucket, &key), + garage.object_table.get(&bucket_id, &key), garage.version_table.get(&version_uuid, &EmptyKey), )?; @@ -510,14 +510,14 @@ pub async fn handle_complete_multipart_upload( version.blocks.items()[0].1.hash, )); - let final_object = Object::new(bucket.clone(), key.clone(), vec![object_version]); + let final_object = Object::new(bucket_id, key.clone(), vec![object_version]); garage.object_table.insert(&final_object).await?; // Send response saying ok we're done let result = s3_xml::CompleteMultipartUploadResult { xmlns: (), location: None, - bucket: s3_xml::Value(bucket), + bucket: s3_xml::Value(bucket_name.to_string()), key: s3_xml::Value(key), etag: s3_xml::Value(etag), }; @@ -528,7 +528,7 @@ pub async fn handle_complete_multipart_upload( pub async fn handle_abort_multipart_upload( garage: Arc<Garage>, - bucket: &str, + bucket_id: Uuid, key: &str, upload_id: &str, ) -> Result<Response<Body>, Error> { @@ -536,7 +536,7 @@ pub async fn handle_abort_multipart_upload( let object = garage .object_table - .get(&bucket.to_string(), &key.to_string()) + .get(&bucket_id, &key.to_string()) .await?; let object = object.ok_or_else(|| Error::BadRequest("Object not found".to_string()))?; @@ -550,7 +550,7 @@ pub async fn handle_abort_multipart_upload( }; object_version.state = ObjectVersionState::Aborted; - let final_object = Object::new(bucket.to_string(), key.to_string(), vec![object_version]); + let final_object = Object::new(bucket_id, key.to_string(), vec![object_version]); garage.object_table.insert(&final_object).await?; Ok(Response::new(Body::from(vec![]))) diff --git a/src/api/s3_website.rs b/src/api/s3_website.rs index 37c8b86c..da67c4cd 100644 --- a/src/api/s3_website.rs +++ b/src/api/s3_website.rs @@ -7,9 +7,10 @@ use serde::{Deserialize, Serialize}; use crate::error::*; use crate::s3_xml::{xmlns_tag, IntValue, Value}; use crate::signature::verify_signed_content; -use garage_model::bucket_table::BucketState; + use garage_model::garage::Garage; use garage_table::*; +use garage_util::crdt; use garage_util::data::Hash; pub async fn handle_delete_website( @@ -17,14 +18,18 @@ pub async fn handle_delete_website( bucket: String, ) -> Result<Response<Body>, Error> { let mut bucket = garage - .bucket_table + .bucket_alias_table .get(&EmptyKey, &bucket) .await? .ok_or(Error::NotFound)?; - if let BucketState::Present(state) = bucket.state.get_mut() { - state.website.update(false); - garage.bucket_table.insert(&bucket).await?; + if let crdt::Deletable::Present(state) = bucket.state.get_mut() { + let mut new_param = state.clone(); + new_param.website_access = false; + bucket.state.update(crdt::Deletable::present(new_param)); + garage.bucket_alias_table.insert(&bucket).await?; + } else { + unreachable!(); } Ok(Response::builder() @@ -43,7 +48,7 @@ pub async fn handle_put_website( verify_signed_content(content_sha256, &body[..])?; let mut bucket = garage - .bucket_table + .bucket_alias_table .get(&EmptyKey, &bucket) .await? .ok_or(Error::NotFound)?; @@ -51,9 +56,13 @@ pub async fn handle_put_website( let conf: WebsiteConfiguration = from_reader(&body as &[u8])?; conf.validate()?; - if let BucketState::Present(state) = bucket.state.get_mut() { - state.website.update(true); - garage.bucket_table.insert(&bucket).await?; + if let crdt::Deletable::Present(state) = bucket.state.get() { + let mut new_param = state.clone(); + new_param.website_access = true; + bucket.state.update(crdt::Deletable::present(new_param)); + garage.bucket_alias_table.insert(&bucket).await?; + } else { + unreachable!(); } Ok(Response::builder() diff --git a/src/api/signature.rs b/src/api/signature.rs index 53ca2ce5..b5da7b62 100644 --- a/src/api/signature.rs +++ b/src/api/signature.rs @@ -64,7 +64,7 @@ pub async fn check_signature( .key_table .get(&EmptyKey, &authorization.key_id) .await? - .filter(|k| !k.deleted.get()) + .filter(|k| !k.state.is_deleted()) .ok_or_else(|| Error::Forbidden(format!("No such key: {}", authorization.key_id)))?; let canonical_request = canonical_request( |