Skip to content

Commit 22d521d

Browse files
Add support for AllVersionsExpiration ilm rule (#2014)
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.
1 parent 13faa64 commit 22d521d

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

pkg/lifecycle/lifecycle.go

+26
Original file line numberDiff line numberDiff line change
@@ -434,12 +434,34 @@ func (de DelMarkerExpiration) MarshalXML(enc *xml.Encoder, start xml.StartElemen
434434
return enc.EncodeElement(delMarkerExp(de), start)
435435
}
436436

437+
// AllVersionsExpiration represents AllVersionsExpiration actions element in an ILM policy
438+
type AllVersionsExpiration struct {
439+
XMLName xml.Name `xml:"AllVersionsExpiration" json:"-"`
440+
Days int `xml:"Days,omitempty" json:"Days,omitempty"`
441+
DeleteMarker ExpireDeleteMarker `xml:"DeleteMarker,omitempty" json:"DeleteMarker,omitempty"`
442+
}
443+
444+
// IsNull returns true if days field is 0
445+
func (e AllVersionsExpiration) IsNull() bool {
446+
return e.Days == 0
447+
}
448+
449+
// MarshalXML satisfies xml.Marshaler to provide custom encoding
450+
func (e AllVersionsExpiration) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {
451+
if e.IsNull() {
452+
return nil
453+
}
454+
type allVersionsExp AllVersionsExpiration
455+
return enc.EncodeElement(allVersionsExp(e), start)
456+
}
457+
437458
// MarshalJSON customizes json encoding by omitting empty values
438459
func (r Rule) MarshalJSON() ([]byte, error) {
439460
type rule struct {
440461
AbortIncompleteMultipartUpload *AbortIncompleteMultipartUpload `json:"AbortIncompleteMultipartUpload,omitempty"`
441462
Expiration *Expiration `json:"Expiration,omitempty"`
442463
DelMarkerExpiration *DelMarkerExpiration `json:"DelMarkerExpiration,omitempty"`
464+
AllVersionsExpiration *AllVersionsExpiration `json:"AllVersionsExpiration,omitempty"`
443465
ID string `json:"ID"`
444466
RuleFilter *Filter `json:"Filter,omitempty"`
445467
NoncurrentVersionExpiration *NoncurrentVersionExpiration `json:"NoncurrentVersionExpiration,omitempty"`
@@ -475,6 +497,9 @@ func (r Rule) MarshalJSON() ([]byte, error) {
475497
if !r.NoncurrentVersionTransition.isNull() {
476498
newr.NoncurrentVersionTransition = &r.NoncurrentVersionTransition
477499
}
500+
if !r.AllVersionsExpiration.IsNull() {
501+
newr.AllVersionsExpiration = &r.AllVersionsExpiration
502+
}
478503

479504
return json.Marshal(newr)
480505
}
@@ -485,6 +510,7 @@ type Rule struct {
485510
AbortIncompleteMultipartUpload AbortIncompleteMultipartUpload `xml:"AbortIncompleteMultipartUpload,omitempty" json:"AbortIncompleteMultipartUpload,omitempty"`
486511
Expiration Expiration `xml:"Expiration,omitempty" json:"Expiration,omitempty"`
487512
DelMarkerExpiration DelMarkerExpiration `xml:"DelMarkerExpiration,omitempty" json:"DelMarkerExpiration,omitempty"`
513+
AllVersionsExpiration AllVersionsExpiration `xml:"AllVersionsExpiration,omitempty" json:"AllVersionsExpiration,omitempty"`
488514
ID string `xml:"ID" json:"ID"`
489515
RuleFilter Filter `xml:"Filter,omitempty" json:"Filter,omitempty"`
490516
NoncurrentVersionExpiration NoncurrentVersionExpiration `xml:"NoncurrentVersionExpiration,omitempty" json:"NoncurrentVersionExpiration,omitempty"`

pkg/lifecycle/lifecycle_test.go

+94
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,28 @@ func TestLifecycleJSONRoundtrip(t *testing.T) {
273273
ID: "rule-7",
274274
Status: "Enabled",
275275
},
276+
{
277+
AllVersionsExpiration: AllVersionsExpiration{
278+
Days: 10,
279+
},
280+
ID: "rule-8",
281+
Status: "Enabled",
282+
},
283+
{
284+
AllVersionsExpiration: AllVersionsExpiration{
285+
Days: 0,
286+
},
287+
ID: "rule-9",
288+
Status: "Enabled",
289+
},
290+
{
291+
AllVersionsExpiration: AllVersionsExpiration{
292+
Days: 7,
293+
DeleteMarker: ExpireDeleteMarker(true),
294+
},
295+
ID: "rule-10",
296+
Status: "Enabled",
297+
},
276298
},
277299
}
278300

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

316+
if !lc.Rules[i].NoncurrentVersionExpiration.equals(got.Rules[i].NoncurrentVersionExpiration) {
317+
t.Fatalf("expected %#v got %#v", lc.Rules[i].NoncurrentVersionExpiration, got.Rules[i].NoncurrentVersionExpiration)
318+
}
319+
294320
if !lc.Rules[i].Transition.equals(got.Rules[i].Transition) {
295321
t.Fatalf("expected %#v got %#v", lc.Rules[i].Transition, got.Rules[i].Transition)
296322
}
@@ -300,6 +326,9 @@ func TestLifecycleJSONRoundtrip(t *testing.T) {
300326
if !lc.Rules[i].DelMarkerExpiration.equals(got.Rules[i].DelMarkerExpiration) {
301327
t.Fatalf("expected %#v got %#v", lc.Rules[i].DelMarkerExpiration, got.Rules[i].DelMarkerExpiration)
302328
}
329+
if !lc.Rules[i].AllVersionsExpiration.equals(got.Rules[i].AllVersionsExpiration) {
330+
t.Fatalf("expected %#v got %#v", lc.Rules[i].AllVersionsExpiration, got.Rules[i].AllVersionsExpiration)
331+
}
303332
}
304333
}
305334

@@ -352,6 +381,27 @@ func TestLifecycleXMLRoundtrip(t *testing.T) {
352381
Days: 5,
353382
},
354383
},
384+
{
385+
ID: "all-versions-expiration-1",
386+
Status: "Enabled",
387+
AllVersionsExpiration: AllVersionsExpiration{
388+
Days: 5,
389+
},
390+
},
391+
{
392+
ID: "all-versions-expiration-2",
393+
Status: "Enabled",
394+
AllVersionsExpiration: AllVersionsExpiration{
395+
Days: 10,
396+
DeleteMarker: ExpireDeleteMarker(true),
397+
},
398+
RuleFilter: Filter{
399+
Tag: Tag{
400+
Key: "key-1",
401+
Value: "value-1",
402+
},
403+
},
404+
},
355405
},
356406
}
357407

