aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/drafts/admin-api.md30
-rw-r--r--src/api/admin/api_server.rs1
-rw-r--r--src/api/admin/cluster.rs33
-rw-r--r--src/api/admin/router.rs2
-rw-r--r--src/rpc/system.rs12
5 files changed, 74 insertions, 4 deletions
diff --git a/doc/drafts/admin-api.md b/doc/drafts/admin-api.md
index cbd73e15..2e1ffa82 100644
--- a/doc/drafts/admin-api.md
+++ b/doc/drafts/admin-api.md
@@ -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`
Returns the cluster's current layout in JSON, including:
diff --git a/src/api/admin/api_server.rs b/src/api/admin/api_server.rs
index 6f568024..61b0d24f 100644
--- a/src/api/admin/api_server.rs
+++ b/src/api/admin/api_server.rs
@@ -124,6 +124,7 @@ impl ApiHandler for AdminApiServer {
Endpoint::Options => self.handle_options(&req),
Endpoint::Metrics => self.handle_metrics(),
Endpoint::GetClusterStatus => handle_get_cluster_status(&self.garage).await,
+ Endpoint::ConnectClusterNodes => handle_connect_cluster_nodes(&self.garage, req).await,
// Layout
Endpoint::GetClusterLayout => handle_get_cluster_layout(&self.garage).await,
Endpoint::UpdateClusterLayout => handle_update_cluster_layout(&self.garage, req).await,
diff --git a/src/api/admin/cluster.rs b/src/api/admin/cluster.rs
index 44ad4a37..3401be42 100644
--- a/src/api/admin/cluster.rs
+++ b/src/api/admin/cluster.rs
@@ -45,6 +45,33 @@ pub async fn handle_get_cluster_status(garage: &Arc<Garage>) -> Result<Response<
.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> {
let res = get_cluster_layout(garage);
let resp_json = serde_json::to_string_pretty(&res).map_err(GarageError::from)?;
@@ -85,6 +112,12 @@ struct GetClusterStatusResponse {
}
#[derive(Serialize)]
+struct ConnectClusterNodesResponse {
+ success: bool,
+ error: Option<String>,
+}
+
+#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct GetClusterLayoutResponse {
version: u64,
diff --git a/src/api/admin/router.rs b/src/api/admin/router.rs
index 909ef102..41e7ed73 100644
--- a/src/api/admin/router.rs
+++ b/src/api/admin/router.rs
@@ -18,6 +18,7 @@ pub enum Endpoint {
Options,
Metrics,
GetClusterStatus,
+ ConnectClusterNodes,
// Layout
GetClusterLayout,
UpdateClusterLayout,
@@ -91,6 +92,7 @@ impl Endpoint {
OPTIONS _ => Options,
GET "/metrics" => Metrics,
GET "/v0/status" => GetClusterStatus,
+ POST "/v0/connect" => ConnectClusterNodes,
// Layout endpoints
GET "/v0/layout" => GetClusterLayout,
POST "/v0/layout" => UpdateClusterLayout,
diff --git a/src/rpc/system.rs b/src/rpc/system.rs
index eb2f2e42..78d538ec 100644
--- a/src/rpc/system.rs
+++ b/src/rpc/system.rs
@@ -383,10 +383,14 @@ impl System {
}
}
}
- return Err(Error::Message(format!(
- "Could not connect to specified peers. Errors: {:?}",
- errors
- )));
+ if errors.len() == 1 {
+ return Err(Error::Message(errors[0].1.to_string()));
+ } else {
+ return Err(Error::Message(format!(
+ "Could not connect to specified peers. Errors: {:?}",
+ errors
+ )));
+ }
}
// ---- INTERNALS ----