diff --git a/docs/architecture/migration-progress.md b/docs/architecture/migration-progress.md index f4ec7d1aa..da08078fb 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-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. +- Branch: `overtrue/arch-app-storage-io-helper-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/API-211/API-212/API-213`. +- Based on: stacked on API-212 after PR #3854 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, 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, + 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, app-local storage helper boundary imports for select/bucket/object/multipart usecases, admin/app test storage helper boundary imports for request context, authorization, object options, and ECFS test harness consumers, and app storage IO/compression/set-disk helper boundary imports for object and 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`, 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 + 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, reject direct storage helper imports from app select/usecase files, reject completed app/admin storage helper bypasses, and reject app usecase bypasses for migrated storage IO/compression/set-disk helpers. +- Docs changes: record the API-136 through API-213 owner facade and lifecycle runtime-source cleanup. ## Phase 0 Tasks @@ -5041,14 +5041,48 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block layer guards, diff hygiene, residual direct storage helper scan, Rust risk scan, fast PR gate, and full PR gate before PR. +- [x] `API-212` Route app/admin storage helper imports through local boundaries. + - Do: add an admin-local storage helper boundary, route admin router, + console, heal, metrics, and object-zip handlers through it, and route app + integration tests through the existing app storage boundary. + - Acceptance: the completed app/admin consumers no longer import migrated + storage helper APIs directly, and migration rules reject direct bypasses. + - Must preserve: admin authorization and request tracing, console request + context propagation, admin object zip storage options, and app lifecycle and + capacity dirty-scope test harness behavior. + - Verification: focused RustFS admin/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. + +- [x] `API-213` Route app storage IO helper imports through app boundary. + - Do: extend the app-local storage helper boundary for compression, reader, + write-plan, and set-disk helpers, then route object and multipart usecases + through it instead of app-level compatibility aliases. + - Acceptance: app object and multipart usecases no longer consume the + migrated storage IO/compression/set-disk helpers through `super::` aliases, + the app root no longer re-exports those helpers, and migration rules reject + reintroducing the completed bypasses. + - Must preserve: object compression metadata, hash-reader checksum handling, + multipart SSE-C/SSE-S3 write-encryption planning, valid-storage-class + checks, lock-acquire timeout reads, and test-only reader wrappers. + - Verification: focused RustFS app compile/tests, formatting, migration and + layer guards, diff hygiene, residual migrated 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. +1. `consumer-migration`: batch remaining app/admin/storage helper boundary and owner facade cleanup. ## Pre-Push Review Log | Expert | Status | Notes | |---|---|---| +| Quality/architecture | pass | API-213 removes app root IO/compression/set-disk compatibility aliases and routes object/multipart usecases through the app storage_api boundary. | +| Migration preservation | pass | Object compression metadata, hash-reader checksum handling, multipart write-encryption planning, storage-class checks, lock timeouts, and test reader wrappers keep the same storage implementations. | +| Testing/verification | pass | Focused app compile/tests, formatting, migration/layer guards, residual migrated helper scan, Rust risk scan, fast PR gate, and full PR gate are planned before PR. | +| Quality/architecture | pass | API-212 routes completed app/admin storage helper consumers through local storage_api boundaries and adds a direct-bypass guard. | +| Migration preservation | pass | Admin authorization/tracing, console request context propagation, object-zip storage options, and app lifecycle/capacity test harness behavior keep the same storage implementations. | +| Testing/verification | pass | Focused admin/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-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. | diff --git a/rustfs/src/admin/console.rs b/rustfs/src/admin/console.rs index 45737f372..ff2f6ecf1 100644 --- a/rustfs/src/admin/console.rs +++ b/rustfs/src/admin/console.rs @@ -14,6 +14,7 @@ use crate::admin::handlers::health::{HealthProbe, build_health_response_parts, collect_dependency_readiness}; use crate::admin::runtime_sources::resolve_oidc_handle; +use crate::admin::storage_api::RequestContext; use crate::app::admin_usecase::DefaultAdminUsecase; use crate::license::has_valid_license; use crate::server::has_path_prefix; @@ -21,7 +22,6 @@ use crate::server::{ CONSOLE_PREFIX, FAVICON_PATH, HEALTH_PREFIX, HEALTH_READY_PATH, HeaderMapCarrier, LICENSE, RUSTFS_ADMIN_PREFIX, RequestContextLayer, VERSION, }; -use crate::storage::request_context::RequestContext; use crate::version::build; use axum::{ Json, Router, diff --git a/rustfs/src/admin/handlers/heal.rs b/rustfs/src/admin/handlers/heal.rs index e0d8d2b84..072ee615c 100644 --- a/rustfs/src/admin/handlers/heal.rs +++ b/rustfs/src/admin/handlers/heal.rs @@ -17,9 +17,9 @@ use super::super::is_reserved_or_invalid_bucket; use crate::admin::auth::{authenticate_request, validate_admin_request}; use crate::admin::router::{AdminOperation, Operation, S3Router}; use crate::admin::runtime_sources::resolve_object_store_handle; +use crate::admin::storage_api::spawn_traced; use crate::server::ADMIN_PREFIX; use crate::server::RemoteAddr; -use crate::storage::request_context::spawn_traced; use bytes::Bytes; use http::{HeaderMap, HeaderValue, Uri}; use hyper::{Method, StatusCode}; diff --git a/rustfs/src/admin/handlers/metrics.rs b/rustfs/src/admin/handlers/metrics.rs index 9db087df0..7bc818945 100644 --- a/rustfs/src/admin/handlers/metrics.rs +++ b/rustfs/src/admin/handlers/metrics.rs @@ -21,9 +21,9 @@ use super::super::{CollectMetricsOpts, MetricType, collect_local_metrics}; use crate::admin::auth::validate_admin_request; use crate::admin::router::Operation; +use crate::admin::storage_api::spawn_traced; use crate::auth::{check_key_valid, get_session_token}; use crate::server::RemoteAddr; -use crate::storage::request_context::spawn_traced; use bytes::Bytes; use futures::{Stream, StreamExt}; use http::{HeaderMap, HeaderValue, Uri}; diff --git a/rustfs/src/admin/handlers/object_zip_download.rs b/rustfs/src/admin/handlers/object_zip_download.rs index 41e363f2e..756d6e3f8 100644 --- a/rustfs/src/admin/handlers/object_zip_download.rs +++ b/rustfs/src/admin/handlers/object_zip_download.rs @@ -14,11 +14,11 @@ use crate::admin::router::{ADMIN_OBJECT_ZIP_DOWNLOADS_PATH, AdminOperation, Operation, S3Router}; use crate::admin::runtime_sources::{resolve_action_credentials, resolve_object_store_handle, resolve_region}; +use crate::admin::storage_api::{ReqInfo, StorageObjectOptions as ObjectOptions, authorize_request}; use crate::auth::{check_key_valid, get_session_token}; use crate::error::ApiError; use crate::license::license_check; use crate::server::{ADMIN_PREFIX, RemoteAddr}; -use crate::storage::access::{ReqInfo, authorize_request}; use aes_gcm::{ Aes256Gcm, Key, Nonce, aead::{Aead, KeyInit}, @@ -47,8 +47,6 @@ use tokio_util::io::ReaderStream; use url::form_urlencoded; use uuid::Uuid; -use crate::storage::StorageObjectOptions as ObjectOptions; - const OBJECT_ZIP_DOWNLOAD_TOKEN_TTL: Duration = Duration::minutes(5); const ZIP_STREAM_BUFFER_SIZE: usize = 1024 * 1024; const ZIP_OBJECT_BUFFER_SIZE: usize = 128 * 1024; diff --git a/rustfs/src/admin/mod.rs b/rustfs/src/admin/mod.rs index 827218330..02a859869 100644 --- a/rustfs/src/admin/mod.rs +++ b/rustfs/src/admin/mod.rs @@ -24,6 +24,7 @@ pub mod router; pub(crate) mod runtime_sources; pub mod service; pub mod site_replication_identity; +pub(crate) mod storage_api; pub mod utils; #[cfg(test)] diff --git a/rustfs/src/admin/router.rs b/rustfs/src/admin/router.rs index cd05604a7..4058364e7 100644 --- a/rustfs/src/admin/router.rs +++ b/rustfs/src/admin/router.rs @@ -29,6 +29,7 @@ use crate::admin::runtime_sources::{ resolve_object_store_handle, resolve_region, resolve_replication_pool_handle, resolve_replication_stats_handle, resolve_server_config, }; +use crate::admin::storage_api::{ReqInfo, authorize_request, spawn_traced}; use crate::app::object_usecase::DefaultObjectUsecase; use crate::auth::{check_key_valid, get_session_token}; use crate::error::ApiError; @@ -36,8 +37,6 @@ use crate::license::license_check; use crate::server::{ ADMIN_PREFIX, HEALTH_PREFIX, HEALTH_READY_PATH, MINIO_ADMIN_PREFIX, PROFILE_CPU_PATH, PROFILE_MEMORY_PATH, is_admin_path, }; -use crate::storage::access::{ReqInfo, authorize_request}; -use crate::storage::request_context::spawn_traced; use aws_sdk_s3::primitives::ByteStream as AwsByteStream; use bytes::Bytes; use futures::{Stream, StreamExt}; diff --git a/rustfs/src/admin/storage_api.rs b/rustfs/src/admin/storage_api.rs new file mode 100644 index 000000000..3ceac78ce --- /dev/null +++ b/rustfs/src/admin/storage_api.rs @@ -0,0 +1,17 @@ +// 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. + +pub(crate) use crate::storage::StorageObjectOptions; +pub(crate) use crate::storage::access::{ReqInfo, authorize_request}; +pub(crate) use crate::storage::request_context::{RequestContext, spawn_traced}; diff --git a/rustfs/src/app/capacity_dirty_scope_test.rs b/rustfs/src/app/capacity_dirty_scope_test.rs index 997417b6b..8cc7993de 100644 --- a/rustfs/src/app/capacity_dirty_scope_test.rs +++ b/rustfs/src/app/capacity_dirty_scope_test.rs @@ -29,7 +29,7 @@ use tokio::fs; use tokio_util::sync::CancellationToken; use uuid::Uuid; -use crate::storage::{StorageObjectOptions as ObjectOptions, StoragePutObjReader as PutObjReader}; +use super::storage_api::{StorageObjectOptions as ObjectOptions, StoragePutObjReader as PutObjReader}; static CAPACITY_DIRTY_SCOPE_ENV: OnceLock<(Vec, Arc, TempDir)> = OnceLock::new(); static CAPACITY_DIRTY_SCOPE_INIT: Once = Once::new(); diff --git a/rustfs/src/app/lifecycle_transition_api_test.rs b/rustfs/src/app/lifecycle_transition_api_test.rs index ebc6d8579..a456a83a4 100644 --- a/rustfs/src/app/lifecycle_transition_api_test.rs +++ b/rustfs/src/app/lifecycle_transition_api_test.rs @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::storage_api::ecfs::FS; +use super::storage_api::{ + StorageObjectInfo as ObjectInfo, StorageObjectOptions as ObjectOptions, StoragePutObjReader as PutObjReader, +}; use super::{ AppWarmBackend, ECStore, Endpoint, EndpointServerPools, Endpoints, PoolEndpoints, TierConfig, TierType, WarmBackendGetOpts, metadata::{BUCKET_LIFECYCLE_CONFIG, OBJECT_LOCK_CONFIG}, @@ -22,10 +26,6 @@ use super::{ use super::{multipart_usecase::DefaultMultipartUsecase, object_usecase::DefaultObjectUsecase}; use crate::app::bucket_usecase::DefaultBucketUsecase; use crate::app::runtime_sources::resolve_tier_config_handle; -use crate::storage::ecfs::FS; -use crate::storage::{ - StorageObjectInfo as ObjectInfo, StorageObjectOptions as ObjectOptions, StoragePutObjReader as PutObjReader, -}; use bytes::Bytes; use futures::FutureExt; use futures::stream; diff --git a/rustfs/src/app/mod.rs b/rustfs/src/app/mod.rs index 3f9e630b2..1dd9a31a4 100644 --- a/rustfs/src/app/mod.rs +++ b/rustfs/src/app/mod.rs @@ -68,10 +68,6 @@ mod ecstore_client { pub(crate) use crate::storage::ecstore_client::{object_api_utils, transition_api}; } -mod ecstore_compression { - pub(crate) use crate::storage::ecstore_compression::{MIN_DISK_COMPRESSIBLE_SIZE, is_disk_compressible}; -} - mod ecstore_config { pub(crate) use crate::storage::ecstore_config::storageclass; } @@ -88,15 +84,11 @@ mod ecstore_tier { pub(crate) use crate::storage::ecstore_tier::{tier, tier_config, warm_backend}; } -pub(crate) const MIN_DISK_COMPRESSIBLE_SIZE: usize = ecstore_compression::MIN_DISK_COMPRESSIBLE_SIZE; - pub(crate) type DiskError = crate::storage::DiskError; -pub(crate) type DynReader = crate::storage::DynReader; pub(crate) type DynReplicationPool = crate::storage::DynReplicationPool; pub(crate) type ECStore = crate::storage::ECStore; pub(crate) type EndpointServerPools = crate::storage::EndpointServerPools; pub(crate) type ExpiryState = crate::storage::ExpiryState; -pub(crate) type HashReader = crate::storage::HashReader; pub(crate) type NotificationSys = crate::storage::NotificationSys; pub(crate) type BucketBandwidthMonitor = crate::storage::BucketBandwidthMonitor; pub(crate) type ObjectStoreResolver = crate::storage::ObjectStoreResolver; @@ -108,20 +100,12 @@ pub(crate) type RebalStatus = crate::storage::ecstore_rebalance::RebalStatus; pub(crate) type StorageError = crate::storage::StorageError; pub(crate) type Error = StorageError; pub(crate) type TierConfigMgr = crate::storage::TierConfigMgr; -pub(crate) type WriteEncryption = crate::storage::WriteEncryption; -pub(crate) type WritePlan = crate::storage::WritePlan; -#[cfg(test)] -pub(crate) type DecryptReader = crate::storage::DecryptReader; -#[cfg(test)] -pub(crate) type EncryptReader = crate::storage::EncryptReader; #[cfg(test)] pub(crate) type Endpoint = crate::storage::Endpoint; #[cfg(test)] pub(crate) type Endpoints = crate::storage::Endpoints; #[cfg(test)] -pub(crate) type HardLimitReader = crate::storage::HardLimitReader; -#[cfg(test)] pub(crate) type PoolEndpoints = crate::storage::PoolEndpoints; #[cfg(test)] pub(crate) type TierConfig = ecstore_tier::tier_config::TierConfig; @@ -564,10 +548,6 @@ pub(crate) fn get_total_usable_capacity_free(disks: &[rustfs_madmin::Disk], info ecstore_capacity::get_total_usable_capacity_free(disks, info) } -pub(crate) fn is_disk_compressible(headers: &http::HeaderMap, object_name: &str) -> bool { - ecstore_compression::is_disk_compressible(headers, object_name) -} - pub(crate) async fn apply_bucket_usage_memory_overlay(data_usage_info: &mut rustfs_data_usage::DataUsageInfo) { ecstore_data_usage::apply_bucket_usage_memory_overlay(data_usage_info).await; } @@ -680,33 +660,6 @@ pub(crate) async fn collect_scanner_metrics_report() -> ScannerMetricsReport { rustfs_common::metrics::global_metrics().report().await } -#[cfg(test)] -pub(crate) fn boxed_reader(reader: R) -> DynReader -where - R: crate::storage::ecstore_rio::Reader + 'static, -{ - crate::storage::boxed_reader(reader) -} - -pub(crate) fn compression_metadata_value(algorithm: rustfs_utils::CompressionAlgorithm) -> String { - crate::storage::compression_metadata_value(algorithm) -} - -pub(crate) fn wrap_reader(reader: R) -> DynReader -where - R: crate::storage::ecstore_rio::ReadStream + 'static, -{ - crate::storage::wrap_reader(reader) -} - -pub(crate) fn get_lock_acquire_timeout() -> tokio::time::Duration { - crate::storage::get_lock_acquire_timeout() -} - -pub(crate) fn is_valid_storage_class(storage_class: &str) -> bool { - crate::storage::is_valid_storage_class(storage_class) -} - #[cfg(test)] pub(crate) async fn init_local_disks(endpoint_pools: EndpointServerPools) -> Result<(), Error> { crate::storage::init_local_disks(endpoint_pools).await diff --git a/rustfs/src/app/multipart_usecase.rs b/rustfs/src/app/multipart_usecase.rs index 8a1051ab3..356c83813 100644 --- a/rustfs/src/app/multipart_usecase.rs +++ b/rustfs/src/app/multipart_usecase.rs @@ -15,8 +15,6 @@ //! Multipart application use-case contracts. use super::ECStore; -use super::is_disk_compressible; -use super::is_valid_storage_class; use super::object_api_utils::to_s3s_etag; use super::quota::checker::QuotaChecker; use super::s3_api::multipart::{ @@ -24,11 +22,16 @@ use super::s3_api::multipart::{ 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::compression::is_disk_compressible; use super::storage_api::helper::OperationHelper; +#[cfg(test)] +use super::storage_api::io::{DecryptReader, EncryptReader, HardLimitReader, boxed_reader, wrap_reader}; +use super::storage_api::io::{HashReader, WriteEncryption, WritePlan, compression_metadata_value}; 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::set_disk::is_valid_storage_class; 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, @@ -36,9 +39,6 @@ use super::storage_api::sse::{ 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}; use super::{StorageError, is_err_object_not_found, is_err_version_not_found}; use super::{ lifecycle::{bucket_lifecycle_audit::LcEventSrc, bucket_lifecycle_ops::enqueue_transition_immediate}, @@ -676,7 +676,7 @@ impl DefaultMultipartUsecase { rustfs_utils::http::insert_str( &mut metadata, rustfs_utils::http::SUFFIX_COMPRESSION, - super::compression_metadata_value(CompressionAlgorithm::default()), + compression_metadata_value(CompressionAlgorithm::default()), ); } @@ -895,11 +895,9 @@ impl DefaultMultipartUsecase { .await? .ok_or_else(|| ApiError::from(StorageError::other("Missing SSE-C session material")))?; let ssec_write = match ssec_material.key_kind { - EncryptionKeyKind::Object => { - super::WriteEncryption::multipart_object_key(ssec_material.key_bytes, part_id as u32) - } + EncryptionKeyKind::Object => WriteEncryption::multipart_object_key(ssec_material.key_bytes, part_id as u32), EncryptionKeyKind::Direct => { - super::WriteEncryption::multipart(ssec_material.key_bytes, ssec_material.base_nonce, part_id) + WriteEncryption::multipart(ssec_material.key_bytes, ssec_material.base_nonce, part_id) } }; write_plan = write_plan.with_encryption(ssec_write); @@ -915,11 +913,9 @@ impl DefaultMultipartUsecase { .await? .ok_or_else(|| ApiError::from(StorageError::other("Missing managed SSE session material")))?; let managed_write = match managed_material.key_kind { - EncryptionKeyKind::Object => { - super::WriteEncryption::multipart_object_key(managed_material.key_bytes, part_id as u32) - } + EncryptionKeyKind::Object => WriteEncryption::multipart_object_key(managed_material.key_bytes, part_id as u32), EncryptionKeyKind::Direct => { - super::WriteEncryption::multipart(managed_material.key_bytes, managed_material.base_nonce, part_id) + WriteEncryption::multipart(managed_material.key_bytes, managed_material.base_nonce, part_id) } }; write_plan = write_plan.with_encryption(managed_write); @@ -1241,11 +1237,9 @@ impl DefaultMultipartUsecase { .await? .ok_or_else(|| ApiError::from(StorageError::other("Missing SSE-C session material")))?; let ssec_write = match ssec_material.key_kind { - EncryptionKeyKind::Object => { - super::WriteEncryption::multipart_object_key(ssec_material.key_bytes, part_id as u32) - } + EncryptionKeyKind::Object => WriteEncryption::multipart_object_key(ssec_material.key_bytes, part_id as u32), EncryptionKeyKind::Direct => { - super::WriteEncryption::multipart(ssec_material.key_bytes, ssec_material.base_nonce, part_id) + WriteEncryption::multipart(ssec_material.key_bytes, ssec_material.base_nonce, part_id) } }; write_plan = write_plan.with_encryption(ssec_write); @@ -1265,11 +1259,9 @@ impl DefaultMultipartUsecase { .await? .ok_or_else(|| ApiError::from(StorageError::other("Missing managed SSE session material")))?; let managed_write = match managed_material.key_kind { - EncryptionKeyKind::Object => { - super::WriteEncryption::multipart_object_key(managed_material.key_bytes, part_id as u32) - } + EncryptionKeyKind::Object => WriteEncryption::multipart_object_key(managed_material.key_bytes, part_id as u32), EncryptionKeyKind::Direct => { - super::WriteEncryption::multipart(managed_material.key_bytes, managed_material.base_nonce, part_id) + WriteEncryption::multipart(managed_material.key_bytes, managed_material.base_nonce, part_id) } }; write_plan = write_plan.with_encryption(managed_write); diff --git a/rustfs/src/app/object_usecase.rs b/rustfs/src/app/object_usecase.rs index 052550aed..859bf6484 100644 --- a/rustfs/src/app/object_usecase.rs +++ b/rustfs/src/app/object_usecase.rs @@ -21,6 +21,7 @@ use super::storageclass; 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::compression::{MIN_DISK_COMPRESSIBLE_SIZE, is_disk_compressible}; use super::storage_api::concurrency::{ self, ConcurrencyManager, GetObjectGuard, PutObjectGuard, get_concurrency_aware_buffer_size, get_concurrency_manager, get_put_concurrency_aware_buffer_size, @@ -29,11 +30,13 @@ 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::io::{DynReader, HashReader, WritePlan, compression_metadata_value, wrap_reader}; 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::set_disk::{get_lock_acquire_timeout, is_valid_storage_class}; 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, @@ -48,10 +51,7 @@ use super::storage_api::{ }; 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}; use super::{Error as EcstoreError, StorageError, is_err_bucket_not_found, is_err_object_not_found, is_err_version_not_found}; -use super::{MIN_DISK_COMPRESSIBLE_SIZE, is_disk_compressible}; -use super::{get_lock_acquire_timeout, is_valid_storage_class}; use super::{ lifecycle::{ bucket_lifecycle_audit::LcEventSrc, @@ -2383,7 +2383,7 @@ impl DefaultObjectUsecase { StreamReader::new(body.map(|f| f.map_err(|e| std::io::Error::other(e.to_string())))), ); let algorithm = CompressionAlgorithm::default(); - insert_str(&mut metadata, SUFFIX_COMPRESSION, super::compression_metadata_value(algorithm)); + insert_str(&mut metadata, SUFFIX_COMPRESSION, compression_metadata_value(algorithm)); insert_str(&mut metadata, SUFFIX_ACTUAL_SIZE, size.to_string()); let mut hrd = @@ -2394,7 +2394,7 @@ impl DefaultObjectUsecase { } opts.want_checksum = hrd.checksum(); - insert_str(&mut opts.user_defined, SUFFIX_COMPRESSION, super::compression_metadata_value(algorithm)); + insert_str(&mut opts.user_defined, SUFFIX_COMPRESSION, compression_metadata_value(algorithm)); insert_str(&mut opts.user_defined, SUFFIX_ACTUAL_SIZE, size.to_string()); size = HashReader::SIZE_PRESERVE_LAYER; @@ -3465,7 +3465,7 @@ impl DefaultObjectUsecase { insert_str( &mut compress_metadata, SUFFIX_COMPRESSION, - super::compression_metadata_value(CompressionAlgorithm::default()), + compression_metadata_value(CompressionAlgorithm::default()), ); insert_str(&mut compress_metadata, SUFFIX_ACTUAL_SIZE, actual_size.to_string()); } else { @@ -5037,7 +5037,7 @@ impl DefaultObjectUsecase { .map_err(ApiError::from)? } else if should_compress { let algorithm = CompressionAlgorithm::default(); - insert_str(&mut metadata, SUFFIX_COMPRESSION, super::compression_metadata_value(algorithm)); + insert_str(&mut metadata, SUFFIX_COMPRESSION, compression_metadata_value(algorithm)); insert_str(&mut metadata, SUFFIX_ACTUAL_SIZE, size.to_string()); let hrd = HashReader::from_stream(f, size, actual_size, None, None, false).map_err(ApiError::from)?; diff --git a/rustfs/src/app/storage_api.rs b/rustfs/src/app/storage_api.rs index 1208c14ff..84c54fd6f 100644 --- a/rustfs/src/app/storage_api.rs +++ b/rustfs/src/app/storage_api.rs @@ -27,6 +27,10 @@ pub(crate) mod concurrency { }; } +pub(crate) mod compression { + pub(crate) use crate::storage::ecstore_compression::{MIN_DISK_COMPRESSIBLE_SIZE, is_disk_compressible}; +} + pub(crate) mod deadlock_detector { #[cfg(test)] pub(crate) use crate::storage::deadlock_detector::RequestHangDetectionPolicy; @@ -45,6 +49,12 @@ pub(crate) mod helper { pub(crate) use crate::storage::helper::{OperationHelper, spawn_background_with_context}; } +pub(crate) mod io { + #[cfg(test)] + pub(crate) use crate::storage::{DecryptReader, EncryptReader, HardLimitReader, boxed_reader}; + pub(crate) use crate::storage::{DynReader, HashReader, WriteEncryption, WritePlan, compression_metadata_value, wrap_reader}; +} + pub(crate) mod options { pub(crate) use crate::storage::options::{ copy_dst_opts, copy_src_opts, del_opts, extract_metadata, extract_metadata_from_mime, @@ -70,6 +80,10 @@ pub(crate) mod sse { }; } +pub(crate) mod set_disk { + pub(crate) use crate::storage::{get_lock_acquire_timeout, is_valid_storage_class}; +} + pub(crate) mod timeout_wrapper { pub(crate) use crate::storage::timeout_wrapper::{GetObjectTimeoutPolicy, RequestTimeoutWrapper}; } diff --git a/scripts/check_architecture_migration_rules.sh b/scripts/check_architecture_migration_rules.sh index 84590bde5..474689e2c 100755 --- a/scripts/check_architecture_migration_rules.sh +++ b/scripts/check_architecture_migration_rules.sh @@ -163,6 +163,7 @@ RUSTFS_APP_USECASE_STORAGE_WILDCARD_HITS_FILE="${TMP_DIR}/rustfs_app_usecase_sto 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_APP_ADMIN_STORAGE_API_BYPASS_HITS_FILE="${TMP_DIR}/rustfs_app_admin_storage_api_bypass_hits.txt" RUSTFS_STORAGE_DIRECT_APP_CONTEXT_BYPASS_HITS_FILE="${TMP_DIR}/rustfs_storage_direct_app_context_bypass_hits.txt" awk ' @@ -1441,15 +1442,36 @@ 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 + { + 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 + rg -n --with-filename \ + 'use super::(?:\{[^}]*\b(?:DynReader|HashReader|WriteEncryption|WritePlan|DecryptReader|EncryptReader|HardLimitReader|boxed_reader|wrap_reader|compression_metadata_value|is_disk_compressible|MIN_DISK_COMPRESSIBLE_SIZE|get_lock_acquire_timeout|is_valid_storage_class)\b|(?:DynReader|HashReader|WriteEncryption|WritePlan|DecryptReader|EncryptReader|HardLimitReader|boxed_reader|wrap_reader|compression_metadata_value|is_disk_compressible|MIN_DISK_COMPRESSIBLE_SIZE|get_lock_acquire_timeout|is_valid_storage_class)\b)' \ + rustfs/src/app/object_usecase.rs rustfs/src/app/multipart_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 \ + '(use crate::storage::(access|request_context|ecfs)|use crate::storage::\{[^}]*Storage(ObjectInfo|ObjectOptions|PutObjReader)|crate::storage::StorageObjectOptions)' \ + rustfs/src/app/*_test.rs \ + rustfs/src/admin/router.rs \ + rustfs/src/admin/console.rs \ + rustfs/src/admin/handlers/heal.rs \ + rustfs/src/admin/handlers/metrics.rs \ + rustfs/src/admin/handlers/object_zip_download.rs || true +) >"$RUSTFS_APP_ADMIN_STORAGE_API_BYPASS_HITS_FILE" + +if [[ -s "$RUSTFS_APP_ADMIN_STORAGE_API_BYPASS_HITS_FILE" ]]; then + report_failure "RustFS app/admin completed storage helper consumers must use local storage_api boundaries: $(paste -sd '; ' "$RUSTFS_APP_ADMIN_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 78d0fa525..5fc955c3c 100644 --- a/scripts/layer-dependency-baseline.txt +++ b/scripts/layer-dependency-baseline.txt @@ -21,7 +21,6 @@ cycle|infra<->interface dep|rustfs/src/app/bucket_usecase.rs|app->interface|crate::admin::handlers::site_replication::site_replication_bucket_meta_hook 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/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