fix(ecstore): handle metadata-less bucket residue (#6591)

* fix(ecstore): handle metadata-less bucket residue

Diagnose metadata-less on-disk residue before non-force DeleteBucket reaches physical deletion, and keep scanner-discovered metadata-missing objects on a non-destructive heal path.

Add explicit heal --remove cleanup for unversioned metadata-less data directories, using the existing data-dir delete primitive and fail-closed shape checks so pre-commit or unknown residue is preserved.

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(connect): adapt offline array validator

Wrap the filesystem summary validator in a closure so Option::is_some_and can pass the concrete array reference accepted by serde_json::Value::as_array.

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(connect): remove redundant offline test clones

Move the temporary path into the swap closure after deriving the output path, keeping clippy's redundant-clone lint clean for offline bundle tests.

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
houseme
2026-08-26 09:17:19 +08:00
committed by GitHub
parent 2bd83a5276
commit 0f987714a1
7 changed files with 595 additions and 21 deletions
+7 -1
View File
@@ -119,6 +119,8 @@ pub enum StorageError {
BucketExists(String),
#[error("Bucket not empty: {0}")]
BucketNotEmpty(String),
#[error("Bucket not empty: {bucket} ({details})")]
BucketNotEmptyWithDetails { bucket: String, details: String },
#[error("Bucket name invalid: {0}")]
BucketNameInvalid(String),
@@ -493,6 +495,10 @@ impl Clone for StorageError {
StorageError::MethodNotAllowed => StorageError::MethodNotAllowed,
StorageError::BucketNotFound(a) => StorageError::BucketNotFound(a.clone()),
StorageError::BucketNotEmpty(a) => StorageError::BucketNotEmpty(a.clone()),
StorageError::BucketNotEmptyWithDetails { bucket, details } => StorageError::BucketNotEmptyWithDetails {
bucket: bucket.clone(),
details: details.clone(),
},
StorageError::BucketNameInvalid(a) => StorageError::BucketNameInvalid(a.clone()),
StorageError::ObjectNameInvalid(a, b) => StorageError::ObjectNameInvalid(a.clone(), b.clone()),
StorageError::BucketExists(a) => StorageError::BucketExists(a.clone()),
@@ -600,7 +606,7 @@ impl StorageError {
StorageError::InvalidArgument(_, _, _) => StorageErrorCode::InvalidArgument,
StorageError::MethodNotAllowed => StorageErrorCode::MethodNotAllowed,
StorageError::BucketNotFound(_) => StorageErrorCode::BucketNotFound,
StorageError::BucketNotEmpty(_) => StorageErrorCode::BucketNotEmpty,
StorageError::BucketNotEmpty(_) | StorageError::BucketNotEmptyWithDetails { .. } => StorageErrorCode::BucketNotEmpty,
StorageError::BucketNameInvalid(_) => StorageErrorCode::BucketNameInvalid,
StorageError::ObjectNameInvalid(_, _) => StorageErrorCode::ObjectNameInvalid,
StorageError::BucketExists(_) => StorageErrorCode::BucketExists,