From 18d7a6f37a954073b7dafc601225a793d6cb6d40 Mon Sep 17 00:00:00 2001 From: Kiril Kabakchiev Date: Wed, 15 Apr 2020 16:57:08 +0300 Subject: [PATCH] Add flag to transfer API/disable delete across platforms (#490) --- Gopkg.lock | 1 + api/api.go | 32 +- api/api_test.go | 3 +- api/filters/check_instance_transfer_filter.go | 16 +- .../platform_id_instance_validation_filter.go | 12 +- .../smaap_service_instance_interceptor.go | 4 + test/common/test_context.go | 47 +- .../poll_instance_last_operation_test.go | 27 +- test/security_test/security_test.go | 2 +- .../service_instance_test.go | 607 ++++++++++-------- test/storage_test/storage_test.go | 36 +- 11 files changed, 469 insertions(+), 318 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index c0f588b65..fe8f91acf 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -742,6 +742,7 @@ "github.com/jmoiron/sqlx/types", "github.com/kubernetes-sigs/go-open-service-broker-client/v2", "github.com/lib/pq", + "github.com/mitchellh/mapstructure", "github.com/onrik/logrus/filename", "github.com/onsi/ginkgo", "github.com/onsi/ginkgo/extensions/table", diff --git a/api/api.go b/api/api.go index c610b9d73..919673f4d 100644 --- a/api/api.go +++ b/api/api.go @@ -49,25 +49,27 @@ const osbVersion = "2.13" // Settings type to be loaded from the environment type Settings struct { - TokenIssuerURL string `mapstructure:"token_issuer_url" description:"url of the token issuer which to use for validating tokens"` - ClientID string `mapstructure:"client_id" description:"id of the client from which the token must be issued"` - TokenBasicAuth bool `mapstructure:"token_basic_auth" description:"specifies if client credentials to the authorization server should be sent in the header as basic auth (true) or in the body (false)"` - ProtectedLabels []string `mapstructure:"protected_labels" description:"defines labels which cannot be modified/added by REST API requests"` - OSBVersion string `mapstructure:"-"` - MaxPageSize int `mapstructure:"max_page_size" description:"maximum number of items that could be returned in a single page"` - DefaultPageSize int `mapstructure:"default_page_size" description:"default number of items returned in a single page if not specified in request"` + TokenIssuerURL string `mapstructure:"token_issuer_url" description:"url of the token issuer which to use for validating tokens"` + ClientID string `mapstructure:"client_id" description:"id of the client from which the token must be issued"` + TokenBasicAuth bool `mapstructure:"token_basic_auth" description:"specifies if client credentials to the authorization server should be sent in the header as basic auth (true) or in the body (false)"` + ProtectedLabels []string `mapstructure:"protected_labels" description:"defines labels which cannot be modified/added by REST API requests"` + OSBVersion string `mapstructure:"-"` + MaxPageSize int `mapstructure:"max_page_size" description:"maximum number of items that could be returned in a single page"` + DefaultPageSize int `mapstructure:"default_page_size" description:"default number of items returned in a single page if not specified in request"` + EnableInstanceTransfer bool `mapstructure:"enable_instance_transfer" description:"whether service instance transfer is enabled or not"` } // DefaultSettings returns default values for API settings func DefaultSettings() *Settings { return &Settings{ - TokenIssuerURL: "", - ClientID: "", - TokenBasicAuth: true, // RFC 6749 section 2.3.1 - OSBVersion: osbVersion, - MaxPageSize: 200, - DefaultPageSize: 50, - ProtectedLabels: []string{}, + TokenIssuerURL: "", + ClientID: "", + TokenBasicAuth: true, // RFC 6749 section 2.3.1 + ProtectedLabels: []string{}, + OSBVersion: osbVersion, + MaxPageSize: 200, + DefaultPageSize: 50, + EnableInstanceTransfer: false, } } @@ -148,7 +150,7 @@ func New(ctx context.Context, e env.Environment, options *Options) (*web.API, er filters.NewPlansFilterByVisibility(options.Repository), filters.NewServicesFilterByVisibility(options.Repository), &filters.CheckBrokerCredentialsFilter{}, - filters.NewServiceInstanceTransferFilter(options.Repository), + filters.NewServiceInstanceTransferFilter(options.Repository, options.APISettings.EnableInstanceTransfer), }, Registry: health.NewDefaultRegistry(), }, nil diff --git a/api/api_test.go b/api/api_test.go index 582ffea7b..ab4248cb5 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -18,9 +18,10 @@ package api_test import ( "context" - "github.com/Peripli/service-manager/operations" "testing" + "github.com/Peripli/service-manager/operations" + "github.com/Peripli/service-manager/pkg/env/envfakes" "github.com/Peripli/service-manager/storage" diff --git a/api/filters/check_instance_transfer_filter.go b/api/filters/check_instance_transfer_filter.go index 4dc8a9c95..8adec0ee1 100644 --- a/api/filters/check_instance_transfer_filter.go +++ b/api/filters/check_instance_transfer_filter.go @@ -33,12 +33,14 @@ import ( const ServiceInstanceTransferFilterName = "ServiceInstanceTransferFilter" type serviceInstanceTransferFilter struct { - repository storage.Repository + repository storage.Repository + enableInstanceTransfer bool } -func NewServiceInstanceTransferFilter(repository storage.Repository) *serviceInstanceTransferFilter { +func NewServiceInstanceTransferFilter(repository storage.Repository, enableInstanceTransfer bool) *serviceInstanceTransferFilter { return &serviceInstanceTransferFilter{ - repository: repository, + repository: repository, + enableInstanceTransfer: enableInstanceTransfer, } } @@ -54,6 +56,14 @@ func (f *serviceInstanceTransferFilter) Run(req *web.Request, next web.Handler) return next.Handle(req) } + if !f.enableInstanceTransfer { + return nil, &util.HTTPError{ + ErrorType: "TransferDisabled", + Description: "Instance transfer is disabled in this service-manager installation", + StatusCode: http.StatusBadRequest, + } + } + instanceID := req.PathParams[web.PathParamResourceID] if instanceID == "" { return next.Handle(req) diff --git a/api/filters/platform_id_instance_validation_filter.go b/api/filters/platform_id_instance_validation_filter.go index 49e91b40c..5741b02f8 100644 --- a/api/filters/platform_id_instance_validation_filter.go +++ b/api/filters/platform_id_instance_validation_filter.go @@ -57,13 +57,6 @@ func (*PlatformIDInstanceValidationFilter) Run(req *web.Request, next web.Handle var err error switch req.Request.Method { case http.MethodPost: - if platformID != "" && platformID != types.SMPlatform { - return nil, &util.HTTPError{ - ErrorType: "BadRequest", - Description: fmt.Sprintf("Providing %s property during provisioning/updating with a value different from %s is forbidden", platformIDProperty, types.SMPlatform), - StatusCode: http.StatusBadRequest, - } - } if platformID == "" { req.Body, err = sjson.SetBytes(req.Body, platformIDProperty, types.SMPlatform) if err != nil { @@ -71,6 +64,9 @@ func (*PlatformIDInstanceValidationFilter) Run(req *web.Request, next web.Handle } } case http.MethodPatch: + fallthrough + //TODO delete case remove when deletion of instances from all platforms is enabled + case http.MethodDelete: // we don't want to explicitly add SMPlatform criteria for patch if instance is being migrated to SM if platformID != types.SMPlatform { byPlatformID := query.ByField(query.EqualsOperator, platformIDProperty, types.SMPlatform) @@ -80,8 +76,6 @@ func (*PlatformIDInstanceValidationFilter) Run(req *web.Request, next web.Handle } req.Request = req.WithContext(ctx) } - case http.MethodDelete: - // we don't want to explicitly add SMPlatform criteria for delete - this allows deleting instances from other platforms that are unreachable } return next.Handle(req) diff --git a/storage/interceptors/smaap_service_instance_interceptor.go b/storage/interceptors/smaap_service_instance_interceptor.go index 9f803cff1..f7d8cff2b 100644 --- a/storage/interceptors/smaap_service_instance_interceptor.go +++ b/storage/interceptors/smaap_service_instance_interceptor.go @@ -338,6 +338,10 @@ func (i *ServiceInstanceInterceptor) AroundTxDelete(f storage.InterceptDeleteAro if instances.Len() != 0 { instance := instances.ItemAt(0).(*types.ServiceInstance) + //TODO remove when deletion of instances from all platforms is enabled + if instance.PlatformID != types.SMPlatform { + return f(ctx, deletionCriteria...) + } operation, found := opcontext.Get(ctx) if !found { return fmt.Errorf("operation missing from context") diff --git a/test/common/test_context.go b/test/common/test_context.go index f24aafdd5..ad0f51ca2 100644 --- a/test/common/test_context.go +++ b/test/common/test_context.go @@ -32,6 +32,8 @@ import ( "sync" "time" + "github.com/mitchellh/mapstructure" + "github.com/Peripli/service-manager/test/tls_settings" "github.com/tidwall/gjson" "golang.org/x/crypto/bcrypt" @@ -74,6 +76,7 @@ type TestContextBuilder struct { tenantTokenClaims map[string]interface{} shouldSkipBasicAuthClient bool + basicAuthPlatformName string Environment func(f ...func(set *pflag.FlagSet)) env.Environment Servers map[string]FakeServer @@ -386,6 +389,12 @@ func TestEnv(additionalFlagFuncs ...func(set *pflag.FlagSet)) env.Environment { return env } +func (tcb *TestContextBuilder) WithBasicAuthPlatformName(name string) *TestContextBuilder { + tcb.basicAuthPlatformName = name + + return tcb +} + func (tcb *TestContextBuilder) SkipBasicAuthClientSetup(shouldSkip bool) *TestContextBuilder { tcb.shouldSkipBasicAuthClient = shouldSkip @@ -449,6 +458,10 @@ func (tcb *TestContextBuilder) Build() *TestContext { return tcb.BuildWithListener(nil, true) } +func (tcb *TestContextBuilder) BuildWithCleanup(shouldCleanup bool) *TestContext { + return tcb.BuildWithListener(nil, shouldCleanup) +} + func (tcb *TestContextBuilder) BuildWithoutCleanup() *TestContext { return tcb.SkipBasicAuthClientSetup(true).BuildWithListener(nil, false) } @@ -508,8 +521,10 @@ func (tcb *TestContextBuilder) BuildWithListener(listener net.Listener, cleanup } if !tcb.shouldSkipBasicAuthClient { - platformJSON := MakePlatform("tcb-platform-test", "tcb-platform-test", "platform-type", "test-platform") - platform := RegisterPlatformInSM(platformJSON, testContext.SMWithOAuth, map[string]string{}) + platform, err := tcb.prepareTestPlatform(testContext.SMWithOAuth) + if err != nil { + panic(err) + } SMWithBasic := SM.Builder(func(req *httpexpect.Request) { username, password := platform.Credentials.Basic.Username, platform.Credentials.Basic.Password req.WithBasicAuth(username, password).WithClient(tcb.HttpClient) @@ -521,6 +536,34 @@ func (tcb *TestContextBuilder) BuildWithListener(listener net.Listener, cleanup return testContext } +func (tcb *TestContextBuilder) prepareTestPlatform(smClient *SMExpect) (*types.Platform, error) { + if tcb.basicAuthPlatformName == "" { + tcb.basicAuthPlatformName = "basic-auth-default-test-platform" + resp := smClient.GET(web.PlatformsURL + "/" + tcb.basicAuthPlatformName).Expect() + if resp.Raw().StatusCode == http.StatusNotFound { + platformJSON := MakePlatform(tcb.basicAuthPlatformName, tcb.basicAuthPlatformName, "platform-type", "test-platform") + platform := RegisterPlatformInSM(platformJSON, smClient, map[string]string{}) + return platform, nil + } + + if resp.Raw().StatusCode != http.StatusOK { + panic(resp.Raw().Status) + } + var platform types.Platform + if err := mapstructure.Decode(resp.JSON().Object().Raw(), &platform); err != nil { + return nil, err + } + + return &platform, nil + } + + smClient.DELETE(web.PlatformsURL + "/" + tcb.basicAuthPlatformName) + platformJSON := MakePlatform(tcb.basicAuthPlatformName, tcb.basicAuthPlatformName, "platform-type", "test-platform") + platform := RegisterPlatformInSM(platformJSON, smClient, map[string]string{}) + + return platform, nil +} + func NewSMListener() (net.Listener, error) { minPort := 8100 maxPort := 9999 diff --git a/test/osb_test/poll_instance_last_operation_test.go b/test/osb_test/poll_instance_last_operation_test.go index 876fcb034..89dbe9d54 100644 --- a/test/osb_test/poll_instance_last_operation_test.go +++ b/test/osb_test/poll_instance_last_operation_test.go @@ -21,8 +21,6 @@ import ( "fmt" "net/http" - "github.com/Peripli/service-manager/pkg/web" - "github.com/Peripli/service-manager/pkg/query" "github.com/Peripli/service-manager/pkg/types" . "github.com/onsi/ginkgo" @@ -128,8 +126,9 @@ var _ = Describe("Get Service Instance Last Operation", func() { brokerServer.ServiceInstanceHandler = parameterizedHandler(http.StatusOK, `{}`) By(fmt.Sprintf("Deleting instance with id %s", SID)) - ctx.SMWithOAuthForTenant.DELETE(web.ServiceInstancesURL+"/"+SID).WithQuery("async", false). - Expect().Status(http.StatusOK) + byID := query.ByField(query.EqualsOperator, "id", SID) + err := ctx.SMRepository.Delete(context.TODO(), types.ServiceInstanceType, byID) + Expect(err).ToNot(HaveOccurred()) ctx.SMWithOAuth.GET("/v1/service_instances/"+SID).WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). Expect().Status(http.StatusNotFound) @@ -360,8 +359,9 @@ var _ = Describe("Get Service Instance Last Operation", func() { brokerServer.ServiceInstanceHandler = parameterizedHandler(http.StatusOK, `{}`) By(fmt.Sprintf("Deleting instance with id %s", SID)) - ctx.SMWithOAuthForTenant.DELETE(web.ServiceInstancesURL+"/"+SID).WithQuery("async", false). - Expect().Status(http.StatusOK) + byID := query.ByField(query.EqualsOperator, "id", SID) + err := ctx.SMRepository.Delete(context.TODO(), types.ServiceInstanceType, byID) + Expect(err).ToNot(HaveOccurred()) ctx.SMWithOAuth.GET("/v1/service_instances/"+SID).WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). Expect().Status(http.StatusNotFound) @@ -504,8 +504,9 @@ var _ = Describe("Get Service Instance Last Operation", func() { brokerServer.ServiceInstanceHandler = parameterizedHandler(http.StatusOK, `{}`) By(fmt.Sprintf("Deleting instance with id %s", SID)) - ctx.SMWithOAuthForTenant.DELETE(web.ServiceInstancesURL+"/"+SID).WithQuery("async", false). - Expect().Status(http.StatusOK) + byID := query.ByField(query.EqualsOperator, "id", SID) + err := ctx.SMRepository.Delete(context.TODO(), types.ServiceInstanceType, byID) + Expect(err).ToNot(HaveOccurred()) ctx.SMWithOAuth.GET("/v1/service_instances/"+SID).WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). Expect().Status(http.StatusNotFound) @@ -583,8 +584,9 @@ var _ = Describe("Get Service Instance Last Operation", func() { brokerServer.ServiceInstanceHandler = parameterizedHandler(http.StatusOK, `{}`) By(fmt.Sprintf("Deleting instance with id %s", SID)) - ctx.SMWithOAuthForTenant.DELETE(web.ServiceInstancesURL+"/"+SID).WithQuery("async", false). - Expect().Status(http.StatusOK) + byID := query.ByField(query.EqualsOperator, "id", SID) + err := ctx.SMRepository.Delete(context.TODO(), types.ServiceInstanceType, byID) + Expect(err).ToNot(HaveOccurred()) ctx.SMWithOAuth.GET("/v1/service_instances/"+SID).WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). Expect().Status(http.StatusNotFound) @@ -677,8 +679,9 @@ var _ = Describe("Get Service Instance Last Operation", func() { brokerServer.ServiceInstanceHandler = parameterizedHandler(http.StatusOK, `{}`) By(fmt.Sprintf("Deleting instance with id %s", SID)) - ctx.SMWithOAuthForTenant.DELETE(web.ServiceInstancesURL+"/"+SID).WithQuery("async", false). - Expect().Status(http.StatusOK) + byID := query.ByField(query.EqualsOperator, "id", SID) + err := ctx.SMRepository.Delete(context.TODO(), types.ServiceInstanceType, byID) + Expect(err).ToNot(HaveOccurred()) ctx.SMWithOAuth.GET("/v1/service_instances/"+SID).WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). Expect().Status(http.StatusNotFound) diff --git a/test/security_test/security_test.go b/test/security_test/security_test.go index 9e3a42371..9b60e2fa8 100644 --- a/test/security_test/security_test.go +++ b/test/security_test/security_test.go @@ -38,7 +38,7 @@ var _ = Describe("Service Manager Security Tests", func() { ) BeforeEach(func() { - contextBuilder = common.NewTestContextBuilder() + contextBuilder = common.NewTestContextBuilder().WithBasicAuthPlatformName("security-tests-platform") }) JustBeforeEach(func() { diff --git a/test/service_instance_test/service_instance_test.go b/test/service_instance_test/service_instance_test.go index 0a5dddfcc..9ad5b3828 100644 --- a/test/service_instance_test/service_instance_test.go +++ b/test/service_instance_test/service_instance_test.go @@ -1335,10 +1335,12 @@ var _ = DescribeTestsFor(TestCase{ var serviceID string var planID string + var testCtx *TestContext BeforeEach(func() { serviceID = "" planID = "" + testCtx = ctx brokerServer.ServiceInstanceHandlerFunc(http.MethodPut, http.MethodPut, verificationHandler(map[string]string{ "context." + TenantIdentifier: TenantIDValue, }, http.StatusCreated)) @@ -1353,9 +1355,9 @@ var _ = DescribeTestsFor(TestCase{ JustBeforeEach(func() { Expect(serviceID).ToNot(BeEmpty()) Expect(planID).ToNot(BeEmpty()) - EnsurePlanVisibility(ctx.SMRepository, TenantIdentifier, ctx.TestPlatform.ID, findPlanIDForCatalogID(ctx, brokerID, serviceID, planID), TenantIDValue) + EnsurePlanVisibility(testCtx.SMRepository, TenantIdentifier, testCtx.TestPlatform.ID, findPlanIDForCatalogID(testCtx, brokerID, serviceID, planID), TenantIDValue) - ctx.SMWithBasic.PUT("/v1/osb/"+brokerID+"/v2/service_instances/"+SID). + testCtx.SMWithBasic.PUT("/v1/osb/"+brokerID+"/v2/service_instances/"+SID). WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). WithJSON(Object{ "service_id": serviceID, @@ -1366,193 +1368,223 @@ var _ = DescribeTestsFor(TestCase{ }). Expect().Status(http.StatusCreated) - ctx.SMWithOAuth.GET(web.ServiceInstancesURL + "/" + SID). + testCtx.SMWithOAuth.GET(web.ServiceInstancesURL + "/" + SID). Expect(). Status(http.StatusOK). - JSON().Object().Value("platform_id").Equal(ctx.TestPlatform.ID) + JSON().Object().Value("platform_id").Equal(testCtx.TestPlatform.ID) }) When("platform_id provided in request body", func() { - Context("which is not service-manager platform", func() { - BeforeEach(func() { - serviceID = service1CatalogID - planID = plan1CatalogID - }) + BeforeEach(func() { + serviceID = service1CatalogID + planID = plan1CatalogID + }) + When("transfer instance is disabled", func() { It("should return 400", func() { - ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). + testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). WithQuery("async", testCase.async). - WithJSON(Object{"platform_id": "another-platform-id"}). - Expect().Status(http.StatusBadRequest) + WithJSON(Object{"platform_id": "service-manager"}). + Expect().Status(http.StatusBadRequest). + JSON().Object().Value("error").Equal("TransferDisabled") - objAfterOp := VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + objAfterOp := VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: SID, Type: types.ServiceInstanceType, Ready: true, }) - objAfterOp.Value("platform_id").Equal(ctx.TestPlatform.ID) + objAfterOp.Value("platform_id").Equal(testCtx.TestPlatform.ID) }) }) - Context("which is service-manager platform", func() { - Context("when plan does not support the platform", func() { - BeforeEach(func() { - serviceID = service1CatalogID - planID = planNotSupportingSMPlatform - }) - - It("should return 400", func() { - ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). - WithQuery("async", testCase.async). - WithJSON(Object{"platform_id": types.SMPlatform}). - Expect().Status(http.StatusBadRequest). - JSON().Object().Value("error").Equal("UnsupportedPlatform") - }) + When("transfer instance is enabled", func() { + BeforeEach(func() { + testCtx = t.ContextBuilder.WithEnvPreExtensions(func(set *pflag.FlagSet) { + Expect(set.Set("api.enable_instance_transfer", "true")).ToNot(HaveOccurred()) + }).WithBasicAuthPlatformName("inner-testCtx-basic-credentials").BuildWithCleanup(false) }) - Context("when service does not support context updates", func() { - BeforeEach(func() { - serviceID = serviceNotSupportingContextUpdates - planID = plan1CatalogID - }) + AfterEach(func() { + testCtx.CleanupAll(false) + }) + Context("which is not service-manager platform", func() { It("should return 400", func() { - ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). + testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). WithQuery("async", testCase.async). - WithJSON(Object{"platform_id": types.SMPlatform}). - Expect().Status(http.StatusBadRequest). - JSON().Object().Value("error").Equal("UnsupportedContextUpdate") - }) - }) + WithJSON(Object{"platform_id": "another-platform-id"}). + Expect().Status(http.StatusBadRequest) - Context("when plan supports the platform and service supports context updates", func() { - BeforeEach(func() { - serviceID = service1CatalogID - planID = plan1CatalogID + objAfterOp := VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ + ID: SID, + Type: types.ServiceInstanceType, + Ready: true, + }) + + objAfterOp.Value("platform_id").Equal(testCtx.TestPlatform.ID) }) + }) - It("should return 2xx and allow management of the transferred instance in SMaaP but not in old platform", func() { - var bindingID string + Context("which is service-manager platform", func() { + Context("when plan does not support the platform", func() { + BeforeEach(func() { + serviceID = service1CatalogID + planID = planNotSupportingSMPlatform + }) - By("verify patch request for instance transfer to SMaaP succeeds") - resp := ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). - WithQuery("async", testCase.async). - WithJSON(Object{"platform_id": types.SMPlatform}). - Expect().Status(testCase.expectedUpdateSuccessStatusCode) + It("should return 400", func() { + testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). + WithQuery("async", testCase.async). + WithJSON(Object{"platform_id": types.SMPlatform}). + Expect().Status(http.StatusBadRequest). + JSON().Object().Value("error").Equal("UnsupportedPlatform") + }) + }) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ - Category: types.UPDATE, - State: types.SUCCEEDED, - ResourceType: types.ServiceInstanceType, - Reschedulable: false, - DeletionScheduled: false, + Context("when service does not support context updates", func() { + BeforeEach(func() { + serviceID = serviceNotSupportingContextUpdates + planID = plan1CatalogID }) - Expect(instanceID).To(Equal(SID)) + It("should return 400", func() { + testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). + WithQuery("async", testCase.async). + WithJSON(Object{"platform_id": types.SMPlatform}). + Expect().Status(http.StatusBadRequest). + JSON().Object().Value("error").Equal("UnsupportedContextUpdate") + }) + }) - objAfterOp := VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ - ID: instanceID, - Type: types.ServiceInstanceType, - Ready: true, + Context("when plan supports the platform and service supports context updates", func() { + BeforeEach(func() { + serviceID = service1CatalogID + planID = plan1CatalogID }) - By("verify instance is transferred to SMaaP") - objAfterOp.Value("platform_id").Equal(types.SMPlatform) + It("should return 2xx and allow management of the transferred instance in SMaaP but not in old platform", func() { + var bindingID string - By("verify instance updates in SMaaP still works after transfer") - resp = ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). - WithQuery("async", testCase.async). - WithJSON(Object{}). - Expect().Status(testCase.expectedUpdateSuccessStatusCode) + By("verify patch request for instance transfer to SMaaP succeeds") + resp := testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). + WithQuery("async", testCase.async). + WithJSON(Object{"platform_id": types.SMPlatform}). + Expect().Status(testCase.expectedUpdateSuccessStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ - Category: types.UPDATE, - State: types.SUCCEEDED, - ResourceType: types.ServiceInstanceType, - Reschedulable: false, - DeletionScheduled: false, - }) + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ + Category: types.UPDATE, + State: types.SUCCEEDED, + ResourceType: types.ServiceInstanceType, + Reschedulable: false, + DeletionScheduled: false, + }) - By("verify instance updates old platform does not work after transfer") - ctx.SMWithBasic.PATCH("/v1/osb/"+brokerID+"/v2/service_instances/"+SID). - WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). - WithJSON(Object{ - "service_id": service1CatalogID, - }). - Expect().Status(http.StatusNotFound) + Expect(instanceID).To(Equal(SID)) - By("verify instance binds in SMaaP still works after transfer") - resp = ctx.SMWithOAuthForTenant.POST(web.ServiceBindingsURL). - WithQuery("async", testCase.async). - WithJSON(Object{ - "name": "binding-to-transferred-instance", - "service_instance_id": SID, - }). - Expect(). - Status(testCase.expectedCreateSuccessStatusCode) - - bindingID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ - Category: types.CREATE, - State: types.SUCCEEDED, - ResourceType: types.ServiceBindingType, - Reschedulable: false, - DeletionScheduled: false, - }) + objAfterOp := VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ + ID: instanceID, + Type: types.ServiceInstanceType, + Ready: true, + }) - By("verify instance unbind in SMaaP still works after transfer") - resp = ctx.SMWithOAuthForTenant.DELETE(web.ServiceBindingsURL+"/"+bindingID). - WithQuery("async", testCase.async). - Expect(). - Status(testCase.expectedDeleteSuccessStatusCode) + By("verify instance is transferred to SMaaP") + objAfterOp.Value("platform_id").Equal(types.SMPlatform) - VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ - Category: types.DELETE, - State: types.SUCCEEDED, - ResourceType: types.ServiceBindingType, - Reschedulable: false, - DeletionScheduled: false, - }) + By("verify instance updates in SMaaP still works after transfer") + resp = testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). + WithQuery("async", testCase.async). + WithJSON(Object{}). + Expect().Status(testCase.expectedUpdateSuccessStatusCode) - VerifyResourceDoesNotExist(ctx.SMWithOAuthForTenant, ResourceExpectations{ - ID: bindingID, - Type: types.ServiceBindingType, - }) + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ + Category: types.UPDATE, + State: types.SUCCEEDED, + ResourceType: types.ServiceInstanceType, + Reschedulable: false, + DeletionScheduled: false, + }) - By("verify instance binds in old platform does not work after transfer") - ctx.SMWithBasic.PUT("/v1/osb/"+brokerID+"/v2/service_instances/"+SID+"/service_bindings/"+bindingID). - WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). - WithJSON(Object{}). - Expect().Status(http.StatusNotFound) + By("verify instance updates old platform does not work after transfer") + testCtx.SMWithBasic.PATCH("/v1/osb/"+brokerID+"/v2/service_instances/"+SID). + WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). + WithJSON(Object{ + "service_id": service1CatalogID, + }). + Expect().Status(http.StatusNotFound) + + By("verify instance binds in SMaaP still works after transfer") + resp = testCtx.SMWithOAuthForTenant.POST(web.ServiceBindingsURL). + WithQuery("async", testCase.async). + WithJSON(Object{ + "name": "binding-to-transferred-instance", + "service_instance_id": SID, + }). + Expect(). + Status(testCase.expectedCreateSuccessStatusCode) + + bindingID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ + Category: types.CREATE, + State: types.SUCCEEDED, + ResourceType: types.ServiceBindingType, + Reschedulable: false, + DeletionScheduled: false, + }) - By("verify instance unbind in old platform does not after transfer") - ctx.SMWithBasic.DELETE("/v1/osb/"+brokerID+"/v2/service_instances/"+SID+"/service_bindings/"+bindingID). - WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). - WithJSON(Object{}). - Expect().Status(http.StatusNotFound) + By("verify instance unbind in SMaaP still works after transfer") + resp = testCtx.SMWithOAuthForTenant.DELETE(web.ServiceBindingsURL+"/"+bindingID). + WithQuery("async", testCase.async). + Expect(). + Status(testCase.expectedDeleteSuccessStatusCode) - By("verify instance deprovision in old platform does not after transfer") - ctx.SMWithBasic.DELETE("/v1/osb/"+brokerID+"/v2/service_instances/"+SID). - WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). - Expect().Status(http.StatusNotFound) + VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ + Category: types.DELETE, + State: types.SUCCEEDED, + ResourceType: types.ServiceBindingType, + Reschedulable: false, + DeletionScheduled: false, + }) - By("verify instance deprovision in SMaaP still works after transfer") - resp = ctx.SMWithOAuthForTenant.DELETE(web.ServiceInstancesURL+"/"+SID). - WithQuery("async", testCase.async). - WithJSON(Object{}). - Expect().Status(testCase.expectedDeleteSuccessStatusCode) + VerifyResourceDoesNotExist(testCtx.SMWithOAuthForTenant, ResourceExpectations{ + ID: bindingID, + Type: types.ServiceBindingType, + }) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ - Category: types.DELETE, - State: types.SUCCEEDED, - ResourceType: types.ServiceInstanceType, - Reschedulable: false, - DeletionScheduled: false, - }) + By("verify instance binds in old platform does not work after transfer") + testCtx.SMWithBasic.PUT("/v1/osb/"+brokerID+"/v2/service_instances/"+SID+"/service_bindings/"+bindingID). + WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). + WithJSON(Object{}). + Expect().Status(http.StatusNotFound) + + By("verify instance unbind in old platform does not after transfer") + testCtx.SMWithBasic.DELETE("/v1/osb/"+brokerID+"/v2/service_instances/"+SID+"/service_bindings/"+bindingID). + WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). + WithJSON(Object{}). + Expect().Status(http.StatusNotFound) + + By("verify instance deprovision in old platform does not after transfer") + testCtx.SMWithBasic.DELETE("/v1/osb/"+brokerID+"/v2/service_instances/"+SID). + WithHeader(brokerAPIVersionHeaderKey, brokerAPIVersionHeaderValue). + Expect().Status(http.StatusNotFound) + + By("verify instance deprovision in SMaaP still works after transfer") + resp = testCtx.SMWithOAuthForTenant.DELETE(web.ServiceInstancesURL+"/"+SID). + WithQuery("async", testCase.async). + WithJSON(Object{}). + Expect().Status(testCase.expectedDeleteSuccessStatusCode) + + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ + Category: types.DELETE, + State: types.SUCCEEDED, + ResourceType: types.ServiceInstanceType, + Reschedulable: false, + DeletionScheduled: false, + }) - VerifyResourceDoesNotExist(ctx.SMWithOAuthForTenant, ResourceExpectations{ - ID: instanceID, - Type: types.ServiceInstanceType, + VerifyResourceDoesNotExist(testCtx.SMWithOAuthForTenant, ResourceExpectations{ + ID: instanceID, + Type: types.ServiceInstanceType, + }) }) }) }) @@ -1566,7 +1598,7 @@ var _ = DescribeTestsFor(TestCase{ }) It("returns 404", func() { - ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). + testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+SID). WithQuery("async", testCase.async). WithJSON(Object{}). Expect().Status(http.StatusNotFound) @@ -1575,11 +1607,17 @@ var _ = DescribeTestsFor(TestCase{ }) When("instance exists in service manager platform", func() { + var testCtx *TestContext + BeforeEach(func() { - EnsurePlanVisibility(ctx.SMRepository, TenantIdentifier, types.SMPlatform, postInstanceRequest["service_plan_id"].(string), TenantIDValue) - resp := createInstance(ctx.SMWithOAuthForTenant, testCase.async, testCase.expectedCreateSuccessStatusCode) + testCtx = ctx + }) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + JustBeforeEach(func() { + EnsurePlanVisibility(testCtx.SMRepository, TenantIdentifier, types.SMPlatform, postInstanceRequest["service_plan_id"].(string), TenantIDValue) + resp := createInstance(testCtx.SMWithOAuthForTenant, testCase.async, testCase.expectedCreateSuccessStatusCode) + + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.CREATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -1587,7 +1625,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -1596,7 +1634,7 @@ var _ = DescribeTestsFor(TestCase{ When("content type is not JSON", func() { It("returns 415", func() { - ctx.SMWithOAuth.PATCH(web.ServiceInstancesURL+"/"+instanceID). + testCtx.SMWithOAuth.PATCH(web.ServiceInstancesURL+"/"+instanceID). WithQuery("async", testCase.async). WithText("text"). Expect().Status(http.StatusUnsupportedMediaType). @@ -1607,7 +1645,7 @@ var _ = DescribeTestsFor(TestCase{ When("request body is not valid JSON", func() { It("returns 400", func() { - ctx.SMWithOAuth.PATCH(web.ServiceInstancesURL+"/"+instanceID). + testCtx.SMWithOAuth.PATCH(web.ServiceInstancesURL+"/"+instanceID). WithQuery("async", testCase.async). WithText("invalid json"). WithHeader("content-type", "application/json"). @@ -1622,13 +1660,13 @@ var _ = DescribeTestsFor(TestCase{ It("should not change created at", func() { createdAt := "2015-01-01T00:00:00Z" - resp := ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). + resp := testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). WithJSON(Object{"created_at": createdAt}). WithQuery("async", testCase.async). Expect(). Status(testCase.expectedUpdateSuccessStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -1636,7 +1674,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - objAfterUpdate := VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + objAfterUpdate := VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -1661,28 +1699,56 @@ var _ = DescribeTestsFor(TestCase{ ValueEqual("platform_id", types.SMPlatform) }) - Context("which is not service-manager platform", func() { + When("transfer instance is disabled", func() { It("should return 400", func() { - ctx.SMWithOAuth.PATCH(web.ServiceInstancesURL+"/"+instanceID). + testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). WithQuery("async", testCase.async). - WithJSON(Object{"platform_id": "test-platform-id"}). - Expect().Status(http.StatusBadRequest) + WithJSON(Object{"platform_id": "service-manager"}). + Expect().Status(http.StatusBadRequest). + JSON().Object().Value("error").Equal("TransferDisabled") + + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ + ID: instanceID, + Type: types.ServiceInstanceType, + Ready: true, + }) }) }) - Context("which is service-manager platform", func() { - It("should return 200", func() { - resp := ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). - WithQuery("async", testCase.async). - WithJSON(Object{"platform_id": types.SMPlatform}). - Expect().Status(testCase.expectedUpdateSuccessStatusCode) + When("transfer instance is enabled", func() { + BeforeEach(func() { + testCtx = t.ContextBuilder.WithEnvPreExtensions(func(set *pflag.FlagSet) { + Expect(set.Set("api.enable_instance_transfer", "true")).ToNot(HaveOccurred()) + }).WithBasicAuthPlatformName("inner-testtestCtx-basic-credentials").BuildWithCleanup(false) + }) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ - Category: types.UPDATE, - State: types.SUCCEEDED, - ResourceType: types.ServiceInstanceType, - Reschedulable: false, - DeletionScheduled: false, + AfterEach(func() { + testCtx.CleanupAll(false) + }) + + Context("which is not service-manager platform", func() { + It("should return 400", func() { + testCtx.SMWithOAuth.PATCH(web.ServiceInstancesURL+"/"+instanceID). + WithQuery("async", testCase.async). + WithJSON(Object{"platform_id": "test-platform-id"}). + Expect().Status(http.StatusBadRequest) + }) + }) + + Context("which is service-manager platform", func() { + It("should return 200", func() { + resp := testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). + WithQuery("async", testCase.async). + WithJSON(Object{"platform_id": types.SMPlatform}). + Expect().Status(testCase.expectedUpdateSuccessStatusCode) + + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ + Category: types.UPDATE, + State: types.SUCCEEDED, + ResourceType: types.ServiceInstanceType, + Reschedulable: false, + DeletionScheduled: false, + }) }) }) }) @@ -1693,19 +1759,19 @@ var _ = DescribeTestsFor(TestCase{ for _, prop := range []string{"name", "maintenance_info", "service_plan_id"} { updatedBrokerJSON := Object{} if prop == "service_plan_id" { - EnsurePlanVisibility(ctx.SMRepository, TenantIdentifier, types.SMPlatform, anotherServicePlanID, TenantIDValue) + EnsurePlanVisibility(testCtx.SMRepository, TenantIdentifier, types.SMPlatform, anotherServicePlanID, TenantIDValue) updatedBrokerJSON[prop] = anotherServicePlanID } else { updatedBrokerJSON[prop] = "updated-" + prop } - resp := ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). + resp := testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). WithQuery("async", testCase.async). WithJSON(updatedBrokerJSON). Expect(). Status(testCase.expectedUpdateSuccessStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -1713,7 +1779,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - objAfterUpdate := VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + objAfterUpdate := VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -1740,9 +1806,9 @@ var _ = DescribeTestsFor(TestCase{ }) It("enriches the osb context with the tenant and sm platform", func() { - ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). + testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). WithQuery("async", testCase.async). - WithJSON(Object{"platform_id": types.SMPlatform}). + WithJSON(Object{}). Expect().Status(testCase.expectedBrokerFailureStatusCode) }) }) @@ -1750,9 +1816,9 @@ var _ = DescribeTestsFor(TestCase{ Context("instance visibility", func() { When("tenant doesn't have plan visibility", func() { It("returns 404", func() { - EnsurePlanVisibilityDoesNotExist(ctx.SMRepository, TenantIdentifier, types.SMPlatform, anotherServicePlanID, TenantIDValue) + EnsurePlanVisibilityDoesNotExist(testCtx.SMRepository, TenantIdentifier, types.SMPlatform, anotherServicePlanID, TenantIDValue) - ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). + testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). WithQuery("async", testCase.async). WithJSON(Object{"service_plan_id": anotherServicePlanID}). Expect().Status(http.StatusNotFound) @@ -1761,13 +1827,13 @@ var _ = DescribeTestsFor(TestCase{ When("tenant has plan visibility", func() { It("returns success", func() { - EnsurePlanVisibility(ctx.SMRepository, TenantIdentifier, types.SMPlatform, anotherServicePlanID, TenantIDValue) - resp := ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). + EnsurePlanVisibility(testCtx.SMRepository, TenantIdentifier, types.SMPlatform, anotherServicePlanID, TenantIDValue) + resp := testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). WithQuery("async", testCase.async). WithJSON(Object{"service_plan_id": anotherServicePlanID}). Expect().Status(testCase.expectedUpdateSuccessStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -1775,7 +1841,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - objAfterUpdate := VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + objAfterUpdate := VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -1790,7 +1856,7 @@ var _ = DescribeTestsFor(TestCase{ Context("instance ownership", func() { When("tenant doesn't have ownership of instance", func() { It("returns 404", func() { - otherTenantExpect := ctx.NewTenantExpect("tenancyClient", "other-tenant") + otherTenantExpect := testCtx.NewTenantExpect("tenancyClient", "other-tenant") otherTenantExpect.PATCH(web.ServiceInstancesURL+"/"+instanceID). WithQuery("async", testCase.async). WithJSON(Object{"service_plan_id": anotherServicePlanID}). @@ -1800,12 +1866,12 @@ var _ = DescribeTestsFor(TestCase{ When("tenant has ownership of instance", func() { It("returns 200", func() { - resp := ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). + resp := testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). WithQuery("async", testCase.async). - WithJSON(Object{"platform_id": types.SMPlatform}). + WithJSON(Object{}). Expect().Status(testCase.expectedUpdateSuccessStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -1813,7 +1879,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -1827,9 +1893,9 @@ var _ = DescribeTestsFor(TestCase{ It("fails to update", func() { instance1ID := instanceID postInstanceRequest["name"] = "instance2" - resp := createInstance(ctx.SMWithOAuthForTenant, testCase.async, testCase.expectedCreateSuccessStatusCode) + resp := createInstance(testCtx.SMWithOAuthForTenant, testCase.async, testCase.expectedCreateSuccessStatusCode) - instance2ID, _ := VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instance2ID, _ := VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.CREATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -1837,18 +1903,18 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instance2ID, Type: types.ServiceInstanceType, Ready: true, }) - resp = ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instance1ID). + resp = testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instance1ID). WithQuery("async", false). WithJSON(Object{"name": "instance2"}). Expect() - VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.FAILED, ResourceType: types.ServiceInstanceType, @@ -1856,7 +1922,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - objAfterUpdate := VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + objAfterUpdate := VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instance1ID, Type: types.ServiceInstanceType, Ready: true, @@ -1869,12 +1935,12 @@ var _ = DescribeTestsFor(TestCase{ Context("different tenants", func() { It("succeeds to update", func() { - EnsurePublicPlanVisibility(ctx.SMRepository, servicePlanID) + EnsurePublicPlanVisibility(testCtx.SMRepository, servicePlanID) postInstanceRequest["name"] = "instance1" - otherTenant := ctx.NewTenantExpect("tenancyClient", "other-tenant") + otherTenant := testCtx.NewTenantExpect("tenancyClient", "other-tenant") resp := createInstance(otherTenant, testCase.async, testCase.expectedCreateSuccessStatusCode) - instance1ID, _ := VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instance1ID, _ := VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.CREATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -1889,9 +1955,9 @@ var _ = DescribeTestsFor(TestCase{ }) postInstanceRequest["name"] = "instance2" - resp = createInstance(ctx.SMWithOAuthForTenant, testCase.async, testCase.expectedCreateSuccessStatusCode) + resp = createInstance(testCtx.SMWithOAuthForTenant, testCase.async, testCase.expectedCreateSuccessStatusCode) - instance2ID, _ := VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instance2ID, _ := VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.CREATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -1899,18 +1965,18 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instance2ID, Type: types.ServiceInstanceType, Ready: true, }) - resp = ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instance2ID). + resp = testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instance2ID). WithQuery("async", testCase.async). WithJSON(Object{"name": "instance1"}). Expect().Status(testCase.expectedUpdateSuccessStatusCode) - instance2ID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instance2ID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -1918,7 +1984,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - objAfterUpdate := VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + objAfterUpdate := VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instance2ID, Type: types.ServiceInstanceType, Ready: true, @@ -1945,13 +2011,13 @@ var _ = DescribeTestsFor(TestCase{ }) It("should update it", func() { - resp := ctx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). + resp := testCtx.SMWithOAuthForTenant.PATCH(web.ServiceInstancesURL+"/"+instanceID). WithQuery("async", testCase.async). WithJSON(Object{}). Expect(). Status(testCase.expectedUpdateSuccessStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -1959,7 +2025,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - objAfterUpdate := VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + objAfterUpdate := VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -1979,10 +2045,10 @@ var _ = DescribeTestsFor(TestCase{ "context.platform": types.SMPlatform, }, http.StatusOK)) - EnsurePlanVisibility(ctx.SMRepository, TenantIdentifier, types.SMPlatform, anotherServicePlanID, TenantIDValue) + EnsurePlanVisibility(testCtx.SMRepository, TenantIdentifier, types.SMPlatform, anotherServicePlanID, TenantIDValue) patchInstanceRequest["service_plan_id"] = anotherServicePlanID - patchInstance(ctx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedUpdateSuccessStatusCode) + patchInstance(testCtx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedUpdateSuccessStatusCode) }) }) @@ -1997,22 +2063,22 @@ var _ = DescribeTestsFor(TestCase{ "context.platform": types.SMPlatform, }, http.StatusOK)) - patchInstance(ctx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedUpdateSuccessStatusCode) + patchInstance(testCtx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedUpdateSuccessStatusCode) }) }) When("an update operation is already in progress", func() { var doneChannel chan interface{} - BeforeEach(func() { + JustBeforeEach(func() { doneChannel = make(chan interface{}) brokerServer.ServiceInstanceHandlerFunc(http.MethodPatch, http.MethodPatch+"1", ParameterizedHandler(http.StatusAccepted, Object{"async": true})) brokerServer.ServiceInstanceLastOpHandlerFunc(http.MethodPatch+"1", DelayingHandler(doneChannel)) - resp := patchInstance(ctx.SMWithOAuthForTenant, true, instanceID, http.StatusAccepted) + resp := patchInstance(testCtx.SMWithOAuthForTenant, true, instanceID, http.StatusAccepted) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.IN_PROGRESS, ResourceType: types.ServiceInstanceType, @@ -2020,7 +2086,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -2032,14 +2098,14 @@ var _ = DescribeTestsFor(TestCase{ }) It("updates fail with operation in progress", func() { - patchInstance(ctx.SMWithOAuthForTenant, testCase.async, instanceID, http.StatusUnprocessableEntity) + patchInstance(testCtx.SMWithOAuthForTenant, testCase.async, instanceID, http.StatusUnprocessableEntity) }) It("deletes succeed", func() { - resp := ctx.SMWithOAuthForTenant.DELETE(web.ServiceInstancesURL+"/"+instanceID).WithQuery("async", testCase.async). + resp := testCtx.SMWithOAuthForTenant.DELETE(web.ServiceInstancesURL+"/"+instanceID).WithQuery("async", testCase.async). Expect().StatusRange(httpexpect.Status2xx) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.DELETE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -2047,7 +2113,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceDoesNotExist(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceDoesNotExist(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, }) @@ -2060,7 +2126,7 @@ var _ = DescribeTestsFor(TestCase{ }) It("update fails", func() { - patchInstance(ctx.SMWithOAuthForTenant, testCase.async, instanceID, http.StatusNotFound) + patchInstance(testCtx.SMWithOAuthForTenant, testCase.async, instanceID, http.StatusNotFound) }) }) @@ -2070,9 +2136,9 @@ var _ = DescribeTestsFor(TestCase{ }) It("stores instance as ready=true and the operation as success, non rescheduable with no deletion scheduled", func() { - resp := patchInstance(ctx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedUpdateSuccessStatusCode) + resp := patchInstance(testCtx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedUpdateSuccessStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -2080,7 +2146,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -2095,9 +2161,9 @@ var _ = DescribeTestsFor(TestCase{ }) It("polling broker last operation until operation succeeds and eventually marks operation as success", func() { - resp := patchInstance(ctx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedUpdateSuccessStatusCode) + resp := patchInstance(testCtx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedUpdateSuccessStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -2105,7 +2171,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -2113,15 +2179,13 @@ var _ = DescribeTestsFor(TestCase{ }) When("maximum polling duration is reached while polling", func() { - var newCtx *TestContext - - BeforeEach(func() { + JustBeforeEach(func() { preparePrerequisitesWithMaxPollingDuration(MaximumPollingDuration) - EnsurePlanVisibility(ctx.SMRepository, TenantIdentifier, types.SMPlatform, postInstanceRequest["service_plan_id"].(string), TenantIDValue) - resp := createInstance(ctx.SMWithOAuthForTenant, testCase.async, testCase.expectedCreateSuccessStatusCode) + EnsurePlanVisibility(testCtx.SMRepository, TenantIdentifier, types.SMPlatform, postInstanceRequest["service_plan_id"].(string), TenantIDValue) + resp := createInstance(testCtx.SMWithOAuthForTenant, testCase.async, testCase.expectedCreateSuccessStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.CREATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, @@ -2129,14 +2193,15 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, }) - newCtx = t.ContextBuilder.WithEnvPreExtensions(func(set *pflag.FlagSet) { + testCtx = t.ContextBuilder.WithEnvPreExtensions(func(set *pflag.FlagSet) { Expect(set.Set("operations.action_timeout", ((MaximumPollingDuration + 5) * time.Second).String())).ToNot(HaveOccurred()) + Expect(set.Set("api.enable_instance_transfer", "true")).ToNot(HaveOccurred()) }).BuildWithoutCleanup() brokerServer.ServiceInstanceHandlerFunc(http.MethodPatch, http.MethodPatch+"1", ParameterizedHandler(http.StatusAccepted, Object{"async": true})) @@ -2144,13 +2209,13 @@ var _ = DescribeTestsFor(TestCase{ }) AfterEach(func() { - newCtx.CleanupAll(false) + testCtx.CleanupAll(false) }) It("keeps instance as ready true and stores the operation as failed", func() { - resp := patchInstance(newCtx.SMWithOAuthForTenant, true, instanceID, http.StatusAccepted) + resp := patchInstance(testCtx.SMWithOAuthForTenant, true, instanceID, http.StatusAccepted) - instanceID, _ = VerifyOperationExists(newCtx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.FAILED, ResourceType: types.ServiceInstanceType, @@ -2158,7 +2223,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(newCtx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -2168,10 +2233,10 @@ var _ = DescribeTestsFor(TestCase{ if testCase.async { When("action timeout is reached while polling", func() { - var newCtx *TestContext + var newtestCtx *TestContext BeforeEach(func() { - newCtx = t.ContextBuilder.WithEnvPreExtensions(func(set *pflag.FlagSet) { + newtestCtx = t.ContextBuilder.WithEnvPreExtensions(func(set *pflag.FlagSet) { Expect(set.Set("operations.action_timeout", (2 * time.Second).String())).ToNot(HaveOccurred()) }).BuildWithoutCleanup() @@ -2180,13 +2245,13 @@ var _ = DescribeTestsFor(TestCase{ }) AfterEach(func() { - newCtx.CleanupAll(false) + newtestCtx.CleanupAll(false) }) It("stores instance as ready true and the operation as reschedulable in progress", func() { - resp := patchInstance(newCtx.SMWithOAuthForTenant, true, instanceID, http.StatusAccepted) + resp := patchInstance(newtestCtx.SMWithOAuthForTenant, true, instanceID, http.StatusAccepted) - instanceID, _ = VerifyOperationExists(newCtx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(newtestCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.IN_PROGRESS, ResourceType: types.ServiceInstanceType, @@ -2194,7 +2259,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(newCtx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(newtestCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -2210,16 +2275,16 @@ var _ = DescribeTestsFor(TestCase{ }) It("keeps polling and eventually updates the instance to ready true and operation to success", func() { - resp := patchInstance(ctx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedUpdateSuccessStatusCode) + resp := patchInstance(testCtx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedUpdateSuccessStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.SUCCEEDED, ResourceType: types.ServiceInstanceType, Reschedulable: false, DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -2234,9 +2299,9 @@ var _ = DescribeTestsFor(TestCase{ }) It("keeps the instance and marks the operation as failed with no deletion scheduled and not reschedulable", func() { - resp := patchInstance(ctx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedBrokerFailureStatusCode) + resp := patchInstance(testCtx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedBrokerFailureStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.FAILED, ResourceType: types.ServiceInstanceType, @@ -2244,7 +2309,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -2258,9 +2323,9 @@ var _ = DescribeTestsFor(TestCase{ }) It("stores the instance as ready true and marks the operation as reschedulable", func() { - resp := patchInstance(ctx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedBrokerFailureStatusCode) + resp := patchInstance(testCtx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedBrokerFailureStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.FAILED, ResourceType: types.ServiceInstanceType, @@ -2268,7 +2333,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -2278,15 +2343,13 @@ var _ = DescribeTestsFor(TestCase{ }) When("broker responds with error due to stopped broker", func() { - BeforeEach(func() { + It("keeps the instance and marks operation with failed", func() { brokerServer.Close() - delete(ctx.Servers, BrokerServerPrefix+brokerID) - }) + delete(testCtx.Servers, BrokerServerPrefix+brokerID) - It("keeps the instance and marks operation with failed", func() { - resp := patchInstance(ctx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedBrokerFailureStatusCode) + resp := patchInstance(testCtx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedBrokerFailureStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.FAILED, ResourceType: types.ServiceInstanceType, @@ -2294,7 +2357,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -2308,9 +2371,9 @@ var _ = DescribeTestsFor(TestCase{ }) It("keeps the instance as ready true and marks the operation as failed", func() { - resp := patchInstance(ctx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedBrokerFailureStatusCode) + resp := patchInstance(testCtx.SMWithOAuthForTenant, testCase.async, instanceID, testCase.expectedBrokerFailureStatusCode) - instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + instanceID, _ = VerifyOperationExists(testCtx, resp.Header("Location").Raw(), OperationExpectations{ Category: types.UPDATE, State: types.FAILED, ResourceType: types.ServiceInstanceType, @@ -2318,7 +2381,7 @@ var _ = DescribeTestsFor(TestCase{ DeletionScheduled: false, }) - VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + VerifyResourceExists(testCtx.SMWithOAuthForTenant, ResourceExpectations{ ID: instanceID, Type: types.ServiceInstanceType, Ready: true, @@ -2384,7 +2447,8 @@ var _ = DescribeTestsFor(TestCase{ JSON().Object().Value("platform_id").Equal(ctx.TestPlatform.ID) }) - It("is successfully deleted", func() { + //TODO enable when deletion of instances of other platforms is allowed via SM APIs + XIt("is successfully deleted", func() { requestsBeforeDeletion := len(brokerServer.ServiceInstanceEndpointRequests) resp := deleteInstance(ctx.SMWithOAuthForTenant, testCase.async, testCase.expectedDeleteSuccessStatusCode) instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ @@ -2402,6 +2466,27 @@ var _ = DescribeTestsFor(TestCase{ Expect(requestsAfterDeletion - requestsBeforeDeletion).To(Equal(1)) }) + + //TODO remove when deletion is enabled + It("cannot be deleted", func() { + expectedStatusCode := http.StatusAccepted + if !testCase.async { + expectedStatusCode = http.StatusNotFound + } + resp := deleteInstance(ctx.SMWithOAuthForTenant, testCase.async, expectedStatusCode) + instanceID, _ = VerifyOperationExists(ctx, resp.Header("Location").Raw(), OperationExpectations{ + Category: types.DELETE, + State: types.FAILED, + ResourceType: types.ServiceInstanceType, + Reschedulable: false, + DeletionScheduled: false, + }) + VerifyResourceExists(ctx.SMWithOAuthForTenant, ResourceExpectations{ + ID: instanceID, + Type: types.ServiceInstanceType, + Ready: true, + }) + }) }) When("instance exists in service manager platform", func() { diff --git a/test/storage_test/storage_test.go b/test/storage_test/storage_test.go index 2e8b318f3..17cd09d1b 100644 --- a/test/storage_test/storage_test.go +++ b/test/storage_test/storage_test.go @@ -97,11 +97,12 @@ var _ = Describe("Test", func() { byID = query.ByField(query.EqualsOperator, "id", platform.GetID()) obj, err = ctx.SMRepository.Get(context.Background(), types.PlatformType, byID) Expect(err).ShouldNot(HaveOccurred()) - Expect(obj.GetLabels()).To(Equal(types.Labels{ + + compareLabels(obj.GetLabels(), types.Labels{ label1Key: []string{label1Value1}, label2Key: []string{label2Value1}, label3Key: []string{label3Value1, label3Value2}, - })) + }) By("Does not fail if label already exists") err = ctx.SMRepository.UpdateLabels(context.Background(), types.PlatformType, platform.GetID(), types.LabelChanges{ @@ -116,11 +117,11 @@ var _ = Describe("Test", func() { byID = query.ByField(query.EqualsOperator, "id", platform.GetID()) obj, err = ctx.SMRepository.Get(context.Background(), types.PlatformType, byID) Expect(err).ShouldNot(HaveOccurred()) - Expect(obj.GetLabels()).To(Equal(types.Labels{ + compareLabels(obj.GetLabels(), types.Labels{ label1Key: []string{label1Value1}, label2Key: []string{label2Value1}, label3Key: []string{label3Value1, label3Value2}, - })) + }) By("Successfully adds new values to existing labels") err = ctx.SMRepository.UpdateLabels(context.Background(), types.PlatformType, platform.GetID(), types.LabelChanges{ @@ -139,11 +140,11 @@ var _ = Describe("Test", func() { byID = query.ByField(query.EqualsOperator, "id", platform.GetID()) obj, err = ctx.SMRepository.Get(context.Background(), types.PlatformType, byID) Expect(err).ShouldNot(HaveOccurred()) - Expect(obj.GetLabels()).To(Equal(types.Labels{ + compareLabels(obj.GetLabels(), types.Labels{ label1Key: []string{label1Value1, label1Value2}, label2Key: []string{label2Value1, label2Value2}, label3Key: []string{label3Value1, label3Value2}, - })) + }) By("Does not fail if label value already exists") err = ctx.SMRepository.UpdateLabels(context.Background(), types.PlatformType, platform.GetID(), types.LabelChanges{ @@ -162,11 +163,11 @@ var _ = Describe("Test", func() { byID = query.ByField(query.EqualsOperator, "id", platform.GetID()) obj, err = ctx.SMRepository.Get(context.Background(), types.PlatformType, byID) Expect(err).ShouldNot(HaveOccurred()) - Expect(obj.GetLabels()).To(Equal(types.Labels{ + compareLabels(obj.GetLabels(), types.Labels{ label1Key: []string{label1Value1, label1Value2}, label2Key: []string{label2Value1, label2Value2}, label3Key: []string{label3Value1, label3Value2}, - })) + }) By("Successfully removes existing values from existing labels") err = ctx.SMRepository.UpdateLabels(context.Background(), types.PlatformType, platform.GetID(), types.LabelChanges{ @@ -186,11 +187,11 @@ var _ = Describe("Test", func() { byID = query.ByField(query.EqualsOperator, "id", platform.GetID()) obj, err = ctx.SMRepository.Get(context.Background(), types.PlatformType, byID) Expect(err).ShouldNot(HaveOccurred()) - Expect(obj.GetLabels()).To(Equal(types.Labels{ + compareLabels(obj.GetLabels(), types.Labels{ label1Key: []string{label1Value1}, label2Key: []string{label2Value1}, label3Key: []string{label3Value1, label3Value2}, - })) + }) By("Does not fail if label value does not exist") err = ctx.SMRepository.UpdateLabels(context.Background(), types.PlatformType, platform.GetID(), types.LabelChanges{ @@ -210,11 +211,11 @@ var _ = Describe("Test", func() { byID = query.ByField(query.EqualsOperator, "id", platform.GetID()) obj, err = ctx.SMRepository.Get(context.Background(), types.PlatformType, byID) Expect(err).ShouldNot(HaveOccurred()) - Expect(obj.GetLabels()).To(Equal(types.Labels{ + compareLabels(obj.GetLabels(), types.Labels{ label1Key: []string{label1Value1}, label2Key: []string{label2Value1}, label3Key: []string{label3Value1, label3Value2}, - })) + }) By("Successfully removes existing labels") err = ctx.SMRepository.UpdateLabels(context.Background(), types.PlatformType, platform.GetID(), types.LabelChanges{ @@ -252,9 +253,9 @@ var _ = Describe("Test", func() { byID = query.ByField(query.EqualsOperator, "id", platform.GetID()) obj, err = ctx.SMRepository.Get(context.Background(), types.PlatformType, byID) Expect(err).ShouldNot(HaveOccurred()) - Expect(obj.GetLabels()).To(Equal(types.Labels{ + compareLabels(obj.GetLabels(), types.Labels{ label3Key: []string{label3Value1, label3Value2}, - })) + }) }) }) @@ -291,3 +292,10 @@ var _ = Describe("Test", func() { }) }) }) + +func compareLabels(actual, expected types.Labels) { + Expect(actual).To(HaveLen(len(expected))) + for labelKey, labelValues := range actual { + Expect(labelValues).To(ConsistOf(expected[labelKey])) + } +}