diff --git a/cmd/bucket-lifecycle.go b/cmd/bucket-lifecycle.go index fa92f3f6a..1bb5b8751 100644 --- a/cmd/bucket-lifecycle.go +++ b/cmd/bucket-lifecycle.go @@ -131,11 +131,11 @@ func (es *expiryState) enqueueByDays(oi ObjectInfo, event lifecycle.Event) { // enqueueByNewerNoncurrent enqueues object versions expired by // NewerNoncurrentVersions limit for expiry. -func (es *expiryState) enqueueByNewerNoncurrent(bucket string, versions []ObjectToDelete) { +func (es *expiryState) enqueueByNewerNoncurrent(bucket string, versions []ObjectToDelete, lcEvent lifecycle.Event) { select { case <-GlobalContext.Done(): es.close() - case es.byNewerNoncurrentCh <- newerNoncurrentTask{bucket: bucket, versions: versions}: + case es.byNewerNoncurrentCh <- newerNoncurrentTask{bucket: bucket, versions: versions, event: lcEvent}: default: } } @@ -183,7 +183,7 @@ func initBackgroundExpiry(ctx context.Context, objectAPI ObjectLayer) { nwk.Take() go func(t newerNoncurrentTask) { defer nwk.Give() - deleteObjectVersions(ctx, objectAPI, t.bucket, t.versions) + deleteObjectVersions(ctx, objectAPI, t.bucket, t.versions, t.event) }(t) } nwk.Wait() @@ -195,6 +195,7 @@ func initBackgroundExpiry(ctx context.Context, objectAPI ObjectLayer) { type newerNoncurrentTask struct { bucket string versions []ObjectToDelete + event lifecycle.Event } type transitionTask struct { @@ -868,17 +869,32 @@ func (oi ObjectInfo) ToLifecycleOpts() lifecycle.ObjectOpts { func auditLifecycleTags(event lifecycle.Event) map[string]interface{} { const ( - ilmAction = "ilm-action" - ilmDue = "ilm-due" - ilmRuleID = "ilm-rule-id" - ilmTier = "ilm-tier" + ilmAction = "ilm-action" + ilmDue = "ilm-due" + ilmRuleID = "ilm-rule-id" + ilmTier = "ilm-tier" + ilmNewerNoncurrentVersions = "ilm-newer-noncurrent-versions" + ilmNoncurrentDays = "ilm-noncurrent-days" ) tags := make(map[string]interface{}, 4) tags[ilmAction] = event.Action.String() - tags[ilmDue] = event.Due tags[ilmRuleID] = event.RuleID + + if !event.Due.IsZero() { + tags[ilmDue] = event.Due + } + + // rule with Transition/NoncurrentVersionTransition in effect if event.StorageClass != "" { tags[ilmTier] = event.StorageClass } + + // rule with NewernoncurrentVersions in effect + if event.NewerNoncurrentVersions > 0 { + tags[ilmNewerNoncurrentVersions] = event.NewerNoncurrentVersions + } + if event.NoncurrentDays > 0 { + tags[ilmNoncurrentDays] = event.NoncurrentDays + } return tags } diff --git a/cmd/data-scanner.go b/cmd/data-scanner.go index abd596d67..d403447f4 100644 --- a/cmd/data-scanner.go +++ b/cmd/data-scanner.go @@ -1011,7 +1011,8 @@ func (i *scannerItem) applyNewerNoncurrentVersionLimit(ctx context.Context, _ Ob return objectInfos, nil } - _, days, lim := i.lifeCycle.NoncurrentVersionsExpirationLimit(lifecycle.ObjectOpts{Name: i.objectPath()}) + event := i.lifeCycle.NoncurrentVersionsExpirationLimit(lifecycle.ObjectOpts{Name: i.objectPath()}) + lim := event.NewerNoncurrentVersions if lim == 0 || len(fivs) <= lim+1 { // fewer than lim _noncurrent_ versions for _, fi := range fivs { objectInfos = append(objectInfos, fi.ToObjectInfo(i.bucket, i.objectPath(), versioned)) @@ -1040,7 +1041,7 @@ func (i *scannerItem) applyNewerNoncurrentVersionLimit(ctx context.Context, _ Ob } // NoncurrentDays not passed yet. - if time.Now().UTC().Before(lifecycle.ExpectedExpiryTime(obj.SuccessorModTime, days)) { + if time.Now().UTC().Before(lifecycle.ExpectedExpiryTime(obj.SuccessorModTime, event.NoncurrentDays)) { // add this version back to remaining versions for // subsequent lifecycle policy applications fivs = append(fivs, fi) @@ -1055,7 +1056,7 @@ func (i *scannerItem) applyNewerNoncurrentVersionLimit(ctx context.Context, _ Ob }) } - globalExpiryState.enqueueByNewerNoncurrent(i.bucket, toDel) + globalExpiryState.enqueueByNewerNoncurrent(i.bucket, toDel, event) return objectInfos, nil } diff --git a/cmd/object-handlers-common.go b/cmd/object-handlers-common.go index 62d99ff66..bcec5f300 100644 --- a/cmd/object-handlers-common.go +++ b/cmd/object-handlers-common.go @@ -25,6 +25,7 @@ import ( "time" "github.com/minio/minio/internal/amztime" + "github.com/minio/minio/internal/bucket/lifecycle" "github.com/minio/minio/internal/event" "github.com/minio/minio/internal/hash" xhttp "github.com/minio/minio/internal/http" @@ -343,7 +344,7 @@ func setPutObjHeaders(w http.ResponseWriter, objInfo ObjectInfo, delete bool) { hash.AddChecksumHeader(w, objInfo.decryptChecksums(0)) } -func deleteObjectVersions(ctx context.Context, o ObjectLayer, bucket string, toDel []ObjectToDelete) { +func deleteObjectVersions(ctx context.Context, o ObjectLayer, bucket string, toDel []ObjectToDelete, lcEvent lifecycle.Event) { for remaining := toDel; len(remaining) > 0; toDel = remaining { if len(toDel) > maxDeleteList { remaining = toDel[maxDeleteList:] @@ -373,8 +374,8 @@ func deleteObjectVersions(ctx context.Context, o ObjectLayer, bucket string, toD VersionID: dobj.VersionID, } traceFn := globalLifecycleSys.trace(oi) - tags := make(map[string]interface{}, 1) - tags["newer-noncurrent-versions"] = true + tags := auditLifecycleTags(lcEvent) + // Send audit for the lifecycle delete operation auditLogLifecycle( ctx, diff --git a/internal/bucket/lifecycle/lifecycle.go b/internal/bucket/lifecycle/lifecycle.go index 45d2b16a4..9a2543e28 100644 --- a/internal/bucket/lifecycle/lifecycle.go +++ b/internal/bucket/lifecycle/lifecycle.go @@ -287,10 +287,12 @@ func (o ObjectOpts) ExpiredObjectDeleteMarker() bool { // Event contains a lifecycle action with associated info type Event struct { - Action Action - RuleID string - Due time.Time - StorageClass string + Action Action + RuleID string + Due time.Time + NoncurrentDays int + NewerNoncurrentVersions int + StorageClass string } // Eval returns the lifecycle event applicable now. @@ -480,15 +482,17 @@ func (lc Lifecycle) SetPredictionHeaders(w http.ResponseWriter, obj ObjectOpts) // NoncurrentVersionsExpirationLimit returns the number of noncurrent versions // to be retained from the first applicable rule per S3 behavior. -func (lc Lifecycle) NoncurrentVersionsExpirationLimit(obj ObjectOpts) (string, int, int) { - var lim int - var days int - var ruleID string +func (lc Lifecycle) NoncurrentVersionsExpirationLimit(obj ObjectOpts) Event { for _, rule := range lc.FilterRules(obj) { if rule.NoncurrentVersionExpiration.NewerNoncurrentVersions == 0 { continue } - return rule.ID, int(rule.NoncurrentVersionExpiration.NoncurrentDays), rule.NoncurrentVersionExpiration.NewerNoncurrentVersions + return Event{ + Action: DeleteVersionAction, + RuleID: rule.ID, + NoncurrentDays: int(rule.NoncurrentVersionExpiration.NoncurrentDays), + NewerNoncurrentVersions: rule.NoncurrentVersionExpiration.NewerNoncurrentVersions, + } } - return ruleID, days, lim + return Event{} } diff --git a/internal/bucket/lifecycle/lifecycle_test.go b/internal/bucket/lifecycle/lifecycle_test.go index 58c0fb48d..23d15beaa 100644 --- a/internal/bucket/lifecycle/lifecycle_test.go +++ b/internal/bucket/lifecycle/lifecycle_test.go @@ -854,8 +854,8 @@ func TestNoncurrentVersionsLimit(t *testing.T) { lc := Lifecycle{ Rules: rules, } - if ruleID, days, lim := lc.NoncurrentVersionsExpirationLimit(ObjectOpts{Name: "obj"}); ruleID != "1" || days != 1 || lim != 1 { - t.Fatalf("Expected (ruleID, days, lim) to be (\"1\", 1, 1) but got (%s, %d, %d)", ruleID, days, lim) + if event := lc.NoncurrentVersionsExpirationLimit(ObjectOpts{Name: "obj"}); event.RuleID != "1" || event.NoncurrentDays != 1 || event.NewerNoncurrentVersions != 1 { + t.Fatalf("Expected (ruleID, days, lim) to be (\"1\", 1, 1) but got (%s, %d, %d)", event.RuleID, event.NoncurrentDays, event.NewerNoncurrentVersions) } }