aboutsummaryrefslogtreecommitdiff
path: root/src/api
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2021-12-14 13:55:11 +0100
committerAlex Auvolat <alex@adnab.me>2022-01-04 12:45:46 +0100
commit5b1117e582db16cc5aa50840a685875cbd5501f4 (patch)
tree06fec47bf56cb08cb51334454dc15f98352c98f2 /src/api
parent8f6026de5ecd44cbe0fc0bcd47638a1ece860439 (diff)
downloadgarage-5b1117e582db16cc5aa50840a685875cbd5501f4.tar.gz
garage-5b1117e582db16cc5aa50840a685875cbd5501f4.zip
New model for buckets
Diffstat (limited to 'src/api')
-rw-r--r--src/api/Cargo.toml8
-rw-r--r--src/api/api_server.rs109
-rw-r--r--src/api/s3_bucket.rs62
-rw-r--r--src/api/s3_copy.rs20
-rw-r--r--src/api/s3_delete.rs14
-rw-r--r--src/api/s3_get.rs9
-rw-r--r--src/api/s3_list.rs8
-rw-r--r--src/api/s3_put.rs44
-rw-r--r--src/api/s3_website.rs27
-rw-r--r--src/api/signature.rs2
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(&copy_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(