mirror of
https://github.com/rustfs/rustfs.git
synced 2026-09-09 13:46:05 +00:00
fix: reuse admin error boundary for legacy tier reconciliation (#7587)
This commit is contained in:
@@ -1023,33 +1023,34 @@ struct LegacyTransitionStateReconcileQuery {
|
||||
|
||||
fn parse_legacy_transition_state_reconcile_query(query: Option<&str>) -> S3Result<LegacyTransitionStateReconcileSelector> {
|
||||
let query: LegacyTransitionStateReconcileQuery = serde_urlencoded::from_bytes(query.unwrap_or_default().as_bytes())
|
||||
.map_err(|_| s3_error!(InvalidArgument, "invalid legacy transition-state reconcile query"))?;
|
||||
.map_err(|_| admin_s3_error(AdminS3ErrorCode::InvalidArgument, "invalid legacy transition-state reconcile query"))?;
|
||||
let bucket = query
|
||||
.bucket
|
||||
.filter(|bucket| !bucket.is_empty())
|
||||
.ok_or_else(|| s3_error!(InvalidRequest, "bucket is required"))?;
|
||||
.ok_or_else(|| admin_s3_error(AdminS3ErrorCode::InvalidRequest, "bucket is required"))?;
|
||||
if is_reserved_or_invalid_bucket(&bucket, false) {
|
||||
return Err(s3_error!(InvalidBucketName, "invalid bucket name"));
|
||||
return Err(admin_s3_error(AdminS3ErrorCode::InvalidBucketName, "invalid bucket name"));
|
||||
}
|
||||
|
||||
let object = query
|
||||
.object
|
||||
.filter(|object| !object.is_empty())
|
||||
.ok_or_else(|| s3_error!(InvalidRequest, "object is required"))?;
|
||||
.ok_or_else(|| admin_s3_error(AdminS3ErrorCode::InvalidRequest, "object is required"))?;
|
||||
if !is_valid_object_prefix(&object) || object.contains('\n') || object.contains('\r') {
|
||||
return Err(s3_error!(InvalidArgument, "invalid object name"));
|
||||
return Err(admin_s3_error(AdminS3ErrorCode::InvalidArgument, "invalid object name"));
|
||||
}
|
||||
|
||||
let version_id = query
|
||||
.version_id
|
||||
.filter(|version_id| !version_id.is_empty())
|
||||
.ok_or_else(|| s3_error!(InvalidRequest, "versionId is required"))?;
|
||||
.ok_or_else(|| admin_s3_error(AdminS3ErrorCode::InvalidRequest, "versionId is required"))?;
|
||||
let version_id = if version_id == "null" {
|
||||
version_id
|
||||
} else {
|
||||
let parsed = Uuid::parse_str(&version_id).map_err(|_| s3_error!(InvalidArgument, "invalid local versionId"))?;
|
||||
let parsed = Uuid::parse_str(&version_id)
|
||||
.map_err(|_| admin_s3_error(AdminS3ErrorCode::InvalidArgument, "invalid local versionId"))?;
|
||||
if parsed.is_nil() {
|
||||
return Err(s3_error!(InvalidArgument, "invalid local versionId"));
|
||||
return Err(admin_s3_error(AdminS3ErrorCode::InvalidArgument, "invalid local versionId"));
|
||||
}
|
||||
parsed.to_string()
|
||||
};
|
||||
@@ -1067,13 +1068,16 @@ fn validate_legacy_transition_state_reconcile_request(
|
||||
request_selector: &LegacyTransitionStateReconcileSelector,
|
||||
) -> S3Result<()> {
|
||||
if !confirm {
|
||||
return Err(s3_error!(
|
||||
InvalidRequest,
|
||||
"legacy transition-state reconciliation requires confirm=true; use GET to inspect without changes"
|
||||
return Err(admin_s3_error(
|
||||
AdminS3ErrorCode::InvalidRequest,
|
||||
"legacy transition-state reconciliation requires confirm=true; use GET to inspect without changes",
|
||||
));
|
||||
}
|
||||
if request_selector != query_selector {
|
||||
return Err(s3_error!(InvalidRequest, "request selector must exactly match the query selector"));
|
||||
return Err(admin_s3_error(
|
||||
AdminS3ErrorCode::InvalidRequest,
|
||||
"request selector must exactly match the query selector",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1081,20 +1085,22 @@ fn validate_legacy_transition_state_reconcile_request(
|
||||
fn map_legacy_transition_state_reconcile_error(err: LegacyTransitionStateReconcileError) -> S3Error {
|
||||
match err {
|
||||
LegacyTransitionStateReconcileError::InvalidSelector(_) | LegacyTransitionStateReconcileError::InvalidRequest(_) => {
|
||||
s3_error!(InvalidRequest, "invalid legacy transition-state reconciliation request")
|
||||
admin_s3_error(AdminS3ErrorCode::InvalidRequest, "invalid legacy transition-state reconciliation request")
|
||||
}
|
||||
LegacyTransitionStateReconcileError::StaleExpectedTuple(_) | LegacyTransitionStateReconcileError::Corrupt(_) => {
|
||||
s3_error!(OperationAborted, "legacy transition-state reconciliation metadata is stale or corrupt")
|
||||
}
|
||||
LegacyTransitionStateReconcileError::WriteFenceUnavailable(_) => {
|
||||
s3_error!(
|
||||
OperationAborted,
|
||||
"legacy transition-state reconciliation could not acquire safe write authority"
|
||||
admin_s3_error(
|
||||
AdminS3ErrorCode::OperationAborted,
|
||||
"legacy transition-state reconciliation metadata is stale or corrupt",
|
||||
)
|
||||
}
|
||||
LegacyTransitionStateReconcileError::BackendUnavailable(_) => {
|
||||
s3_error!(InternalError, "legacy transition-state reconciliation backend is unavailable")
|
||||
}
|
||||
LegacyTransitionStateReconcileError::WriteFenceUnavailable(_) => admin_s3_error(
|
||||
AdminS3ErrorCode::OperationAborted,
|
||||
"legacy transition-state reconciliation could not acquire safe write authority",
|
||||
),
|
||||
LegacyTransitionStateReconcileError::BackendUnavailable(_) => admin_s3_error(
|
||||
AdminS3ErrorCode::InternalError,
|
||||
"legacy transition-state reconciliation backend is unavailable",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2066,7 +2072,7 @@ impl Operation for LegacyTransitionStateReconcileInspectHandler {
|
||||
authorize_transition_admin_request(&req, AdminAction::ListTierAction).await?;
|
||||
let selector = parse_legacy_transition_state_reconcile_query(req.uri.query())?;
|
||||
let Some(store) = object_store_from_extensions(&req.extensions) else {
|
||||
return Err(s3_error!(InternalError, "object store is not initialized"));
|
||||
return Err(admin_s3_error(AdminS3ErrorCode::InternalError, "object store is not initialized"));
|
||||
};
|
||||
let response: LegacyTransitionStateReconcileResponse = store
|
||||
.inspect_legacy_transition_state(selector)
|
||||
@@ -2085,15 +2091,21 @@ impl Operation for LegacyTransitionStateReconcileApplyHandler {
|
||||
let selector = parse_legacy_transition_state_reconcile_query(req.uri.query())?;
|
||||
let store = object_store_from_extensions(&req.extensions);
|
||||
let mut input = req.input;
|
||||
let body = input
|
||||
.store_all_limited(MAX_ADMIN_REQUEST_BODY_SIZE)
|
||||
.await
|
||||
.map_err(|_| s3_error!(InvalidRequest, "legacy transition-state reconciliation body is too large or unreadable"))?;
|
||||
let request: LegacyTransitionStateReconcileRequest = serde_json::from_slice(&body)
|
||||
.map_err(|_| s3_error!(InvalidRequest, "legacy transition-state reconciliation request must be valid JSON"))?;
|
||||
let body = input.store_all_limited(MAX_ADMIN_REQUEST_BODY_SIZE).await.map_err(|_| {
|
||||
admin_s3_error(
|
||||
AdminS3ErrorCode::InvalidRequest,
|
||||
"legacy transition-state reconciliation body is too large or unreadable",
|
||||
)
|
||||
})?;
|
||||
let request: LegacyTransitionStateReconcileRequest = serde_json::from_slice(&body).map_err(|_| {
|
||||
admin_s3_error(
|
||||
AdminS3ErrorCode::InvalidRequest,
|
||||
"legacy transition-state reconciliation request must be valid JSON",
|
||||
)
|
||||
})?;
|
||||
validate_legacy_transition_state_reconcile_request(&selector, request.confirm, &request.selector)?;
|
||||
let Some(store) = store else {
|
||||
return Err(s3_error!(InternalError, "object store is not initialized"));
|
||||
return Err(admin_s3_error(AdminS3ErrorCode::InternalError, "object store is not initialized"));
|
||||
};
|
||||
|
||||
let response: LegacyTransitionStateReconcileResponse = store
|
||||
|
||||
Reference in New Issue
Block a user