more complete admin API #298

Merged
lx merged 48 commits from admin-api into main 2022-05-24 10:16:40 +00:00
3 changed files with 108 additions and 6 deletions
Showing only changes of commit de1a5b87b6 - Show all commits

View file

@ -435,8 +435,9 @@ OR
```json ```json
{ {
"localAlias": { "localAlias": {
"key": "GK31c2f218a2e44f485b94239e", "accessKeyId": "GK31c2f218a2e44f485b94239e",
"alias": "NameOfMyBucket" "alias": "NameOfMyBucket",
"allPermissions": true
} }
} }
``` ```

View file

@ -145,6 +145,7 @@ impl ApiHandler for AdminApiServer {
Endpoint::GetBucketInfo { id, global_alias } => { Endpoint::GetBucketInfo { id, global_alias } => {
handle_get_bucket_info(&self.garage, id, global_alias).await handle_get_bucket_info(&self.garage, id, global_alias).await
} }
Endpoint::CreateBucket => handle_create_bucket(&self.garage, req).await,
_ => Err(Error::NotImplemented(format!( _ => Err(Error::NotImplemented(format!(
"Admin endpoint {} not implemented yet", "Admin endpoint {} not implemented yet",
endpoint.name() endpoint.name()

View file

@ -9,9 +9,10 @@ use garage_util::error::Error as GarageError;
use garage_table::*; use garage_table::*;
use garage_model::bucket_alias_table::*;
use garage_model::bucket_table::*; use garage_model::bucket_table::*;
use garage_model::garage::Garage; use garage_model::garage::Garage;
use garage_model::key_table::*; use garage_model::permission::*;
use crate::admin::key::KeyBucketPermResult; use crate::admin::key::KeyBucketPermResult;
use crate::error::*; use crate::error::*;
@ -47,7 +48,7 @@ pub async fn handle_list_buckets(garage: &Arc<Garage>) -> Result<Response<Body>,
.items() .items()
.iter() .iter()
.filter(|(_, _, a)| *a) .filter(|(_, _, a)| *a)
.map(|((k, n), _, _)| ListBucketLocalAlias { .map(|((k, n), _, _)| BucketLocalAlias {
access_key_id: k.to_string(), access_key_id: k.to_string(),
alias: n.to_string(), alias: n.to_string(),
}) })
@ -68,11 +69,11 @@ struct ListBucketResultItem {
#[serde(rename = "globalAliases")] #[serde(rename = "globalAliases")]
global_aliases: Vec<String>, global_aliases: Vec<String>,
#[serde(rename = "localAliases")] #[serde(rename = "localAliases")]
local_aliases: Vec<ListBucketLocalAlias>, local_aliases: Vec<BucketLocalAlias>,
} }
#[derive(Serialize)] #[derive(Serialize)]
struct ListBucketLocalAlias { struct BucketLocalAlias {
#[serde(rename = "accessKeyId")] #[serde(rename = "accessKeyId")]
access_key_id: String, access_key_id: String,
alias: String, alias: String,
@ -105,6 +106,13 @@ pub async fn handle_get_bucket_info(
.get_existing_bucket(bucket_id) .get_existing_bucket(bucket_id)
.await?; .await?;
bucket_info_results(garage, bucket).await
}
async fn bucket_info_results(
garage: &Arc<Garage>,
bucket: Bucket,
) -> Result<Response<Body>, Error> {
let mut relevant_keys = HashMap::new(); let mut relevant_keys = HashMap::new();
for (k, _) in bucket for (k, _) in bucket
.state .state
@ -206,3 +214,95 @@ struct GetBucketInfoKey {
#[serde(rename = "bucketLocalAliases")] #[serde(rename = "bucketLocalAliases")]
bucket_local_aliases: Vec<String>, bucket_local_aliases: Vec<String>,
} }
pub async fn handle_create_bucket(
garage: &Arc<Garage>,
req: Request<Body>,
) -> Result<Response<Body>, Error> {
let req = parse_json_body::<CreateBucketRequest>(req).await?;
if let Some(ga) = &req.global_alias {
if !is_valid_bucket_name(ga) {
return Err(Error::BadRequest(format!(
"{}: {}",
ga, INVALID_BUCKET_NAME_MESSAGE
)));
}
if let Some(alias) = garage.bucket_alias_table.get(&EmptyKey, ga).await? {
if alias.state.get().is_some() {
return Err(Error::BucketAlreadyExists);
}
}
}
if let Some(la) = &req.local_alias {
if !is_valid_bucket_name(&la.alias) {
return Err(Error::BadRequest(format!(
"{}: {}",
la.alias, INVALID_BUCKET_NAME_MESSAGE
)));
}
let key = garage
.key_table
.get(&EmptyKey, &la.access_key_id)
.await?
.ok_or(Error::NoSuchKey)?;
let state = key.state.as_option().ok_or(Error::NoSuchKey)?;
if matches!(state.local_aliases.get(&la.alias), Some(_)) {
return Err(Error::BadRequest("Local alias already exists".into()));
}
}
let bucket = Bucket::new();
garage.bucket_table.insert(&bucket).await?;
if let Some(ga) = &req.global_alias {
garage
.bucket_helper()
.set_global_bucket_alias(bucket.id, ga)
.await?;
}
if let Some(la) = &req.local_alias {
garage
.bucket_helper()
.set_local_bucket_alias(bucket.id, &la.access_key_id, &la.alias)
.await?;
if la.all_permissions {
garage
.bucket_helper()
.set_bucket_key_permissions(
bucket.id,
&la.access_key_id,
BucketKeyPerm::ALL_PERMISSIONS,
)
.await?;
}
}
let bucket = garage
.bucket_table
.get(&EmptyKey, &bucket.id)
.await?
.ok_or_internal_error("Bucket should now exist but doesn't")?;
bucket_info_results(garage, bucket).await
}
#[derive(Deserialize)]
struct CreateBucketRequest {
#[serde(rename = "globalAlias")]
global_alias: Option<String>,
#[serde(rename = "localAlias")]
local_alias: Option<CreateBucketLocalAlias>,
}
#[derive(Deserialize)]
struct CreateBucketLocalAlias {
#[serde(rename = "accessKeyId")]
access_key_id: String,
alias: String,
#[serde(rename = "allPermissions", default)]
all_permissions: bool,
}