diff --git a/cmd/api-errors.go b/cmd/api-errors.go index 35a34eea5..57d839067 100644 --- a/cmd/api-errors.go +++ b/cmd/api-errors.go @@ -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 diff --git a/cmd/bucket-lifecycle-handlers.go b/cmd/bucket-lifecycle-handlers.go index ef7f78ef3..76888f357 100644 --- a/cmd/bucket-lifecycle-handlers.go +++ b/cmd/bucket-lifecycle-handlers.go @@ -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 } diff --git a/cmd/bucket-lifecycle.go b/cmd/bucket-lifecycle.go index bdfa3d208..bac908e3e 100644 --- a/cmd/bucket-lifecycle.go +++ b/cmd/bucket-lifecycle.go @@ -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 } diff --git a/cmd/bucket-lifecycle_test.go b/cmd/bucket-lifecycle_test.go index b466afded..52b0bc641 100644 --- a/cmd/bucket-lifecycle_test.go +++ b/cmd/bucket-lifecycle_test.go @@ -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(`ruleEnabled1"NONEXISTENT"`), + expectedErr: errInvalidStorageClass, + }, + { + // no transition rule + xml: []byte(`ruleEnabled1`), + 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) + } + } +} diff --git a/pkg/bucket/lifecycle/transition.go b/pkg/bucket/lifecycle/transition.go index 96494caec..db61e13fb 100644 --- a/pkg/bucket/lifecycle/transition.go +++ b/pkg/bucket/lifecycle/transition.go @@ -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