mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 08:18:18 +00:00
feat(site-replication): make repair operations durable (#5217)
* feat(site-replication): make repair operations durable * fix(site-replication): serialize durable repair execution
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -62,6 +62,8 @@ pub(crate) const RUNTIME_CAPABILITIES_ROUTE_SUFFIX: &str = "/v4/runtime/capabili
|
||||
const SITE_REPLICATION_INFO_ROUTE: &str = "/rustfs/admin/v3/site-replication/info";
|
||||
const SITE_REPLICATION_EDIT_ROUTE: &str = "/rustfs/admin/v3/site-replication/edit";
|
||||
const SITE_REPLICATION_RESYNC_ROUTE: &str = "/rustfs/admin/v3/site-replication/resync/op";
|
||||
const SITE_REPLICATION_REPAIR_ROUTE: &str = "/rustfs/admin/v3/site-replication/repair";
|
||||
const SITE_REPLICATION_REPAIR_STATUS_ROUTE: &str = "/rustfs/admin/v3/site-replication/repair/status";
|
||||
|
||||
macro_rules! log_system_request_rejected {
|
||||
($operation:expr, $reason:expr) => {
|
||||
@@ -631,6 +633,8 @@ pub struct RuntimeCapabilitiesSummary {
|
||||
pub site_replication_edit: CapabilityStatus,
|
||||
#[serde(default)]
|
||||
pub site_replication_resync: CapabilityStatus,
|
||||
#[serde(default)]
|
||||
pub site_replication_repair: CapabilityStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
@@ -639,6 +643,7 @@ pub struct RuntimeCapabilitiesResponse {
|
||||
pub diagnostic_probes: DiagnosticProbeCapabilities,
|
||||
pub inspect_archive: super::inspect_archive::InspectArchiveCapability,
|
||||
pub storage_classes: StorageClassCapabilities,
|
||||
pub site_replication_repair: SiteReplicationRepairCapabilities,
|
||||
pub cluster_snapshot_path: String,
|
||||
pub cluster_snapshot_summary: Option<CapabilityStatus>,
|
||||
pub observability: crate::admin::storage_api::cluster::ObservabilitySnapshot,
|
||||
@@ -647,6 +652,42 @@ pub struct RuntimeCapabilitiesResponse {
|
||||
pub topology_status: CapabilityStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
pub struct SiteReplicationRepairCapabilities {
|
||||
pub contract_version: u32,
|
||||
pub status: CapabilityStatus,
|
||||
pub modes: [&'static str; 2],
|
||||
pub execute_route: &'static str,
|
||||
pub status_route: &'static str,
|
||||
pub preflight_token_contract: &'static str,
|
||||
pub operation_id_format: &'static str,
|
||||
pub max_retained_successful_operations: usize,
|
||||
pub disabled_by_default: bool,
|
||||
}
|
||||
|
||||
impl SiteReplicationRepairCapabilities {
|
||||
fn current() -> Self {
|
||||
let execute = admin_route_capability(HttpMethod::Put, SITE_REPLICATION_REPAIR_ROUTE);
|
||||
let status = admin_route_capability(HttpMethod::Get, SITE_REPLICATION_REPAIR_STATUS_ROUTE);
|
||||
let supported = execute.state == CapabilityState::Supported && status.state == CapabilityState::Supported;
|
||||
Self {
|
||||
contract_version: 1,
|
||||
status: if supported {
|
||||
CapabilityStatus::supported().with_reason("durable dry-run, execute, and status contracts are registered")
|
||||
} else {
|
||||
CapabilityStatus::unsupported().with_reason("one or more durable repair routes are unavailable")
|
||||
},
|
||||
modes: ["dry-run", "execute"],
|
||||
execute_route: SITE_REPLICATION_REPAIR_ROUTE,
|
||||
status_route: SITE_REPLICATION_REPAIR_STATUS_ROUTE,
|
||||
preflight_token_contract: "hmac-sha256-v1",
|
||||
operation_id_format: "uuid",
|
||||
max_retained_successful_operations: 32,
|
||||
disabled_by_default: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
pub struct DiagnosticProbeCapabilities {
|
||||
pub contract_version: u32,
|
||||
@@ -789,6 +830,7 @@ pub(crate) async fn build_runtime_capabilities_response()
|
||||
diagnostic_probes: DiagnosticProbeCapabilities::current(),
|
||||
inspect_archive: super::inspect_archive::InspectArchiveCapability::current(local_drive_count),
|
||||
storage_classes: StorageClassCapabilities::current(),
|
||||
site_replication_repair: SiteReplicationRepairCapabilities::current(),
|
||||
cluster_snapshot_path: usecase.cluster_snapshot_route().to_string(),
|
||||
cluster_snapshot_summary: cluster_snapshot_discovery.summary,
|
||||
observability,
|
||||
@@ -865,6 +907,7 @@ fn build_runtime_capabilities_summary(
|
||||
site_replication_info: admin_route_capability(HttpMethod::Get, SITE_REPLICATION_INFO_ROUTE),
|
||||
site_replication_edit: admin_route_capability(HttpMethod::Put, SITE_REPLICATION_EDIT_ROUTE),
|
||||
site_replication_resync: admin_route_capability(HttpMethod::Put, SITE_REPLICATION_RESYNC_ROUTE),
|
||||
site_replication_repair: SiteReplicationRepairCapabilities::current().status,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -997,9 +1040,10 @@ impl Operation for DataUsageInfoHandler {
|
||||
mod tests {
|
||||
use super::{
|
||||
OBSERVABILITY_SUMMARY_RESOLVED, RuntimeCapabilitiesHandler, SITE_REPLICATION_EDIT_ROUTE, SITE_REPLICATION_INFO_ROUTE,
|
||||
SITE_REPLICATION_RESYNC_ROUTE, ServerInfoResponse, TOPOLOGY_SNAPSHOT_NOT_AVAILABLE, TOPOLOGY_SUMMARY_RESOLVED,
|
||||
admin_route_capability_from_inventory, build_runtime_capabilities_response, build_runtime_capabilities_summary,
|
||||
data_usage_info_gate_actions, runtime_capabilities_gate_actions, system_admin_discovery,
|
||||
SITE_REPLICATION_REPAIR_ROUTE, SITE_REPLICATION_REPAIR_STATUS_ROUTE, SITE_REPLICATION_RESYNC_ROUTE, ServerInfoResponse,
|
||||
TOPOLOGY_SNAPSHOT_NOT_AVAILABLE, TOPOLOGY_SUMMARY_RESOLVED, admin_route_capability_from_inventory,
|
||||
build_runtime_capabilities_response, build_runtime_capabilities_summary, data_usage_info_gate_actions,
|
||||
runtime_capabilities_gate_actions, system_admin_discovery,
|
||||
};
|
||||
use crate::admin::router::Operation;
|
||||
use crate::admin::runtime_sources::DefaultAdminUsecase;
|
||||
@@ -1051,6 +1095,16 @@ mod tests {
|
||||
assert_eq!(response.summary.site_replication_info.state, CapabilityState::Supported);
|
||||
assert_eq!(response.summary.site_replication_edit.state, CapabilityState::Supported);
|
||||
assert_eq!(response.summary.site_replication_resync.state, CapabilityState::Supported);
|
||||
assert_eq!(response.summary.site_replication_repair.state, CapabilityState::Supported);
|
||||
assert_eq!(response.site_replication_repair.contract_version, 1);
|
||||
assert_eq!(response.site_replication_repair.status.state, CapabilityState::Supported);
|
||||
assert_eq!(response.site_replication_repair.modes, ["dry-run", "execute"]);
|
||||
assert_eq!(response.site_replication_repair.execute_route, SITE_REPLICATION_REPAIR_ROUTE);
|
||||
assert_eq!(response.site_replication_repair.status_route, SITE_REPLICATION_REPAIR_STATUS_ROUTE);
|
||||
assert_eq!(response.site_replication_repair.preflight_token_contract, "hmac-sha256-v1");
|
||||
assert_eq!(response.site_replication_repair.operation_id_format, "uuid");
|
||||
assert_eq!(response.site_replication_repair.max_retained_successful_operations, 32);
|
||||
assert!(!response.site_replication_repair.disabled_by_default);
|
||||
assert_eq!(response.diagnostic_probes.contract_version, 1);
|
||||
assert_eq!(response.diagnostic_probes.health_info.status.state, CapabilityState::Supported);
|
||||
assert_eq!(response.diagnostic_probes.observed_drive_metrics.mode, "observed_storage_metrics");
|
||||
@@ -1087,6 +1141,14 @@ mod tests {
|
||||
assert_eq!(value["summary"]["site_replication_info"]["state"], "supported");
|
||||
assert_eq!(value["summary"]["site_replication_edit"]["state"], "supported");
|
||||
assert_eq!(value["summary"]["site_replication_resync"]["state"], "supported");
|
||||
assert_eq!(value["summary"]["site_replication_repair"]["state"], "supported");
|
||||
assert_eq!(value["site_replication_repair"]["contract_version"], 1);
|
||||
assert_eq!(value["site_replication_repair"]["status"]["state"], "supported");
|
||||
assert_eq!(value["site_replication_repair"]["modes"], json!(["dry-run", "execute"]));
|
||||
assert_eq!(value["site_replication_repair"]["execute_route"], SITE_REPLICATION_REPAIR_ROUTE);
|
||||
assert_eq!(value["site_replication_repair"]["status_route"], SITE_REPLICATION_REPAIR_STATUS_ROUTE);
|
||||
assert_eq!(value["site_replication_repair"]["max_retained_successful_operations"], 32);
|
||||
assert_eq!(value["site_replication_repair"]["disabled_by_default"], false);
|
||||
assert_eq!(value["diagnostic_probes"]["client_devnull"]["status"]["state"], "supported");
|
||||
assert_eq!(
|
||||
value["diagnostic_probes"]["client_devnull"]["max_bytes"],
|
||||
@@ -1112,6 +1174,8 @@ mod tests {
|
||||
(HttpMethod::Get, SITE_REPLICATION_INFO_ROUTE),
|
||||
(HttpMethod::Put, SITE_REPLICATION_EDIT_ROUTE),
|
||||
(HttpMethod::Put, SITE_REPLICATION_RESYNC_ROUTE),
|
||||
(HttpMethod::Put, SITE_REPLICATION_REPAIR_ROUTE),
|
||||
(HttpMethod::Get, SITE_REPLICATION_REPAIR_STATUS_ROUTE),
|
||||
] {
|
||||
assert_eq!(
|
||||
admin_route_capability_from_inventory(
|
||||
@@ -1170,6 +1234,7 @@ mod tests {
|
||||
assert_eq!(summary.site_replication_info.state, CapabilityState::Unknown);
|
||||
assert_eq!(summary.site_replication_edit.state, CapabilityState::Unknown);
|
||||
assert_eq!(summary.site_replication_resync.state, CapabilityState::Unknown);
|
||||
assert_eq!(summary.site_replication_repair.state, CapabilityState::Unknown);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -637,6 +637,12 @@ pub const ADMIN_ROUTE_POLICY_SPECS: &[AdminRouteSpec] = &[
|
||||
SITE_REPLICATION_OPERATION,
|
||||
RouteRiskLevel::High,
|
||||
),
|
||||
admin(
|
||||
HttpMethod::Get,
|
||||
"/rustfs/admin/v3/site-replication/repair/status",
|
||||
SITE_REPLICATION_OPERATION,
|
||||
RouteRiskLevel::Sensitive,
|
||||
),
|
||||
admin(HttpMethod::Get, "/rustfs/admin/debug/pprof/profile", PROFILING, RouteRiskLevel::High),
|
||||
admin(HttpMethod::Get, "/rustfs/admin/debug/pprof/status", PROFILING, RouteRiskLevel::High),
|
||||
admin(HttpMethod::Get, "/rustfs/admin/debug/tls/status", PROFILING, RouteRiskLevel::High),
|
||||
@@ -1854,6 +1860,11 @@ mod tests {
|
||||
#[test]
|
||||
fn route_policy_requires_operation_for_site_replication_repair() {
|
||||
assert_action(HttpMethod::Put, "/rustfs/admin/v3/site-replication/repair", SITE_REPLICATION_OPERATION);
|
||||
assert_action(
|
||||
HttpMethod::Get,
|
||||
"/rustfs/admin/v3/site-replication/repair/status",
|
||||
SITE_REPLICATION_OPERATION,
|
||||
);
|
||||
}
|
||||
|
||||
fn route_policy_inventory_keys() -> BTreeSet<String> {
|
||||
|
||||
@@ -280,6 +280,7 @@ fn expected_admin_route_matrix() -> Vec<RouteMatrixEntry> {
|
||||
admin_route(Method::PUT, "/v3/site-replication/resync/op"),
|
||||
admin_route(Method::PUT, "/v3/site-replication/state/edit"),
|
||||
admin_route(Method::PUT, "/v3/site-replication/repair"),
|
||||
admin_route(Method::GET, "/v3/site-replication/repair/status"),
|
||||
admin_route(Method::GET, "/debug/pprof/profile"),
|
||||
admin_route(Method::GET, "/debug/pprof/status"),
|
||||
admin_route(Method::POST, "/v3/profiling/start"),
|
||||
@@ -1206,6 +1207,7 @@ fn test_register_routes_cover_representative_admin_paths() {
|
||||
assert_route(&router, Method::PUT, &admin_path("/v3/site-replication/resync/op"));
|
||||
assert_route(&router, Method::PUT, &admin_path("/v3/site-replication/state/edit"));
|
||||
assert_route(&router, Method::PUT, &admin_path("/v3/site-replication/repair"));
|
||||
assert_route(&router, Method::GET, &admin_path("/v3/site-replication/repair/status"));
|
||||
assert_route(&router, Method::GET, &admin_path("/debug/pprof/profile"));
|
||||
assert_route(&router, Method::GET, &admin_path("/debug/tls/status"));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user