Simplify remote tier validation in lifecycle rule validation (#12329)

This commit is contained in:
Krishnan Parthasarathi 2021-05-19 18:51:23 -07:00 committed by GitHub
parent 2ca9c533ef
commit cfa94cc35c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 46 additions and 23 deletions

View File

@ -1814,6 +1814,8 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) {
apiErr = ErrAdminInvalidAccessKey
case auth.ErrInvalidSecretKeyLength:
apiErr = ErrAdminInvalidSecretKey
case errInvalidStorageClass:
apiErr = ErrInvalidStorageClass
// SSE errors
case errInvalidEncryptionParameters:
apiErr = ErrInvalidEncryptionParameters

View File

@ -80,7 +80,7 @@ func (api objectAPIHandlers) PutBucketLifecycleHandler(w http.ResponseWriter, r
}
// Validate the transition storage ARNs
if err = validateLifecycleTransition(ctx, bucket, bucketLifecycle); err != nil {
if err = validateTransitionTier(ctx, bucketLifecycle); err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}

View File

@ -20,6 +20,7 @@ package cmd
import (
"context"
"encoding/xml"
"errors"
"fmt"
"io"
"net/http"
@ -175,28 +176,16 @@ func initBackgroundTransition(ctx context.Context, objectAPI ObjectLayer) {
}
}
func validateLifecycleTransition(ctx context.Context, bucket string, lfc *lifecycle.Lifecycle) error {
for _, rule := range lfc.Rules {
if rule.Transition.StorageClass != "" {
err := validateTransitionDestination(rule.Transition.StorageClass)
if err != nil {
return err
}
}
}
return nil
}
var errInvalidStorageClass = errors.New("invalid storage class")
// validateTransitionDestination returns error if transition destination bucket missing or not configured
// It also returns true if transition destination is same as this server.
func validateTransitionDestination(sc string) error {
backend, err := globalTierConfigMgr.getDriver(sc)
if err != nil {
return TransitionStorageClassNotFound{}
}
_, err = backend.Get(context.Background(), "probeobject", WarmBackendGetOpts{})
if !isErrObjectNotFound(err) {
return err
func validateTransitionTier(ctx context.Context, lfc *lifecycle.Lifecycle) error {
for _, rule := range lfc.Rules {
if rule.Transition.StorageClass == "" {
continue
}
if valid := globalTierConfigMgr.IsTierValid(rule.Transition.StorageClass); !valid {
return errInvalidStorageClass
}
}
return nil
}

View File

@ -18,6 +18,8 @@
package cmd
import (
"bytes"
"context"
"net/http"
"testing"
"time"
@ -214,3 +216,33 @@ func TestObjectIsRemote(t *testing.T) {
t.Fatalf("Expected object not to be remote but got %v", got)
}
}
func TestValidateTransitionTier(t *testing.T) {
globalTierConfigMgr = NewTierConfigMgr()
testCases := []struct {
xml []byte
expectedErr error
}{
{
// non-existent storage-class
xml: []byte(`<LifecycleConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Rule><ID>rule</ID><Prefix /><Status>Enabled</Status><Transition><Days>1</Days><StorageClass>"NONEXISTENT"</StorageClass></Transition></Rule></LifecycleConfiguration>`),
expectedErr: errInvalidStorageClass,
},
{
// no transition rule
xml: []byte(`<LifecycleConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Rule><ID>rule</ID><Prefix /><Status>Enabled</Status><Expiration><Days>1</Days></Expiration></Rule></LifecycleConfiguration>`),
expectedErr: nil,
},
}
for i, tc := range testCases {
lc, err := lifecycle.ParseLifecycleConfig(bytes.NewReader(tc.xml))
if err != nil {
t.Fatalf("Test %d: Failed to parse lifecycle config %v", i+1, err)
}
err = validateTransitionTier(context.Background(), lc)
if err != tc.expectedErr {
t.Fatalf("Test %d: Expected %v but got %v", i+1, tc.expectedErr, err)
}
}
}

View File

@ -129,7 +129,7 @@ func (t *Transition) UnmarshalXML(d *xml.Decoder, startElement xml.StartElement)
return nil
}
// Validate - validates the "Expiration" element
// Validate - validates the "Transition" element
func (t Transition) Validate() error {
if !t.set {
return nil