Skip to content

Commit

Permalink
Add support for AllVersionsExpiration ilm rule (#2014)
Browse files Browse the repository at this point in the history
This is a minio-only rule for removing all versions of an
object upon expiration. The rule takes two parameters:
Days and DeleteMarker.
"Days" indicates the number of days after which the object and
all its versions must be expired.
The optional "DeleteMarker" flag indicates that the expiration
be applied if the latest version of this object is a delete marker.
  • Loading branch information
dhananjaykrutika authored Oct 28, 2024
1 parent 13faa64 commit 22d521d
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 0 deletions.
26 changes: 26 additions & 0 deletions pkg/lifecycle/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,12 +434,34 @@ func (de DelMarkerExpiration) MarshalXML(enc *xml.Encoder, start xml.StartElemen
return enc.EncodeElement(delMarkerExp(de), start)
}

// AllVersionsExpiration represents AllVersionsExpiration actions element in an ILM policy
type AllVersionsExpiration struct {
XMLName xml.Name `xml:"AllVersionsExpiration" json:"-"`
Days int `xml:"Days,omitempty" json:"Days,omitempty"`
DeleteMarker ExpireDeleteMarker `xml:"DeleteMarker,omitempty" json:"DeleteMarker,omitempty"`
}

// IsNull returns true if days field is 0
func (e AllVersionsExpiration) IsNull() bool {
return e.Days == 0
}

// MarshalXML satisfies xml.Marshaler to provide custom encoding
func (e AllVersionsExpiration) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {
if e.IsNull() {
return nil
}
type allVersionsExp AllVersionsExpiration
return enc.EncodeElement(allVersionsExp(e), start)
}

// MarshalJSON customizes json encoding by omitting empty values
func (r Rule) MarshalJSON() ([]byte, error) {
type rule struct {
AbortIncompleteMultipartUpload *AbortIncompleteMultipartUpload `json:"AbortIncompleteMultipartUpload,omitempty"`
Expiration *Expiration `json:"Expiration,omitempty"`
DelMarkerExpiration *DelMarkerExpiration `json:"DelMarkerExpiration,omitempty"`
AllVersionsExpiration *AllVersionsExpiration `json:"AllVersionsExpiration,omitempty"`
ID string `json:"ID"`
RuleFilter *Filter `json:"Filter,omitempty"`
NoncurrentVersionExpiration *NoncurrentVersionExpiration `json:"NoncurrentVersionExpiration,omitempty"`
Expand Down Expand Up @@ -475,6 +497,9 @@ func (r Rule) MarshalJSON() ([]byte, error) {
if !r.NoncurrentVersionTransition.isNull() {
newr.NoncurrentVersionTransition = &r.NoncurrentVersionTransition
}
if !r.AllVersionsExpiration.IsNull() {
newr.AllVersionsExpiration = &r.AllVersionsExpiration
}

return json.Marshal(newr)
}
Expand All @@ -485,6 +510,7 @@ type Rule struct {
AbortIncompleteMultipartUpload AbortIncompleteMultipartUpload `xml:"AbortIncompleteMultipartUpload,omitempty" json:"AbortIncompleteMultipartUpload,omitempty"`
Expiration Expiration `xml:"Expiration,omitempty" json:"Expiration,omitempty"`
DelMarkerExpiration DelMarkerExpiration `xml:"DelMarkerExpiration,omitempty" json:"DelMarkerExpiration,omitempty"`
AllVersionsExpiration AllVersionsExpiration `xml:"AllVersionsExpiration,omitempty" json:"AllVersionsExpiration,omitempty"`
ID string `xml:"ID" json:"ID"`
RuleFilter Filter `xml:"Filter,omitempty" json:"Filter,omitempty"`
NoncurrentVersionExpiration NoncurrentVersionExpiration `xml:"NoncurrentVersionExpiration,omitempty" json:"NoncurrentVersionExpiration,omitempty"`
Expand Down
94 changes: 94 additions & 0 deletions pkg/lifecycle/lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,28 @@ func TestLifecycleJSONRoundtrip(t *testing.T) {
ID: "rule-7",
Status: "Enabled",
},
{
AllVersionsExpiration: AllVersionsExpiration{
Days: 10,
},
ID: "rule-8",
Status: "Enabled",
},
{
AllVersionsExpiration: AllVersionsExpiration{
Days: 0,
},
ID: "rule-9",
Status: "Enabled",
},
{
AllVersionsExpiration: AllVersionsExpiration{
Days: 7,
DeleteMarker: ExpireDeleteMarker(true),
},
ID: "rule-10",
Status: "Enabled",
},
},
}

Expand All @@ -291,6 +313,10 @@ func TestLifecycleJSONRoundtrip(t *testing.T) {
t.Fatalf("expected %#v got %#v", lc.Rules[i].NoncurrentVersionTransition, got.Rules[i].NoncurrentVersionTransition)
}

if !lc.Rules[i].NoncurrentVersionExpiration.equals(got.Rules[i].NoncurrentVersionExpiration) {
t.Fatalf("expected %#v got %#v", lc.Rules[i].NoncurrentVersionExpiration, got.Rules[i].NoncurrentVersionExpiration)
}

if !lc.Rules[i].Transition.equals(got.Rules[i].Transition) {
t.Fatalf("expected %#v got %#v", lc.Rules[i].Transition, got.Rules[i].Transition)
}
Expand All @@ -300,6 +326,9 @@ func TestLifecycleJSONRoundtrip(t *testing.T) {
if !lc.Rules[i].DelMarkerExpiration.equals(got.Rules[i].DelMarkerExpiration) {
t.Fatalf("expected %#v got %#v", lc.Rules[i].DelMarkerExpiration, got.Rules[i].DelMarkerExpiration)
}
if !lc.Rules[i].AllVersionsExpiration.equals(got.Rules[i].AllVersionsExpiration) {
t.Fatalf("expected %#v got %#v", lc.Rules[i].AllVersionsExpiration, got.Rules[i].AllVersionsExpiration)
}
}
}

Expand Down Expand Up @@ -352,6 +381,27 @@ func TestLifecycleXMLRoundtrip(t *testing.T) {
Days: 5,
},
},
{
ID: "all-versions-expiration-1",
Status: "Enabled",
AllVersionsExpiration: AllVersionsExpiration{
Days: 5,
},
},
{
ID: "all-versions-expiration-2",
Status: "Enabled",
AllVersionsExpiration: AllVersionsExpiration{
Days: 10,
DeleteMarker: ExpireDeleteMarker(true),
},
RuleFilter: Filter{
Tag: Tag{
Key: "key-1",
Value: "value-1",
},
},
},
},
}

