mirror of
https://github.com/rustfs/rustfs.git
synced 2026-10-13 08:02:03 +00:00
fix(rustfs): keep recovery handler within S3 gateway (#8429)
* fix(rustfs): route recovery errors through S3 gateway Co-Authored-By: heihutu <heihutu@gmail.com> Co-Authored-By: zhi22915 <qiuzgang@gmail.com> * fix(ecstore): satisfy strict Clippy pagination checks Co-Authored-By: heihutu <heihutu@gmail.com> Co-Authored-By: zhi22915 <qiuzgang@gmail.com> * fix(heal): avoid cloning Copy purge identity Co-Authored-By: heihutu <heihutu@gmail.com> Co-Authored-By: zhi22915 <qiuzgang@gmail.com> * test: restore one-second drive request budgets * fix(ecstore): resume pending lock bucket creation Allow retries to reuse an uncommitted Object Lock bucket intent when the same incarnation remains unretired and all physical volumes are absent. Isolate the recovery entrypoint test from the shared production leader lock. Co-Authored-By: heihutu <heihutu@gmail.com> Co-Authored-By: zhi22915 <qiuzgang@gmail.com> * test: provision stack for deep catalog test futures Apply the existing 4 MiB test-thread stack setup to table-catalog handler/store tests and the relocated-pool GET regression in both local and CI nextest profiles. These cases overflowed libtest's 2 MiB default stack on Linux. Co-Authored-By: heihutu <heihutu@gmail.com> Co-Authored-By: zhi22915 <qiuzgang@gmail.com> --------- Co-authored-by: zhi22915 <qiuzgang@gmail.com> Co-authored-by: overtrue <anzhengchao@gmail.com>
This commit is contained in:
@@ -17,12 +17,12 @@
|
||||
use crate::admin::auth::authorize_admin_request;
|
||||
use crate::admin::router::{AdminOperation, Operation, S3Router};
|
||||
use crate::admin::runtime_sources::current_object_store_handle;
|
||||
use crate::admin::storage_api::s3::{self, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result};
|
||||
use crate::server::ADMIN_PREFIX;
|
||||
use crate::site_replication::{site_replication_enabled, with_site_replication_bucket_mutation_lock};
|
||||
use hyper::{Method, StatusCode};
|
||||
use matchit::Params;
|
||||
use rustfs_policy::policy::action::{Action, AdminAction};
|
||||
use s3s::{Body, S3Request, S3Response, S3Result, s3_error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::info;
|
||||
use uuid::Uuid;
|
||||
@@ -63,23 +63,23 @@ impl Operation for RecoverOrphanedBucketHandler {
|
||||
.credentials
|
||||
.as_ref()
|
||||
.map(|credentials| credentials.secret_key.clone())
|
||||
.ok_or_else(|| s3_error!(InvalidRequest, "authentication required"))?;
|
||||
.ok_or_else(|| s3::error(S3ErrorCode::InvalidRequest, "authentication required"))?;
|
||||
let bucket = params
|
||||
.get("bucket")
|
||||
.filter(|bucket| !bucket.is_empty())
|
||||
.ok_or_else(|| s3_error!(InvalidRequest, "bucket name is required"))?;
|
||||
.ok_or_else(|| s3::error(S3ErrorCode::InvalidRequest, "bucket name is required"))?;
|
||||
let body = req
|
||||
.input
|
||||
.store_all_limited(rustfs_config::MAX_ADMIN_REQUEST_BODY_SIZE)
|
||||
.await
|
||||
.map_err(|_| s3_error!(InvalidRequest, "failed to read recovery request body"))?;
|
||||
.map_err(|_| s3::error(S3ErrorCode::InvalidRequest, "failed to read recovery request body"))?;
|
||||
let request: RecoverOrphanedBucketRequest = serde_json::from_slice(&body)
|
||||
.map_err(|_| s3_error!(InvalidRequest, "expected JSON with a non-nil expectedIncarnationId"))?;
|
||||
.map_err(|_| s3::error(S3ErrorCode::InvalidRequest, "expected JSON with a non-nil expectedIncarnationId"))?;
|
||||
if request.expected_incarnation_id.is_nil() {
|
||||
return Err(s3_error!(InvalidRequest, "expectedIncarnationId must be non-nil"));
|
||||
return Err(s3::error(S3ErrorCode::InvalidRequest, "expectedIncarnationId must be non-nil"));
|
||||
}
|
||||
let Some(store) = current_object_store_handle() else {
|
||||
return Err(s3_error!(InternalError, "object store is not initialized"));
|
||||
return Err(s3::error(S3ErrorCode::InternalError, "object store is not initialized"));
|
||||
};
|
||||
|
||||
let operation_bucket = bucket.to_owned();
|
||||
@@ -91,21 +91,21 @@ impl Operation for RecoverOrphanedBucketHandler {
|
||||
let lock_bucket = operation_bucket.clone();
|
||||
with_site_replication_bucket_mutation_lock(lock_store, &lock_bucket, move || async move {
|
||||
if site_replication_enabled().await? {
|
||||
return Err(s3_error!(
|
||||
OperationAborted,
|
||||
"orphaned bucket recovery is local; reconcile every site before recreating this bucket"
|
||||
return Err(s3::error(
|
||||
S3ErrorCode::OperationAborted,
|
||||
"orphaned bucket recovery is local; reconcile every site before recreating this bucket",
|
||||
));
|
||||
}
|
||||
operation_store
|
||||
.recover_orphaned_bucket(&operation_bucket, expected_incarnation)
|
||||
.await
|
||||
.map_err(|error| s3s::S3Error::from(crate::error::ApiError::from(error)))
|
||||
.map_err(|error| S3Error::from(crate::error::ApiError::from(error)))
|
||||
})
|
||||
.await??;
|
||||
Ok::<(), s3s::S3Error>(())
|
||||
Ok::<(), S3Error>(())
|
||||
})
|
||||
.await
|
||||
.map_err(|error| s3s::S3Error::from(crate::error::ApiError::from(error)))?;
|
||||
.map_err(|error| S3Error::from(crate::error::ApiError::from(error)))?;
|
||||
completed?;
|
||||
|
||||
info!(
|
||||
|
||||
Reference in New Issue
Block a user