mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-24 21:26:28 +00:00
fix(ilm): implement expire_restored delete semantics for restore expiry (#4950)
DeleteRestoredAction is supposed to demote a restored object back to its pure transitioned state: remove only the local restored copy, strip the x-amz-restore headers, and leave the version (and the remote tier data) untouched. expire_transitioned_object set opts.transition.expire_restored accordingly, but no delete path ever read the flag, so delete_object ran an ordinary delete: on unversioned buckets the whole object vanished and the free-version record scheduled remote tier cleanup (tier data loss); on versioned buckets the latest version got a spurious delete marker that replication propagated. Route expire_restored explicitly in SetDisks::delete_object before delete-marker resolution and replication dispatch: target the found version with FileInfo.expire_restored=true and return early. The FileMeta::delete_version layer already implements the semantics (strip restore headers, keep the version, hand back the local data dir); this wires it up. Also fix the action matching in expire_transitioned_object (extracted into transitioned_object_delete_opts): DeleteRestoredVersionAction previously fell through to the full transitioned-object delete, which removed the remote tier data of a noncurrent restored version. It now routes through the same restored-copy cleanup with the exact version id, matching MinIO's Action.DeleteVersioned()/DeleteRestored() dispatch. Re-enable test_restore_chain_local_read_expiry_keeps_remote_and_allows_ re_restore in the ILM Integration (serial) lane; add unit tests pinning the event->options routing and the filemeta expire_restored branch. Closes rustfs/backlog#1302
This commit is contained in:
@@ -1140,6 +1140,53 @@ mod test {
|
||||
assert_eq!(fm, newfm)
|
||||
}
|
||||
|
||||
/// Regression for rustfs/backlog#1302: `delete_version` with
|
||||
/// `expire_restored` must only strip the `x-amz-restore` headers and hand
|
||||
/// back the local data dir for cleanup; the version itself must survive
|
||||
/// with its transition metadata (and thus the remote tier copy) intact.
|
||||
#[test]
|
||||
fn test_delete_version_expire_restored_keeps_transitioned_version() {
|
||||
use rustfs_utils::http::headers::{AMZ_RESTORE, AMZ_RESTORE_EXPIRY_DAYS, AMZ_RESTORE_REQUEST_DATE};
|
||||
|
||||
let mut fm = FileMeta::new();
|
||||
let vid = Uuid::new_v4();
|
||||
let data_dir = Uuid::new_v4();
|
||||
|
||||
let mut fi = FileInfo::new("restored.bin", 3, 2);
|
||||
fi.version_id = Some(vid);
|
||||
fi.data_dir = Some(data_dir);
|
||||
fi.mod_time = Some(OffsetDateTime::now_utc());
|
||||
fi.transition_status = TRANSITION_COMPLETE.to_string();
|
||||
fi.transitioned_objname = "remote/obj".to_string();
|
||||
fi.transition_tier = "COLDTIER".to_string();
|
||||
fi.metadata.insert(
|
||||
AMZ_RESTORE.to_string(),
|
||||
"ongoing-request=\"false\", expiry-date=\"Fri, 17 Jul 2026 00:00:00 GMT\"".to_string(),
|
||||
);
|
||||
fi.metadata.insert(AMZ_RESTORE_EXPIRY_DAYS.to_string(), "1".to_string());
|
||||
fi.metadata
|
||||
.insert(AMZ_RESTORE_REQUEST_DATE.to_string(), "Thu, 16 Jul 2026 00:00:00 GMT".to_string());
|
||||
fm.add_version(fi).unwrap();
|
||||
|
||||
let expire_fi = FileInfo {
|
||||
name: "restored.bin".to_string(),
|
||||
version_id: Some(vid),
|
||||
expire_restored: true,
|
||||
..Default::default()
|
||||
};
|
||||
let freed = fm.delete_version(&expire_fi).unwrap();
|
||||
assert_eq!(freed, Some(data_dir), "the restored copy's local data dir must be handed back");
|
||||
|
||||
assert_eq!(fm.versions.len(), 1, "the version must survive restored-copy expiry");
|
||||
let after = fm.into_fileinfo("vol", "restored.bin", "", false, false, true).unwrap();
|
||||
assert!(!after.metadata.contains_key(AMZ_RESTORE), "x-amz-restore must be stripped");
|
||||
assert!(!after.metadata.contains_key(AMZ_RESTORE_EXPIRY_DAYS));
|
||||
assert!(!after.metadata.contains_key(AMZ_RESTORE_REQUEST_DATE));
|
||||
assert_eq!(after.transition_status, TRANSITION_COMPLETE);
|
||||
assert_eq!(after.transitioned_objname, "remote/obj");
|
||||
assert_eq!(after.transition_tier, "COLDTIER");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_idx_out_of_bounds_returns_error_without_panic() {
|
||||
let mut fm = FileMeta::new();
|
||||
|
||||
Reference in New Issue
Block a user