Expand All @@ -374,13 +424,29 @@ func TestLifecycleXMLRoundtrip(t *testing.T) {
if !lc.Rules[i].Transition.equals(got.Rules[i].Transition) {
t.Fatalf("%d: expected %#v got %#v", i+1, lc.Rules[i].Transition, got.Rules[i].Transition)
}

if !lc.Rules[i].NoncurrentVersionExpiration.equals(got.Rules[i].NoncurrentVersionExpiration) {
t.Fatalf("%d: expected %#v got %#v", i+1, lc.Rules[i].NoncurrentVersionExpiration, got.Rules[i].NoncurrentVersionExpiration)
}

if !lc.Rules[i].DelMarkerExpiration.equals(got.Rules[i].DelMarkerExpiration) {
t.Fatalf("%d: expected %#v got %#v", i+1, lc.Rules[i].DelMarkerExpiration, got.Rules[i].DelMarkerExpiration)
}

if !lc.Rules[i].AllVersionsExpiration.equals(got.Rules[i].AllVersionsExpiration) {
t.Fatalf("%d: expected %#v got %#v", i+1, lc.Rules[i].AllVersionsExpiration, got.Rules[i].AllVersionsExpiration)
}
}
}

func (n NoncurrentVersionTransition) equals(m NoncurrentVersionTransition) bool {
return n.NoncurrentDays == m.NoncurrentDays && n.StorageClass == m.StorageClass
}

func (n NoncurrentVersionExpiration) equals(m NoncurrentVersionExpiration) bool {
return n.NoncurrentDays == m.NoncurrentDays && n.NewerNoncurrentVersions == m.NewerNoncurrentVersions
}

func (t Transition) equals(u Transition) bool {
return t.Days == u.Days && t.Date.Equal(u.Date.Time) && t.StorageClass == u.StorageClass
}
Expand All @@ -389,6 +455,10 @@ func (a DelMarkerExpiration) equals(b DelMarkerExpiration) bool {
return a.Days == b.Days
}

func (a AllVersionsExpiration) equals(b AllVersionsExpiration) bool {
return a.Days == b.Days && a.DeleteMarker == b.DeleteMarker
}

func TestExpiredObjectDeleteMarker(t *testing.T) {
expected := []byte(`{"Rules":[{"Expiration":{"ExpiredObjectDeleteMarker":true},"ID":"expired-object-delete-marker","Status":"Enabled"}]}`)
lc := Configuration{
Expand All @@ -411,3 +481,27 @@ func TestExpiredObjectDeleteMarker(t *testing.T) {
t.Fatalf("Expected %s but got %s", expected, got)
}
}

func TestAllVersionsExpiration(t *testing.T) {
expected := []byte(`{"Rules":[{"AllVersionsExpiration":{"Days":2,"DeleteMarker":true},"ID":"all-versions-expiration","Status":"Enabled"}]}`)
lc := Configuration{
Rules: []Rule{
{
AllVersionsExpiration: AllVersionsExpiration{
Days: 2,
DeleteMarker: ExpireDeleteMarker(true),
},
ID: "all-versions-expiration",
Status: "Enabled",
},
},
}

got, err := json.Marshal(lc)
if err != nil {
t.Fatalf("Failed to marshal due to %v", err)
}
if !bytes.Equal(expected, got) {
t.Fatalf("Expected %s but got %s", expected, got)
}
}

0 comments on commit 22d521d

Please sign in to comment.