more complete admin API #298

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

View file

@ -209,3 +209,52 @@ Similarly to the CLI, the body must include the incremented
version number, which MUST be 1 + the value of the currently version number, which MUST be 1 + the value of the currently
existing layout in the cluster. existing layout in the cluster.
## Access key operations
### ListKeys `GET /key`
Returns all API access keys in the cluster.
Example response:
```json
#TODO
```
### CreateKey `POST /key`
Creates a new API access key.
Request body format:
```json
{
"name": "NameOfMyKey"
}
```
### GetKeyInfo `GET /key?id=<acces key id>`
Returns information about the requested API access key.
Example response:
```json
#TODO
```
### DeleteKey `DELETE /key?id=<acces key id>`
Deletes an API access key.
### UpdateKey `POST /key?id=<acces key id>`
Updates information about the specified API access key.
Request body format:
```json
#TODO
```

View file

@ -129,12 +129,10 @@ impl ApiHandler for AdminApiServer {
Endpoint::UpdateClusterLayout => handle_update_cluster_layout(&self.garage, req).await, Endpoint::UpdateClusterLayout => handle_update_cluster_layout(&self.garage, req).await,
Endpoint::ApplyClusterLayout => handle_apply_cluster_layout(&self.garage, req).await, Endpoint::ApplyClusterLayout => handle_apply_cluster_layout(&self.garage, req).await,
Endpoint::RevertClusterLayout => handle_revert_cluster_layout(&self.garage, req).await, Endpoint::RevertClusterLayout => handle_revert_cluster_layout(&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

@ -1,8 +1,9 @@
use crate::error::*; use std::borrow::Cow;
use hyper::{Method, Request}; use hyper::{Method, Request};
use crate::router_macros::router_match; use crate::error::*;
use crate::router_macros::*;
pub enum Authorization { pub enum Authorization {
MetricsToken, MetricsToken,
@ -21,6 +22,17 @@ pub enum Endpoint {
UpdateClusterLayout, UpdateClusterLayout,
ApplyClusterLayout, ApplyClusterLayout,
RevertClusterLayout, RevertClusterLayout,
ListKeys,
CreateKey,
GetKeyInfo {
id: String,
},
DeleteKey {
id: String,
},
UpdateKey {
id: String,
},
}} }}
impl Endpoint { impl Endpoint {
@ -28,24 +40,32 @@ impl Endpoint {
/// possibly extracted from the Host header. /// possibly extracted from the Host header.
/// Returns Self plus bucket name, if endpoint is not Endpoint::ListBuckets /// Returns Self plus bucket name, if endpoint is not Endpoint::ListBuckets
pub fn from_request<T>(req: &Request<T>) -> Result<Self, Error> { pub fn from_request<T>(req: &Request<T>) -> Result<Self, Error> {
let path = req.uri().path(); let uri = req.uri();
let path = uri.path();
let query = uri.query();
use Endpoint::*; let mut query = QueryParameters::from_query(query.unwrap_or_default())?;
let res = match (req.method(), path) {
(&Method::OPTIONS, _) => Options, let res = router_match!(@gen_path_parser (req.method(), path, query) [
(&Method::GET, "/metrics") => Metrics, OPTIONS _ => Options,
(&Method::GET, "/status") => GetClusterStatus, GET "/metrics" => Metrics,
(&Method::GET, "/layout") => GetClusterLayout, GET "/status" => GetClusterStatus,
(&Method::POST, "/layout") => UpdateClusterLayout, // Layout endpoints
(&Method::POST, "/layout/apply") => ApplyClusterLayout, GET "/layout" => GetClusterLayout,
(&Method::POST, "/layout/revert") => RevertClusterLayout, POST "/layout" => UpdateClusterLayout,
(m, p) => { POST "/layout/apply" => ApplyClusterLayout,
return Err(Error::BadRequest(format!( POST "/layout/revert" => RevertClusterLayout,
"Unknown API endpoint: {} {}", // API key endpoints
m, p GET "/key" if id => GetKeyInfo (query::id),
))) POST "/key" if id => UpdateKey (query::id),
POST "/key" => CreateKey,
DELETE "/key" if id => DeleteKey (query::id),
GET "/key" => ListKeys,
]);
if let Some(message) = query.nonempty_message() {
debug!("Unused query parameter: {}", message)
} }
};
Ok(res) Ok(res)
} }
@ -57,3 +77,7 @@ impl Endpoint {
} }
} }
} }
generateQueryParameters! {
"id" => id
}

View file

@ -23,6 +23,29 @@ macro_rules! router_match {
_ => None _ => None
} }
}}; }};
(@gen_path_parser ($method:expr, $reqpath:expr, $query:expr)
[
$($meth:ident $path:pat $(if $required:ident)? => $api:ident $(($($conv:ident :: $param:ident),*))?,)*
]) => {{
{
use Endpoint::*;
match ($method, $reqpath) {
$(
(&Method::$meth, $path) if true $(&& $query.$required.is_some())? => $api {
$($(
$param: router_match!(@@parse_param $query, $conv, $param),
)*)?
},
)*
(m, p) => {
return Err(Error::BadRequest(format!(
"Unknown API endpoint: {} {}",
m, p
)))
}
}
}
}};
(@gen_parser ($keyword:expr, $key:ident, $query:expr, $header:expr), (@gen_parser ($keyword:expr, $key:ident, $query:expr, $header:expr),
key: [$($kw_k:ident $(if $required_k:ident)? $(header $header_k:expr)? => $api_k:ident $(($($conv_k:ident :: $param_k:ident),*))?,)*], key: [$($kw_k:ident $(if $required_k:ident)? $(header $header_k:expr)? => $api_k:ident $(($($conv_k:ident :: $param_k:ident),*))?,)*],
no_key: [$($kw_nk:ident $(if $required_nk:ident)? $(if_header $header_nk:expr)? => $api_nk:ident $(($($conv_nk:ident :: $param_nk:ident),*))?,)*]) => {{ no_key: [$($kw_nk:ident $(if $required_nk:ident)? $(if_header $header_nk:expr)? => $api_nk:ident $(($($conv_nk:ident :: $param_nk:ident),*))?,)*]) => {{

View file

@ -1,10 +1,9 @@
use crate::error::{Error, OkOrBadRequest};
use std::borrow::Cow; use std::borrow::Cow;
use hyper::header::HeaderValue; use hyper::header::HeaderValue;
use hyper::{HeaderMap, Method, Request}; use hyper::{HeaderMap, Method, Request};
use crate::error::{Error, OkOrBadRequest};
use crate::helpers::Authorization; use crate::helpers::Authorization;
use crate::router_macros::{generateQueryParameters, router_match}; use crate::router_macros::{generateQueryParameters, router_match};