From 3942186f3d3619b1bac697d294099586ccaef7e6 Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Thu, 25 Jun 2026 18:10:49 +0800 Subject: [PATCH] refactor: route app storage helper imports (#3856) --- docs/architecture/migration-progress.md | 31 +++++-- rustfs/src/app/bucket_usecase.rs | 14 ++-- rustfs/src/app/mod.rs | 1 + rustfs/src/app/multipart_usecase.rs | 60 +++++++------ rustfs/src/app/object_usecase.rs | 56 ++++++------- rustfs/src/app/select_object.rs | 6 +- rustfs/src/app/storage_api.rs | 84 +++++++++++++++++++ rustfs/src/storage/mod.rs | 6 +- scripts/check_architecture_migration_rules.sh | 12 +++ scripts/layer-dependency-baseline.txt | 2 +- 10 files changed, 193 insertions(+), 79 deletions(-) create mode 100644 rustfs/src/app/storage_api.rs diff --git a/docs/architecture/migration-progress.md b/docs/architecture/migration-progress.md index 54075f113..f4ec7d1aa 100644 --- a/docs/architecture/migration-progress.md +++ b/docs/architecture/migration-progress.md @@ -5,9 +5,9 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block ## Current Context - Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660) -- Branch: `overtrue/arch-app-s3-api-boundary` -- Baseline: completed `C-011/C-012/C-013/API-055/API-059/API-079/API-080/API-081/API-082/API-083/API-084/API-085/API-086/API-087/API-088/API-089/API-090/API-091/API-092/API-093/API-094/API-095/API-096/API-097/API-098/API-099/API-100/API-101/API-102/API-103/API-104/API-105/API-106/API-107/API-108/API-109/API-110/API-111/API-112/API-113/API-114/API-115/API-116/API-117/API-118/API-119/API-120/API-121/API-122/API-123/API-124/API-125/API-126/API-127/API-128/API-129/API-130/API-131/API-132/API-133/API-134/API-135/API-136/API-137/API-138/API-139/API-140/API-141/API-142/API-143/API-144/API-145/API-146/API-147/API-148/API-149/API-150/API-151/API-152/API-153/API-154/API-155/API-156/API-157/API-158/API-159/API-160/API-161/API-162/API-163/API-164/API-165/API-166/API-167/API-168/API-169/API-170/API-171/API-172/API-173/API-174/API-175/API-176/API-177/API-178/API-179/API-180/API-181/API-182/API-183/API-184/API-185/API-186/API-187/API-188/API-189/API-190/API-191/API-192/API-193/API-194/API-195/API-196/API-197/API-198/API-199/API-200/API-201/API-202/API-203/API-204/API-205/API-206/API-207/API-208/API-209/API-210`. -- Based on: latest main after PR #3853 merged. +- Branch: `overtrue/arch-app-storage-boundary-imports` +- Baseline: completed `C-011/C-012/C-013/API-055/API-059/API-079/API-080/API-081/API-082/API-083/API-084/API-085/API-086/API-087/API-088/API-089/API-090/API-091/API-092/API-093/API-094/API-095/API-096/API-097/API-098/API-099/API-100/API-101/API-102/API-103/API-104/API-105/API-106/API-107/API-108/API-109/API-110/API-111/API-112/API-113/API-114/API-115/API-116/API-117/API-118/API-119/API-120/API-121/API-122/API-123/API-124/API-125/API-126/API-127/API-128/API-129/API-130/API-131/API-132/API-133/API-134/API-135/API-136/API-137/API-138/API-139/API-140/API-141/API-142/API-143/API-144/API-145/API-146/API-147/API-148/API-149/API-150/API-151/API-152/API-153/API-154/API-155/API-156/API-157/API-158/API-159/API-160/API-161/API-162/API-163/API-164/API-165/API-166/API-167/API-168/API-169/API-170/API-171/API-172/API-173/API-174/API-175/API-176/API-177/API-178/API-179/API-180/API-181/API-182/API-183/API-184/API-185/API-186/API-187/API-188/API-189/API-190/API-191/API-192/API-193/API-194/API-195/API-196/API-197/API-198/API-199/API-200/API-201/API-202/API-203/API-204/API-205/API-206/API-207/API-208/API-209/API-210/API-211`. +- Based on: stacked on API-210 after PR #3853 merged. - PR type for this branch: `consumer-migration` - Runtime behavior changes: none. - Rust code changes: route replication pool, outbound TLS generation, runtime @@ -45,7 +45,7 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block readiness-time, observability, metrics, buffer, and TLS runtime source helpers, plus server readiness/audit/event/module-switch runtime source helpers, storage request/RPC/SSE runtime source helpers, and admin - handler/service/router runtime source helpers, plus root auth/init/config/protocol/workload, app usecase, storage node-service, remaining admin grouped context import runtime source helpers, app bucket/object/multipart usecase explicit storage imports, app select/bucket/object/multipart explicit S3 DTO plus ECFS owner imports, and app-local S3 API response helper imports for bucket/object/multipart usecases, + handler/service/router runtime source helpers, plus root auth/init/config/protocol/workload, app usecase, storage node-service, remaining admin grouped context import runtime source helpers, app bucket/object/multipart usecase explicit storage imports, app select/bucket/object/multipart explicit S3 DTO plus ECFS owner imports, app-local S3 API response helper imports for bucket/object/multipart usecases, and app-local storage helper boundary imports for select/bucket/object/multipart usecases, through AppContext-first or owner-crate resolver boundaries. - CI/script changes: lock completed owner and test/fuzz boundaries against bare/glob imports, scattered raw ECStore facade subpaths, and startup @@ -56,8 +56,8 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block event-bridge thin module regressions, plus IAM runtime-source bypasses; accept the reviewed AppContext resolver reverse dependencies in the layer baseline, and block direct admin AppContext resolver consumers outside the - admin runtime-source boundary, block root, app usecase, and storage direct AppContext resolver consumers outside their runtime-source boundaries, catch grouped AppContext imports, reject app usecase storage wildcard imports, reject app-layer S3 DTO and ECFS wildcard imports, narrow the object-usecase ECFS layer baseline entry to `FS`, and reject direct storage S3 API helper imports from app usecase files. -- Docs changes: record the API-136 through API-210 owner facade and lifecycle + admin runtime-source boundary, block root, app usecase, and storage direct AppContext resolver consumers outside their runtime-source boundaries, catch grouped AppContext imports, reject app usecase storage wildcard imports, reject app-layer S3 DTO and ECFS wildcard imports, narrow the object-usecase ECFS layer baseline entry to `FS`, reject direct storage S3 API helper imports from app usecase files, and reject direct storage helper imports from app select/usecase files. +- Docs changes: record the API-136 through API-211 owner facade and lifecycle runtime-source cleanup. ## Phase 0 Tasks @@ -5025,6 +5025,22 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block layer guards, diff hygiene, residual direct S3 API scan, Rust risk scan, fast PR gate, and full PR gate before PR. +- [x] `API-211` Route app storage helper imports through app boundary. + - Do: add an app-local storage helper boundary and route select, bucket, + object, and multipart usecases through it instead of direct storage helper + imports for access, helper, options, SSE, request-context, concurrency, + prefix, timeout, and storage object helper types. + - Acceptance: app select/usecase files no longer import the migrated storage + helper APIs directly, layer baseline records the reviewed app boundary, and + migration rules reject direct storage helper bypasses. + - Must preserve: select-object preflight and traced execution, bucket request + authorization/background metadata updates, multipart SSE/options parsing, + object SSE/concurrency/prefix/timeout behavior, and existing storage helper + implementations. + - Verification: focused RustFS app compile/tests, formatting, migration and + layer guards, diff hygiene, residual direct storage helper scan, Rust risk + scan, fast PR gate, and full PR gate before PR. + ## Next PRs 1. `consumer-migration`: continue reducing direct global reads behind AppContext resolver boundaries. @@ -5033,6 +5049,9 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block | Expert | Status | Notes | |---|---|---| +| Quality/architecture | pass | API-211 routes app select/usecase storage helper imports through an app-local storage_api boundary and adds a direct-bypass guard. | +| Migration preservation | pass | Select preflight, bucket authorization/background work, multipart SSE/options parsing, and object SSE/concurrency/prefix/timeout paths keep the same storage helper implementations. | +| Testing/verification | pass | Focused RustFS app compile/tests, formatting, migration/layer guards, residual direct storage helper scan, Rust risk scan, fast PR gate, and full PR gate are planned before PR. | | Quality/architecture | pass | API-210 routes app usecase S3 API helper imports through an app-local boundary and adds a direct-bypass guard. | | Migration preservation | pass | Bucket list response mapping, multipart list/part-number parsing, object list-parts parsing, and S3 owner metadata keep the same storage helper implementations. | | Testing/verification | pass | Focused RustFS app compile/tests, formatting, migration/layer guards, residual direct S3 API scan, Rust risk scan, fast PR gate, and full PR gate are planned before PR. | diff --git a/rustfs/src/app/bucket_usecase.rs b/rustfs/src/app/bucket_usecase.rs index 8304b2186..bcc00be32 100644 --- a/rustfs/src/app/bucket_usecase.rs +++ b/rustfs/src/app/bucket_usecase.rs @@ -22,6 +22,13 @@ use super::s3_api::bucket::{ build_list_objects_output, build_list_objects_v2_output, parse_list_object_versions_params, parse_list_objects_v2_params, rustfs_owner, }; +use super::storage_api::StorageObjectInfo as ObjectInfo; +use super::storage_api::access::{ReqInfo, authorize_request, req_info_ref}; +use super::storage_api::helper::{OperationHelper, spawn_background_with_context}; +use super::storage_api::{ + get_validated_store, process_lambda_configurations, process_queue_configurations, process_topic_configurations, + request_context, validate_list_object_unordered_with_delimiter, +}; use super::{AppObjectLockConfigExt as _, AppVersioningConfigExt as _}; use super::{ bucket_target_sys::BucketTargetSys, @@ -50,13 +57,6 @@ use crate::app::runtime_sources::{ use crate::auth::get_condition_values_with_client_info; use crate::error::ApiError; use crate::server::RemoteAddr; -use crate::storage::StorageObjectInfo as ObjectInfo; -use crate::storage::access::{ReqInfo, authorize_request, req_info_ref}; -use crate::storage::helper::{OperationHelper, spawn_background_with_context}; -use crate::storage::{ - get_validated_store, process_lambda_configurations, process_queue_configurations, process_topic_configurations, - request_context, validate_list_object_unordered_with_delimiter, -}; use futures::StreamExt; use http::StatusCode; use metrics::counter; diff --git a/rustfs/src/app/mod.rs b/rustfs/src/app/mod.rs index eb0e0795c..3f9e630b2 100644 --- a/rustfs/src/app/mod.rs +++ b/rustfs/src/app/mod.rs @@ -23,6 +23,7 @@ pub mod object_usecase; pub(crate) mod runtime_sources; pub(crate) mod s3_api; mod select_object; +pub(crate) mod storage_api; #[cfg(test)] mod capacity_dirty_scope_test; diff --git a/rustfs/src/app/multipart_usecase.rs b/rustfs/src/app/multipart_usecase.rs index 2eceddece..6829ff479 100644 --- a/rustfs/src/app/multipart_usecase.rs +++ b/rustfs/src/app/multipart_usecase.rs @@ -23,6 +23,19 @@ use super::s3_api::multipart::{ ListMultipartUploadsParams, build_list_multipart_uploads_output, build_list_parts_output, parse_list_multipart_uploads_params, parse_list_parts_params, parse_upload_part_number, }; +use super::storage_api::access::has_bypass_governance_header; +use super::storage_api::helper::OperationHelper; +use super::storage_api::options::{ + copy_src_opts, extract_metadata_from_mime, get_complete_multipart_upload_opts, get_content_sha256_with_query, get_opts, + parse_copy_source_range, put_opts, validate_archive_content_encoding, +}; +use super::storage_api::sse::{ + DecryptionRequest, EncryptionKeyKind, EncryptionRequest, PrepareEncryptionRequest, apply_bucket_default_lock_retention, + build_ssec_read_headers, encryption_material_to_metadata, extract_server_side_encryption_from_headers, + extract_ssec_params_from_headers, extract_ssekms_context_from_headers, get_buffer_size_opt_in, map_get_object_reader_error, + mark_encrypted_multipart_metadata, sse_decryption, sse_prepare_encryption, +}; +use super::storage_api::{StorageObjectOptions as ObjectOptions, StoragePutObjReader as PutObjReader}; #[cfg(test)] use super::{DecryptReader, EncryptReader, HardLimitReader, boxed_reader, wrap_reader}; use super::{HashReader, WritePlan}; @@ -38,21 +51,6 @@ use crate::app::object_usecase::{build_put_like_object_lock_metadata, validate_e use crate::app::runtime_sources::{AppContext, get_global_app_context, resolve_object_store_handle_for_context}; use crate::capacity::record_capacity_write; use crate::error::ApiError; -use crate::storage::access::has_bypass_governance_header; -use crate::storage::helper::OperationHelper; -use crate::storage::options::{ - copy_src_opts, extract_metadata_from_mime, get_complete_multipart_upload_opts, get_content_sha256_with_query, get_opts, - parse_copy_source_range, put_opts, validate_archive_content_encoding, -}; -use crate::storage::sse::{ - build_ssec_read_headers, encryption_material_to_metadata, extract_ssec_params_from_headers, - extract_ssekms_context_from_headers, map_get_object_reader_error, mark_encrypted_multipart_metadata, -}; -use crate::storage::{ - DecryptionRequest, EncryptionRequest, PrepareEncryptionRequest, apply_bucket_default_lock_retention, - extract_server_side_encryption_from_headers, get_buffer_size_opt_in, sse_decryption, sse_prepare_encryption, -}; -use crate::storage::{StorageObjectOptions as ObjectOptions, StoragePutObjReader as PutObjReader}; use crate::table_catalog; use bytes::Bytes; use futures::StreamExt; @@ -664,7 +662,7 @@ impl DefaultMultipartUsecase { let ssekms_key_id = material.kms_key_id.clone(); let mut encryption_metadata = encryption_material_to_metadata(&material)?; - if material.key_kind == crate::storage::sse::EncryptionKeyKind::Object { + if material.key_kind == EncryptionKeyKind::Object { mark_encrypted_multipart_metadata(&mut encryption_metadata); } metadata.extend(encryption_metadata); @@ -896,10 +894,10 @@ impl DefaultMultipartUsecase { .await? .ok_or_else(|| ApiError::from(StorageError::other("Missing SSE-C session material")))?; let ssec_write = match ssec_material.key_kind { - crate::storage::sse::EncryptionKeyKind::Object => { + EncryptionKeyKind::Object => { super::WriteEncryption::multipart_object_key(ssec_material.key_bytes, part_id as u32) } - crate::storage::sse::EncryptionKeyKind::Direct => { + EncryptionKeyKind::Direct => { super::WriteEncryption::multipart(ssec_material.key_bytes, ssec_material.base_nonce, part_id) } }; @@ -916,10 +914,10 @@ impl DefaultMultipartUsecase { .await? .ok_or_else(|| ApiError::from(StorageError::other("Missing managed SSE session material")))?; let managed_write = match managed_material.key_kind { - crate::storage::sse::EncryptionKeyKind::Object => { + EncryptionKeyKind::Object => { super::WriteEncryption::multipart_object_key(managed_material.key_bytes, part_id as u32) } - crate::storage::sse::EncryptionKeyKind::Direct => { + EncryptionKeyKind::Direct => { super::WriteEncryption::multipart(managed_material.key_bytes, managed_material.base_nonce, part_id) } }; @@ -1235,10 +1233,10 @@ impl DefaultMultipartUsecase { .await? .ok_or_else(|| ApiError::from(StorageError::other("Missing SSE-C session material")))?; let ssec_write = match ssec_material.key_kind { - crate::storage::sse::EncryptionKeyKind::Object => { + EncryptionKeyKind::Object => { super::WriteEncryption::multipart_object_key(ssec_material.key_bytes, part_id as u32) } - crate::storage::sse::EncryptionKeyKind::Direct => { + EncryptionKeyKind::Direct => { super::WriteEncryption::multipart(ssec_material.key_bytes, ssec_material.base_nonce, part_id) } }; @@ -1259,10 +1257,10 @@ impl DefaultMultipartUsecase { .await? .ok_or_else(|| ApiError::from(StorageError::other("Missing managed SSE session material")))?; let managed_write = match managed_material.key_kind { - crate::storage::sse::EncryptionKeyKind::Object => { + EncryptionKeyKind::Object => { super::WriteEncryption::multipart_object_key(managed_material.key_bytes, part_id as u32) } - crate::storage::sse::EncryptionKeyKind::Direct => { + EncryptionKeyKind::Direct => { super::WriteEncryption::multipart(managed_material.key_bytes, managed_material.base_nonce, part_id) } }; @@ -1502,12 +1500,12 @@ mod tests { let mut encrypted_one = Vec::new(); #[cfg(feature = "rio-v2")] let mut part_one_reader = match part_one_material.key_kind { - crate::storage::sse::EncryptionKeyKind::Object => EncryptReader::new_multipart_with_object_key( + EncryptionKeyKind::Object => EncryptReader::new_multipart_with_object_key( Cursor::new(part_one_plaintext.clone()), part_one_material.key_bytes, 1, ), - crate::storage::sse::EncryptionKeyKind::Direct => EncryptReader::new_multipart( + EncryptionKeyKind::Direct => EncryptReader::new_multipart( Cursor::new(part_one_plaintext.clone()), part_one_material.key_bytes, part_one_material.base_nonce, @@ -1539,12 +1537,12 @@ mod tests { let mut encrypted_two = Vec::new(); #[cfg(feature = "rio-v2")] let mut part_two_reader = match part_two_material.key_kind { - crate::storage::sse::EncryptionKeyKind::Object => EncryptReader::new_multipart_with_object_key( + EncryptionKeyKind::Object => EncryptReader::new_multipart_with_object_key( Cursor::new(part_two_plaintext.clone()), part_two_material.key_bytes, 2, ), - crate::storage::sse::EncryptionKeyKind::Direct => EncryptReader::new_multipart( + EncryptionKeyKind::Direct => EncryptReader::new_multipart( Cursor::new(part_two_plaintext.clone()), part_two_material.key_bytes, part_two_material.base_nonce, @@ -1563,7 +1561,7 @@ mod tests { .await .expect("read encrypted part two"); - if session_material.key_kind == crate::storage::sse::EncryptionKeyKind::Object { + if session_material.key_kind == EncryptionKeyKind::Object { assert!(session_metadata.contains_key("X-Minio-Internal-Encrypted-Multipart")); assert!(session_metadata.contains_key("X-Minio-Internal-Server-Side-Encryption-S3-Sealed-Key")); } else { @@ -1603,12 +1601,12 @@ mod tests { let plaintext_size = multipart_plaintext_size(&parts, -1); #[cfg(feature = "rio-v2")] let decrypted_stream = match decryption_material.key_kind { - crate::storage::sse::EncryptionKeyKind::Object => boxed_reader(DecryptReader::new_multipart_with_object_key( + EncryptionKeyKind::Object => boxed_reader(DecryptReader::new_multipart_with_object_key( wrap_reader(Cursor::new(encrypted_stream)), decryption_material.key_bytes, multipart_part_numbers(&parts), )), - crate::storage::sse::EncryptionKeyKind::Direct => boxed_reader(DecryptReader::new_multipart( + EncryptionKeyKind::Direct => boxed_reader(DecryptReader::new_multipart( wrap_reader(Cursor::new(encrypted_stream)), decryption_material.key_bytes, decryption_material.base_nonce, diff --git a/rustfs/src/app/object_usecase.rs b/rustfs/src/app/object_usecase.rs index e42629174..2e16fbc84 100644 --- a/rustfs/src/app/object_usecase.rs +++ b/rustfs/src/app/object_usecase.rs @@ -20,6 +20,32 @@ use super::storageclass; // Performance metrics recording (with zero-copy-metrics integration) use super::ECStore; use super::s3_api::multipart::parse_list_parts_params; +use super::storage_api::access::{PostObjectRequestMarker, authorize_request, has_bypass_governance_header, req_info_mut}; +use super::storage_api::concurrency::{ + self, ConcurrencyManager, GetObjectGuard, PutObjectGuard, get_concurrency_aware_buffer_size, get_concurrency_manager, + get_put_concurrency_aware_buffer_size, +}; +use super::storage_api::deadlock_detector; +use super::storage_api::ecfs::FS; +use super::storage_api::head_prefix::{head_prefix_not_found_message, probe_prefix_has_children}; +use super::storage_api::helper::{OperationHelper, spawn_background_with_context}; +use super::storage_api::options::{ + copy_dst_opts, copy_src_opts, del_opts, extract_metadata, extract_metadata_from_mime_with_object_name, + filter_object_metadata, get_content_sha256_with_query, get_opts, normalize_content_encoding_for_storage, put_opts, +}; +use super::storage_api::request_context::{self, spawn_traced}; +use super::storage_api::sse::{ + DecryptionRequest, EncryptionRequest, SSEType, apply_bucket_default_lock_retention, build_ssec_read_headers, + encryption_material_to_metadata, extract_server_side_encryption_from_headers, extract_ssec_params_from_headers, + extract_ssekms_context_from_headers, get_buffer_size_opt_in, map_get_object_reader_error, sse_decryption, sse_encryption, +}; +use super::storage_api::timeout_wrapper::{GetObjectTimeoutPolicy, RequestTimeoutWrapper}; +use super::storage_api::{ + RFC1123, check_preconditions, get_validated_store, has_replication_rules, parse_object_lock_legal_hold, + parse_object_lock_retention, parse_part_number_i32_to_usize, remove_object_lock_metadata_for_copy, + strip_managed_encryption_metadata, validate_bucket_object_lock_enabled, validate_object_key, validate_sse_headers_for_read, + validate_sse_headers_for_write, validate_ssec_for_read, wrap_response_with_cors, +}; use super::{AppReplicationConfigExt as _, AppVersioningConfigExt as _, predict_lifecycle_expiration, validate_restore_request}; use super::{DiskError, is_all_buckets_not_found}; use super::{DynReader, HashReader, WritePlan, wrap_reader}; @@ -53,32 +79,6 @@ use crate::config::RustFSBufferConfig; use crate::delete_tail_activity::{DeleteTailActivityGuard, DeleteTailStage}; use crate::error::ApiError; use crate::server::convert_ecstore_object_info; -use crate::storage::access::{PostObjectRequestMarker, authorize_request, has_bypass_governance_header, req_info_mut}; -use crate::storage::concurrency::{ - ConcurrencyManager, GetObjectGuard, PutObjectGuard, get_concurrency_aware_buffer_size, get_concurrency_manager, - get_put_concurrency_aware_buffer_size, -}; -use crate::storage::ecfs::FS; -use crate::storage::head_prefix::{head_prefix_not_found_message, probe_prefix_has_children}; -use crate::storage::helper::{OperationHelper, spawn_background_with_context}; -use crate::storage::options::{ - copy_dst_opts, copy_src_opts, del_opts, extract_metadata, extract_metadata_from_mime_with_object_name, - filter_object_metadata, get_content_sha256_with_query, get_opts, normalize_content_encoding_for_storage, put_opts, -}; -use crate::storage::request_context::spawn_traced; -use crate::storage::sse::{ - SSEType, build_ssec_read_headers, encryption_material_to_metadata, extract_ssekms_context_from_headers, - map_get_object_reader_error, -}; -use crate::storage::timeout_wrapper::{GetObjectTimeoutPolicy, RequestTimeoutWrapper}; -use crate::storage::{ - DecryptionRequest, EncryptionRequest, RFC1123, apply_bucket_default_lock_retention, check_preconditions, concurrency, - deadlock_detector, extract_server_side_encryption_from_headers, extract_ssec_params_from_headers, get_buffer_size_opt_in, - get_validated_store, has_replication_rules, parse_object_lock_legal_hold, parse_object_lock_retention, - parse_part_number_i32_to_usize, remove_object_lock_metadata_for_copy, request_context, sse_decryption, sse_encryption, - strip_managed_encryption_metadata, validate_bucket_object_lock_enabled, validate_object_key, validate_sse_headers_for_read, - validate_sse_headers_for_write, validate_ssec_for_read, wrap_response_with_cors, -}; use crate::table_catalog; use bytes::Bytes; use futures::StreamExt; @@ -155,7 +155,7 @@ use tokio_util::io::{ReaderStream, StreamReader}; use tracing::{debug, error, instrument, warn}; use uuid::Uuid; -use crate::storage::{ +use super::storage_api::{ StorageDeletedObject, StorageObjectInfo as ObjectInfo, StorageObjectOptions as ObjectOptions, StorageObjectToDelete as ObjectToDelete, StoragePutObjReader as PutObjReader, }; @@ -638,7 +638,7 @@ fn should_buffer_get_object_in_memory_with_threshold( #[cfg(test)] mod deadlock_request_guard_tests { use super::DeadlockRequestGuard; - use crate::storage::deadlock_detector::{DeadlockDetector, RequestHangDetectionPolicy}; + use crate::app::storage_api::deadlock_detector::{DeadlockDetector, RequestHangDetectionPolicy}; use std::sync::Arc; #[test] diff --git a/rustfs/src/app/select_object.rs b/rustfs/src/app/select_object.rs index f6eff9bf8..cda5ae119 100644 --- a/rustfs/src/app/select_object.rs +++ b/rustfs/src/app/select_object.rs @@ -1,8 +1,8 @@ +use super::storage_api::options::get_opts; +use super::storage_api::request_context::spawn_traced; +use super::storage_api::{get_validated_store, validate_sse_headers_for_read, validate_ssec_for_read}; use crate::app::runtime_sources::resolve_s3select_db; use crate::error::ApiError; -use crate::storage::options::get_opts; -use crate::storage::request_context::spawn_traced; -use crate::storage::{get_validated_store, validate_sse_headers_for_read, validate_ssec_for_read}; use bytes::Bytes; use datafusion::arrow::{ csv::{QuoteStyle, WriterBuilder as CsvWriterBuilder, writer::Terminator}, diff --git a/rustfs/src/app/storage_api.rs b/rustfs/src/app/storage_api.rs new file mode 100644 index 000000000..1208c14ff --- /dev/null +++ b/rustfs/src/app/storage_api.rs @@ -0,0 +1,84 @@ +// Copyright 2024 RustFS Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! App-local boundary for storage-layer helper APIs used by S3 use cases. + +pub(crate) mod access { + pub(crate) use crate::storage::access::{ + PostObjectRequestMarker, ReqInfo, authorize_request, has_bypass_governance_header, req_info_mut, req_info_ref, + }; +} + +pub(crate) mod concurrency { + pub(crate) use crate::storage::concurrency::{ + ConcurrencyManager, GetObjectGuard, IoQueueStatus, IoStrategy, PutObjectGuard, get_concurrency_aware_buffer_size, + get_concurrency_manager, get_put_concurrency_aware_buffer_size, + }; +} + +pub(crate) mod deadlock_detector { + #[cfg(test)] + pub(crate) use crate::storage::deadlock_detector::RequestHangDetectionPolicy; + pub(crate) use crate::storage::deadlock_detector::{DeadlockDetector, get_deadlock_detector}; +} + +pub(crate) mod ecfs { + pub(crate) use crate::storage::ecfs::FS; +} + +pub(crate) mod head_prefix { + pub(crate) use crate::storage::head_prefix::{head_prefix_not_found_message, probe_prefix_has_children}; +} + +pub(crate) mod helper { + pub(crate) use crate::storage::helper::{OperationHelper, spawn_background_with_context}; +} + +pub(crate) mod options { + pub(crate) use crate::storage::options::{ + copy_dst_opts, copy_src_opts, del_opts, extract_metadata, extract_metadata_from_mime, + extract_metadata_from_mime_with_object_name, filter_object_metadata, get_complete_multipart_upload_opts, + get_content_sha256_with_query, get_opts, normalize_content_encoding_for_storage, parse_copy_source_range, put_opts, + validate_archive_content_encoding, + }; +} + +pub(crate) mod request_context { + pub(crate) use crate::storage::request_context::{RequestContext, spawn_traced}; +} + +pub(crate) mod sse { + pub(crate) use crate::storage::sse::{ + EncryptionKeyKind, SSEType, build_ssec_read_headers, encryption_material_to_metadata, extract_ssec_params_from_headers, + extract_ssekms_context_from_headers, map_get_object_reader_error, mark_encrypted_multipart_metadata, + }; + pub(crate) use crate::storage::{ + DecryptionRequest, EncryptionRequest, PrepareEncryptionRequest, apply_bucket_default_lock_retention, + extract_server_side_encryption_from_headers, get_buffer_size_opt_in, sse_decryption, sse_encryption, + sse_prepare_encryption, + }; +} + +pub(crate) mod timeout_wrapper { + pub(crate) use crate::storage::timeout_wrapper::{GetObjectTimeoutPolicy, RequestTimeoutWrapper}; +} + +pub(crate) use crate::storage::{ + RFC1123, StorageDeletedObject, StorageObjectInfo, StorageObjectOptions, StorageObjectToDelete, StoragePutObjReader, + check_preconditions, get_validated_store, has_replication_rules, parse_object_lock_legal_hold, parse_object_lock_retention, + parse_part_number_i32_to_usize, process_lambda_configurations, process_queue_configurations, process_topic_configurations, + remove_object_lock_metadata_for_copy, strip_managed_encryption_metadata, validate_bucket_object_lock_enabled, + validate_list_object_unordered_with_delimiter, validate_object_key, validate_sse_headers_for_read, + validate_sse_headers_for_write, validate_ssec_for_read, wrap_response_with_cors, +}; diff --git a/rustfs/src/storage/mod.rs b/rustfs/src/storage/mod.rs index be834afcc..662eb24d5 100644 --- a/rustfs/src/storage/mod.rs +++ b/rustfs/src/storage/mod.rs @@ -50,9 +50,9 @@ mod sse_test; pub(crate) use ecfs_extend::*; pub(crate) use sse::{ - DecryptionRequest, EncryptionRequest, PrepareEncryptionRequest, extract_server_side_encryption_from_headers, - extract_ssec_params_from_headers, sse_decryption, sse_encryption, sse_prepare_encryption, strip_managed_encryption_metadata, - validate_sse_headers_for_read, validate_sse_headers_for_write, validate_ssec_for_read, + DecryptionRequest, EncryptionRequest, PrepareEncryptionRequest, extract_server_side_encryption_from_headers, sse_decryption, + sse_encryption, sse_prepare_encryption, strip_managed_encryption_metadata, validate_sse_headers_for_read, + validate_sse_headers_for_write, validate_ssec_for_read, }; use std::sync::Arc; diff --git a/scripts/check_architecture_migration_rules.sh b/scripts/check_architecture_migration_rules.sh index 1d214d60d..84590bde5 100755 --- a/scripts/check_architecture_migration_rules.sh +++ b/scripts/check_architecture_migration_rules.sh @@ -162,6 +162,7 @@ RUSTFS_APP_USECASE_RUNTIME_SOURCE_BYPASS_HITS_FILE="${TMP_DIR}/rustfs_app_usecas RUSTFS_APP_USECASE_STORAGE_WILDCARD_HITS_FILE="${TMP_DIR}/rustfs_app_usecase_storage_wildcard_hits.txt" RUSTFS_APP_WILDCARD_IMPORT_HITS_FILE="${TMP_DIR}/rustfs_app_wildcard_import_hits.txt" RUSTFS_APP_USECASE_S3_API_BYPASS_HITS_FILE="${TMP_DIR}/rustfs_app_usecase_s3_api_bypass_hits.txt" +RUSTFS_APP_USECASE_STORAGE_API_BYPASS_HITS_FILE="${TMP_DIR}/rustfs_app_usecase_storage_api_bypass_hits.txt" RUSTFS_STORAGE_DIRECT_APP_CONTEXT_BYPASS_HITS_FILE="${TMP_DIR}/rustfs_storage_direct_app_context_bypass_hits.txt" awk ' @@ -1438,6 +1439,17 @@ if [[ -s "$RUSTFS_APP_USECASE_S3_API_BYPASS_HITS_FILE" ]]; then report_failure "RustFS app usecases must consume S3 API helpers through rustfs/src/app/s3_api.rs: $(paste -sd '; ' "$RUSTFS_APP_USECASE_S3_API_BYPASS_HITS_FILE")" fi +( + cd "$ROOT_DIR" + rg -n --with-filename \ + '(use crate::storage::(access|helper|options|request_context|sse|timeout_wrapper|head_prefix|concurrency|ecfs)|crate::storage::sse::EncryptionKeyKind|use crate::storage::\{|use crate::storage::[A-Z])' \ + rustfs/src/app/select_object.rs rustfs/src/app/*_usecase.rs || true +) >"$RUSTFS_APP_USECASE_STORAGE_API_BYPASS_HITS_FILE" + +if [[ -s "$RUSTFS_APP_USECASE_STORAGE_API_BYPASS_HITS_FILE" ]]; then + report_failure "RustFS app usecases must consume storage helper APIs through rustfs/src/app/storage_api.rs: $(paste -sd '; ' "$RUSTFS_APP_USECASE_STORAGE_API_BYPASS_HITS_FILE")" +fi + ( cd "$ROOT_DIR" rg -n --with-filename 'crate::app::context::|use crate::app::context|app::context::' rustfs/src/storage --glob '*.rs' | diff --git a/scripts/layer-dependency-baseline.txt b/scripts/layer-dependency-baseline.txt index 51f1f22cb..78d0fa525 100644 --- a/scripts/layer-dependency-baseline.txt +++ b/scripts/layer-dependency-baseline.txt @@ -22,7 +22,7 @@ dep|rustfs/src/app/bucket_usecase.rs|app->interface|crate::admin::handlers::site dep|rustfs/src/app/bucket_usecase.rs|app->interface|crate::admin::handlers::site_replication::site_replication_delete_bucket_hook dep|rustfs/src/app/bucket_usecase.rs|app->interface|crate::admin::handlers::site_replication::site_replication_make_bucket_hook dep|rustfs/src/app/lifecycle_transition_api_test.rs|app->interface|crate::storage::ecfs::FS -dep|rustfs/src/app/object_usecase.rs|app->interface|crate::storage::ecfs::FS +dep|rustfs/src/app/storage_api.rs|app->interface|crate::storage::ecfs::FS dep|rustfs/src/app/s3_api.rs|app->interface|crate::storage::s3_api::bucket::ListObjectVersionsParams dep|rustfs/src/app/s3_api.rs|app->interface|crate::storage::s3_api::bucket::ListObjectsV2Params dep|rustfs/src/app/s3_api.rs|app->interface|crate::storage::s3_api::bucket::build_list_buckets_output