mirror of
https://github.com/minio/minio.git
synced 2025-11-07 04:42:56 -05:00
Add MaxNoncurrentVersions to NoncurrentExpiration action (#13580)
This unit allows users to limit the maximum number of noncurrent
versions of an object.
To enable this rule you need the following *ilm.json*
```
cat >> ilm.json <<EOF
{
"Rules": [
{
"ID": "test-max-noncurrent",
"Status": "Enabled",
"Filter": {
"Prefix": "user-uploads/"
},
"NoncurrentVersionExpiration": {
"MaxNoncurrentVersions": 5
}
}
]
}
EOF
mc ilm import myminio/mybucket < ilm.json
```
This commit is contained in:
committed by
GitHub
parent
1e2fac054c
commit
3da9ee15d3
@@ -29,10 +29,11 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
errLifecycleTooManyRules = Errorf("Lifecycle configuration allows a maximum of 1000 rules")
|
||||
errLifecycleNoRule = Errorf("Lifecycle configuration should have at least one rule")
|
||||
errLifecycleDuplicateID = Errorf("Lifecycle configuration has rule with the same ID. Rule ID must be unique.")
|
||||
errXMLNotWellFormed = Errorf("The XML you provided was not well-formed or did not validate against our published schema")
|
||||
errLifecycleTooManyRules = Errorf("Lifecycle configuration allows a maximum of 1000 rules")
|
||||
errLifecycleNoRule = Errorf("Lifecycle configuration should have at least one rule")
|
||||
errLifecycleDuplicateID = Errorf("Lifecycle configuration has rule with the same ID. Rule ID must be unique.")
|
||||
errXMLNotWellFormed = Errorf("The XML you provided was not well-formed or did not validate against our published schema")
|
||||
errLifecycleInvalidNoncurrentExpiration = Errorf("Exactly one of NoncurrentDays (positive integer) or MaxNoncurrentVersions should be specified in a NoncurrentExpiration rule.")
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -140,6 +141,9 @@ func (lc Lifecycle) HasActiveRules(prefix string, recursive bool) bool {
|
||||
if rule.NoncurrentVersionExpiration.NoncurrentDays > 0 {
|
||||
return true
|
||||
}
|
||||
if rule.NoncurrentVersionExpiration.MaxNoncurrentVersions > 0 {
|
||||
return true
|
||||
}
|
||||
if !rule.NoncurrentVersionTransition.IsNull() {
|
||||
return true
|
||||
}
|
||||
@@ -234,6 +238,10 @@ func (lc Lifecycle) FilterActionableRules(obj ObjectOpts) []Rule {
|
||||
rules = append(rules, rule)
|
||||
continue
|
||||
}
|
||||
if rule.NoncurrentVersionExpiration.MaxNoncurrentVersions > 0 {
|
||||
rules = append(rules, rule)
|
||||
continue
|
||||
}
|
||||
// The NoncurrentVersionTransition action requests MinIO to transition
|
||||
// noncurrent versions of objects x days after the objects become
|
||||
// noncurrent.
|
||||
@@ -468,3 +476,32 @@ func (lc Lifecycle) TransitionTier(obj ObjectOpts) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// NoncurrentVersionsExpirationLimit returns the minimum limit on number of
|
||||
// noncurrent versions across rules.
|
||||
func (lc Lifecycle) NoncurrentVersionsExpirationLimit(obj ObjectOpts) int {
|
||||
var lim int
|
||||
for _, rule := range lc.FilterActionableRules(obj) {
|
||||
if rule.NoncurrentVersionExpiration.MaxNoncurrentVersions == 0 {
|
||||
continue
|
||||
}
|
||||
if lim == 0 || lim > rule.NoncurrentVersionExpiration.MaxNoncurrentVersions {
|
||||
lim = rule.NoncurrentVersionExpiration.MaxNoncurrentVersions
|
||||
}
|
||||
}
|
||||
return lim
|
||||
}
|
||||
|
||||
// HasMaxNoncurrentVersions returns true if there exists a rule with
|
||||
// MaxNoncurrentVersions limit set.
|
||||
func (lc Lifecycle) HasMaxNoncurrentVersions() bool {
|
||||
for _, rule := range lc.Rules {
|
||||
if rule.Status == Disabled {
|
||||
continue
|
||||
}
|
||||
if rule.NoncurrentVersionExpiration.MaxNoncurrentVersions > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -111,6 +112,12 @@ func TestParseAndValidateLifecycleConfig(t *testing.T) {
|
||||
expectedParsingErr: nil,
|
||||
expectedValidationErr: nil,
|
||||
},
|
||||
// Lifecycle with max noncurrent versions
|
||||
{
|
||||
inputConfig: `<LifecycleConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Rule><ID>rule</ID>><Status>Enabled</Status><Filter></Filter><NoncurrentVersionExpiration><MaxNoncurrentVersions>5</MaxNoncurrentVersions></NoncurrentVersionExpiration></Rule></LifecycleConfiguration>`,
|
||||
expectedParsingErr: nil,
|
||||
expectedValidationErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
@@ -619,3 +626,24 @@ func TestTransitionTier(t *testing.T) {
|
||||
t.Fatalf("Expected TIER-2 but got %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoncurrentVersionsLimit(t *testing.T) {
|
||||
// test that the lowest max noncurrent versions limit is returned among
|
||||
// matching rules
|
||||
var rules []Rule
|
||||
for i := 1; i <= 10; i++ {
|
||||
rules = append(rules, Rule{
|
||||
ID: strconv.Itoa(i),
|
||||
Status: "Enabled",
|
||||
NoncurrentVersionExpiration: NoncurrentVersionExpiration{
|
||||
MaxNoncurrentVersions: i,
|
||||
},
|
||||
})
|
||||
}
|
||||
lc := Lifecycle{
|
||||
Rules: rules,
|
||||
}
|
||||
if lim := lc.NoncurrentVersionsExpirationLimit(ObjectOpts{Name: "obj"}); lim != 1 {
|
||||
t.Fatalf("Expected max noncurrent versions limit to be 1 but got %d", lim)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,14 +24,15 @@ import (
|
||||
|
||||
// NoncurrentVersionExpiration - an action for lifecycle configuration rule.
|
||||
type NoncurrentVersionExpiration struct {
|
||||
XMLName xml.Name `xml:"NoncurrentVersionExpiration"`
|
||||
NoncurrentDays ExpirationDays `xml:"NoncurrentDays,omitempty"`
|
||||
set bool
|
||||
XMLName xml.Name `xml:"NoncurrentVersionExpiration"`
|
||||
NoncurrentDays ExpirationDays `xml:"NoncurrentDays,omitempty"`
|
||||
MaxNoncurrentVersions int `xml:"MaxNoncurrentVersions,omitempty"`
|
||||
set bool
|
||||
}
|
||||
|
||||
// MarshalXML if non-current days not set to non zero value
|
||||
func (n NoncurrentVersionExpiration) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
if n.IsDaysNull() {
|
||||
if n.IsNull() {
|
||||
return nil
|
||||
}
|
||||
type noncurrentVersionExpirationWrapper NoncurrentVersionExpiration
|
||||
@@ -51,6 +52,11 @@ func (n *NoncurrentVersionExpiration) UnmarshalXML(d *xml.Decoder, startElement
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsNull returns if both NoncurrentDays and NoncurrentVersions are empty
|
||||
func (n NoncurrentVersionExpiration) IsNull() bool {
|
||||
return n.IsDaysNull() && n.MaxNoncurrentVersions == 0
|
||||
}
|
||||
|
||||
// IsDaysNull returns true if days field is null
|
||||
func (n NoncurrentVersionExpiration) IsDaysNull() bool {
|
||||
return n.NoncurrentDays == ExpirationDays(0)
|
||||
@@ -62,8 +68,17 @@ func (n NoncurrentVersionExpiration) Validate() error {
|
||||
return nil
|
||||
}
|
||||
val := int(n.NoncurrentDays)
|
||||
if val <= 0 {
|
||||
switch {
|
||||
case val == 0 && n.MaxNoncurrentVersions == 0:
|
||||
// both fields can't be zero
|
||||
return errXMLNotWellFormed
|
||||
|
||||
case val > 0 && n.MaxNoncurrentVersions > 0:
|
||||
// both tags can't be non-zero simultaneously
|
||||
return errLifecycleInvalidNoncurrentExpiration
|
||||
|
||||
case val < 0, n.MaxNoncurrentVersions < 0:
|
||||
// negative values are not supported
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user