more complete admin API #298
5 changed files with 74 additions and 4 deletions
|
@ -94,6 +94,36 @@ Example response body:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### ConnectClusterNodes `POST /v0/connect`
|
||||||
|
|
||||||
|
Instructs this Garage node to connect to other Garage nodes at specified addresses.
|
||||||
|
|
||||||
|
Example request body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f@10.0.0.11:3901",
|
||||||
|
"4a6ae5a1d0d33bf895f5bb4f0a418b7dc94c47c0dd2eb108d1158f3c8f60b0ff@10.0.0.12:3901"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The format of the string for a node to connect to is: `<node ID>@<ip address>:<port>`, same as in the `garage node connect` CLI call.
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"error": null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "Handshake error",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
### GetClusterLayout `GET /v0/layout`
|
### GetClusterLayout `GET /v0/layout`
|
||||||
|
|
||||||
Returns the cluster's current layout in JSON, including:
|
Returns the cluster's current layout in JSON, including:
|
||||||
|
|
|
@ -124,6 +124,7 @@ impl ApiHandler for AdminApiServer {
|
||||||
Endpoint::Options => self.handle_options(&req),
|
Endpoint::Options => self.handle_options(&req),
|
||||||
Endpoint::Metrics => self.handle_metrics(),
|
Endpoint::Metrics => self.handle_metrics(),
|
||||||
Endpoint::GetClusterStatus => handle_get_cluster_status(&self.garage).await,
|
Endpoint::GetClusterStatus => handle_get_cluster_status(&self.garage).await,
|
||||||
|
Endpoint::ConnectClusterNodes => handle_connect_cluster_nodes(&self.garage, req).await,
|
||||||
// Layout
|
// Layout
|
||||||
Endpoint::GetClusterLayout => handle_get_cluster_layout(&self.garage).await,
|
Endpoint::GetClusterLayout => handle_get_cluster_layout(&self.garage).await,
|
||||||
Endpoint::UpdateClusterLayout => handle_update_cluster_layout(&self.garage, req).await,
|
Endpoint::UpdateClusterLayout => handle_update_cluster_layout(&self.garage, req).await,
|
||||||
|
|
|
@ -45,6 +45,33 @@ pub async fn handle_get_cluster_status(garage: &Arc<Garage>) -> Result<Response<
|
||||||
.body(Body::from(resp_json))?)
|
.body(Body::from(resp_json))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn handle_connect_cluster_nodes(
|
||||||
|
garage: &Arc<Garage>,
|
||||||
|
req: Request<Body>,
|
||||||
|
) -> Result<Response<Body>, Error> {
|
||||||
|
let req = parse_json_body::<Vec<String>>(req).await?;
|
||||||
|
|
||||||
|
let res = futures::future::join_all(req.iter().map(|node| garage.system.connect(node)))
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.map(|r| match r {
|
||||||
|
Ok(()) => ConnectClusterNodesResponse {
|
||||||
|
success: true,
|
||||||
|
error: None,
|
||||||
|
},
|
||||||
|
Err(e) => ConnectClusterNodesResponse {
|
||||||
|
success: false,
|
||||||
|
error: Some(format!("{}", e)),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let resp_json = serde_json::to_string_pretty(&res).map_err(GarageError::from)?;
|
||||||
|
Ok(Response::builder()
|
||||||
|
.status(StatusCode::OK)
|
||||||
|
.body(Body::from(resp_json))?)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn handle_get_cluster_layout(garage: &Arc<Garage>) -> Result<Response<Body>, Error> {
|
pub async fn handle_get_cluster_layout(garage: &Arc<Garage>) -> Result<Response<Body>, Error> {
|
||||||
let res = get_cluster_layout(garage);
|
let res = get_cluster_layout(garage);
|
||||||
let resp_json = serde_json::to_string_pretty(&res).map_err(GarageError::from)?;
|
let resp_json = serde_json::to_string_pretty(&res).map_err(GarageError::from)?;
|
||||||
|
@ -84,6 +111,12 @@ struct GetClusterStatusResponse {
|
||||||
layout: GetClusterLayoutResponse,
|
layout: GetClusterLayoutResponse,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct ConnectClusterNodesResponse {
|
||||||
|
success: bool,
|
||||||
|
error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct GetClusterLayoutResponse {
|
struct GetClusterLayoutResponse {
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub enum Endpoint {
|
||||||
Options,
|
Options,
|
||||||
Metrics,
|
Metrics,
|
||||||
GetClusterStatus,
|
GetClusterStatus,
|
||||||
|
ConnectClusterNodes,
|
||||||
// Layout
|
// Layout
|
||||||
GetClusterLayout,
|
GetClusterLayout,
|
||||||
UpdateClusterLayout,
|
UpdateClusterLayout,
|
||||||
|
@ -91,6 +92,7 @@ impl Endpoint {
|
||||||
OPTIONS _ => Options,
|
OPTIONS _ => Options,
|
||||||
GET "/metrics" => Metrics,
|
GET "/metrics" => Metrics,
|
||||||
GET "/v0/status" => GetClusterStatus,
|
GET "/v0/status" => GetClusterStatus,
|
||||||
|
POST "/v0/connect" => ConnectClusterNodes,
|
||||||
// Layout endpoints
|
// Layout endpoints
|
||||||
GET "/v0/layout" => GetClusterLayout,
|
GET "/v0/layout" => GetClusterLayout,
|
||||||
POST "/v0/layout" => UpdateClusterLayout,
|
POST "/v0/layout" => UpdateClusterLayout,
|
||||||
|
|
|
@ -383,10 +383,14 @@ impl System {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Err(Error::Message(format!(
|
if errors.len() == 1 {
|
||||||
"Could not connect to specified peers. Errors: {:?}",
|
return Err(Error::Message(errors[0].1.to_string()));
|
||||||
errors
|
} else {
|
||||||
)));
|
return Err(Error::Message(format!(
|
||||||
|
"Could not connect to specified peers. Errors: {:?}",
|
||||||
|
errors
|
||||||
|
)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- INTERNALS ----
|
// ---- INTERNALS ----
|
||||||
|
|
Loading…
Reference in a new issue