@@ -374,13 +424,29 @@ func TestLifecycleXMLRoundtrip(t *testing.T) {
374424
if !lc.Rules[i].Transition.equals(got.Rules[i].Transition) {
375425
t.Fatalf("%d: expected %#v got %#v", i+1, lc.Rules[i].Transition, got.Rules[i].Transition)
376426
}
427+
428+
if !lc.Rules[i].NoncurrentVersionExpiration.equals(got.Rules[i].NoncurrentVersionExpiration) {
429+
t.Fatalf("%d: expected %#v got %#v", i+1, lc.Rules[i].NoncurrentVersionExpiration, got.Rules[i].NoncurrentVersionExpiration)
430+
}
431+
432+
if !lc.Rules[i].DelMarkerExpiration.equals(got.Rules[i].DelMarkerExpiration) {
433+
t.Fatalf("%d: expected %#v got %#v", i+1, lc.Rules[i].DelMarkerExpiration, got.Rules[i].DelMarkerExpiration)
434+
}
435+
436+
if !lc.Rules[i].AllVersionsExpiration.equals(got.Rules[i].AllVersionsExpiration) {
437+
t.Fatalf("%d: expected %#v got %#v", i+1, lc.Rules[i].AllVersionsExpiration, got.Rules[i].AllVersionsExpiration)
438+
}
377439
}
378440
}
379441

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

446+
func (n NoncurrentVersionExpiration) equals(m NoncurrentVersionExpiration) bool {
447+
return n.NoncurrentDays == m.NoncurrentDays && n.NewerNoncurrentVersions == m.NewerNoncurrentVersions
448+
}
449+
384450
func (t Transition) equals(u Transition) bool {
385451
return t.Days == u.Days && t.Date.Equal(u.Date.Time) && t.StorageClass == u.StorageClass
386452
}
@@ -389,6 +455,10 @@ func (a DelMarkerExpiration) equals(b DelMarkerExpiration) bool {
389455
return a.Days == b.Days
390456
}
391457

458+
func (a AllVersionsExpiration) equals(b AllVersionsExpiration) bool {
459+
return a.Days == b.Days && a.DeleteMarker == b.DeleteMarker
460+
}
461+
392462
func TestExpiredObjectDeleteMarker(t *testing.T) {
393463
expected := []byte(`{"Rules":[{"Expiration":{"ExpiredObjectDeleteMarker":true},"ID":"expired-object-delete-marker","Status":"Enabled"}]}`)
394464
lc := Configuration{
@@ -411,3 +481,27 @@ func TestExpiredObjectDeleteMarker(t *testing.T) {
411481
t.Fatalf("Expected %s but got %s", expected, got)
412482
}
413483
}
484+
485+
func TestAllVersionsExpiration(t *testing.T) {
486+
expected := []byte(`{"Rules":[{"AllVersionsExpiration":{"Days":2,"DeleteMarker":true},"ID":"all-versions-expiration","Status":"Enabled"}]}`)
487+
lc := Configuration{
488+
Rules: []Rule{
489+
{
490+
AllVersionsExpiration: AllVersionsExpiration{
491+
Days: 2,
492+
DeleteMarker: ExpireDeleteMarker(true),
493+
},
494+
ID: "all-versions-expiration",
495+
Status: "Enabled",
496+
},
497+
},
498+
}
499+
500+
got, err := json.Marshal(lc)
501+
if err != nil {
502+
t.Fatalf("Failed to marshal due to %v", err)
503+
}
504+
if !bytes.Equal(expected, got) {
505+
t.Fatalf("Expected %s but got %s", expected, got)
506+
}
507+
}

0 commit comments

Comments
 (0)