diff --git a/internal/bucket/lifecycle/lifecycle.go b/internal/bucket/lifecycle/lifecycle.go index f0873f801..8adc9ed14 100644 --- a/internal/bucket/lifecycle/lifecycle.go +++ b/internal/bucket/lifecycle/lifecycle.go @@ -287,12 +287,23 @@ func (lc Lifecycle) ComputeAction(obj ObjectOpts) Action { return action } for _, rule := range lc.FilterActionableRules(obj) { - if obj.ExpiredObjectDeleteMarker() && rule.Expiration.DeleteMarker.val { - // Indicates whether MinIO will remove a delete marker with no noncurrent versions. - // Only latest marker is removed. If set to true, the delete marker will be expired; - // if set to false the policy takes no action. This cannot be specified with Days or - // Date in a Lifecycle Expiration Policy. - return DeleteVersionAction + if obj.ExpiredObjectDeleteMarker() { + if rule.Expiration.DeleteMarker.val { + // Indicates whether MinIO will remove a delete marker with no noncurrent versions. + // Only latest marker is removed. If set to true, the delete marker will be expired; + // if set to false the policy takes no action. This cannot be specified with Days or + // Date in a Lifecycle Expiration Policy. + return DeleteVersionAction + } + + if !rule.Expiration.IsDaysNull() { + // Specifying the Days tag will automatically perform ExpiredObjectDeleteMarker cleanup + // once delete markers are old enough to satisfy the age criteria. + // https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-configuration-examples.html + if time.Now().After(ExpectedExpiryTime(obj.ModTime, int(rule.Expiration.Days))) { + return DeleteVersionAction + } + } } if !rule.NoncurrentVersionExpiration.IsDaysNull() { @@ -303,17 +314,6 @@ func (lc Lifecycle) ComputeAction(obj ObjectOpts) Action { return DeleteVersionAction } } - - if obj.VersionID != "" && obj.ExpiredObjectDeleteMarker() { - // From https: //docs.aws.amazon.com/AmazonS3/latest/dev/lifecycle-configuration-examples.html : - // The NoncurrentVersionExpiration action in the same Lifecycle configuration removes noncurrent objects X days - // after they become noncurrent. Thus, in this example, all object versions are permanently removed X days after - // object creation. You will have expired object delete markers, but Amazon S3 detects and removes the expired - // object delete markers for you. - if time.Now().After(ExpectedExpiryTime(obj.ModTime, int(rule.NoncurrentVersionExpiration.NoncurrentDays))) { - return DeleteVersionAction - } - } } if !rule.NoncurrentVersionTransition.IsDaysNull() { diff --git a/internal/bucket/lifecycle/lifecycle_test.go b/internal/bucket/lifecycle/lifecycle_test.go index 6df8afbcb..700ca4a41 100644 --- a/internal/bucket/lifecycle/lifecycle_test.go +++ b/internal/bucket/lifecycle/lifecycle_test.go @@ -210,11 +210,12 @@ func TestExpectedExpiryTime(t *testing.T) { func TestComputeActions(t *testing.T) { testCases := []struct { - inputConfig string - objectName string - objectTags string - objectModTime time.Time - expectedAction Action + inputConfig string + objectName string + objectTags string + objectModTime time.Time + isExpiredDelMarker bool + expectedAction Action }{ // Empty object name (unexpected case) should always return NoneAction { @@ -355,6 +356,30 @@ func TestComputeActions(t *testing.T) { objectModTime: time.Now().UTC().Add(-24 * time.Hour), // Created 1 day ago expectedAction: DeleteAction, }, + // Should delete expired delete marker right away + { + inputConfig: `trueEnabled`, + objectName: "foodir/fooobject", + objectModTime: time.Now().UTC().Add(-1 * time.Hour), // Created one hour ago + isExpiredDelMarker: true, + expectedAction: DeleteVersionAction, + }, + // Should not delete expired marker if its time has not come yet + { + inputConfig: `Enabled1`, + objectName: "foodir/fooobject", + objectModTime: time.Now().UTC().Add(-12 * time.Hour), // Created 12 hours ago + isExpiredDelMarker: true, + expectedAction: NoneAction, + }, + // Should delete expired marker since its time has come + { + inputConfig: `Enabled1`, + objectName: "foodir/fooobject", + objectModTime: time.Now().UTC().Add(-10 * 24 * time.Hour), // Created 10 days ago + isExpiredDelMarker: true, + expectedAction: DeleteVersionAction, + }, } for _, tc := range testCases { @@ -365,10 +390,12 @@ func TestComputeActions(t *testing.T) { t.Fatalf("Got unexpected error: %v", err) } if resultAction := lc.ComputeAction(ObjectOpts{ - Name: tc.objectName, - UserTags: tc.objectTags, - ModTime: tc.objectModTime, - IsLatest: true, + Name: tc.objectName, + UserTags: tc.objectTags, + ModTime: tc.objectModTime, + DeleteMarker: tc.isExpiredDelMarker, + NumVersions: 1, + IsLatest: true, }); resultAction != tc.expectedAction { t.Fatalf("Expected action: `%v`, got: `%v`", tc.expectedAction, resultAction) }