mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-29 01:29:00 +00:00
refactor(lifecycle): add object lock boundary (#4084)
This commit is contained in:
@@ -10,8 +10,8 @@ and tier services.
|
||||
| Module | Current role | Split blocker |
|
||||
|---|---|---|
|
||||
| `core.rs` | Lifecycle rule model, action evaluation, object options, and transition/expiry decisions. | Uses ECStore object metadata types and compatibility DTO re-exports. |
|
||||
| `bucket_lifecycle_ops.rs` | Worker orchestration, expiry, transition, stale multipart cleanup, audit, replication delete scheduling, and queue state. | Depends on `ECStore`, `SetDisks`, runtime globals, bucket metadata/versioning/replication, disk internals, event notification, and tier services. |
|
||||
| `evaluator.rs` | Bucket lifecycle evaluation wrapper. | Reads object-lock and replication config from ECStore bucket modules. |
|
||||
| `bucket_lifecycle_ops.rs` | Worker orchestration, expiry, transition, stale multipart cleanup, audit, replication delete scheduling, and queue state. | Depends on `ECStore`, `SetDisks`, runtime globals, bucket metadata/versioning/replication, object-lock checks, disk internals, event notification, and tier services. |
|
||||
| `evaluator.rs` | Bucket lifecycle evaluation wrapper. | Uses lifecycle-local object-lock boundary and still reads replication config from ECStore bucket modules. |
|
||||
| `rule.rs` | Lifecycle rule filter helpers. | Uses lifecycle-local tagging boundary. |
|
||||
| `tier_delete_journal.rs` | Remote tier delete journal persistence and recovery. | Uses lifecycle-local config persistence boundary, object IO contracts, metadata bucket paths, and `ECStore`. |
|
||||
| `tier_free_version_recovery.rs` | Free-version recovery queue and object restoration path. | Depends on `ECStore`, object metadata, storage-api contracts, and lifecycle queue callbacks. |
|
||||
@@ -28,6 +28,7 @@ and tier services.
|
||||
| `LifecycleRuntime` | Expiry state, transition state, tier config, deployment ID, local node name, queue metrics, cancellation, and worker sizing. | Direct runtime source/global access and process environment reads inside worker code. |
|
||||
| `LifecycleConfigStore` | Persist, read, and remove lifecycle-owned journal/config objects. | Direct ECStore config persistence helper imports from worker paths. |
|
||||
| `LifecycleTagFilter` | Decode object tag strings for lifecycle rule matching. | Direct bucket tagging helper imports from lifecycle rule paths. |
|
||||
| `LifecycleObjectLockStore` | Object-lock retention and deletion checks used by lifecycle evaluation and worker deletion paths. | Direct object-lock module imports from lifecycle code. |
|
||||
| `LifecycleReplicationSink` | Lifecycle-originated delete and version-purge replication scheduling. | Direct imports from bucket replication modules. |
|
||||
| `LifecycleAuditSink` | Lifecycle audit and notification event emission. | Direct event notification service calls and audit-side effects from worker code. |
|
||||
|
||||
@@ -56,3 +57,7 @@ Current first boundary: `runtime_boundary.rs` centralizes lifecycle access to
|
||||
runtime state while preserving the existing ECStore-backed implementations.
|
||||
`config_boundary.rs` centralizes lifecycle-owned config object persistence for
|
||||
tier delete journal recovery while preserving the existing ECStore config store.
|
||||
`tagging_boundary.rs` centralizes lifecycle tag decoding while preserving the
|
||||
existing ECStore bucket tagging implementation.
|
||||
`object_lock_boundary.rs` centralizes lifecycle object-lock checks while
|
||||
preserving the existing ECStore object-lock implementation.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::runtime_boundary as runtime_sources;
|
||||
use super::{object_lock_boundary, runtime_boundary as runtime_sources};
|
||||
use crate::bucket::lifecycle::bucket_lifecycle_audit::{
|
||||
LcAuditEvent, LcEventSrc, emit_non_transitioned_expiration_event, emit_transition_complete_event,
|
||||
emit_transition_failed_event, emit_transitioned_expiration_event,
|
||||
@@ -25,7 +25,6 @@ use crate::bucket::lifecycle::tier_delete_journal::{process_tier_delete_journal_
|
||||
use crate::bucket::lifecycle::tier_free_version_recovery::{DEFAULT_FREE_VERSION_RECOVERY_LIMIT, recover_tier_free_versions};
|
||||
use crate::bucket::lifecycle::tier_last_day_stats::{DailyAllTierStats, LastDayTierStats};
|
||||
use crate::bucket::lifecycle::tier_sweeper::{Jentry, delete_object_from_remote_tier_idempotent};
|
||||
use crate::bucket::object_lock::objectlock_sys::check_object_lock_for_deletion;
|
||||
use crate::bucket::replication::{
|
||||
DeletedObjectReplicationInfo, ReplicationConfig, check_replicate_delete, schedule_replication_delete,
|
||||
};
|
||||
@@ -2522,7 +2521,11 @@ pub async fn eval_action_from_lifecycle(
|
||||
return lifecycle::Event::default();
|
||||
}
|
||||
// Lifecycle operations should never bypass governance retention
|
||||
if lock_enabled && check_object_lock_for_deletion(&oi.bucket, oi, false).await.is_some() {
|
||||
if lock_enabled
|
||||
&& object_lock_boundary::check_object_lock_for_deletion(&oi.bucket, oi, false)
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
//if serverDebugLog {
|
||||
if oi.version_id.is_some() {
|
||||
debug!(
|
||||
|
||||
@@ -18,8 +18,8 @@ use s3s::dto::{BucketLifecycleConfiguration, ObjectLockConfiguration, ObjectLock
|
||||
use time::OffsetDateTime;
|
||||
use tracing::info;
|
||||
|
||||
use super::object_lock_boundary;
|
||||
use crate::bucket::lifecycle::lifecycle::{Event, Lifecycle, ObjectOpts};
|
||||
use crate::bucket::object_lock::objectlock_sys::is_object_locked_by_metadata;
|
||||
use crate::bucket::replication::ReplicationConfig;
|
||||
use rustfs_common::metrics::IlmAction;
|
||||
|
||||
@@ -89,7 +89,7 @@ impl Evaluator {
|
||||
}
|
||||
|
||||
// Use the common function to check if the object is locked
|
||||
is_object_locked_by_metadata(&obj.user_defined, obj.delete_marker)
|
||||
object_lock_boundary::is_object_locked_by_metadata(&obj.user_defined, obj.delete_marker)
|
||||
}
|
||||
|
||||
/// eval will return a lifecycle event for each object in objs for a given time.
|
||||
|
||||
@@ -17,6 +17,7 @@ pub mod bucket_lifecycle_ops;
|
||||
mod config_boundary;
|
||||
pub mod core;
|
||||
pub mod evaluator;
|
||||
mod object_lock_boundary;
|
||||
pub use self::core as lifecycle;
|
||||
pub mod rule;
|
||||
mod runtime_boundary;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::bucket::object_lock::objectlock_sys::{self, ObjectLockBlockReason};
|
||||
use crate::object_api::ObjectInfo;
|
||||
|
||||
pub(crate) fn is_object_locked_by_metadata(user_defined: &HashMap<String, String>, is_delete_marker: bool) -> bool {
|
||||
objectlock_sys::is_object_locked_by_metadata(user_defined, is_delete_marker)
|
||||
}
|
||||
|
||||
pub(crate) async fn check_object_lock_for_deletion(
|
||||
bucket: &str,
|
||||
obj_info: &ObjectInfo,
|
||||
bypass_governance: bool,
|
||||
) -> Option<ObjectLockBlockReason> {
|
||||
objectlock_sys::check_object_lock_for_deletion(bucket, obj_info, bypass_governance).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn is_object_locked_by_metadata_preserves_object_lock_parser_behavior() {
|
||||
let mut user_defined = HashMap::new();
|
||||
user_defined.insert("x-amz-object-lock-legal-hold".to_string(), "ON".to_string());
|
||||
|
||||
assert!(is_object_locked_by_metadata(&user_defined, false));
|
||||
assert!(!is_object_locked_by_metadata(&user_defined, true));
|
||||
}
|
||||
}
|
||||
@@ -125,6 +125,7 @@ STORE_API_LIFECYCLE_HELPER_OLD_CONSUMER_HITS_FILE="${TMP_DIR}/store_api_lifecycl
|
||||
LIFECYCLE_AUDIT_SINK_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_audit_sink_bypass_hits.txt"
|
||||
LIFECYCLE_CONFIG_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_config_boundary_bypass_hits.txt"
|
||||
LIFECYCLE_TAGGING_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_tagging_boundary_bypass_hits.txt"
|
||||
LIFECYCLE_OBJECT_LOCK_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/lifecycle_object_lock_boundary_bypass_hits.txt"
|
||||
STORE_API_EXTERNAL_LIST_CONSUMER_HITS_FILE="${TMP_DIR}/store_api_external_list_consumer_hits.txt"
|
||||
STORE_API_EXTERNAL_OPERATION_CONSUMER_HITS_FILE="${TMP_DIR}/store_api_external_operation_consumer_hits.txt"
|
||||
STORE_API_OBJECT_OPERATION_LOCAL_METHOD_HITS_FILE="${TMP_DIR}/store_api_object_operation_local_method_hits.txt"
|
||||
@@ -828,6 +829,18 @@ if [[ -s "$LIFECYCLE_TAGGING_BOUNDARY_BYPASS_HITS_FILE" ]]; then
|
||||
report_failure "lifecycle tag decoding must stay behind lifecycle tagging_boundary: $(paste -sd '; ' "$LIFECYCLE_TAGGING_BOUNDARY_BYPASS_HITS_FILE")"
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$ROOT_DIR"
|
||||
rg -n --with-filename 'crate::bucket::object_lock::objectlock_sys::(check_object_lock_for_deletion|is_object_locked_by_metadata)|use crate::bucket::object_lock::objectlock_sys' \
|
||||
crates/ecstore/src/bucket/lifecycle \
|
||||
--glob '*.rs' |
|
||||
rg -v '^crates/ecstore/src/bucket/lifecycle/object_lock_boundary\.rs:' || true
|
||||
) >"$LIFECYCLE_OBJECT_LOCK_BOUNDARY_BYPASS_HITS_FILE"
|
||||
|
||||
if [[ -s "$LIFECYCLE_OBJECT_LOCK_BOUNDARY_BYPASS_HITS_FILE" ]]; then
|
||||
report_failure "lifecycle object-lock checks must stay behind lifecycle object_lock_boundary: $(paste -sd '; ' "$LIFECYCLE_OBJECT_LOCK_BOUNDARY_BYPASS_HITS_FILE")"
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$ROOT_DIR"
|
||||
rg -n --no-heading 'rustfs_ecstore::store_api(?:::\{[^}]*\b(?:ListObjectVersionsInfo|ListObjectsV2Info|ObjectInfoOrErr)\b|::(?:ListObjectVersionsInfo|ListObjectsV2Info|ObjectInfoOrErr)\b)' \
|
||||
|
||||
Reference in New Issue
Block a user