diff --git a/pkg/bucket/lifecycle/lifecycle.go b/pkg/bucket/lifecycle/lifecycle.go index 8dd0c550b..f7f0af66d 100644 --- a/pkg/bucket/lifecycle/lifecycle.go +++ b/pkg/bucket/lifecycle/lifecycle.go @@ -93,49 +93,54 @@ func (lc Lifecycle) Validate() error { return nil } -// FilterRuleActions returns the expiration and transition from the object name -// after evaluating all rules. -func (lc Lifecycle) FilterRuleActions(objName, objTags string) (string, Expiration, Transition) { +// FilterActionableRules returns the rules actions that need to be executed +// after evaluating prefix/tag filtering +func (lc Lifecycle) FilterActionableRules(objName, objTags string) []Rule { if objName == "" { - return "", Expiration{}, Transition{} + return nil } + var rules []Rule for _, rule := range lc.Rules { if rule.Status == Disabled { continue } - tags := rule.Tags() if strings.HasPrefix(objName, rule.Prefix()) { + tags := rule.Tags() if tags != "" { if strings.Contains(objTags, tags) { - return rule.ID, rule.Expiration, Transition{} + rules = append(rules, rule) } } else { - return rule.ID, rule.Expiration, Transition{} + rules = append(rules, rule) } } } - return "", Expiration{}, Transition{} + return rules } // ComputeAction returns the action to perform by evaluating all lifecycle rules // against the object name and its modification time. -func (lc Lifecycle) ComputeAction(objName, objTags string, modTime time.Time) Action { - var action = NoneAction +func (lc Lifecycle) ComputeAction(objName, objTags string, modTime time.Time) (action Action) { + action = NoneAction if modTime.IsZero() { - return action + return } - _, exp, _ := lc.FilterRuleActions(objName, objTags) - if !exp.IsDateNull() { - if time.Now().After(exp.Date.Time) { - action = DeleteAction + rules := lc.FilterActionableRules(objName, objTags) + for _, rule := range rules { + if !rule.Expiration.IsDateNull() { + if time.Now().After(rule.Expiration.Date.Time) { + action = DeleteAction + return + } } - } - if !exp.IsDaysNull() { - if time.Now().After(expectedExpiryTime(modTime, exp.Days)) { - action = DeleteAction + if !rule.Expiration.IsDaysNull() { + if time.Now().After(expectedExpiryTime(modTime, rule.Expiration.Days)) { + action = DeleteAction + return + } } } - return action + return } // expectedExpiryTime calculates the expiry date/time based on a object modtime. @@ -149,13 +154,27 @@ func expectedExpiryTime(modTime time.Time, days ExpirationDays) time.Time { } // PredictExpiryTime returns the expiry date/time of a given object +// after evaluting the current lifecycle document. func (lc Lifecycle) PredictExpiryTime(objName, objTags string) (string, time.Time) { - ruleID, exp, _ := lc.FilterRuleActions(objName, objTags) - if !exp.IsDateNull() { - return ruleID, exp.Date.Time - } - if !exp.IsDaysNull() { - return ruleID, expectedExpiryTime(time.Now(), exp.Days) + var finalExpiryDate time.Time + var finalExpiryRuleID string + + // Iterate over all actionable rules and find the earliest + // expiration date and its associated rule ID. + for _, rule := range lc.FilterActionableRules(objName, objTags) { + if !rule.Expiration.IsDateNull() { + if finalExpiryDate.IsZero() || finalExpiryDate.After(rule.Expiration.Date.Time) { + finalExpiryRuleID = rule.ID + finalExpiryDate = rule.Expiration.Date.Time + } + } + if !rule.Expiration.IsDaysNull() { + expectedExpiry := expectedExpiryTime(time.Now(), rule.Expiration.Days) + if finalExpiryDate.IsZero() || finalExpiryDate.After(expectedExpiry) { + finalExpiryRuleID = rule.ID + finalExpiryDate = expectedExpiry + } + } } - return "", time.Time{} + return finalExpiryRuleID, finalExpiryDate } diff --git a/pkg/bucket/lifecycle/lifecycle_test.go b/pkg/bucket/lifecycle/lifecycle_test.go index e87928bf0..367075d3e 100644 --- a/pkg/bucket/lifecycle/lifecycle_test.go +++ b/pkg/bucket/lifecycle/lifecycle_test.go @@ -298,6 +298,13 @@ func TestComputeActions(t *testing.T) { objectModTime: time.Now().UTC().Add(-24 * time.Hour), // Created 1 day ago expectedAction: NoneAction, }, + // Should remove, the second rule has expiration kicked in + { + inputConfig: `Enabled` + time.Now().Truncate(24*time.Hour).UTC().Add(24*time.Hour).Format(time.RFC3339) + `foxdir/Enabled` + time.Now().Truncate(24*time.Hour).UTC().Add(-24*time.Hour).Format(time.RFC3339) + ``, + objectName: "foxdir/fooobject", + objectModTime: time.Now().UTC().Add(-24 * time.Hour), // Created 1 day ago + expectedAction: DeleteAction, + }, } for i, tc := range testCases {