From 353d91af4055c1973ccfb7afd16a985e19543695 Mon Sep 17 00:00:00 2001 From: Krishnan Parthasarathi Date: Tue, 23 Apr 2024 13:58:13 -0700 Subject: [PATCH] Add support for DelMarkerExpiration element pkg/lifecycle: - Add marshal/unmarshal support for DelMarkerExpiration action pkg/notification: - Add bucket event type for deletion of all versions of an object with DEL marker for current version "s3:LifecycleDelMarkerExpiration:Delete" --- pkg/lifecycle/lifecycle.go | 25 +++++++++++++++++++++++++ pkg/lifecycle/lifecycle_test.go | 21 +++++++++++++++++++++ pkg/notification/notification.go | 1 + 3 files changed, 47 insertions(+) diff --git a/pkg/lifecycle/lifecycle.go b/pkg/lifecycle/lifecycle.go index 10c95ffe5..e706b57de 100644 --- a/pkg/lifecycle/lifecycle.go +++ b/pkg/lifecycle/lifecycle.go @@ -414,11 +414,32 @@ func (e Expiration) MarshalXML(en *xml.Encoder, startElement xml.StartElement) e return en.EncodeElement(expirationWrapper(e), startElement) } +// DelMarkerExpiration represents DelMarkerExpiration actions element in an ILM policy +type DelMarkerExpiration struct { + XMLName xml.Name `xml:"DelMarkerExpiration" json:"-"` + Days int `xml:"Days,omitempty" json:"Days,omitempty"` +} + +// IsNull returns true if Days isn't specified and false otherwise. +func (de DelMarkerExpiration) IsNull() bool { + return de.Days == 0 +} + +// MarshalXML avoids serializing an empty DelMarkerExpiration element +func (de DelMarkerExpiration) MarshalXML(enc *xml.Encoder, start xml.StartElement) error { + if de.IsNull() { + return nil + } + type delMarkerExp DelMarkerExpiration + return enc.EncodeElement(delMarkerExp(de), 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"` ID string `json:"ID"` RuleFilter *Filter `json:"Filter,omitempty"` NoncurrentVersionExpiration *NoncurrentVersionExpiration `json:"NoncurrentVersionExpiration,omitempty"` @@ -442,6 +463,9 @@ func (r Rule) MarshalJSON() ([]byte, error) { if !r.Expiration.IsNull() { newr.Expiration = &r.Expiration } + if !r.DelMarkerExpiration.IsNull() { + newr.DelMarkerExpiration = &r.DelMarkerExpiration + } if !r.Transition.IsNull() { newr.Transition = &r.Transition } @@ -460,6 +484,7 @@ type Rule struct { XMLName xml.Name `xml:"Rule,omitempty" json:"-"` AbortIncompleteMultipartUpload AbortIncompleteMultipartUpload `xml:"AbortIncompleteMultipartUpload,omitempty" json:"AbortIncompleteMultipartUpload,omitempty"` Expiration Expiration `xml:"Expiration,omitempty" json:"Expiration,omitempty"` + DelMarkerExpiration DelMarkerExpiration `xml:"DelMarkerExpiration,omitempty" json:"DelMarkerExpiration,omitempty"` ID string `xml:"ID" json:"ID"` RuleFilter Filter `xml:"Filter,omitempty" json:"Filter,omitempty"` NoncurrentVersionExpiration NoncurrentVersionExpiration `xml:"NoncurrentVersionExpiration,omitempty" json:"NoncurrentVersionExpiration,omitempty"` diff --git a/pkg/lifecycle/lifecycle_test.go b/pkg/lifecycle/lifecycle_test.go index 878472e9d..d730ed3b3 100644 --- a/pkg/lifecycle/lifecycle_test.go +++ b/pkg/lifecycle/lifecycle_test.go @@ -266,6 +266,13 @@ func TestLifecycleJSONRoundtrip(t *testing.T) { ID: "rule-6", Status: "Enabled", }, + { + DelMarkerExpiration: DelMarkerExpiration{ + Days: 10, + }, + ID: "rule-7", + Status: "Enabled", + }, }, } @@ -290,6 +297,9 @@ func TestLifecycleJSONRoundtrip(t *testing.T) { if lc.Rules[i].Expiration != got.Rules[i].Expiration { t.Fatalf("expected %#v got %#v", lc.Rules[i].Expiration, got.Rules[i].Expiration) } + if !lc.Rules[i].DelMarkerExpiration.equals(got.Rules[i].DelMarkerExpiration) { + t.Fatalf("expected %#v got %#v", lc.Rules[i].DelMarkerExpiration, got.Rules[i].DelMarkerExpiration) + } } } @@ -335,6 +345,13 @@ func TestLifecycleXMLRoundtrip(t *testing.T) { NewerNoncurrentVersions: 5, }, }, + { + ID: "delmarker-expiration", + Status: "Enabled", + DelMarkerExpiration: DelMarkerExpiration{ + Days: 5, + }, + }, }, } @@ -368,6 +385,10 @@ func (t Transition) equals(u Transition) bool { return t.Days == u.Days && t.Date.Equal(u.Date.Time) && t.StorageClass == u.StorageClass } +func (a DelMarkerExpiration) equals(b DelMarkerExpiration) bool { + return a.Days == b.Days +} + func TestExpiredObjectDeleteMarker(t *testing.T) { expected := []byte(`{"Rules":[{"Expiration":{"ExpiredObjectDeleteMarker":true},"ID":"expired-object-delete-marker","Status":"Enabled"}]}`) lc := Configuration{ diff --git a/pkg/notification/notification.go b/pkg/notification/notification.go index a44799d24..151ca21e8 100644 --- a/pkg/notification/notification.go +++ b/pkg/notification/notification.go @@ -50,6 +50,7 @@ const ( ObjectRemovedAll EventType = "s3:ObjectRemoved:*" ObjectRemovedDelete EventType = "s3:ObjectRemoved:Delete" ObjectRemovedDeleteMarkerCreated EventType = "s3:ObjectRemoved:DeleteMarkerCreated" + ILMDelMarkerExpirationDelete EventType = "s3:LifecycleDelMarkerExpiration:Delete" ObjectReducedRedundancyLostObject EventType = "s3:ReducedRedundancyLostObject" ObjectTransitionAll EventType = "s3:ObjectTransition:*" ObjectTransitionFailed EventType = "s3:ObjectTransition:Failed"