Skip to content

Commit 55ca5dc

Browse files
committed
Fix SetBucketLifecycle() for global rules
https://docs.aws.amazon.com/AmazonS3/latest/API/API_LifecycleRule.html mentions that Filter is required if the LifecycleRule does not contain a Prefix element. After #2177, this requirement is broken for bucket-global (=prefixless) rules. And clients setting such may run into server side validation issues like. HTTP/1.1 400 Bad Request <?xml version="1.0" encoding="UTF-8"?> <Error> <Code>MalformedXML</Code> ... </Error>
1 parent a071aa0 commit 55ca5dc

2 files changed

Lines changed: 46 additions & 9 deletions

File tree

pkg/lifecycle/lifecycle.go

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,10 +267,6 @@ func (f Filter) MarshalJSON() ([]byte, error) {
267267
// MarshalXML - produces the xml representation of the Filter struct
268268
// only one of Prefix, And and Tag should be present in the output.
269269
func (f Filter) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
270-
if f.IsNull() {
271-
return nil
272-
}
273-
274270
if err := e.EncodeToken(start); err != nil {
275271
return err
276272
}
@@ -508,6 +504,50 @@ func (r Rule) MarshalJSON() ([]byte, error) {
508504
return json.Marshal(newr)
509505
}
510506

507+
// MarshalXML customizes marshal XML and ensures that <Filter/> is present if Prefix is empty.
508+
func (r Rule) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
509+
// If both Filter and Prefix are empty, we need to emit an empty <Filter/> element.
510+
if r.RuleFilter.IsNull() {
511+
type ruleWrapper struct {
512+
XMLName xml.Name `xml:"Rule,omitempty"`
513+
AbortIncompleteMultipartUpload AbortIncompleteMultipartUpload `xml:"AbortIncompleteMultipartUpload,omitempty"`
514+
Expiration Expiration `xml:"Expiration,omitempty"`
515+
DelMarkerExpiration DelMarkerExpiration `xml:"DelMarkerExpiration,omitempty"`
516+
AllVersionsExpiration AllVersionsExpiration `xml:"AllVersionsExpiration,omitempty"`
517+
ID string `xml:"ID"`
518+
RuleFilter *Filter `xml:"Filter,omitempty"`
519+
NoncurrentVersionExpiration NoncurrentVersionExpiration `xml:"NoncurrentVersionExpiration,omitempty"`
520+
NoncurrentVersionTransition NoncurrentVersionTransition `xml:"NoncurrentVersionTransition,omitempty"`
521+
Prefix string `xml:"Prefix,omitempty"`
522+
Status string `xml:"Status"`
523+
Transition Transition `xml:"Transition,omitempty"`
524+
}
525+
526+
w := ruleWrapper{
527+
AbortIncompleteMultipartUpload: r.AbortIncompleteMultipartUpload,
528+
Expiration: r.Expiration,
529+
DelMarkerExpiration: r.DelMarkerExpiration,
530+
AllVersionsExpiration: r.AllVersionsExpiration,
531+
ID: r.ID,
532+
NoncurrentVersionExpiration: r.NoncurrentVersionExpiration,
533+
NoncurrentVersionTransition: r.NoncurrentVersionTransition,
534+
Prefix: r.Prefix,
535+
Status: r.Status,
536+
Transition: r.Transition,
537+
}
538+
if r.Prefix == "" {
539+
// will be explicitly marshaled as empty <Filter/>
540+
w.RuleFilter = &r.RuleFilter
541+
}
542+
543+
return e.EncodeElement(w, start)
544+
}
545+
546+
// Default XML marshal for Rule (uses struct tags as defined).
547+
type ruleAlias Rule
548+
return e.EncodeElement(ruleAlias(r), start)
549+
}
550+
511551
// Rule represents a single rule in lifecycle configuration
512552
type Rule struct {
513553
XMLName xml.Name `xml:"Rule,omitempty" json:"-"`

pkg/lifecycle/lifecycle_test.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,6 @@ func TestLifecycleMarshalXML(t *testing.T) {
518518
input: Configuration{
519519
Rules: []Rule{
520520
{
521-
522521
ID: "expire-incomplete-uploads-1",
523522
Status: "Enabled",
524523
Prefix: "my_dir",
@@ -543,8 +542,7 @@ func TestLifecycleMarshalXML(t *testing.T) {
543542
},
544543
},
545544
},
546-
// this is wrong fixed behavior fixed in a following commit
547-
expectedXMLOut: "<LifecycleConfiguration><Rule><AbortIncompleteMultipartUpload><DaysAfterInitiation>1</DaysAfterInitiation></AbortIncompleteMultipartUpload><ID>expire-incomplete-uploads-2</ID><Status>Enabled</Status></Rule></LifecycleConfiguration>",
545+
expectedXMLOut: "<LifecycleConfiguration><Rule><AbortIncompleteMultipartUpload><DaysAfterInitiation>1</DaysAfterInitiation></AbortIncompleteMultipartUpload><ID>expire-incomplete-uploads-2</ID><Filter><Prefix></Prefix></Filter><Status>Enabled</Status></Rule></LifecycleConfiguration>",
548546
},
549547
{
550548
testDescription: "Ensure we always export Filter or Prefix. Specification explicitly mentions: 'Filter is required if the LifecycleRule does not contain a Prefix element.' (https://docs.aws.amazon.com/AmazonS3/latest/API/API_LifecycleRule.html)",
@@ -559,8 +557,7 @@ func TestLifecycleMarshalXML(t *testing.T) {
559557
},
560558
},
561559
},
562-
// this is wrong fixed behavior fixed in a following commit
563-
expectedXMLOut: "<LifecycleConfiguration><Rule><AbortIncompleteMultipartUpload><DaysAfterInitiation>1</DaysAfterInitiation></AbortIncompleteMultipartUpload><ID>expire-incomplete-uploads-3</ID><Status>Enabled</Status></Rule></LifecycleConfiguration>",
560+
expectedXMLOut: "<LifecycleConfiguration><Rule><AbortIncompleteMultipartUpload><DaysAfterInitiation>1</DaysAfterInitiation></AbortIncompleteMultipartUpload><ID>expire-incomplete-uploads-3</ID><Filter><Prefix></Prefix></Filter><Status>Enabled</Status></Rule></LifecycleConfiguration>",
564561
},
565562
}
566563

0 commit comments

Comments
 (0)