From 8e860f7d899fb92b270c69fbfc59b34ccb5df975 Mon Sep 17 00:00:00 2001 From: Georg Friedrich Date: Tue, 20 Aug 2019 19:41:12 +0200 Subject: [PATCH] Invert workflow when deleting service instances Resolves #262 --- ...ploymentDeleteServiceInstanceWorkflow.java | 4 +- .../cloudfoundry/CloudFoundryAppDeployer.java | 28 ++++-- .../CloudFoundryAppDeployerTest.java | 98 +++++++++++++++++++ 3 files changed, 121 insertions(+), 9 deletions(-) diff --git a/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/workflow/instance/AppDeploymentDeleteServiceInstanceWorkflow.java b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/workflow/instance/AppDeploymentDeleteServiceInstanceWorkflow.java index b58fb02..7b20d9c 100644 --- a/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/workflow/instance/AppDeploymentDeleteServiceInstanceWorkflow.java +++ b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/workflow/instance/AppDeploymentDeleteServiceInstanceWorkflow.java @@ -58,8 +58,8 @@ public class AppDeploymentDeleteServiceInstanceWorkflow @Override public Mono delete(DeleteServiceInstanceRequest request, DeleteServiceInstanceResponse response) { - return deleteBackingServices(request) - .thenMany(undeployBackingApplications(request)) + return undeployBackingApplications(request) + .thenMany(deleteBackingServices(request)) .then(); } diff --git a/spring-cloud-app-broker-deployer-cloudfoundry/src/main/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryAppDeployer.java b/spring-cloud-app-broker-deployer-cloudfoundry/src/main/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryAppDeployer.java index 171f45e..eb02d04 100644 --- a/spring-cloud-app-broker-deployer-cloudfoundry/src/main/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryAppDeployer.java +++ b/spring-cloud-app-broker-deployer-cloudfoundry/src/main/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryAppDeployer.java @@ -872,13 +872,27 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware @Override public Mono deleteServiceInstance(DeleteServiceInstanceRequest request) { - return operationsUtils.getOperations(request.getProperties()) - .flatMap(cfOperations -> Mono.just(request.getServiceInstanceName()) - .flatMap(serviceInstanceName -> unbindServiceInstance(serviceInstanceName, cfOperations) - .then(deleteServiceInstance(serviceInstanceName, cfOperations) - .thenReturn(DeleteServiceInstanceResponse.builder() - .name(serviceInstanceName) - .build())))); + String serviceInstanceName = request.getServiceInstanceName(); + Map deploymentProperties = request.getProperties(); + + Mono requestDeleteServiceInstance; + if (deploymentProperties.containsKey(DeploymentProperties.TARGET_PROPERTY_KEY)) { + String space = deploymentProperties.get(DeploymentProperties.TARGET_PROPERTY_KEY); + requestDeleteServiceInstance = operationsUtils.getOperations(deploymentProperties) + .flatMap(cfOperations -> unbindServiceInstance(serviceInstanceName, cfOperations) + .then(deleteServiceInstance(serviceInstanceName, cfOperations))) + .then(deleteSpace(space)); + } else { + requestDeleteServiceInstance = unbindServiceInstance(serviceInstanceName, operations) + .then(deleteServiceInstance(serviceInstanceName, operations)); + } + + return requestDeleteServiceInstance + .doOnSuccess(v -> logger.info("Successfully deleted service instance {}", serviceInstanceName)) + .doOnError(logError(String.format("Failed to delete service instance %s", serviceInstanceName))) + .thenReturn(DeleteServiceInstanceResponse.builder() + .name(serviceInstanceName) + .build()); } private Mono deleteServiceInstance(String serviceInstanceName, CloudFoundryOperations cloudFoundryOperations) { diff --git a/spring-cloud-app-broker-deployer-cloudfoundry/src/test/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryAppDeployerTest.java b/spring-cloud-app-broker-deployer-cloudfoundry/src/test/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryAppDeployerTest.java index d827d27..a9906c2 100644 --- a/spring-cloud-app-broker-deployer-cloudfoundry/src/test/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryAppDeployerTest.java +++ b/spring-cloud-app-broker-deployer-cloudfoundry/src/test/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryAppDeployerTest.java @@ -21,17 +21,21 @@ import java.util.ArrayList; import java.util.Map; import org.cloudfoundry.client.CloudFoundryClient; +import org.cloudfoundry.client.v2.Metadata; import org.cloudfoundry.client.v2.organizations.GetOrganizationRequest; import org.cloudfoundry.client.v2.organizations.GetOrganizationResponse; import org.cloudfoundry.client.v2.organizations.ListOrganizationSpacesRequest; +import org.cloudfoundry.client.v2.organizations.ListOrganizationSpacesResponse; import org.cloudfoundry.client.v2.organizations.OrganizationEntity; import org.cloudfoundry.client.v2.serviceinstances.GetServiceInstanceResponse; import org.cloudfoundry.client.v2.serviceinstances.ServiceInstanceEntity; import org.cloudfoundry.client.v2.serviceinstances.ServiceInstances; import org.cloudfoundry.client.v2.spaces.CreateSpaceRequest; +import org.cloudfoundry.client.v2.spaces.DeleteSpaceRequest; import org.cloudfoundry.client.v2.spaces.GetSpaceRequest; import org.cloudfoundry.client.v2.spaces.GetSpaceResponse; import org.cloudfoundry.client.v2.spaces.SpaceEntity; +import org.cloudfoundry.client.v2.spaces.SpaceResource; import org.cloudfoundry.operations.CloudFoundryOperations; import org.cloudfoundry.operations.applications.ApplicationHealthCheck; import org.cloudfoundry.operations.applications.ApplicationManifest; @@ -422,6 +426,100 @@ class CloudFoundryAppDeployerTest { } + @Test + void deleteServiceInstanceWithTarget() { + when(operationsServices.deleteInstance( + org.cloudfoundry.operations.services.DeleteServiceInstanceRequest.builder() + .name("service-instance-name") + .build())) + .thenReturn(Mono.empty()); + + when(operationsServices.getInstance(GetServiceInstanceRequest.builder().name("service-instance-name").build())) + .thenReturn(Mono.just(ServiceInstance.builder() + .id("siid") + .type(ServiceInstanceType.MANAGED) + .name("service-instance-name") + .applications("app1", "app2") + .build())); + + when(operationsServices.unbind(UnbindServiceInstanceRequest.builder() + .serviceInstanceName("service-instance-name") + .applicationName("app1") + .build())) + .thenReturn(Mono.empty()); + + when(operationsServices.unbind(UnbindServiceInstanceRequest.builder() + .serviceInstanceName("service-instance-name") + .applicationName("app2") + .build())) + .thenReturn(Mono.empty()); + + when(operationsOrganizations + .get( + OrganizationInfoRequest + .builder() + .name("default-org") + .build())) + .thenReturn(Mono.just( + OrganizationDetail + .builder() + .id("default-org-id") + .name("default-org") + .quota(OrganizationQuota + .builder() + .id("quota-id") + .instanceMemoryLimit(0) + .organizationId("default-org-id") + .name("quota") + .paidServicePlans(false) + .totalMemoryLimit(0) + .totalRoutes(0) + .totalServiceInstances(0) + .build()) + .build())); + + when(clientOrganizations + .listSpaces(ListOrganizationSpacesRequest + .builder() + .name("service-instance-id") + .organizationId("default-org-id") + .page(1) + .build())) + .thenReturn(Mono.just(ListOrganizationSpacesResponse + .builder() + .resource(SpaceResource + .builder() + .entity(SpaceEntity + .builder() + .name("service-instance-id") + .build()) + .metadata(Metadata + .builder() + .id("service-instance-space-id") + .build()) + .build()) + .build())); + + when(clientSpaces + .delete(DeleteSpaceRequest + .builder() + .spaceId("service-instance-space-id") + .build())) + .thenReturn(Mono.empty()); + + DeleteServiceInstanceRequest request = + DeleteServiceInstanceRequest.builder() + .serviceInstanceName("service-instance-name") + .properties(emptyMap()) + .properties(singletonMap(TARGET_PROPERTY_KEY, "service-instance-id")) + .build(); + + StepVerifier.create( + appDeployer.deleteServiceInstance(request)) + .assertNext(response -> assertThat(response.getName()).isEqualTo("service-instance-name")) + .verifyComplete(); + } + @Test void createServiceInstance() { when(operationsServices.createInstance(