s3api: preserve lifecycle config responses for Terraform (#8805)

* s3api: preserve lifecycle configs for terraform

* s3api: bound lifecycle config request bodies

* s3api: make bucket config updates copy-on-write

* s3api: tighten string slice cloning
This commit is contained in:
Chris Lu
2026-03-27 22:50:02 -07:00
committed by GitHub
parent 0adb78bc6b
commit e3f052cd84
8 changed files with 475 additions and 18 deletions

View File

@@ -7,6 +7,7 @@ import (
"encoding/xml"
"errors"
"fmt"
"io"
"math"
"net/http"
"sort"
@@ -815,6 +816,14 @@ func (s3a *S3ApiServer) GetBucketLifecycleConfigurationHandler(w http.ResponseWr
s3err.WriteErrorResponse(w, r, err)
return
}
if lifecycleXML, transitionMinimumObjectSize, found, errCode := s3a.getStoredBucketLifecycleConfiguration(bucket); errCode != s3err.ErrNone {
s3err.WriteErrorResponse(w, r, errCode)
return
} else if found {
w.Header().Set(bucketLifecycleTransitionMinimumObjectSizeHeader, transitionMinimumObjectSize)
writeSuccessResponseXMLBytes(w, r, lifecycleXML)
return
}
// ReadFilerConfFromFilers provides multi-filer failover
fc, err := filer.ReadFilerConfFromFilers(s3a.option.Filers, s3a.option.GrpcDialOption, nil)
if err != nil {
@@ -855,6 +864,9 @@ func (s3a *S3ApiServer) GetBucketLifecycleConfigurationHandler(w http.ResponseWr
})
}
if len(response.Rules) > 0 {
w.Header().Set(bucketLifecycleTransitionMinimumObjectSizeHeader, defaultLifecycleTransitionMinimumObjectSize)
}
writeSuccessResponseXML(w, r, response)
}
@@ -892,8 +904,21 @@ func (s3a *S3ApiServer) PutBucketLifecycleConfigurationHandler(w http.ResponseWr
return
}
r.Body = http.MaxBytesReader(w, r.Body, maxBucketLifecycleConfigurationSize)
lifecycleXML, err := io.ReadAll(r.Body)
if err != nil {
glog.Warningf("PutBucketLifecycleConfigurationHandler read body: %s", err)
var maxBytesErr *http.MaxBytesError
if errors.As(err, &maxBytesErr) {
s3err.WriteErrorResponse(w, r, s3err.ErrEntityTooLarge)
return
}
s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
return
}
lifeCycleConfig := Lifecycle{}
if err := xmlDecoder(r.Body, &lifeCycleConfig, r.ContentLength); err != nil {
if err := xmlDecoder(bytes.NewReader(lifecycleXML), &lifeCycleConfig, int64(len(lifecycleXML))); err != nil {
glog.Warningf("PutBucketLifecycleConfigurationHandler xml decode: %s", err)
s3err.WriteErrorResponse(w, r, s3err.ErrMalformedXML)
return
@@ -988,6 +1013,11 @@ func (s3a *S3ApiServer) PutBucketLifecycleConfigurationHandler(w http.ResponseWr
}
}
if errCode := s3a.storeBucketLifecycleConfiguration(bucket, lifecycleXML, r.Header.Get(bucketLifecycleTransitionMinimumObjectSizeHeader)); errCode != s3err.ErrNone {
s3err.WriteErrorResponse(w, r, errCode)
return
}
writeSuccessResponseEmpty(w, r)
}
@@ -1038,6 +1068,11 @@ func (s3a *S3ApiServer) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *h
}
}
if errCode := s3a.clearStoredBucketLifecycleConfiguration(bucket); errCode != s3err.ErrNone {
s3err.WriteErrorResponse(w, r, errCode)
return
}
s3err.WriteEmptyResponse(w, r, http.StatusNoContent)
}