Fix some error codes

This commit is contained in:
Alex Auvolat 2022-01-05 17:07:36 +01:00
parent fb1e31add0
commit 168a90dfb5
No known key found for this signature in database
GPG key ID: EDABF9711E244EB1
8 changed files with 53 additions and 36 deletions

View file

@ -107,6 +107,7 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
.and_then(|root_domain| host_to_bucket(&host, root_domain)); .and_then(|root_domain| host_to_bucket(&host, root_domain));
let endpoint = Endpoint::from_request(&req, bucket.map(ToOwned::to_owned))?; let endpoint = Endpoint::from_request(&req, bucket.map(ToOwned::to_owned))?;
debug!("Endpoint: {:?}", endpoint);
// Special code path for CreateBucket API endpoint // Special code path for CreateBucket API endpoint
if let Endpoint::CreateBucket { bucket } = endpoint { if let Endpoint::CreateBucket { bucket } = endpoint {
@ -306,7 +307,7 @@ async fn resolve_bucket(
.bucket_helper() .bucket_helper()
.resolve_global_bucket_name(bucket_name) .resolve_global_bucket_name(bucket_name)
.await? .await?
.ok_or(Error::NotFound)?) .ok_or(Error::NoSuchBucket)?)
} }
} }

View file

@ -35,8 +35,24 @@ pub enum Error {
AuthorizationHeaderMalformed(String), AuthorizationHeaderMalformed(String),
/// The object requested don't exists /// The object requested don't exists
#[error(display = "Not found")] #[error(display = "Key not found")]
NotFound, NoSuchKey,
/// The bucket requested don't exists
#[error(display = "Bucket not found")]
NoSuchBucket,
/// The multipart upload requested don't exists
#[error(display = "Upload not found")]
NoSuchUpload,
/// Tried to create a bucket that already exist
#[error(display = "Bucket already exists")]
BucketAlreadyExists,
/// Tried to delete a non-empty bucket
#[error(display = "Tried to delete a non-empty bucket")]
BucketNotEmpty,
// Category: bad request // Category: bad request
/// The request contained an invalid UTF-8 sequence in its path or in other parameters /// The request contained an invalid UTF-8 sequence in its path or in other parameters
@ -97,7 +113,8 @@ impl Error {
/// Get the HTTP status code that best represents the meaning of the error for the client /// Get the HTTP status code that best represents the meaning of the error for the client
pub fn http_status_code(&self) -> StatusCode { pub fn http_status_code(&self) -> StatusCode {
match self { match self {
Error::NotFound => StatusCode::NOT_FOUND, Error::NoSuchKey | Error::NoSuchBucket | Error::NoSuchUpload => StatusCode::NOT_FOUND,
Error::BucketNotEmpty | Error::BucketAlreadyExists => StatusCode::CONFLICT,
Error::Forbidden(_) => StatusCode::FORBIDDEN, Error::Forbidden(_) => StatusCode::FORBIDDEN,
Error::InternalError( Error::InternalError(
GarageError::Timeout GarageError::Timeout
@ -115,9 +132,14 @@ impl Error {
pub fn aws_code(&self) -> &'static str { pub fn aws_code(&self) -> &'static str {
match self { match self {
Error::NotFound => "NoSuchKey", Error::NoSuchKey => "NoSuchKey",
Error::NoSuchBucket => "NoSuchBucket",
Error::NoSuchUpload => "NoSuchUpload",
Error::BucketAlreadyExists => "BucketAlreadyExists",
Error::BucketNotEmpty => "BucketNotEmpty",
Error::Forbidden(_) => "AccessDenied", Error::Forbidden(_) => "AccessDenied",
Error::AuthorizationHeaderMalformed(_) => "AuthorizationHeaderMalformed", Error::AuthorizationHeaderMalformed(_) => "AuthorizationHeaderMalformed",
Error::NotImplemented(_) => "NotImplemented",
Error::InternalError( Error::InternalError(
GarageError::Timeout GarageError::Timeout
| GarageError::RemoteError(_) | GarageError::RemoteError(_)

View file

@ -153,10 +153,7 @@ pub async fn handle_create_bucket(
// otherwise return a forbidden error. // otherwise return a forbidden error.
let kp = api_key.bucket_permissions(&bucket_id); let kp = api_key.bucket_permissions(&bucket_id);
if !(kp.allow_write || kp.allow_owner) { if !(kp.allow_write || kp.allow_owner) {
return Err(Error::Forbidden(format!( return Err(Error::BucketAlreadyExists);
"Key {} does not have write or owner permissions on bucket {}",
api_key.key_id, bucket_name
)));
} }
} else { } else {
// Create the bucket! // Create the bucket!
@ -231,10 +228,7 @@ pub async fn handle_delete_bucket(
.get_range(&bucket_id, None, Some(DeletedFilter::NotDeleted), 10) .get_range(&bucket_id, None, Some(DeletedFilter::NotDeleted), 10)
.await?; .await?;
if !objects.is_empty() { if !objects.is_empty() {
return Err(Error::BadRequest(format!( return Err(Error::BucketNotEmpty);
"Bucket {} is not empty",
bucket_name
)));
} }
// --- done checking, now commit --- // --- done checking, now commit ---

View file

@ -27,14 +27,14 @@ pub async fn handle_copy(
.object_table .object_table
.get(&source_bucket_id, &source_key.to_string()) .get(&source_bucket_id, &source_key.to_string())
.await? .await?
.ok_or(Error::NotFound)?; .ok_or(Error::NoSuchKey)?;
let source_last_v = source_object let source_last_v = source_object
.versions() .versions()
.iter() .iter()
.rev() .rev()
.find(|v| v.is_complete()) .find(|v| v.is_complete())
.ok_or(Error::NotFound)?; .ok_or(Error::NoSuchKey)?;
let source_last_state = match &source_last_v.state { let source_last_state = match &source_last_v.state {
ObjectVersionState::Complete(x) => x, ObjectVersionState::Complete(x) => x,
@ -47,7 +47,7 @@ pub async fn handle_copy(
// Implement x-amz-metadata-directive: REPLACE // Implement x-amz-metadata-directive: REPLACE
let old_meta = match source_last_state { let old_meta = match source_last_state {
ObjectVersionData::DeleteMarker => { ObjectVersionData::DeleteMarker => {
return Err(Error::NotFound); return Err(Error::NoSuchKey);
} }
ObjectVersionData::Inline(meta, _bytes) => meta, ObjectVersionData::Inline(meta, _bytes) => meta,
ObjectVersionData::FirstBlock(meta, _fbh) => meta, ObjectVersionData::FirstBlock(meta, _fbh) => meta,
@ -88,7 +88,7 @@ pub async fn handle_copy(
.version_table .version_table
.get(&source_last_v.uuid, &EmptyKey) .get(&source_last_v.uuid, &EmptyKey)
.await?; .await?;
let source_version = source_version.ok_or(Error::NotFound)?; let source_version = source_version.ok_or(Error::NoSuchKey)?;
// Write an "uploading" marker in Object table // Write an "uploading" marker in Object table
// This holds a reference to the object in the Version table // This holds a reference to the object in the Version table

View file

@ -21,7 +21,7 @@ async fn handle_delete_internal(
.object_table .object_table
.get(&bucket_id, &key.to_string()) .get(&bucket_id, &key.to_string())
.await? .await?
.ok_or(Error::NotFound)?; // No need to delete .ok_or(Error::NoSuchKey)?; // No need to delete
let interesting_versions = object.versions().iter().filter(|v| { let interesting_versions = object.versions().iter().filter(|v| {
!matches!( !matches!(
@ -40,7 +40,7 @@ async fn handle_delete_internal(
timestamp = std::cmp::max(timestamp, v.timestamp + 1); timestamp = std::cmp::max(timestamp, v.timestamp + 1);
} }
let deleted_version = version_to_delete.ok_or(Error::NotFound)?; let deleted_version = version_to_delete.ok_or(Error::NoSuchKey)?;
let version_uuid = gen_uuid(); let version_uuid = gen_uuid();

View file

@ -92,14 +92,14 @@ pub async fn handle_head(
.object_table .object_table
.get(&bucket_id, &key.to_string()) .get(&bucket_id, &key.to_string())
.await? .await?
.ok_or(Error::NotFound)?; .ok_or(Error::NoSuchKey)?;
let version = object let version = object
.versions() .versions()
.iter() .iter()
.rev() .rev()
.find(|v| v.is_data()) .find(|v| v.is_data())
.ok_or(Error::NotFound)?; .ok_or(Error::NoSuchKey)?;
let version_meta = match &version.state { let version_meta = match &version.state {
ObjectVersionState::Complete(ObjectVersionData::Inline(meta, _)) => meta, ObjectVersionState::Complete(ObjectVersionData::Inline(meta, _)) => meta,
@ -131,21 +131,21 @@ pub async fn handle_get(
.object_table .object_table
.get(&bucket_id, &key.to_string()) .get(&bucket_id, &key.to_string())
.await? .await?
.ok_or(Error::NotFound)?; .ok_or(Error::NoSuchKey)?;
let last_v = object let last_v = object
.versions() .versions()
.iter() .iter()
.rev() .rev()
.find(|v| v.is_complete()) .find(|v| v.is_complete())
.ok_or(Error::NotFound)?; .ok_or(Error::NoSuchKey)?;
let last_v_data = match &last_v.state { let last_v_data = match &last_v.state {
ObjectVersionState::Complete(x) => x, ObjectVersionState::Complete(x) => x,
_ => unreachable!(), _ => unreachable!(),
}; };
let last_v_meta = match last_v_data { let last_v_meta = match last_v_data {
ObjectVersionData::DeleteMarker => return Err(Error::NotFound), ObjectVersionData::DeleteMarker => return Err(Error::NoSuchKey),
ObjectVersionData::Inline(meta, _) => meta, ObjectVersionData::Inline(meta, _) => meta,
ObjectVersionData::FirstBlock(meta, _) => meta, ObjectVersionData::FirstBlock(meta, _) => meta,
}; };
@ -196,7 +196,7 @@ pub async fn handle_get(
let get_next_blocks = garage.version_table.get(&last_v.uuid, &EmptyKey); let get_next_blocks = garage.version_table.get(&last_v.uuid, &EmptyKey);
let (first_block, version) = futures::try_join!(read_first_block, get_next_blocks)?; let (first_block, version) = futures::try_join!(read_first_block, get_next_blocks)?;
let version = version.ok_or(Error::NotFound)?; let version = version.ok_or(Error::NoSuchKey)?;
let mut blocks = version let mut blocks = version
.blocks .blocks
@ -261,7 +261,7 @@ async fn handle_get_range(
let version = garage.version_table.get(&version.uuid, &EmptyKey).await?; let version = garage.version_table.get(&version.uuid, &EmptyKey).await?;
let version = match version { let version = match version {
Some(v) => v, Some(v) => v,
None => return Err(Error::NotFound), None => return Err(Error::NoSuchKey),
}; };
// We will store here the list of blocks that have an intersection with the requested // We will store here the list of blocks that have an intersection with the requested

View file

@ -382,7 +382,7 @@ pub async fn handle_put_part(
.iter() .iter()
.any(|v| v.uuid == version_uuid && v.is_uploading()) .any(|v| v.uuid == version_uuid && v.is_uploading())
{ {
return Err(Error::NotFound); return Err(Error::NoSuchUpload);
} }
// Copy block to store // Copy block to store
@ -449,15 +449,15 @@ pub async fn handle_complete_multipart_upload(
garage.version_table.get(&version_uuid, &EmptyKey), garage.version_table.get(&version_uuid, &EmptyKey),
)?; )?;
let object = object.ok_or_else(|| Error::BadRequest("Object not found".to_string()))?; let object = object.ok_or(Error::NoSuchKey)?;
let mut object_version = object let mut object_version = object
.versions() .versions()
.iter() .iter()
.find(|v| v.uuid == version_uuid && v.is_uploading()) .find(|v| v.uuid == version_uuid && v.is_uploading())
.cloned() .cloned()
.ok_or_else(|| Error::BadRequest("Version not found".to_string()))?; .ok_or(Error::NoSuchUpload)?;
let version = version.ok_or_else(|| Error::BadRequest("Version not found".to_string()))?; let version = version.ok_or(Error::NoSuchKey)?;
if version.blocks.is_empty() { if version.blocks.is_empty() {
return Err(Error::BadRequest("No data was uploaded".to_string())); return Err(Error::BadRequest("No data was uploaded".to_string()));
} }
@ -538,14 +538,14 @@ pub async fn handle_abort_multipart_upload(
.object_table .object_table
.get(&bucket_id, &key.to_string()) .get(&bucket_id, &key.to_string())
.await?; .await?;
let object = object.ok_or_else(|| Error::BadRequest("Object not found".to_string()))?; let object = object.ok_or(Error::NoSuchKey)?;
let object_version = object let object_version = object
.versions() .versions()
.iter() .iter()
.find(|v| v.uuid == version_uuid && v.is_uploading()); .find(|v| v.uuid == version_uuid && v.is_uploading());
let mut object_version = match object_version { let mut object_version = match object_version {
None => return Err(Error::NotFound), None => return Err(Error::NoSuchUpload),
Some(x) => x.clone(), Some(x) => x.clone(),
}; };
@ -611,9 +611,9 @@ pub(crate) fn get_headers(req: &Request<Body>) -> Result<ObjectVersionHeaders, E
} }
fn decode_upload_id(id: &str) -> Result<Uuid, Error> { fn decode_upload_id(id: &str) -> Result<Uuid, Error> {
let id_bin = hex::decode(id).ok_or_bad_request("Invalid upload ID")?; let id_bin = hex::decode(id).map_err(|_| Error::NoSuchUpload)?;
if id_bin.len() != 32 { if id_bin.len() != 32 {
return None.ok_or_bad_request("Invalid upload ID"); return Err(Error::NoSuchUpload);
} }
let mut uuid = [0u8; 32]; let mut uuid = [0u8; 32];
uuid.copy_from_slice(&id_bin[..]); uuid.copy_from_slice(&id_bin[..]);

View file

@ -22,7 +22,7 @@ pub async fn handle_delete_website(
.bucket_table .bucket_table
.get(&EmptyKey, &bucket_id) .get(&EmptyKey, &bucket_id)
.await? .await?
.ok_or(Error::NotFound)?; .ok_or(Error::NoSuchBucket)?;
if let crdt::Deletable::Present(param) = &mut bucket.state { if let crdt::Deletable::Present(param) = &mut bucket.state {
param.website_config.update(None); param.website_config.update(None);
@ -50,7 +50,7 @@ pub async fn handle_put_website(
.bucket_table .bucket_table
.get(&EmptyKey, &bucket_id) .get(&EmptyKey, &bucket_id)
.await? .await?
.ok_or(Error::NotFound)?; .ok_or(Error::NoSuchBucket)?;
let conf: WebsiteConfiguration = from_reader(&body as &[u8])?; let conf: WebsiteConfiguration = from_reader(&body as &[u8])?;
conf.validate()?; conf.validate()?;