From 913e6da41baa260c710477dd79140d6dff73e96e Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Thu, 6 Mar 2025 17:26:28 +0100 Subject: [PATCH] admin api: implement PreviewClusterLayoutChanges --- doc/api/garage-admin-v2.json | 57 ++++++++++++++++++++++++++++++++++++ src/api/admin/api.rs | 18 ++++++++++++ src/api/admin/cluster.rs | 24 +++++++++++++++ src/api/admin/openapi.rs | 16 ++++++++++ src/api/admin/router_v2.rs | 1 + 5 files changed, 116 insertions(+) diff --git a/doc/api/garage-admin-v2.json b/doc/api/garage-admin-v2.json index a13252b3..cc2911e5 100644 --- a/doc/api/garage-admin-v2.json +++ b/doc/api/garage-admin-v2.json @@ -950,6 +950,30 @@ } } }, + "/v2/PreviewClusterLayoutChanges": { + "post": { + "tags": [ + "Cluster layout" + ], + "description": "\nComputes a new layout taking into account the staged parameters, and returns it with detailed statistics. The new layout is not applied in the cluster.\n\n*Note: do not try to parse the `message` field of the response, it is given as an array of string specifically because its format is not stable.*\n ", + "operationId": "PreviewClusterLayoutChanges", + "responses": { + "200": { + "description": "Information about the new layout", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PreviewClusterLayoutChangesResponse" + } + } + } + }, + "500": { + "description": "Internal server error" + } + } + } + }, "/v2/PurgeBlocks": { "post": { "tags": [ @@ -2992,6 +3016,39 @@ } } }, + "PreviewClusterLayoutChangesResponse": { + "oneOf": [ + { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "message", + "newLayout" + ], + "properties": { + "message": { + "type": "array", + "items": { + "type": "string" + } + }, + "newLayout": { + "$ref": "#/components/schemas/GetClusterLayoutResponse" + } + } + } + ] + }, "RemoveBucketAliasRequest": { "allOf": [ { diff --git a/src/api/admin/api.rs b/src/api/admin/api.rs index 0c2d31ab..474225b9 100644 --- a/src/api/admin/api.rs +++ b/src/api/admin/api.rs @@ -52,6 +52,7 @@ admin_endpoints![ // Layout operations GetClusterLayout, UpdateClusterLayout, + PreviewClusterLayoutChanges, ApplyClusterLayout, RevertClusterLayout, @@ -318,6 +319,23 @@ pub enum ZoneRedundancy { Maximum, } +// ---- PreviewClusterLayoutChanges ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PreviewClusterLayoutChangesRequest; + +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(untagged)] +pub enum PreviewClusterLayoutChangesResponse { + #[serde(rename_all = "camelCase")] + Error { error: String }, + #[serde(rename_all = "camelCase")] + Success { + message: Vec, + new_layout: GetClusterLayoutResponse, + }, +} + // ---- UpdateClusterLayout ---- #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] diff --git a/src/api/admin/cluster.rs b/src/api/admin/cluster.rs index 485979c4..1cb2a52e 100644 --- a/src/api/admin/cluster.rs +++ b/src/api/admin/cluster.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use garage_util::crdt::*; use garage_util::data::*; +use garage_util::error::Error as GarageError; use garage_rpc::layout; @@ -308,6 +309,29 @@ impl RequestHandler for UpdateClusterLayoutRequest { } } +impl RequestHandler for PreviewClusterLayoutChangesRequest { + type Response = PreviewClusterLayoutChangesResponse; + + async fn handle( + self, + garage: &Arc, + _admin: &Admin, + ) -> Result { + let layout = garage.system.cluster_layout().inner().clone(); + let new_ver = layout.current().version + 1; + match layout.apply_staged_changes(Some(new_ver)) { + Err(GarageError::Message(error)) => { + Ok(PreviewClusterLayoutChangesResponse::Error { error }) + } + Err(e) => Err(e.into()), + Ok((new_layout, msg)) => Ok(PreviewClusterLayoutChangesResponse::Success { + message: msg, + new_layout: format_cluster_layout(&new_layout), + }), + } + } +} + impl RequestHandler for ApplyClusterLayoutRequest { type Response = ApplyClusterLayoutResponse; diff --git a/src/api/admin/openapi.rs b/src/api/admin/openapi.rs index 0e48bf54..50991c46 100644 --- a/src/api/admin/openapi.rs +++ b/src/api/admin/openapi.rs @@ -117,6 +117,21 @@ Contrary to the CLI that may update only a subset of the fields capacity, zone a )] fn UpdateClusterLayout() -> () {} +#[utoipa::path(post, + path = "/v2/PreviewClusterLayoutChanges", + tag = "Cluster layout", + description = " +Computes a new layout taking into account the staged parameters, and returns it with detailed statistics. The new layout is not applied in the cluster. + +*Note: do not try to parse the `message` field of the response, it is given as an array of string specifically because its format is not stable.* + ", + responses( + (status = 200, description = "Information about the new layout", body = PreviewClusterLayoutChangesResponse), + (status = 500, description = "Internal server error") + ), +)] +fn PreviewClusterLayoutChanges() -> () {} + #[utoipa::path(post, path = "/v2/ApplyClusterLayout", tag = "Cluster layout", @@ -686,6 +701,7 @@ impl Modify for SecurityAddon { // Layout operations GetClusterLayout, UpdateClusterLayout, + PreviewClusterLayoutChanges, ApplyClusterLayout, RevertClusterLayout, // Key operations diff --git a/src/api/admin/router_v2.rs b/src/api/admin/router_v2.rs index 2397f276..e6e6ee91 100644 --- a/src/api/admin/router_v2.rs +++ b/src/api/admin/router_v2.rs @@ -37,6 +37,7 @@ impl AdminApiRequest { // Layout endpoints GET GetClusterLayout (), POST UpdateClusterLayout (body), + POST PreviewClusterLayoutChanges (), POST ApplyClusterLayout (body), POST RevertClusterLayout (), // API key endpoints