Common error type and admin error type that uses it

This commit is contained in:
Alex Auvolat
2022-05-13 14:30:30 +02:00
parent 983037d965
commit c0fb9fd0fe
11 changed files with 249 additions and 29 deletions
+1 -1
View File
@@ -15,9 +15,9 @@ use prometheus::{Encoder, TextEncoder};
use garage_model::garage::Garage;
use garage_util::error::Error as GarageError;
use crate::error::*;
use crate::generic_server::*;
use crate::admin::error::*;
use crate::admin::bucket::*;
use crate::admin::cluster::*;
use crate::admin::key::*;
+11 -11
View File
@@ -17,8 +17,8 @@ use garage_model::permission::*;
use garage_model::s3::object_table::ObjectFilter;
use crate::admin::key::ApiBucketKeyPerm;
use crate::error::*;
use crate::helpers::*;
use crate::admin::error::*;
use crate::admin::parse_json_body;
pub async fn handle_list_buckets(garage: &Arc<Garage>) -> Result<Response<Body>, Error> {
let buckets = garage
@@ -97,9 +97,9 @@ pub async fn handle_get_bucket_info(
.await?
.ok_or_bad_request("Bucket not found")?,
_ => {
return Err(Error::BadRequest(
"Either id or globalAlias must be provided (but not both)".into(),
))
return Err(Error::bad_request(
"Either id or globalAlias must be provided (but not both)"
));
}
};
@@ -225,7 +225,7 @@ pub async fn handle_create_bucket(
if let Some(ga) = &req.global_alias {
if !is_valid_bucket_name(ga) {
return Err(Error::BadRequest(format!(
return Err(Error::bad_request(format!(
"{}: {}",
ga, INVALID_BUCKET_NAME_MESSAGE
)));
@@ -240,7 +240,7 @@ pub async fn handle_create_bucket(
if let Some(la) = &req.local_alias {
if !is_valid_bucket_name(&la.alias) {
return Err(Error::BadRequest(format!(
return Err(Error::bad_request(format!(
"{}: {}",
la.alias, INVALID_BUCKET_NAME_MESSAGE
)));
@@ -250,10 +250,10 @@ pub async fn handle_create_bucket(
.key_table
.get(&EmptyKey, &la.access_key_id)
.await?
.ok_or(Error::NoSuchKey)?;
let state = key.state.as_option().ok_or(Error::NoSuchKey)?;
.ok_or(Error::NoSuchAccessKey)?;
let state = key.state.as_option().ok_or(Error::NoSuchAccessKey)?;
if matches!(state.local_aliases.get(&la.alias), Some(_)) {
return Err(Error::BadRequest("Local alias already exists".into()));
return Err(Error::bad_request("Local alias already exists"));
}
}
@@ -333,7 +333,7 @@ pub async fn handle_delete_bucket(
)
.await?;
if !objects.is_empty() {
return Err(Error::BadRequest("Bucket is not empty".into()));
return Err(Error::bad_request("Bucket is not empty"));
}
// --- done checking, now commit ---
+2 -2
View File
@@ -13,8 +13,8 @@ use garage_rpc::layout::*;
use garage_model::garage::Garage;
use crate::error::*;
use crate::helpers::*;
use crate::admin::error::*;
use crate::admin::parse_json_body;
pub async fn handle_get_cluster_status(garage: &Arc<Garage>) -> Result<Response<Body>, Error> {
let res = GetClusterStatusResponse {
+94
View File
@@ -0,0 +1,94 @@
use err_derive::Error;
use hyper::header::HeaderValue;
use hyper::{Body, HeaderMap, StatusCode};
use garage_model::helper::error::Error as HelperError;
use garage_util::error::Error as GarageError;
use crate::generic_server::ApiError;
pub use crate::common_error::*;
/// Errors of this crate
#[derive(Debug, Error)]
pub enum Error {
#[error(display = "{}", _0)]
/// Error from common error
CommonError(CommonError),
// Category: cannot process
/// No proper api key was used, or the signature was invalid
#[error(display = "Forbidden: {}", _0)]
Forbidden(String),
/// The API access key does not exist
#[error(display = "Access key not found")]
NoSuchAccessKey,
/// The bucket requested don't exists
#[error(display = "Bucket not found")]
NoSuchBucket,
/// 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
/// Bucket name is not valid according to AWS S3 specs
#[error(display = "Invalid bucket name")]
InvalidBucketName,
/// The client sent a request for an action not supported by garage
#[error(display = "Unimplemented action: {}", _0)]
NotImplemented(String),
}
impl<T> From<T> for Error
where CommonError: From<T> {
fn from(err: T) -> Self {
Error::CommonError(CommonError::from(err))
}
}
impl From<HelperError> for Error {
fn from(err: HelperError) -> Self {
match err {
HelperError::Internal(i) => Self::CommonError(CommonError::InternalError(i)),
HelperError::BadRequest(b) => Self::CommonError(CommonError::BadRequest(b)),
HelperError::InvalidBucketName(_) => Self::InvalidBucketName,
HelperError::NoSuchAccessKey(_) => Self::NoSuchAccessKey,
HelperError::NoSuchBucket(_) => Self::NoSuchBucket,
}
}
}
impl ApiError for Error {
/// Get the HTTP status code that best represents the meaning of the error for the client
fn http_status_code(&self) -> StatusCode {
match self {
Error::CommonError(c) => c.http_status_code(),
Error::NoSuchAccessKey | Error::NoSuchBucket => StatusCode::NOT_FOUND,
Error::BucketNotEmpty | Error::BucketAlreadyExists => StatusCode::CONFLICT,
Error::Forbidden(_) => StatusCode::FORBIDDEN,
Error::NotImplemented(_) => StatusCode::NOT_IMPLEMENTED,
Error::InvalidBucketName => StatusCode::BAD_REQUEST,
}
}
fn add_http_headers(&self, _header_map: &mut HeaderMap<HeaderValue>) {
// nothing
}
fn http_body(&self, garage_region: &str, path: &str) -> Body {
Body::from(format!("ERROR: {}\n\ngarage region: {}\npath: {}", self, garage_region, path))
}
}
impl Error {
pub fn bad_request<M: ToString>(msg: M) -> Self {
Self::CommonError(CommonError::BadRequest(msg.to_string()))
}
}
+8 -8
View File
@@ -11,8 +11,8 @@ use garage_table::*;
use garage_model::garage::Garage;
use garage_model::key_table::*;
use crate::error::*;
use crate::helpers::*;
use crate::admin::error::*;
use crate::admin::parse_json_body;
pub async fn handle_list_keys(garage: &Arc<Garage>) -> Result<Response<Body>, Error> {
let res = garage
@@ -54,13 +54,13 @@ pub async fn handle_get_key_info(
.key_table
.get(&EmptyKey, &id)
.await?
.ok_or(Error::NoSuchKey)?
.ok_or(Error::NoSuchAccessKey)?
} else if let Some(search) = search {
garage
.key_helper()
.get_existing_matching_key(&search)
.await
.map_err(|_| Error::NoSuchKey)?
.map_err(|_| Error::NoSuchAccessKey)?
} else {
unreachable!();
};
@@ -96,9 +96,9 @@ pub async fn handle_update_key(
.key_table
.get(&EmptyKey, &id)
.await?
.ok_or(Error::NoSuchKey)?;
.ok_or(Error::NoSuchAccessKey)?;
let key_state = key.state.as_option_mut().ok_or(Error::NoSuchKey)?;
let key_state = key.state.as_option_mut().ok_or(Error::NoSuchAccessKey)?;
if let Some(new_name) = req.name {
key_state.name.update(new_name);
@@ -131,9 +131,9 @@ pub async fn handle_delete_key(garage: &Arc<Garage>, id: String) -> Result<Respo
.key_table
.get(&EmptyKey, &id)
.await?
.ok_or(Error::NoSuchKey)?;
.ok_or(Error::NoSuchAccessKey)?;
key.state.as_option().ok_or(Error::NoSuchKey)?;
key.state.as_option().ok_or(Error::NoSuchAccessKey)?;
garage.key_helper().delete_key(&mut key).await?;
+13
View File
@@ -1,6 +1,19 @@
pub mod api_server;
mod router;
mod error;
mod bucket;
mod cluster;
mod key;
use serde::{Deserialize};
use hyper::{Request, Body};
use error::*;
pub async fn parse_json_body<T: for<'de> Deserialize<'de>>(req: Request<Body>) -> Result<T, Error> {
let body = hyper::body::to_bytes(req.into_body()).await?;
let resp: T = serde_json::from_slice(&body).ok_or_bad_request("Invalid JSON")?;
Ok(resp)
}
+1 -1
View File
@@ -2,7 +2,7 @@ use std::borrow::Cow;
use hyper::{Method, Request};
use crate::error::*;
use crate::admin::error::*;
use crate::router_macros::*;
pub enum Authorization {