diff --git a/crates/protocols/src/swift/account.rs b/crates/protocols/src/swift/account.rs index 7ec08defe..5ea1cf0d6 100644 --- a/crates/protocols/src/swift/account.rs +++ b/crates/protocols/src/swift/account.rs @@ -14,9 +14,9 @@ //! Swift account operations and validation +use super::storage_compat::{get_swift_bucket_metadata, resolve_swift_object_store_handle, set_swift_bucket_metadata}; use super::{SwiftError, SwiftResult}; use rustfs_credentials::Credentials; -use rustfs_ecstore::resolve_object_store_handle; use rustfs_storage_api::{BucketOperations, MakeBucketOptions}; use s3s::dto::{Tag, Tagging}; use sha2::{Digest, Sha256}; @@ -119,7 +119,7 @@ pub async fn get_account_metadata(account: &str, _credentials: &Option meta, Err(_) => { // Bucket doesn't exist - return empty metadata @@ -159,12 +159,12 @@ pub async fn update_account_metadata( ) -> SwiftResult<()> { let bucket_name = get_account_metadata_bucket_name(account); - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; // Create bucket if it doesn't exist - let bucket_exists = rustfs_ecstore::bucket::metadata_sys::get(&bucket_name).await.is_ok(); + let bucket_exists = get_swift_bucket_metadata(&bucket_name).await.is_ok(); if !bucket_exists { // Create bucket for account metadata store @@ -174,7 +174,7 @@ pub async fn update_account_metadata( } // Load current bucket metadata - let bucket_meta = rustfs_ecstore::bucket::metadata_sys::get(&bucket_name) + let bucket_meta = get_swift_bucket_metadata(&bucket_name) .await .map_err(|e| SwiftError::InternalServerError(format!("Failed to load bucket metadata: {}", e)))?; @@ -221,7 +221,7 @@ pub async fn update_account_metadata( } // Save updated metadata - rustfs_ecstore::bucket::metadata_sys::set_bucket_metadata(bucket_name.clone(), bucket_meta_clone) + set_swift_bucket_metadata(bucket_name.clone(), bucket_meta_clone) .await .map_err(|e| SwiftError::InternalServerError(format!("Failed to save metadata: {}", e)))?; diff --git a/crates/protocols/src/swift/container.rs b/crates/protocols/src/swift/container.rs index 9af747b8f..becbafd8e 100644 --- a/crates/protocols/src/swift/container.rs +++ b/crates/protocols/src/swift/container.rs @@ -17,10 +17,10 @@ //! This module implements Swift container CRUD operations and container-bucket translation. use super::account::validate_account_access; +use super::storage_compat::{get_swift_bucket_metadata, resolve_swift_object_store_handle, set_swift_bucket_metadata}; use super::types::Container; use super::{SwiftError, SwiftResult}; use rustfs_credentials::Credentials; -use rustfs_ecstore::resolve_object_store_handle; use rustfs_storage_api::{ BucketInfo, BucketOperations, BucketOptions, DeleteBucketOptions, ListOperations as _, MakeBucketOptions, }; @@ -243,7 +243,7 @@ pub async fn list_containers(account: &str, credentials: &Credentials) -> SwiftR let mapper = ContainerMapper::default(); // Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -301,7 +301,7 @@ pub async fn create_container(account: &str, container: &str, credentials: &Cred let bucket_name = mapper.swift_to_s3_bucket(container, &project_id); // Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -395,7 +395,7 @@ pub async fn get_container_metadata(account: &str, container: &str, credentials: let bucket_name = mapper.swift_to_s3_bucket(container, &project_id); // Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -413,7 +413,7 @@ pub async fn get_container_metadata(account: &str, container: &str, credentials: })?; // Load bucket metadata to get custom metadata from tags - let custom_metadata = match rustfs_ecstore::bucket::metadata_sys::get(&bucket_name).await { + let custom_metadata = match get_swift_bucket_metadata(&bucket_name).await { Ok(bucket_meta) => { if let Some(tagging) = &bucket_meta.tagging_config { s3_tags_to_swift_metadata(tagging) @@ -472,7 +472,7 @@ pub async fn update_container_metadata( let bucket_name = mapper.swift_to_s3_bucket(container, &project_id); // Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -489,7 +489,7 @@ pub async fn update_container_metadata( })?; // Load current bucket metadata - let bucket_meta = rustfs_ecstore::bucket::metadata_sys::get(&bucket_name) + let bucket_meta = get_swift_bucket_metadata(&bucket_name) .await .map_err(|e| SwiftError::InternalServerError(format!("Failed to load bucket metadata: {}", e)))?; @@ -536,7 +536,7 @@ pub async fn update_container_metadata( } // Save updated metadata - rustfs_ecstore::bucket::metadata_sys::set_bucket_metadata(bucket_name, bucket_meta_clone) + set_swift_bucket_metadata(bucket_name, bucket_meta_clone) .await .map_err(|e| SwiftError::InternalServerError(format!("Failed to save metadata: {}", e)))?; @@ -571,7 +571,7 @@ pub async fn delete_container(account: &str, container: &str, credentials: &Cred let bucket_name = mapper.swift_to_s3_bucket(container, &project_id); // Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -659,7 +659,7 @@ pub async fn list_objects( let bucket = mapper.swift_to_s3_bucket(container, &project_id); // Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -769,7 +769,7 @@ pub async fn enable_versioning( let archive_bucket_name = mapper.swift_to_s3_bucket(archive_container, &project_id); // Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -802,7 +802,7 @@ pub async fn enable_versioning( })?; // Load current bucket metadata - let bucket_meta = rustfs_ecstore::bucket::metadata_sys::get(&bucket_name) + let bucket_meta = get_swift_bucket_metadata(&bucket_name) .await .map_err(|e| SwiftError::InternalServerError(format!("Failed to load bucket metadata: {}", e)))?; @@ -836,7 +836,7 @@ pub async fn enable_versioning( bucket_meta_clone.tagging_config = Some(existing_tagging); // Save updated metadata - rustfs_ecstore::bucket::metadata_sys::set_bucket_metadata(bucket_name, bucket_meta_clone) + set_swift_bucket_metadata(bucket_name, bucket_meta_clone) .await .map_err(|e| SwiftError::InternalServerError(format!("Failed to save metadata: {}", e)))?; @@ -865,12 +865,12 @@ pub async fn disable_versioning(account: &str, container: &str, credentials: &Cr let bucket_name = mapper.swift_to_s3_bucket(container, &project_id); // Verify container exists - let Some(_store) = resolve_object_store_handle() else { + let Some(_store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; // Load current bucket metadata - let bucket_meta = rustfs_ecstore::bucket::metadata_sys::get(&bucket_name) + let bucket_meta = get_swift_bucket_metadata(&bucket_name) .await .map_err(|e| SwiftError::InternalServerError(format!("Failed to load bucket metadata: {}", e)))?; @@ -905,7 +905,7 @@ pub async fn disable_versioning(account: &str, container: &str, credentials: &Cr } // Save updated metadata - rustfs_ecstore::bucket::metadata_sys::set_bucket_metadata(bucket_name, bucket_meta_clone) + set_swift_bucket_metadata(bucket_name, bucket_meta_clone) .await .map_err(|e| SwiftError::InternalServerError(format!("Failed to save metadata: {}", e)))?; @@ -937,7 +937,7 @@ pub async fn get_versions_location(account: &str, container: &str, credentials: let bucket_name = mapper.swift_to_s3_bucket(container, &project_id); // Load bucket metadata - let bucket_meta = match rustfs_ecstore::bucket::metadata_sys::get(&bucket_name).await { + let bucket_meta = match get_swift_bucket_metadata(&bucket_name).await { Ok(meta) => meta, Err(_) => { // Container doesn't exist @@ -1016,7 +1016,7 @@ pub async fn set_container_acl( let bucket_name = mapper.swift_to_s3_bucket(container, &project_id); // Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -1033,7 +1033,7 @@ pub async fn set_container_acl( })?; // Load current bucket metadata - let bucket_meta = rustfs_ecstore::bucket::metadata_sys::get(&bucket_name) + let bucket_meta = get_swift_bucket_metadata(&bucket_name) .await .map_err(|e| SwiftError::InternalServerError(format!("Failed to load bucket metadata: {}", e)))?; @@ -1088,7 +1088,7 @@ pub async fn set_container_acl( } // Save updated metadata - rustfs_ecstore::bucket::metadata_sys::set_bucket_metadata(bucket_name, bucket_meta_clone) + set_swift_bucket_metadata(bucket_name, bucket_meta_clone) .await .map_err(|e| SwiftError::InternalServerError(format!("Failed to save metadata: {}", e)))?; @@ -1135,7 +1135,7 @@ pub async fn get_container_acl( let bucket_name = mapper.swift_to_s3_bucket(container, &project_id); // Load bucket metadata - let bucket_meta = rustfs_ecstore::bucket::metadata_sys::get(&bucket_name).await.map_err(|e| { + let bucket_meta = get_swift_bucket_metadata(&bucket_name).await.map_err(|e| { if e.to_string().contains("not found") { SwiftError::NotFound(format!("Container '{}' not found", container)) } else { diff --git a/crates/protocols/src/swift/object.rs b/crates/protocols/src/swift/object.rs index 1acb5fa61..e268e19de 100644 --- a/crates/protocols/src/swift/object.rs +++ b/crates/protocols/src/swift/object.rs @@ -51,10 +51,10 @@ use super::account::validate_account_access; use super::container::ContainerMapper; +use super::storage_compat::resolve_swift_object_store_handle; use super::{SwiftError, SwiftResult}; use axum::http::HeaderMap; use rustfs_credentials::Credentials; -use rustfs_ecstore::resolve_object_store_handle; use rustfs_rio::HashReader; use rustfs_storage_api::{BucketOperations, BucketOptions, ObjectIO as _, ObjectOperations as _}; use std::collections::HashMap; @@ -384,7 +384,7 @@ where } // 12. Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -464,7 +464,7 @@ where validate_metadata(metadata)?; // Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -550,7 +550,7 @@ pub async fn get_object( let bucket = mapper.swift_to_s3_bucket(container, &project_id); // 5. Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -608,7 +608,7 @@ pub async fn head_object( let bucket = mapper.swift_to_s3_bucket(container, &project_id); // 5. Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -666,7 +666,7 @@ pub async fn delete_object(account: &str, container: &str, object: &str, credent let bucket = mapper.swift_to_s3_bucket(container, &project_id); // 5. Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -729,7 +729,7 @@ pub async fn update_object_metadata( let bucket = mapper.swift_to_s3_bucket(container, &project_id); // 5. Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -853,7 +853,7 @@ pub async fn copy_object( let dst_bucket = mapper.swift_to_s3_bucket(dst_container, &dst_project_id); // 6. Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; diff --git a/crates/protocols/src/swift/storage_compat.rs b/crates/protocols/src/swift/storage_compat.rs index d9effb419..21e51eaeb 100644 --- a/crates/protocols/src/swift/storage_compat.rs +++ b/crates/protocols/src/swift/storage_compat.rs @@ -12,12 +12,30 @@ // See the License for the specific language governing permissions and // limitations under the License. -use rustfs_ecstore::store_api::{ - GetObjectReader as EcstoreGetObjectReader, ObjectInfo as EcstoreObjectInfo, ObjectOptions as EcstoreObjectOptions, - PutObjReader as EcstorePutObjReader, +use rustfs_ecstore::{ + bucket::{metadata::BucketMetadata, metadata_sys}, + error::Result as EcstoreResult, + store::ECStore, + store_api::{ + GetObjectReader as EcstoreGetObjectReader, ObjectInfo as EcstoreObjectInfo, ObjectOptions as EcstoreObjectOptions, + PutObjReader as EcstorePutObjReader, + }, }; +use std::sync::Arc; pub type SwiftGetObjectReader = EcstoreGetObjectReader; pub type SwiftObjectInfo = EcstoreObjectInfo; pub type SwiftObjectOptions = EcstoreObjectOptions; pub type SwiftPutObjReader = EcstorePutObjReader; + +pub fn resolve_swift_object_store_handle() -> Option> { + rustfs_ecstore::resolve_object_store_handle() +} + +pub async fn get_swift_bucket_metadata(bucket: &str) -> EcstoreResult> { + metadata_sys::get(bucket).await +} + +pub async fn set_swift_bucket_metadata(bucket: String, metadata: BucketMetadata) -> EcstoreResult<()> { + metadata_sys::set_bucket_metadata(bucket, metadata).await +} diff --git a/crates/protocols/src/swift/versioning.rs b/crates/protocols/src/swift/versioning.rs index 85a648f0e..6b4ad57f5 100644 --- a/crates/protocols/src/swift/versioning.rs +++ b/crates/protocols/src/swift/versioning.rs @@ -54,9 +54,9 @@ use super::account::validate_account_access; use super::container::ContainerMapper; use super::object::{ObjectKeyMapper, SwiftObjectOptions as ObjectOptions, head_object}; +use super::storage_compat::resolve_swift_object_store_handle; use super::{SwiftError, SwiftResult}; use rustfs_credentials::Credentials; -use rustfs_ecstore::resolve_object_store_handle; use rustfs_storage_api::{ListOperations as _, ObjectOperations as _}; use std::time::{SystemTime, UNIX_EPOCH}; use tracing::{debug, error}; @@ -197,7 +197,7 @@ pub async fn archive_current_version( let version_key = ObjectKeyMapper::swift_to_s3_key(&version_name)?; // Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -340,7 +340,7 @@ pub async fn restore_previous_version( let version_key = ObjectKeyMapper::swift_to_s3_key(newest_version)?; // Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; @@ -476,7 +476,7 @@ pub async fn list_object_versions( let archive_bucket = mapper.swift_to_s3_bucket(archive_container, &project_id); // Get storage layer - let Some(store) = resolve_object_store_handle() else { + let Some(store) = resolve_swift_object_store_handle() else { return Err(SwiftError::InternalServerError("Storage layer not initialized".to_string())); }; diff --git a/docs/architecture/migration-progress.md b/docs/architecture/migration-progress.md index 262af2996..6091d74a2 100644 --- a/docs/architecture/migration-progress.md +++ b/docs/architecture/migration-progress.md @@ -824,6 +824,26 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block diff hygiene, direct import scan, Rust risk scan, full pre-commit, and required three-expert review passed. +- [x] `API-028` Clean Swift ECStore runtime boundary imports. + - Current branch: `overtrue/arch-swift-ecstore-boundaries`. + - Completed slice: move Swift account, container, object, and versioning + access to ECStore object-store resolver and bucket metadata get/set calls + behind the Swift-local `storage_compat` module. + - Acceptance: direct Swift module references to `rustfs_ecstore` for object + store resolution, bucket metadata reads, bucket metadata writes, and object + DTO aliases are limited to `swift::storage_compat`; Swift business modules + consume Swift-owned compatibility names. + - Must preserve: Swift account metadata tags, container metadata tags, + versioning location tags, ACL tag storage, object CRUD/copy/range behavior, + storage-not-initialized error mapping, and bucket metadata load/save error + mapping. + - Risk defense: this slice changes import ownership and thin wrapper + boundaries only; it does not move ECStore definitions, alter metadata + serialization, change Swift bucket naming, or adjust runtime control flow. + - Verification: focused Swift compile/tests, migration/layer guards, + formatting, diff hygiene, direct Swift import scan, Rust risk scan, full + pre-commit, and required three-expert review passed. + ## Phase 8 Background Controller Tasks - [x] `BGC-001` Inventory background services. @@ -1093,61 +1113,46 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block ## Next PRs 1. `pure-move`/`consumer-migration`: continue larger cleanup slices with the - loss-prevention guards active for public re-exports and remaining storage - compatibility contracts. + loss-prevention guards active for remaining storage compatibility contracts + such as scanner/heal runtime boundaries. ## Pre-Push Review Log | Expert | Status | Notes | |---|---|---| -| Quality/architecture | passed | Table catalog, IAM, admin zip, capacity, heal integration, scanner, Swift, S3 Select, and notify consumers now use local DTO compatibility aliases instead of raw ECStore `store_api` imports. | -| Migration preservation | passed | ECStore remains the owner of object DTO/readers; alias boundaries preserve concrete associated types for storage traits, IAM config reads, zip preflight reads, scanner cache I/O, Swift/S3 Select reads, notify payloads, capacity assertions, and heal integration writes. | -| Testing/verification | passed | Focused compile/tests, migration/layer guards, direct import scan, formatting, diff hygiene, Rust risk scan, and full `make pre-commit` passed. | +| Quality/architecture | passed | Swift account, container, object, and versioning modules now use Swift-local storage compatibility wrappers for ECStore resolver and bucket metadata access. | +| Migration preservation | passed | ECStore remains the owner of object-store resolution and bucket metadata persistence; Swift metadata tags, ACL tags, versioning tags, and object read/write/copy semantics are preserved. | +| Testing/verification | passed | Focused Swift compile/tests, migration/layer guards, direct Swift import scan, formatting, diff hygiene, Rust risk scan, and full `make pre-commit` passed. | ## Verification Notes Passed before push: -- `cargo check --tests -p rustfs -p rustfs-iam -p rustfs-heal`: passed. -- `cargo check --tests -p rustfs-iam -p rustfs-heal -p rustfs-scanner -p - rustfs-protocols -p rustfs-s3select-api -p rustfs-notify`: passed. -- `cargo test -p rustfs --lib table_catalog_store_trait`: passed; 2 passed. -- `cargo test -p rustfs --lib admin::handlers::object_zip_download::tests`: - passed; 28 passed. -- `cargo test -p rustfs --lib app::capacity_dirty_scope_test`: passed; 2 - passed. -- `cargo test -p rustfs-iam store::object::tests`: passed; 8 passed. -- `cargo test -p rustfs-heal --test heal_integration_test`: passed; 5 passed. -- `cargo check --tests -p rustfs-scanner -p rustfs-protocols -p - rustfs-s3select-api -p rustfs-notify`: passed. -- `cargo test -p rustfs-scanner -p rustfs-protocols -p rustfs-s3select-api -p - rustfs-notify`: passed; notify 82 passed, protocols 13 passed, S3 Select 49 - passed, scanner 151 passed plus lifecycle integration 1 passed and 14 - ignored. -- `rg -n 'rustfs_ecstore::store_api|store_api::\{' rustfs/src crates --glob - '!crates/ecstore/**' --glob '*.rs'`: remaining matches are deliberate - boundary alias definitions in RustFS storage and crate-local compat modules. +- `cargo check --tests -p rustfs-protocols`: passed. +- `cargo test -p rustfs-protocols`: passed; 13 passed. +- `rg -n 'rustfs_ecstore|resolve_object_store_handle|metadata_sys' + crates/protocols/src/swift/*.rs`: remaining matches are deliberate Swift + compatibility boundary definitions. - `./scripts/check_architecture_migration_rules.sh`: passed. - `./scripts/check_layer_dependencies.sh`: passed. - `cargo fmt --all --check`: passed. - `git diff --check`: passed. -- Rust risk scan: new hits are crate-local alias casts only; no new - `unwrap`/`expect`, panic/todo markers, `unsafe`, process-spawning calls, - println/eprintln, or relaxed ordering in added Rust lines. +- Rust risk scan: no new `unwrap`/`expect`, panic/todo markers, `unsafe`, + process-spawning calls, println/eprintln, relaxed ordering, or numeric casts + in added Rust lines. - `make pre-commit`: passed. Notes: -- This slice is stacked on API-026 while `rustfs/rustfs#3566` is pending. -- Direct ECStore `store_api` references outside ECStore now remain only at - explicit alias boundary points. -- The slice does not alter table catalog storage behavior, IAM config storage, - admin zip download object reads, capacity dirty-disk behavior, heal - integration semantics, scanner cache I/O, Swift/S3 Select object reads, - notification event payloads, or ECStore DTO definitions. +- This slice builds on the merged API-027 compatibility cleanup. +- Direct Swift ECStore resolver and bucket metadata access now remains only in + the Swift compatibility boundary. +- The slice does not alter Swift account/container metadata behavior, object + read/write/copy behavior, versioning behavior, ACL behavior, or ECStore + definitions. ## Handoff Notes -- Continue with larger consumer-migration batches; keep ECStore-owned - DTOs/readers/walk filters in ECStore until their concrete behavior is isolated - enough for a pure-move slice. +- Continue with larger consumer-migration batches around scanner/heal runtime + boundaries; keep ECStore-owned behavior in ECStore until concrete behavior is + isolated enough for a pure-move slice.