Set target on bound services so deleteServiceInstance can find the right service

Also temporarily disable delete space on deleteServiceInstance

Connects to #413
This commit is contained in:
Yuxin Bai
2021-03-09 13:26:15 -05:00
committed by Yuxin Bai
parent 8e99069c95
commit 02696c45aa
26 changed files with 1105 additions and 187 deletions

View File

@@ -0,0 +1,177 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.appbroker.acceptance;
import java.util.Collections;
import java.util.Optional;
import org.cloudfoundry.operations.applications.ApplicationSummary;
import org.cloudfoundry.operations.services.ServiceInstance;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
import static org.assertj.core.api.Assertions.assertThat;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class UpdateInstanceWithNewServiceAndTargetAcceptanceTest extends CloudFoundryAcceptanceTest {
private static final String APP_NAME = "app-new-service";
private static final int FIRST_TEST = 1;
private static final int SECOND_TEST = 2;
private static final String SI_NAME = "si-new-service";
private static final String OLD_BACKING_SI_NAME = "backing-service-instance-old";
private static final String NEW_BACKING_SI_NAME = "backing-service-instance-new";
private static final String SUFFIX = "update-with-new-services-and-target";
private static final String APP_SERVICE_NAME = "app-service-" + SUFFIX;
private static final String BACKING_SERVICE_NAME = "backing-service-" + SUFFIX;
@Autowired
private HealthListener healthListener;
@Override
protected String testSuffix() {
return SUFFIX;
}
@Override
protected String appServiceName() {
return APP_SERVICE_NAME;
}
@Override
protected String backingServiceName() {
return BACKING_SERVICE_NAME;
}
@Test
@Tag("first")
@Order(FIRST_TEST)
@AppBrokerTestProperties({
"spring.cloud.appbroker.services[0].service-name=" + APP_SERVICE_NAME,
"spring.cloud.appbroker.services[0].plan-name=" + PLAN_NAME,
"spring.cloud.appbroker.services[0].apps[0].name=" + APP_NAME,
"spring.cloud.appbroker.services[0].apps[0].path=" + BACKING_APP_PATH,
"spring.cloud.appbroker.services[0].apps[0].services[0].service-instance-name=" + OLD_BACKING_SI_NAME,
"spring.cloud.appbroker.services[0].services[0].name=" + BACKING_SERVICE_NAME,
"spring.cloud.appbroker.services[0].services[0].plan=" + PLAN_NAME,
"spring.cloud.appbroker.services[0].services[0].service-instance-name=" + OLD_BACKING_SI_NAME,
"spring.cloud.appbroker.services[0].target.name=SpacePerServiceInstance"
})
void weCreateAService() {
// when a service instance is created
createServiceInstance(SI_NAME);
// then a backing application is deployed
String spaceName = getServiceInstanceGuid(SI_NAME);
Optional<ApplicationSummary> backingApplication = getApplicationSummary(APP_NAME, spaceName);
assertThat(backingApplication).hasValueSatisfying(app -> assertThat(app.getRunningInstances()).isEqualTo(1));
// and the services are bound to it
ServiceInstance backingServiceInstance = getBackingServiceInstance(OLD_BACKING_SI_NAME, spaceName);
assertThat(backingServiceInstance.getApplications()).contains(APP_NAME);
String path = backingApplication.get().getUrls().get(0);
healthListener.start(path);
}
@Test
@Order(SECOND_TEST)
@Tag("last")
@AppBrokerTestProperties({
"spring.cloud.appbroker.services[0].service-name=" + APP_SERVICE_NAME,
"spring.cloud.appbroker.services[0].plan-name=" + PLAN_NAME,
"spring.cloud.appbroker.services[0].apps[0].name=" + APP_NAME,
"spring.cloud.appbroker.services[0].apps[0].path=" + BACKING_APP_PATH,
"spring.cloud.appbroker.services[0].apps[0].services[0].service-instance-name=" + NEW_BACKING_SI_NAME,
"spring.cloud.appbroker.services[0].services[0].name=" + BACKING_SERVICE_NAME,
"spring.cloud.appbroker.services[0].services[0].plan=" + PLAN_NAME,
"spring.cloud.appbroker.services[0].services[0].service-instance-name=" + NEW_BACKING_SI_NAME,
"spring.cloud.appbroker.services[0].target.name=SpacePerServiceInstance"
})
void weUpdateTheServiceInstanceWithANewBackingService() {
// when the service instance is updated with a new service
updateServiceInstance(SI_NAME, Collections.emptyMap());
String spaceName = getServiceInstanceGuid(SI_NAME);
// then the backing application was updated with zero downtime
healthListener.stop();
assertThat(healthListener.getFailures()).isEqualTo(0);
assertThat(healthListener.getSuccesses()).isGreaterThan(0);
// then a backing application is re-deployed
Optional<ApplicationSummary> updatedBackingApplication = getApplicationSummary(APP_NAME, spaceName);
assertThat(updatedBackingApplication).hasValueSatisfying(app ->
assertThat(app.getRunningInstances()).isEqualTo(1));
// and the new backing service is bound to it
ServiceInstance newBackingServiceInstance = getBackingServiceInstance(NEW_BACKING_SI_NAME, spaceName);
assertThat(newBackingServiceInstance.getApplications()).contains(APP_NAME);
// and the old backing service is deleted
assertThat(listServiceInstances()).doesNotContain(OLD_BACKING_SI_NAME);
// then the service instance is deleted
deleteServiceInstance(SI_NAME);
// and the backing service is deleted
assertThat(listServiceInstances()).doesNotContain(NEW_BACKING_SI_NAME);
}
@Override
@BeforeEach
void setUp(TestInfo testInfo, BrokerProperties brokerProperties) {
if (testInfo.getTags().contains("first")) {
super.setUp(testInfo, brokerProperties);
}
else {
setUpForBrokerUpdate(brokerProperties);
}
}
@Override
@AfterEach
public void tearDown(TestInfo testInfo) {
if (testInfo.getTags().contains("last")) {
super.tearDown(testInfo);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,9 +31,11 @@ import org.springframework.cloud.appbroker.deployer.BackingAppDeploymentService;
import org.springframework.cloud.appbroker.deployer.BackingApplication;
import org.springframework.cloud.appbroker.deployer.BackingService;
import org.springframework.cloud.appbroker.deployer.BackingServicesProvisionService;
import org.springframework.cloud.appbroker.deployer.BackingSpaceManagementService;
import org.springframework.cloud.appbroker.deployer.BrokeredServices;
import org.springframework.cloud.appbroker.deployer.DefaultBackingAppDeploymentService;
import org.springframework.cloud.appbroker.deployer.DefaultBackingServicesProvisionService;
import org.springframework.cloud.appbroker.deployer.DefaultBackingSpaceManagementService;
import org.springframework.cloud.appbroker.deployer.DeployerClient;
import org.springframework.cloud.appbroker.extensions.credentials.CredentialGenerator;
import org.springframework.cloud.appbroker.extensions.credentials.CredentialProviderFactory;
@@ -324,6 +326,18 @@ public class AppBrokerAutoConfiguration {
return new DefaultBackingServicesProvisionService(deployerClient);
}
/**
* Provide a {@link BackingSpaceManagementService} bean
*
* @param deployerClient the DeployerClient bean
* @return the bean
*/
@Bean
@ConditionalOnMissingBean
public BackingSpaceManagementService backingSpaceProvisionService(DeployerClient deployerClient) {
return new DefaultBackingSpaceManagementService(deployerClient);
}
/**
* Provide a {@link CreateServiceInstanceWorkflow} bean
*
@@ -401,6 +415,7 @@ public class AppBrokerAutoConfiguration {
BrokeredServices brokeredServices, BackingAppDeploymentService backingAppDeploymentService,
BackingAppManagementService backingAppManagementService,
BackingServicesProvisionService backingServicesProvisionService,
BackingSpaceManagementService backingSpaceManagementService,
CredentialProviderService credentialProviderService, TargetService targetService) {
return new AppDeploymentDeleteServiceInstanceWorkflow(
@@ -408,6 +423,7 @@ public class AppBrokerAutoConfiguration {
backingAppDeploymentService,
backingAppManagementService,
backingServicesProvisionService,
backingSpaceManagementService,
credentialProviderService,
targetService
);

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.appbroker.deployer;
import java.util.List;
import reactor.core.publisher.Flux;
public interface BackingSpaceManagementService {
Flux<String> deleteTargetSpaces(List<String> targetSpaces);
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.appbroker.deployer;
import java.util.List;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
import reactor.util.Logger;
import reactor.util.Loggers;
public class DefaultBackingSpaceManagementService implements BackingSpaceManagementService {
private static final Logger LOG = Loggers.getLogger(DefaultBackingSpaceManagementService.class);
private static final String BACKINGSPACES_LOG_TEMPLATE = "backingSpaces={}";
private final DeployerClient deployerClient;
public DefaultBackingSpaceManagementService(DeployerClient deployerClient) {
this.deployerClient = deployerClient;
}
@Override
public Flux<String> deleteTargetSpaces(List<String> targetSpaces) {
return Flux.fromIterable(targetSpaces)
.parallel()
.runOn(Schedulers.parallel())
.flatMap(deployerClient::deleteSpace)
.sequential()
.doOnRequest(l -> {
LOG.info("Deleting backing spaces");
LOG.debug(BACKINGSPACES_LOG_TEMPLATE, targetSpaces);
})
.doOnComplete(() -> {
LOG.info("Finish deleting backing spaces");
LOG.debug(BACKINGSPACES_LOG_TEMPLATE, targetSpaces);
})
.doOnError(e -> LOG.error(String.format("Error deleting backing spaces. error=%s", e.getMessage()), e));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -204,6 +204,29 @@ public class DeployerClient {
.map(DeleteServiceInstanceResponse::getName);
}
public Mono<String> deleteSpace(String spaceName) {
return appDeployer
.deleteBackingSpace(
DeleteBackingSpaceRequest
.builder()
.name(spaceName)
.build())
.doOnRequest(l -> {
LOG.info("Deleting backing space {}", spaceName);
})
.doOnSuccess(response -> {
LOG.info("Success deleting backing space {}", spaceName);
})
.doOnError(e -> {
LOG.error(String.format("Error deleting backing space. backingSpaceName=%s, error=%s",
spaceName, e.getMessage()), e);
})
.onErrorReturn(DeleteBackingSpaceResponse.builder()
.name(spaceName)
.build())
.map(DeleteBackingSpaceResponse::getName);
}
private static UpdateApplicationRequest getUpdateApplicationRequest(BackingApplication backingApplication,
String serviceInstanceId) {
return UpdateApplicationRequest

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors
* Copyright 2002-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -275,6 +275,7 @@ public class BackingAppManagementService {
.builder()
.name(response.getName())
.services(services)
.properties(app.getProperties())
.environment(response.getEnvironment())
.build()))
.doOnRequest(l -> {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.cloud.appbroker.workflow.instance;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import reactor.core.publisher.Flux;
@@ -25,8 +26,10 @@ import reactor.util.Logger;
import reactor.util.Loggers;
import org.springframework.cloud.appbroker.deployer.BackingAppDeploymentService;
import org.springframework.cloud.appbroker.deployer.BackingApplication;
import org.springframework.cloud.appbroker.deployer.BackingService;
import org.springframework.cloud.appbroker.deployer.BackingServicesProvisionService;
import org.springframework.cloud.appbroker.deployer.BackingSpaceManagementService;
import org.springframework.cloud.appbroker.deployer.BrokeredServices;
import org.springframework.cloud.appbroker.deployer.DeploymentProperties;
import org.springframework.cloud.appbroker.extensions.credentials.CredentialProviderService;
@@ -52,21 +55,25 @@ public class AppDeploymentDeleteServiceInstanceWorkflow
private final BackingAppManagementService backingAppManagementService;
private final BackingServicesProvisionService backingServicesProvisionService;
private final BackingSpaceManagementService backingSpaceManagementService;
private final CredentialProviderService credentialProviderService;
private final TargetService targetService;
private final BackingServicesProvisionService backingServicesProvisionService;
public AppDeploymentDeleteServiceInstanceWorkflow(BrokeredServices brokeredServices,
BackingAppDeploymentService deploymentService,
BackingAppManagementService backingAppManagementService,
BackingServicesProvisionService backingServicesProvisionService,
BackingSpaceManagementService backingSpaceManagementService,
CredentialProviderService credentialProviderService,
TargetService targetService) {
super(brokeredServices);
this.deploymentService = deploymentService;
this.backingAppManagementService = backingAppManagementService;
this.backingSpaceManagementService = backingSpaceManagementService;
this.credentialProviderService = credentialProviderService;
this.targetService = targetService;
this.backingServicesProvisionService = backingServicesProvisionService;
@@ -75,36 +82,47 @@ public class AppDeploymentDeleteServiceInstanceWorkflow
@Override
public Mono<Void> delete(DeleteServiceInstanceRequest request, DeleteServiceInstanceResponse response) {
return deleteBackingServices(request)
.thenMany(undeployBackingApplications(request))
.flatMapMany(Flux::fromIterable)
.map(BackingService::getProperties)
.concatWith(undeployBackingApplications(request)
.flatMapMany(Flux::fromIterable)
.map(BackingApplication::getProperties))
.filter(properties ->
properties != null && properties.containsKey(DeploymentProperties.TARGET_PROPERTY_KEY))
.map(properties -> properties.get(DeploymentProperties.TARGET_PROPERTY_KEY))
.distinct()
.collectList()
.flatMapMany(backingSpaceManagementService::deleteTargetSpaces)
.then();
}
private Flux<String> deleteBackingServices(DeleteServiceInstanceRequest request) {
private Mono<List<BackingService>> deleteBackingServices(DeleteServiceInstanceRequest request) {
return collectBackingServices(request)
.collectList()
.flatMapMany(backingServices -> {
.delayUntil(backingServices -> {
if (!CollectionUtils.isEmpty(backingServices)) {
return backingServicesProvisionService.deleteServiceInstance(backingServices);
return backingServicesProvisionService.deleteServiceInstance(backingServices)
.doOnRequest(l -> {
LOG.info("Deleting backing services. serviceDefinitionName={}, planName={}",
request.getServiceDefinition().getName(), request.getPlan().getName());
LOG.debug(REQUEST_LOG_TEMPLATE, request);
})
.doOnComplete(() -> {
LOG.info("Finish deleting backing services. serviceDefinitionName={}, planName={}",
request.getServiceDefinition().getName(), request.getPlan().getName());
LOG.debug(REQUEST_LOG_TEMPLATE, request);
})
.doOnError(e -> {
if (LOG.isErrorEnabled()) {
LOG.error(String.format("Error deleting backing services. " +
"serviceDefinitionName=%s, planName=%s, error=%s",
request.getServiceDefinition().getName(),
request.getPlan().getName(), e.getMessage()), e);
}
LOG.debug(REQUEST_LOG_TEMPLATE, request);
});
}
return Flux.empty();
})
.doOnRequest(l -> {
LOG.info("Deleting backing services. serviceDefinitionName={}, planName={}",
request.getServiceDefinition().getName(), request.getPlan().getName());
LOG.debug(REQUEST_LOG_TEMPLATE, request);
})
.doOnComplete(() -> {
LOG.info("Finish deleting backing services. serviceDefinitionName={}, planName={}",
request.getServiceDefinition().getName(), request.getPlan().getName());
LOG.debug(REQUEST_LOG_TEMPLATE, request);
})
.doOnError(e -> {
if (LOG.isErrorEnabled()) {
LOG.error(String.format("Error deleting backing services. " +
"serviceDefinitionName=%s, planName=%s, error=%s", request.getServiceDefinition().getName(),
request.getPlan().getName(), e.getMessage()), e);
}
LOG.debug(REQUEST_LOG_TEMPLATE, request);
});
}
@@ -144,7 +162,7 @@ public class AppDeploymentDeleteServiceInstanceWorkflow
}));
}
private Flux<String> undeployBackingApplications(DeleteServiceInstanceRequest request) {
private Mono<List<BackingApplication>> undeployBackingApplications(DeleteServiceInstanceRequest request) {
return getBackingApplicationsForService(request.getServiceDefinition(), request.getPlan())
.flatMap(backingApps ->
credentialProviderService.deleteCredentials(backingApps,
@@ -153,25 +171,26 @@ public class AppDeploymentDeleteServiceInstanceWorkflow
.flatMap(targetSpec -> targetService.addToBackingApplications(backingApps, targetSpec,
request.getServiceInstanceId()))
.defaultIfEmpty(backingApps))
.flatMapMany(deploymentService::undeploy)
.doOnRequest(l -> {
LOG.info("Undeploying backing applications. serviceDefinitionName={}, planName={}",
request.getServiceDefinition().getName(), request.getPlan().getName());
LOG.debug(REQUEST_LOG_TEMPLATE, request);
})
.doOnComplete(() -> {
LOG.info("Finish undeploying backing applications. serviceDefinitionName={}, planName={}",
request.getServiceDefinition().getName(), request.getPlan().getName());
LOG.debug(REQUEST_LOG_TEMPLATE, request);
})
.doOnError(e -> {
if (LOG.isErrorEnabled()) {
LOG.error(String.format("Error undeploying backing applications. serviceDefinitionName=%s, " +
"planName=%s, error=%s", request.getServiceDefinition().getName(), request.getPlan().getName(),
e.getMessage()), e);
}
LOG.debug(REQUEST_LOG_TEMPLATE, request);
});
.delayUntil(backingApps -> deploymentService.undeploy(backingApps)
.doOnRequest(l -> {
LOG.info("Undeploying backing applications. serviceDefinitionName={}, planName={}",
request.getServiceDefinition().getName(), request.getPlan().getName());
LOG.debug(REQUEST_LOG_TEMPLATE, request);
})
.doOnComplete(() -> {
LOG.info("Finish undeploying backing applications. serviceDefinitionName={}, planName={}",
request.getServiceDefinition().getName(), request.getPlan().getName());
LOG.debug(REQUEST_LOG_TEMPLATE, request);
})
.doOnError(e -> {
if (LOG.isErrorEnabled()) {
LOG.error(String.format("Error undeploying backing applications. serviceDefinitionName=%s, " +
"planName=%s, error=%s", request.getServiceDefinition().getName(),
request.getPlan().getName(),
e.getMessage()), e);
}
LOG.debug(REQUEST_LOG_TEMPLATE, request);
}));
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.cloud.appbroker.workflow.instance;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -34,6 +35,7 @@ import org.springframework.cloud.appbroker.deployer.BackingApplication;
import org.springframework.cloud.appbroker.deployer.BackingService;
import org.springframework.cloud.appbroker.deployer.BackingServicesProvisionService;
import org.springframework.cloud.appbroker.deployer.BrokeredServices;
import org.springframework.cloud.appbroker.deployer.DeploymentProperties;
import org.springframework.cloud.appbroker.extensions.parameters.BackingApplicationsParametersTransformationService;
import org.springframework.cloud.appbroker.extensions.parameters.BackingServicesParametersTransformationService;
import org.springframework.cloud.appbroker.extensions.targets.TargetService;
@@ -178,12 +180,22 @@ public class AppDeploymentUpdateServiceInstanceWorkflow extends AppDeploymentIns
return backingAppManagementService.getDeployedBackingApplications(request.getServiceInstanceId(),
request.getServiceDefinition().getName(), request.getPlan().getName())
.flatMapMany(Flux::fromIterable)
.map(BackingApplication::getServices)
.flatMap(Flux::fromIterable)
.flatMap(backingApplication -> Flux
.fromIterable(backingApplication.getServices())
.map(servicesSpec -> {
Map<String, String> properties = null;
if (backingApplication.getProperties() != null) {
String target = backingApplication.getProperties().get(DeploymentProperties.TARGET_PROPERTY_KEY);
if (target != null) {
properties = Collections.singletonMap(DeploymentProperties.TARGET_PROPERTY_KEY, target);
}
}
return BackingService.builder()
.serviceInstanceName(servicesSpec.getServiceInstanceName())
.properties(properties)
.build();
}))
.distinct()
.map(servicesSpec -> BackingService.builder()
.serviceInstanceName(servicesSpec.getServiceInstanceName())
.build())
.collectMap(BackingService::getServiceInstanceName, Function.identity());
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.appbroker.deployer;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static java.util.Arrays.asList;
import static org.mockito.Mockito.doReturn;
@ExtendWith(MockitoExtension.class)
class DefaultBackingSpaceManagementServiceTest {
@Mock
private DeployerClient deployerClient;
private BackingSpaceManagementService backingSpaceManagementService;
@BeforeEach
void setUp() {
backingSpaceManagementService = new DefaultBackingSpaceManagementService(deployerClient);
}
@Test
@SuppressWarnings("UnassignedFluxMonoInstance")
void deleteSpace() {
final String spaceName1 = "space1";
final String spaceName2 = "space2";
doReturn(Mono.just("returned-space-1"))
.when(deployerClient).deleteSpace(spaceName1);
doReturn(Mono.just("returned-space-2"))
.when(deployerClient).deleteSpace(spaceName2);
List<String> expectedValues = new ArrayList<>();
expectedValues.add("returned-space-1");
expectedValues.add("returned-space-2");
StepVerifier.create(backingSpaceManagementService.deleteTargetSpaces(asList(spaceName1, spaceName2)))
// deployments are run in parallel, so the order of completion is not predictable
// ensure that both expected signals are sent in any order
.expectNextMatches(expectedValues::remove)
.expectNextMatches(expectedValues::remove)
.verifyComplete();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -420,4 +420,32 @@ class DeployerClientTest {
}
@Nested
class DeleteSpace {
private static final String RETURNED_SPACE_NAME = "space";
@Test
void shouldDeleteSpace() {
// given
setupAppDeployer();
// when
StepVerifier.create(deployerClient.deleteSpace("test-space"))
// then
.expectNext(RETURNED_SPACE_NAME)
.verifyComplete();
then(appDeployer).should().deleteBackingSpace(argThat(space -> "test-space".equals(space.getName())));
}
private void setupAppDeployer() {
given(appDeployer.deleteBackingSpace(any()))
.willReturn(Mono.just(DeleteBackingSpaceResponse.builder()
.name(RETURNED_SPACE_NAME)
.build()));
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors
* Copyright 2002-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
package org.springframework.cloud.appbroker.manager;
import java.util.Collections;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -68,6 +70,7 @@ class BackingAppManagementServiceTest {
this.backingApps = BackingApplications.builder()
.backingApplication(BackingApplication.builder()
.name("testApp1")
.properties(Collections.singletonMap("target", "customTarget"))
.path("https://myfiles/app1.jar")
.build())
.backingApplication(BackingApplication.builder()
@@ -344,6 +347,7 @@ class BackingAppManagementServiceTest {
.expectNext(BackingApplications.builder()
.backingApplication(BackingApplication.builder()
.name("testApp1")
.properties(Collections.singletonMap("target", "customTarget"))
.services(
ServicesSpec.builder().serviceInstanceName("service1").build(),
ServicesSpec.builder().serviceInstanceName("service2").build())

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,8 @@
package org.springframework.cloud.appbroker.workflow.instance;
import java.util.Collections;
import java.util.Arrays;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -34,6 +35,7 @@ import org.springframework.cloud.appbroker.deployer.BackingApplications;
import org.springframework.cloud.appbroker.deployer.BackingService;
import org.springframework.cloud.appbroker.deployer.BackingServices;
import org.springframework.cloud.appbroker.deployer.BackingServicesProvisionService;
import org.springframework.cloud.appbroker.deployer.BackingSpaceManagementService;
import org.springframework.cloud.appbroker.deployer.BrokeredService;
import org.springframework.cloud.appbroker.deployer.BrokeredServices;
import org.springframework.cloud.appbroker.deployer.DeploymentProperties;
@@ -48,6 +50,9 @@ import org.springframework.cloud.servicebroker.model.catalog.ServiceDefinition;
import org.springframework.cloud.servicebroker.model.instance.DeleteServiceInstanceRequest;
import org.springframework.cloud.servicebroker.model.instance.DeleteServiceInstanceResponse;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
@@ -64,15 +69,18 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
@Mock
private BackingAppManagementService backingAppManagementService;
@Mock
private BackingServicesProvisionService backingServicesProvisionService;
@Mock
private BackingSpaceManagementService backingSpaceManagementService;
@Mock
private TargetService targetService;
@Mock
private CredentialProviderService credentialProviderService;
@Mock
private BackingServicesProvisionService backingServicesProvisionService;
private BackingApplications backingApps;
private BackingServices backingServices;
@@ -116,7 +124,7 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
.name("my-service2")
.plan("a-plan2")
.serviceInstanceName("my-service-instance2")
.properties(Collections.singletonMap(DeploymentProperties.TARGET_PROPERTY_KEY, "my-space2"))
.properties(getPropertiesWithSpace("my-space2"))
.build())
.build();
@@ -153,7 +161,9 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
new AppDeploymentDeleteServiceInstanceWorkflow(
brokeredServices,
backingAppDeploymentService,
backingAppManagementService, backingServicesProvisionService,
backingAppManagementService,
backingServicesProvisionService,
backingSpaceManagementService,
credentialProviderService,
targetService
);
@@ -186,13 +196,56 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
return sizeMatch && nameMatch;
}))).willReturn(Flux.just("my-service-instance"));
given(this.backingSpaceManagementService.deleteTargetSpaces(any())).willReturn(Flux.empty());
StepVerifier
.create(deleteServiceInstanceWorkflow.delete(request, response))
.expectNext()
.expectNext()
.verifyComplete();
verify(this.backingServicesProvisionService, Mockito.times(1)).deleteServiceInstance(any());
verify(this.backingSpaceManagementService).deleteTargetSpaces(eq(emptyList()));
verifyNoMoreInteractionsWithServices();
}
@Test
void deleteBackingSpacesSpecifiedInTargetProperties() {
DeleteServiceInstanceRequest request = buildRequest("service1", "plan1");
DeleteServiceInstanceResponse response = DeleteServiceInstanceResponse.builder().build();
String space1 = "CustomSpace1";
String space2 = "CustomSpace2";
// configured backing services
BackingServices servicesWithTarget = BackingServices.builder().backingServices(backingServices).build();
servicesWithTarget.get(0).setProperties(getPropertiesWithSpace(space1));
given(this.targetService.addToBackingServices(eq(backingServices), eq(targetSpec), eq("service-instance-id")))
.willReturn(Mono.just(servicesWithTarget));
// configured backing apps
BackingApplications appsWithTarget = BackingApplications.builder().backingApplications(backingApps).build();
appsWithTarget.get(0).setProperties(getPropertiesWithSpace(space2));
given(this.targetService.addToBackingApplications(eq(backingApps), eq(targetSpec), eq("service-instance-id")))
.willReturn(Mono.just(appsWithTarget));
// services bound to deployed apps
given(this.backingAppManagementService.getDeployedBackingApplications(request.getServiceInstanceId(),
request.getServiceDefinition().getName(), request.getPlan().getName()))
.willReturn(Mono.just(getExistingBackingAppsWithService("my-service-instance")));
given(this.credentialProviderService.deleteCredentials(eq(backingApps), eq(request.getServiceInstanceId())))
.willReturn(Mono.just(backingApps));
// delete in action
given(this.backingAppDeploymentService.undeploy(eq(appsWithTarget)))
.willReturn(Flux.just("undeployed1", "undeployed2"));
given(this.backingServicesProvisionService.deleteServiceInstance(any()))
.willReturn(Flux.just("my-service-instance"));
given(this.backingSpaceManagementService.deleteTargetSpaces(any())).willReturn(Flux.just("space-name"));
StepVerifier
.create(deleteServiceInstanceWorkflow.delete(request, response))
.verifyComplete();
verify(this.backingServicesProvisionService, Mockito.times(1)).deleteServiceInstance(any());
verify(this.backingSpaceManagementService).deleteTargetSpaces(Arrays.asList(space1, space2));
verifyNoMoreInteractionsWithServices();
}
@@ -228,6 +281,9 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
return sizeMatch && (nameMatch0 && spaceMatch0 || nameMatch1 && spaceMatch1);
}))).willReturn(Flux.just("different-service-instance"));
given(this.backingSpaceManagementService.deleteTargetSpaces(eq(singletonList("my-space2"))))
.willReturn(Flux.just("space-name"));
StepVerifier.create(deleteServiceInstanceWorkflow.delete(request, response))
.expectNext()
.expectNext()
@@ -246,6 +302,8 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
request.getServiceDefinition().getName(), request.getPlan().getName()))
.willReturn(Mono.empty());
given(this.backingSpaceManagementService.deleteTargetSpaces(any())).willReturn(Flux.empty());
StepVerifier
.create(deleteServiceInstanceWorkflow.delete(request, response))
.verifyComplete();
@@ -273,6 +331,8 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
return sizeMatch && nameMatch;
}))).willReturn(Flux.just("my-service-instance"));
given(this.backingSpaceManagementService.deleteTargetSpaces(any())).willReturn(Flux.empty());
StepVerifier.create(deleteServiceInstanceWorkflow.delete(request, response))
.expectNext()
.expectNext()
@@ -285,10 +345,15 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
private void verifyNoMoreInteractionsWithServices() {
verifyNoMoreInteractions(this.backingServicesProvisionService);
verifyNoMoreInteractions(this.backingAppDeploymentService);
verifyNoMoreInteractions(this.backingSpaceManagementService);
verifyNoMoreInteractions(this.credentialProviderService);
verifyNoMoreInteractions(this.targetService);
}
private Map<String, String> getPropertiesWithSpace(String customSpace) {
return singletonMap(DeploymentProperties.TARGET_PROPERTY_KEY, customSpace);
}
private DeleteServiceInstanceRequest buildRequest(String serviceName, String planName) {
return DeleteServiceInstanceRequest
.builder()

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -207,6 +207,7 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
.builder()
.backingService(BackingService
.builder()
.properties(singletonMap("target", "customTarget"))
.serviceInstanceName("existing-service-instance")
.build())
.build());
@@ -295,11 +296,15 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
}
private BackingApplications getExistingBackingAppsWithService(String serviceInstanceName) {
Map<String, String> properties = new HashMap<>();
properties.put("target", "customTarget");
properties.put("not-important-property", "not-important-value");
return BackingApplications
.builder()
.backingApplication(BackingApplication
.builder()
.name("app1")
.properties(properties)
.services(ServicesSpec
.builder()
.serviceInstanceName(serviceInstanceName)
@@ -308,6 +313,7 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
.backingApplication(BackingApplication
.builder()
.name("app2")
.properties(properties)
.services(ServicesSpec
.builder()
.serviceInstanceName(serviceInstanceName)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -108,6 +108,8 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.appbroker.deployer.AppDeployer;
import org.springframework.cloud.appbroker.deployer.CreateServiceInstanceRequest;
import org.springframework.cloud.appbroker.deployer.CreateServiceInstanceResponse;
import org.springframework.cloud.appbroker.deployer.DeleteBackingSpaceRequest;
import org.springframework.cloud.appbroker.deployer.DeleteBackingSpaceResponse;
import org.springframework.cloud.appbroker.deployer.DeleteServiceInstanceRequest;
import org.springframework.cloud.appbroker.deployer.DeleteServiceInstanceResponse;
import org.springframework.cloud.appbroker.deployer.DeployApplicationRequest;
@@ -802,8 +804,7 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
Mono<Void> requestDeleteApplication;
if (deploymentProperties.containsKey(DeploymentProperties.TARGET_PROPERTY_KEY)) {
String space = deploymentProperties.get(DeploymentProperties.TARGET_PROPERTY_KEY);
requestDeleteApplication = deleteApplicationInSpace(appName, space)
.then(deleteSpace(space));
requestDeleteApplication = deleteApplicationInSpace(appName, space);
}
else {
requestDeleteApplication = deleteApplication(appName);
@@ -840,19 +841,21 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
.onErrorResume(e -> Mono.empty());
}
private Mono<Void> deleteSpace(String spaceName) {
@Override
public Mono<DeleteBackingSpaceResponse> deleteBackingSpace(DeleteBackingSpaceRequest request) {
String spaceName = request.getName();
return getSpaceId(spaceName)
.doOnError(e -> LOG.error(String.format("Unable to get space name. spaceName=%s, " + ERROR_LOG_TEMPLATE,
spaceName, e.getMessage()), e))
.onErrorResume(e -> Mono.empty())
.flatMap(spaceId -> this.client.spaces()
.delete(DeleteSpaceRequest.builder()
.spaceId(spaceId)
.recursive(true)
.build())
.then())
.build()))
.doOnError(e -> LOG.error(String.format("Error deleting space. spaceName=%s, " + ERROR_LOG_TEMPLATE,
spaceName, e.getMessage()), e))
.onErrorResume(e -> Mono.empty());
.thenReturn(DeleteBackingSpaceResponse.builder().name(spaceName).build());
}
private Mono<String> getSpaceId(String spaceName) {
@@ -1185,11 +1188,9 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
Mono<Void> 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, deploymentProperties)))
.then(deleteSpace(space));
.then(deleteServiceInstance(serviceInstanceName, cfOperations, deploymentProperties)));
}
else {
requestDeleteServiceInstance = unbindServiceInstance(serviceInstanceName, operations)

View File

@@ -0,0 +1,180 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.appbroker.deployer.cloudfoundry;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.client.v2.Metadata;
import org.cloudfoundry.client.v2.organizations.ListOrganizationSpacesRequest;
import org.cloudfoundry.client.v2.organizations.ListOrganizationSpacesResponse;
import org.cloudfoundry.client.v2.spaces.DeleteSpaceRequest;
import org.cloudfoundry.client.v2.spaces.SpaceEntity;
import org.cloudfoundry.client.v2.spaces.SpaceResource;
import org.cloudfoundry.operations.CloudFoundryOperations;
import org.cloudfoundry.operations.organizations.OrganizationDetail;
import org.cloudfoundry.operations.organizations.OrganizationInfoRequest;
import org.cloudfoundry.operations.organizations.OrganizationQuota;
import org.cloudfoundry.operations.organizations.Organizations;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.cloud.appbroker.deployer.AppDeployer;
import org.springframework.cloud.appbroker.deployer.DeleteBackingSpaceRequest;
import org.springframework.core.io.ResourceLoader;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@ExtendWith(MockitoExtension.class)
class CloudFoundryAppDeployerDeleteSpaceTest {
private AppDeployer appDeployer;
@Mock
private Organizations operationsOrganizations;
@Mock
private org.cloudfoundry.client.v2.spaces.Spaces clientSpaces;
@Mock
private org.cloudfoundry.client.v2.organizations.Organizations clientOrganizations;
@Mock
private CloudFoundryOperations cloudFoundryOperations;
@Mock
private CloudFoundryClient cloudFoundryClient;
@Mock
private CloudFoundryOperationsUtils operationsUtils;
@Mock
private ResourceLoader resourceLoader;
@BeforeEach
void setUp() {
CloudFoundryDeploymentProperties deploymentProperties = new CloudFoundryDeploymentProperties();
CloudFoundryTargetProperties targetProperties = new CloudFoundryTargetProperties();
targetProperties.setDefaultOrg("default-org");
targetProperties.setDefaultSpace("default-space");
given(cloudFoundryOperations.organizations()).willReturn(operationsOrganizations);
given(cloudFoundryClient.organizations()).willReturn(clientOrganizations);
given(operationsOrganizations
.get(
OrganizationInfoRequest
.builder()
.name("default-org")
.build()))
.willReturn(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()));
appDeployer = new CloudFoundryAppDeployer(deploymentProperties, cloudFoundryOperations, cloudFoundryClient,
operationsUtils, targetProperties, resourceLoader);
}
@Test
void deleteSpaceIfExists() {
given(clientOrganizations
.listSpaces(ListOrganizationSpacesRequest
.builder()
.name("test-space-name")
.organizationId("default-org-id")
.page(1)
.build()))
.willReturn(Mono.just(ListOrganizationSpacesResponse
.builder()
.resource(SpaceResource
.builder()
.entity(SpaceEntity
.builder()
.name("test-space-name")
.build())
.metadata(Metadata
.builder()
.id("test-space-id")
.build())
.build())
.build()));
given(cloudFoundryClient.spaces()).willReturn(clientSpaces);
given(clientSpaces
.delete(DeleteSpaceRequest
.builder()
.spaceId("test-space-id")
.recursive(true)
.build()))
.willReturn(Mono.empty());
DeleteBackingSpaceRequest request =
DeleteBackingSpaceRequest.builder()
.name("test-space-name")
.build();
StepVerifier.create(
appDeployer.deleteBackingSpace(request))
.assertNext(response -> assertThat(response.getName()).isEqualTo("test-space-name"))
.verifyComplete();
}
@Test
void doNothingIfSpaceDoesNotExist() {
given(clientOrganizations
.listSpaces(ListOrganizationSpacesRequest
.builder()
.name("test-space-name")
.organizationId("default-org-id")
.page(1)
.build()))
.willReturn(Mono.just(ListOrganizationSpacesResponse
.builder()
.resources()
.build()));
DeleteBackingSpaceRequest request =
DeleteBackingSpaceRequest.builder()
.name("test-space-name")
.build();
StepVerifier.create(
appDeployer.deleteBackingSpace(request))
.assertNext(response -> assertThat(response.getName()).isEqualTo("test-space-name"))
.verifyComplete();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,24 +23,20 @@ import java.util.Collections;
import java.util.Map;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.client.v2.Metadata;
import org.cloudfoundry.client.v2.applications.SummaryApplicationRequest;
import org.cloudfoundry.client.v2.applications.SummaryApplicationResponse;
import org.cloudfoundry.client.v2.applications.UpdateApplicationResponse;
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.ApplicationDetail;
import org.cloudfoundry.operations.applications.ApplicationHealthCheck;
@@ -618,59 +614,6 @@ class CloudFoundryAppDeployerTest {
.build()))
.willReturn(Mono.empty());
given(operationsOrganizations
.get(
OrganizationInfoRequest
.builder()
.name("default-org")
.build()))
.willReturn(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()));
given(clientOrganizations
.listSpaces(ListOrganizationSpacesRequest
.builder()
.name("service-instance-id")
.organizationId("default-org-id")
.page(1)
.build()))
.willReturn(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()));
given(clientSpaces
.delete(DeleteSpaceRequest
.builder()
.spaceId("service-instance-space-id")
.build()))
.willReturn(Mono.empty());
DeleteServiceInstanceRequest request =
DeleteServiceInstanceRequest.builder()
.serviceInstanceName("service-instance-name")

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -56,4 +56,8 @@ public interface AppDeployer {
return Mono.empty();
}
default Mono<DeleteBackingSpaceResponse> deleteBackingSpace(DeleteBackingSpaceRequest request) {
return Mono.empty();
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.appbroker.deployer;
public class DeleteBackingSpaceRequest {
private final String name;
protected DeleteBackingSpaceRequest(String name) {
this.name = name;
}
public static DeleteBackingSpaceRequestBuilder builder() {
return new DeleteBackingSpaceRequestBuilder();
}
public String getName() {
return name;
}
public static final class DeleteBackingSpaceRequestBuilder {
private String name;
private DeleteBackingSpaceRequestBuilder() {
}
public DeleteBackingSpaceRequestBuilder name(String name) {
this.name = name;
return this;
}
public DeleteBackingSpaceRequest build() {
return new DeleteBackingSpaceRequest(name);
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.appbroker.deployer;
public class DeleteBackingSpaceResponse {
private final String name;
protected DeleteBackingSpaceResponse(String name) {
this.name = name;
}
public static DeleteBackingSpaceResponseBuilder builder() {
return new DeleteBackingSpaceResponseBuilder();
}
public String getName() {
return name;
}
public static final class DeleteBackingSpaceResponseBuilder {
private String name;
private DeleteBackingSpaceResponseBuilder() {
}
public DeleteBackingSpaceResponseBuilder name(String name) {
this.name = name;
return this;
}
public DeleteBackingSpaceResponse build() {
return new DeleteBackingSpaceResponse(name);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,9 +38,9 @@ import static io.restassured.RestAssured.given;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.springframework.cloud.appbroker.integration.CreateInstanceWithSpacePerServiceInstanceTargetComponentTest.APP_NAME;
import static org.springframework.cloud.appbroker.integration.CreateInstanceWithSpacePerServiceInstanceTargetComponentTest.BACKING_SERVICE_NAME;
import static org.springframework.cloud.appbroker.integration.CreateInstanceWithSpacePerServiceInstanceTargetComponentTest.BACKING_SI_NAME;
import static org.springframework.cloud.appbroker.integration.CreateInstanceWithCustomTargetComponentTest.APP_NAME;
import static org.springframework.cloud.appbroker.integration.CreateInstanceWithCustomTargetComponentTest.BACKING_SERVICE_NAME;
import static org.springframework.cloud.appbroker.integration.CreateInstanceWithCustomTargetComponentTest.BACKING_SI_NAME;
@TestPropertySource(properties = {
"spring.cloud.appbroker.services[0].service-name=example",
@@ -76,18 +76,18 @@ class CreateInstanceWithCustomTargetComponentTest extends WiremockComponentTest
void pushAppWithServicesInSpace() {
String serviceInstanceId = "instance-id";
String customSpace = "my-space";
cloudControllerFixture.stubCreateSpace(customSpace);
cloudControllerFixture.stubAssociatePermissions(customSpace);
String customSpaceGuid = "my-space-guid";
cloudControllerFixture.stubCreateSpace(customSpace, customSpaceGuid);
cloudControllerFixture.stubAssociatePermissions(customSpace, customSpaceGuid);
cloudControllerFixture.stubPushApp(APP_NAME);
// given services are available in the marketplace
cloudControllerFixture.stubServiceExists(BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
cloudControllerFixture.stubServiceExistsInSpace(BACKING_SERVICE_NAME, BACKING_PLAN_NAME, customSpaceGuid);
// will create and bind the service instance
cloudControllerFixture.stubCreateServiceInstance(BACKING_SI_NAME);
cloudControllerFixture.stubCreateServiceBinding(APP_NAME, BACKING_SI_NAME);
cloudControllerFixture.stubServiceInstanceExists(BACKING_SI_NAME);
cloudControllerFixture.stubServiceInstanceExistsInSpace(BACKING_SI_NAME, customSpaceGuid);
// when a service instance is created
given(brokerFixture.serviceInstanceRequest())

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,18 +65,19 @@ class CreateInstanceWithSpacePerServiceInstanceTargetComponentTest extends Wirem
@Test
void pushAppWithServicesInSpace() {
String serviceInstanceId = "instance-id";
String backingSpaceGuid = "my-space-guid";
cloudControllerFixture.stubCreateSpace(serviceInstanceId);
cloudControllerFixture.stubAssociatePermissions(serviceInstanceId);
cloudControllerFixture.stubCreateSpace(serviceInstanceId, backingSpaceGuid);
cloudControllerFixture.stubAssociatePermissions(serviceInstanceId, backingSpaceGuid);
cloudControllerFixture.stubPushAppWithHost(APP_NAME, APP_NAME + "-" + serviceInstanceId);
// given services are available in the marketplace
cloudControllerFixture.stubServiceExists(BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
cloudControllerFixture.stubServiceExistsInSpace(BACKING_SERVICE_NAME, BACKING_PLAN_NAME, backingSpaceGuid);
// will create and bind the service instance
cloudControllerFixture.stubCreateServiceInstance(BACKING_SI_NAME);
cloudControllerFixture.stubCreateServiceBinding(APP_NAME, BACKING_SI_NAME);
cloudControllerFixture.stubServiceInstanceExists(BACKING_SI_NAME);
cloudControllerFixture.stubServiceInstanceExistsInSpace(BACKING_SI_NAME, backingSpaceGuid);
// when a service instance is created
given(brokerFixture.serviceInstanceRequest())

View File

@@ -0,0 +1,121 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.appbroker.integration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.appbroker.integration.fixtures.CloudControllerStubFixture;
import org.springframework.cloud.appbroker.integration.fixtures.OpenServiceBrokerApiFixture;
import org.springframework.cloud.servicebroker.model.instance.OperationState;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.TestPropertySource;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static io.restassured.RestAssured.given;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithNewServiceAndTargetComponentTest.APP_NAME;
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithNewServiceAndTargetComponentTest.NEW_BACKING_PLAN_NAME;
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithNewServiceAndTargetComponentTest.NEW_BACKING_SERVICE_NAME;
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithNewServiceAndTargetComponentTest.NEW_BACKING_SI_NAME;
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithNewServiceAndTargetComponentTest.PLAN_NAME;
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithNewServiceAndTargetComponentTest.SERVICE_NAME;
@TestPropertySource(properties = {
"spring.cloud.appbroker.services[0].service-name=" + SERVICE_NAME,
"spring.cloud.appbroker.services[0].plan-name=" + PLAN_NAME,
"spring.cloud.appbroker.services[0].apps[0].path=classpath:demo.jar",
"spring.cloud.appbroker.services[0].apps[0].name=" + APP_NAME,
"spring.cloud.appbroker.services[0].apps[0].services[0].service-instance-name=" + NEW_BACKING_SI_NAME,
"spring.cloud.appbroker.services[0].services[0].service-instance-name=" + NEW_BACKING_SI_NAME,
"spring.cloud.appbroker.services[0].services[0].name=" + NEW_BACKING_SERVICE_NAME,
"spring.cloud.appbroker.services[0].services[0].plan=" + NEW_BACKING_PLAN_NAME,
"spring.cloud.appbroker.services[0].target.name=SpacePerServiceInstance"
})
class UpdateInstanceWithNewServiceAndTargetComponentTest extends WiremockComponentTest {
protected static final String APP_NAME = "app-update-with-new-service-and-target";
protected static final String BACKING_SI_NAME = "my-db-service";
protected static final String BACKING_SERVICE_NAME = "db-service";
protected static final String BACKING_PLAN_NAME = "backing-standard";
protected static final String NEW_BACKING_SERVICE_NAME = "new-service";
protected static final String NEW_BACKING_SI_NAME = "my-new-service";
protected static final String NEW_BACKING_PLAN_NAME = "new-backing-standard";
protected static final String SERVICE_NAME = "example";
protected static final String PLAN_NAME = "standard";
@Autowired
private OpenServiceBrokerApiFixture brokerFixture;
@Autowired
private CloudControllerStubFixture cloudControllerFixture;
@Test
void updateAppWithNewServiceAndTarget() {
final String backingSpaceGuid = "instance-id-space-guid";
final String serviceInstanceId = "instance-id";
cloudControllerFixture.stubFindSpace(serviceInstanceId, backingSpaceGuid, STARTED);
cloudControllerFixture.stubSpaceExists(serviceInstanceId, backingSpaceGuid, STARTED);
cloudControllerFixture.stubAppExistsWithBackingService(APP_NAME, BACKING_SI_NAME,
BACKING_SERVICE_NAME, BACKING_PLAN_NAME, backingSpaceGuid);
cloudControllerFixture.stubUpdateAppWithTarget(APP_NAME, backingSpaceGuid);
// will unbind and delete the existing service instance
cloudControllerFixture.stubGetBackingServiceInstanceFromSpace(BACKING_SI_NAME, BACKING_SERVICE_NAME,
BACKING_PLAN_NAME, backingSpaceGuid);
cloudControllerFixture.stubServiceBindingExists(APP_NAME, BACKING_SI_NAME);
cloudControllerFixture.stubDeleteServiceBinding(APP_NAME, BACKING_SI_NAME);
cloudControllerFixture.stubDeleteServiceInstance(BACKING_SI_NAME);
// will create and bind the service instance
cloudControllerFixture.stubServiceExistsInSpace(NEW_BACKING_SERVICE_NAME, NEW_BACKING_PLAN_NAME, backingSpaceGuid);
cloudControllerFixture.stubCreateServiceInstance(NEW_BACKING_SI_NAME);
cloudControllerFixture.stubCreateServiceBinding(APP_NAME, NEW_BACKING_SI_NAME);
cloudControllerFixture.stubServiceInstanceExistsInSpace(NEW_BACKING_SI_NAME, backingSpaceGuid);
// when a service instance is updated
given(brokerFixture.serviceInstanceRequest())
.when()
.patch(brokerFixture.createServiceInstanceUrl(), serviceInstanceId)
.then()
.statusCode(HttpStatus.ACCEPTED.value());
// when the "last_operation" API is polled
given(brokerFixture.serviceInstanceRequest())
.when()
.get(brokerFixture.getLastInstanceOperationUrl(), serviceInstanceId)
.then()
.statusCode(HttpStatus.OK.value())
.body("state", is(equalTo(OperationState.IN_PROGRESS.toString())));
String state = brokerFixture.waitForAsyncOperationComplete(serviceInstanceId);
assertThat(state).isEqualTo(OperationState.SUCCEEDED.toString());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,10 +35,15 @@ import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.put;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
@TestComponent
public class CloudControllerStubFixture extends WiremockStubFixture {
private static final String SCENARIO_NAME = "CreateSpace";
private static final String SPACE_CREATED_STATE = "SpaceCreated";
private static final String TEST_SPACE_GUID = "TEST-SPACE-GUID";
private static final String TEST_ORG_GUID = "TEST-ORG-GUID";
@@ -52,7 +57,7 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
public void stubCommonCloudControllerRequests() {
stubGetPlatformInfo();
stubFindTestOrg();
stubFindTestSpace();
stubFindSpace("development", TEST_SPACE_GUID, STARTED);
stubFindDomains();
}
@@ -82,29 +87,39 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
replace("@org-guid", TEST_ORG_GUID)))));
}
private void stubFindTestSpace() {
public void stubFindSpace(String spaceName, String spaceGuid, String scenario) {
stubFor(get(urlPathEqualTo("/v2/spaces"))
.inScenario(SCENARIO_NAME)
.whenScenarioStateIs(scenario)
.withQueryParam("q", equalTo("name:" + spaceName))
.withMetadata(optionalStubMapping())
.willReturn(ok()
.withBody(cc("list-spaces",
replace("@org-guid", TEST_ORG_GUID),
replace("@space-guid", TEST_SPACE_GUID)))));
replace("@name", spaceName),
replace("@space-guid", spaceGuid)))));
stubFor(get(urlPathEqualTo("/v2/spaces/" + TEST_SPACE_GUID))
stubFor(get(urlPathEqualTo("/v2/spaces/" + spaceGuid))
.inScenario(SCENARIO_NAME)
.whenScenarioStateIs(scenario)
.withMetadata(optionalStubMapping())
.willReturn(ok()
.withBody(cc("get-space",
replace("@name", "test"),
replace("@org-guid", TEST_ORG_GUID),
replace("@space-guid", TEST_SPACE_GUID)))));
replace("@space-guid", spaceGuid)))));
stubFor(get(urlPathEqualTo("/v2/spaces/" + TEST_SPACE_GUID + "/apps"))
stubFor(get(urlPathEqualTo("/v2/spaces/" + spaceGuid + "/apps"))
.inScenario(SCENARIO_NAME)
.whenScenarioStateIs(scenario)
.withQueryParam("page", equalTo("1"))
.withMetadata(optionalStubMapping())
.willReturn(ok()
.withBody(cc("empty-query-results"))));
stubFor(get(urlPathEqualTo("/v2/spaces/" + TEST_SPACE_GUID + "/security_groups"))
stubFor(get(urlPathEqualTo("/v2/spaces/" + spaceGuid + "/security_groups"))
.inScenario(SCENARIO_NAME)
.whenScenarioStateIs(scenario)
.withMetadata(optionalStubMapping())
.willReturn(ok()
.withBody(cc("get-space-security_groups"))));
@@ -144,15 +159,20 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
replace("@org-guid", TEST_ORG_GUID)))));
}
public void stubCreateSpace(final String spaceName) {
public void stubCreateSpace(final String spaceName, final String spaceGuid) {
stubFor(post(urlPathEqualTo("/v2/spaces"))
.inScenario(SCENARIO_NAME)
.whenScenarioStateIs(STARTED)
.willSetStateTo(SPACE_CREATED_STATE)
.withRequestBody(matchingJsonPath("$.[?(@.name == '" + spaceName + "')]"))
.withRequestBody(matchingJsonPath("$.[?(@.organization_guid == '" + TEST_ORG_GUID + "')]"))
.willReturn(ok()
.withBody(cc("get-space",
replace("@name", spaceName),
replace("@space-guid", "CREATED-SPACE-GUID"),
replace("@space-guid", spaceGuid),
replace("@org-guid", TEST_ORG_GUID)))));
stubFindSpace(spaceName, spaceGuid, SPACE_CREATED_STATE);
}
public void stubAppDoesNotExist(final String appName) {
@@ -163,26 +183,24 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
.withBody(cc("empty-query-results"))));
}
public void stubAppExists(final String appName) {
public void stubAppExistsInSpace(final String appName, final String spaceGuid) {
stubFor(get(urlPathEqualTo("/v2/apps/" + appGuid(appName)))
.withMetadata(optionalStubMapping())
.willReturn(ok()
.withBody(cc("get-app-STAGED",
replace("@name", appName)))));
stubFor(get(urlPathEqualTo("/v2/spaces/" + TEST_SPACE_GUID + "/apps"))
stubFor(get(urlPathEqualTo("/v2/spaces/" + spaceGuid + "/apps"))
.withQueryParam("q", equalTo("name:" + appName))
.withQueryParam("page", equalTo("1"))
.withMetadata(optionalStubMapping())
.willReturn(ok()
.withBody(cc("list-space-apps",
replace("@name", appName),
replace("@guid", appGuid(appName)),
replace("@space-guid", TEST_SPACE_GUID),
replace("@space-guid", spaceGuid),
replace("@stack-guid", stackGuid(appName))))));
stubFor(get(urlPathEqualTo("/v2/apps/" + appGuid(appName) + "/instances"))
.withMetadata(optionalStubMapping())
.willReturn(ok()
.withBody(cc("get-app-instances"))));
@@ -196,24 +214,30 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
replace("@route-guid", routeGuid(appName))))));
stubFor(get(urlPathEqualTo("/v2/apps/" + appGuid(appName) + "/stats"))
.withMetadata(optionalStubMapping())
.willReturn(ok()
.withBody(cc("get-app-stats",
replace("@name", appName)))));
stubFor(get(urlPathEqualTo("/v2/stacks/" + stackGuid(appName)))
.withMetadata(optionalStubMapping())
.willReturn(ok()
.withBody(cc("get-stack",
replace("@guid", stackGuid(appName))))));
}
public void stubAppExists(final String appName) {
stubAppExistsInSpace(appName, TEST_SPACE_GUID);
}
public void stubAppExistsWithBackingService(final String appName, final String serviceInstanceName,
final String serviceName, final String planName) {
stubAppExists(appName);
stubAppExistsWithBackingService(appName, serviceInstanceName, serviceName, planName, TEST_SPACE_GUID);
}
public void stubAppExistsWithBackingService(final String appName, final String serviceInstanceName,
final String serviceName, final String planName, final String spaceGuid) {
stubAppExistsInSpace(appName, spaceGuid);
stubFor(get(urlPathEqualTo("/v2/apps/" + appGuid(appName) + "/summary"))
.withMetadata(optionalStubMapping())
.willReturn(ok()
.withBody(cc("get-app-summary-with-backing-service",
replace("@name", appName),
@@ -248,6 +272,11 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
stubCreateRoute(appName);
}
public void stubUpdateAppWithTarget(final String appName, final String spaceGuid) {
stubUpdateApp(appName);
stubCreateRouteInSpace(appName, spaceGuid);
}
public void stubUpdateApp(final String appName) {
stubGetV3App(appName);
stubUpdateEnvironment(appName);
@@ -425,14 +454,14 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
.withBody(cc("get-app-instances"))));
}
private void stubCreateRoute(final String appName) {
private void stubCreateRouteInSpace(final String appName, final String spaceGuid) {
stubFor(get(urlPathEqualTo("/v2/private_domains"))
.withQueryParam("page", equalTo("1"))
.withMetadata(optionalStubMapping())
.willReturn(ok()
.withBody(cc("empty-query-results"))));
stubFor(get(urlPathEqualTo("/v2/spaces/" + TEST_SPACE_GUID + "/services"))
stubFor(get(urlPathEqualTo("/v2/spaces/" + spaceGuid + "/services"))
.withQueryParam("page", equalTo("1"))
.willReturn(ok()
.withBody(cc("list-space-services")
@@ -449,6 +478,10 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
.withBody(cc("get-app-summary"))));
}
private void stubCreateRoute(final String appName) {
stubCreateRouteInSpace(appName, TEST_SPACE_GUID);
}
public void stubDeleteApp(String appName) {
stubFor(delete(urlPathEqualTo("/v2/routes/" + appName + "-ROUTE-GUID"))
.willReturn(ok()
@@ -463,19 +496,30 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
stubServiceInstanceExists(serviceInstanceName, "doNotCare", "doNotCare");
}
public void stubServiceInstanceExists(String serviceInstanceName, String serviceName, String planName) {
stubServiceInstanceExists(serviceInstanceGuid(serviceInstanceName), serviceInstanceName, serviceName, planName);
public void stubServiceInstanceExistsInSpace(String serviceInstanceName, String spaceGuid) {
stubServiceInstanceExistsInSpace(serviceInstanceName, "doNotCare", "doNotCare", spaceGuid);
}
private void stubServiceInstanceExists(String serviceInstanceId, String serviceInstanceName, String serviceName,
String planName) {
stubFor(get(urlPathEqualTo("/v2/spaces/" + TEST_SPACE_GUID + "/service_instances"))
public void stubServiceInstanceExists(String serviceInstanceName, String serviceName, String planName) {
stubServiceInstanceExists(serviceInstanceGuid(serviceInstanceName), serviceInstanceName, serviceName,
planName, TEST_SPACE_GUID);
}
public void stubServiceInstanceExistsInSpace(String serviceInstanceName, String serviceName, String planName,
String spaceGuid) {
stubServiceInstanceExists(serviceInstanceGuid(serviceInstanceName), serviceInstanceName, serviceName,
planName, spaceGuid);
}
private void stubServiceInstanceExists(String serviceInstanceId, String serviceInstanceName,
String serviceName, String planName, String spaceGuid) {
stubFor(get(urlPathEqualTo("/v2/spaces/" + spaceGuid + "/service_instances"))
.withQueryParam("q", equalTo("name:" + serviceInstanceName))
.withQueryParam("page", equalTo("1"))
.withQueryParam("return_user_provided_service_instances", equalTo("true"))
.willReturn(ok()
.withBody(cc("list-space-service_instances",
replace("@space-guid", TEST_SPACE_GUID),
replace("@space-guid", spaceGuid),
replace("@service-guid", serviceGuid(serviceName)),
replace("@plan-guid", planGuid(planName)),
replace("@name", serviceInstanceName),
@@ -491,13 +535,18 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
}
public void stubGetBackingServiceInstance(String serviceInstanceName, String serviceName, String planName) {
stubGetBackingServiceInstanceFromSpace(serviceInstanceName, serviceName, planName, TEST_SPACE_GUID);
}
public void stubGetBackingServiceInstanceFromSpace(String serviceInstanceName, String serviceName,
String planName, String spaceGuid) {
String serviceInstanceId = serviceInstanceGuid(serviceInstanceName);
stubServiceInstanceExists(serviceInstanceId, serviceInstanceName, serviceName, planName);
stubServiceInstanceExists(serviceInstanceId, serviceInstanceName, serviceName, planName, spaceGuid);
stubGetServiceAndGetPlan(serviceName, planName);
}
public void stubServiceExists(String serviceName, String planName) {
stubFor(get(urlPathEqualTo("/v2/spaces/" + TEST_SPACE_GUID + "/services"))
public void stubServiceExistsInSpace(String serviceName, String planName, String spaceGuid) {
stubFor(get(urlPathEqualTo("/v2/spaces/" + spaceGuid + "/services"))
.withQueryParam("q", equalTo("label:" + serviceName))
.withQueryParam("page", equalTo("1"))
.willReturn(ok()
@@ -516,6 +565,10 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
.replace("@plan-guid", planGuid(planName)))));
}
public void stubServiceExists(String serviceName, String planName) {
stubServiceExistsInSpace(serviceName, planName, TEST_SPACE_GUID);
}
public void stubCreateServiceInstance(String serviceInstanceName) {
stubFor(post(urlPathEqualTo("/v2/service_instances"))
.withQueryParam("accepts_incomplete", equalTo("true"))
@@ -596,11 +649,11 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
.withBody(cc("empty-query-results"))));
}
public void stubAssociatePermissions(final String spaceName) {
public void stubAssociatePermissions(final String spaceName, final String spaceGuid) {
stubFor(get(urlPathEqualTo("/v2/config/feature_flags/set_roles_by_username"))
.willReturn(ok()
.withBody(cc("get-feature-flag-roles"))));
stubSpaceExists(spaceName);
stubSpaceExists(spaceName, spaceGuid, SPACE_CREATED_STATE);
stubFor(put(urlPathEqualTo("/v2/organizations/" + TEST_ORG_GUID + "/users"))
.willReturn(ok()));
@@ -621,13 +674,15 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
.replace("@service-name", serviceName))));
}
private void stubSpaceExists(final String spaceName) {
public void stubSpaceExists(final String spaceName, final String spaceGuid, final String scenario) {
stubFor(get(urlPathEqualTo("/v2/organizations/" + TEST_ORG_GUID + "/spaces"))
.inScenario(SCENARIO_NAME)
.whenScenarioStateIs(scenario)
.withQueryParam("q", equalTo("name:" + spaceName))
.willReturn(ok()
.withBody(cc("list-spaces",
replace("@org-guid", TEST_ORG_GUID),
replace("@space-guid", TEST_SPACE_GUID),
replace("@space-guid", spaceGuid),
replace("@name", spaceName)))));
}
@@ -659,7 +714,7 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
return planName + "-PLAN-GUID";
}
private static String serviceInstanceGuid(String serviceInstanceName) {
public static String serviceInstanceGuid(String serviceInstanceName) {
return serviceInstanceName + "-INSTANCE-GUID";
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
import com.github.tomakehurst.wiremock.admin.model.ListStubMappingsResult;
import com.github.tomakehurst.wiremock.client.MappingBuilder;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.common.Metadata;
@@ -45,6 +46,10 @@ public class WiremockStubFixture {
.build();
}
public ListStubMappingsResult getAllStubs() {
return wireMock.allStubMappings();
}
protected StubMapping stubFor(MappingBuilder mappingBuilder) {
return givenThat(mappingBuilder);
}

View File

@@ -1,5 +1,5 @@
{
"total_results": 2,
"total_results": 3,
"total_pages": 1,
"prev_url": null,
"next_url": null,

View File

@@ -12,7 +12,7 @@
"updated_at": "2018-07-19T20:34:16Z"
},
"entity": {
"name": "development",
"name": "@name",
"organization_guid": "@org-guid",
"space_quota_definition_guid": null,
"isolation_segment_guid": null,