UpdateServiceInstance creates/binds new backing services and unbinds/deletes old backing services.
To keep the behavior consisten, also updated delete flow to delete backing services that the backing app are actually bound to. As part of making the new AT pass, moved delete backing services step to before deleting backing app. Connects to #293
This commit is contained in:
@@ -10,4 +10,5 @@
|
||||
|
||||
logging:
|
||||
level:
|
||||
cloudfoundry-client: DEBUG
|
||||
cloudfoundry-client: DEBUG
|
||||
org.springframework.cloud.appbroker: DEBUG
|
||||
|
||||
@@ -47,10 +47,12 @@ import org.cloudfoundry.operations.applications.ApplicationEnvironments;
|
||||
import org.cloudfoundry.operations.applications.ApplicationSummary;
|
||||
import org.cloudfoundry.operations.organizations.OrganizationSummary;
|
||||
import org.cloudfoundry.operations.services.ServiceInstance;
|
||||
import org.cloudfoundry.operations.services.ServiceInstanceSummary;
|
||||
import org.cloudfoundry.operations.spaces.SpaceSummary;
|
||||
import org.cloudfoundry.uaa.clients.GetClientResponse;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -89,6 +91,14 @@ abstract class CloudFoundryAcceptanceTest {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CloudFoundryAcceptanceTest.class);
|
||||
|
||||
private static final String BACKING_SERVICE_PLAN_ID = UUID.randomUUID().toString();
|
||||
|
||||
private static final String SERVICE_ID = UUID.randomUUID().toString();
|
||||
|
||||
private static final String PLAN_ID = UUID.randomUUID().toString();
|
||||
|
||||
private static final String BACKING_SERVICE_ID = UUID.randomUUID().toString();
|
||||
|
||||
protected static final String PLAN_NAME = "standard";
|
||||
|
||||
protected static final String BACKING_APP_PATH = "classpath:backing-app.jar";
|
||||
@@ -123,22 +133,32 @@ abstract class CloudFoundryAcceptanceTest {
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp(BrokerProperties brokerProperties) {
|
||||
void setUp(TestInfo testInfo, BrokerProperties brokerProperties) {
|
||||
List<String> appBrokerProperties = getAppBrokerProperties(brokerProperties);
|
||||
blockingSubscribe(initializeBroker(appBrokerProperties));
|
||||
}
|
||||
|
||||
void setUpForBrokerUpdate(BrokerProperties brokerProperties) {
|
||||
List<String> appBrokerProperties = getAppBrokerProperties(brokerProperties);
|
||||
blockingSubscribe(updateBroker(appBrokerProperties));
|
||||
}
|
||||
|
||||
private List<String> getAppBrokerProperties(BrokerProperties brokerProperties) {
|
||||
String[] openServiceBrokerProperties = {
|
||||
"spring.cloud.openservicebroker.catalog.services[0].id=" + UUID.randomUUID().toString(),
|
||||
"spring.cloud.openservicebroker.catalog.services[0].id=" + SERVICE_ID,
|
||||
"spring.cloud.openservicebroker.catalog.services[0].name=" + appServiceName(),
|
||||
"spring.cloud.openservicebroker.catalog.services[0].description=A service that deploys a backing app",
|
||||
"spring.cloud.openservicebroker.catalog.services[0].bindable=true",
|
||||
"spring.cloud.openservicebroker.catalog.services[0].plans[0].id=" + UUID.randomUUID().toString(),
|
||||
"spring.cloud.openservicebroker.catalog.services[0].plans[0].id=" + PLAN_ID,
|
||||
"spring.cloud.openservicebroker.catalog.services[0].plans[0].name=standard",
|
||||
"spring.cloud.openservicebroker.catalog.services[0].plans[0].bindable=true",
|
||||
"spring.cloud.openservicebroker.catalog.services[0].plans[0].description=A simple plan",
|
||||
"spring.cloud.openservicebroker.catalog.services[0].plans[0].free=true",
|
||||
"spring.cloud.openservicebroker.catalog.services[1].id=" + UUID.randomUUID().toString(),
|
||||
"spring.cloud.openservicebroker.catalog.services[1].id=" + BACKING_SERVICE_ID,
|
||||
"spring.cloud.openservicebroker.catalog.services[1].name=" + backingServiceName(),
|
||||
"spring.cloud.openservicebroker.catalog.services[1].description=A backing service that can be bound to backing apps",
|
||||
"spring.cloud.openservicebroker.catalog.services[1].bindable=true",
|
||||
"spring.cloud.openservicebroker.catalog.services[1].plans[0].id=" + UUID.randomUUID().toString(),
|
||||
"spring.cloud.openservicebroker.catalog.services[1].plans[0].id=" + BACKING_SERVICE_PLAN_ID,
|
||||
"spring.cloud.openservicebroker.catalog.services[1].plans[0].name=standard",
|
||||
"spring.cloud.openservicebroker.catalog.services[1].plans[0].bindable=true",
|
||||
"spring.cloud.openservicebroker.catalog.services[1].plans[0].description=A simple plan",
|
||||
@@ -148,8 +168,7 @@ abstract class CloudFoundryAcceptanceTest {
|
||||
List<String> appBrokerProperties = new ArrayList<>();
|
||||
appBrokerProperties.addAll(Arrays.asList(openServiceBrokerProperties));
|
||||
appBrokerProperties.addAll(brokerProperties.getProperties());
|
||||
|
||||
blockingSubscribe(initializeBroker(appBrokerProperties));
|
||||
return appBrokerProperties;
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@@ -177,7 +196,7 @@ abstract class CloudFoundryAcceptanceTest {
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
public void tearDown(TestInfo testInfo) {
|
||||
blockingSubscribe(cloudFoundryService.getOrCreateDefaultOrganization()
|
||||
.map(OrganizationSummary::getId)
|
||||
.flatMap(orgId -> cloudFoundryService.getOrCreateDefaultSpace()
|
||||
@@ -206,6 +225,12 @@ abstract class CloudFoundryAcceptanceTest {
|
||||
.then(cloudFoundryService.enableServiceBrokerAccess(backingServiceName()))));
|
||||
}
|
||||
|
||||
private Mono<Void> updateBroker(List<String> appBrokerProperties) {
|
||||
return cloudFoundryService
|
||||
.pushBrokerApp(testBrokerAppName(), getTestBrokerAppPath(), brokerClientId(), appBrokerProperties)
|
||||
.then(cloudFoundryService.updateServiceBroker(serviceBrokerName(), testBrokerAppName()));
|
||||
}
|
||||
|
||||
private Mono<Void> cleanup(String orgId, String spaceId) {
|
||||
return cloudFoundryService.deleteServiceBroker(serviceBrokerName())
|
||||
.then(cloudFoundryService.deleteApp(testBrokerAppName()))
|
||||
@@ -252,6 +277,13 @@ abstract class CloudFoundryAcceptanceTest {
|
||||
blockingSubscribe(cloudFoundryService.deleteServiceInstance(serviceInstanceName));
|
||||
}
|
||||
|
||||
protected List<String> listServiceInstances() {
|
||||
return cloudFoundryService.listServiceInstances()
|
||||
.map(ServiceInstanceSummary::getName)
|
||||
.collectList()
|
||||
.block();
|
||||
}
|
||||
|
||||
protected ServiceInstance getServiceInstance(String serviceInstanceName) {
|
||||
return getServiceInstanceMono(serviceInstanceName).block();
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ class CreateInstanceAcceptanceTest extends CloudFoundryAcceptanceTest {
|
||||
Optional<ApplicationSummary> backingApplication1 = getApplicationSummary(APP_CREATE_1);
|
||||
assertThat(backingApplication1).hasValueSatisfying(app -> {
|
||||
assertThat(app.getInstances()).isEqualTo(2);
|
||||
assertThat(app.getRunningInstances()).isEqualTo(2);
|
||||
assertThat(app.getRunningInstances()).isGreaterThanOrEqualTo(1);
|
||||
assertThat(app.getMemoryLimit()).isEqualTo(2048);
|
||||
});
|
||||
|
||||
|
||||
@@ -87,6 +87,9 @@ class CreateInstanceWithServiceInstanceGuidSuffixTargetAcceptanceTest extends Cl
|
||||
|
||||
// when the service instance is deleted
|
||||
deleteServiceInstance(SI_NAME);
|
||||
|
||||
// and the backing service is deleted
|
||||
assertThat(listServiceInstances()).doesNotContain(expectedServiceInstanceName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -90,11 +90,16 @@ class CreateInstanceWithServicesAcceptanceTest extends CloudFoundryAcceptanceTes
|
||||
// when the service instance is deleted
|
||||
deleteServiceInstance(SI_NAME);
|
||||
|
||||
// service has no applications bound to it
|
||||
ServiceInstance serviceInstance2AfterDeletion = getServiceInstance(BACKING_SI_2_NAME);
|
||||
assertThat(serviceInstance2AfterDeletion.getApplications()).isEmpty();
|
||||
// and the backing services are deleted
|
||||
assertThat(listServiceInstances()).doesNotContain(BACKING_SI_1_NAME);
|
||||
assertThat(listServiceInstances()).doesNotContain(BACKING_SI_2_NAME);
|
||||
|
||||
deleteServiceInstance(BACKING_SI_2_NAME);
|
||||
// TODO: another story to only remove the instances with service definition specified (https://github.com/spring-cloud/spring-cloud-app-broker/issues/316)
|
||||
// // service without specification has no applications bound to it
|
||||
// ServiceInstance serviceInstance2AfterDeletion = getServiceInstance(BACKING_SI_2_NAME);
|
||||
// assertThat(serviceInstance2AfterDeletion.getApplications()).isEmpty();
|
||||
//
|
||||
// deleteServiceInstance(BACKING_SI_2_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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 UpdateInstanceWithNewServiceAcceptanceTest extends CloudFoundryAcceptanceTest {
|
||||
|
||||
private static final String APP_NAME = "app-update-with-new-services";
|
||||
|
||||
private static final int FIRST_TEST = 1;
|
||||
|
||||
private static final int SECOND_TEST = 2;
|
||||
|
||||
private static final String SI_NAME = "si-update-with-new-services";
|
||||
|
||||
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";
|
||||
|
||||
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
|
||||
})
|
||||
void weCreateAService() {
|
||||
// when a service instance is created
|
||||
createServiceInstance(SI_NAME);
|
||||
|
||||
// then a backing application is deployed
|
||||
Optional<ApplicationSummary> backingApplication = getApplicationSummary(APP_NAME);
|
||||
assertThat(backingApplication).hasValueSatisfying(app ->
|
||||
assertThat(app.getRunningInstances()).isEqualTo(1));
|
||||
|
||||
// and the services are bound to it
|
||||
ServiceInstance backingServiceInstance = getServiceInstance(OLD_BACKING_SI_NAME);
|
||||
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
|
||||
})
|
||||
void weUpdateTheServiceInstanceWithANewBackingService() {
|
||||
// when the service instance is updated with a new service
|
||||
updateServiceInstance(SI_NAME, Collections.emptyMap());
|
||||
|
||||
// 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);
|
||||
assertThat(updatedBackingApplication).hasValueSatisfying(app ->
|
||||
assertThat(app.getRunningInstances()).isEqualTo(1));
|
||||
|
||||
// and the new backing service is bound to it
|
||||
ServiceInstance newBackingServiceInstance = getServiceInstance(NEW_BACKING_SI_NAME);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -113,6 +113,9 @@ class UpdateInstanceWithServicesAcceptanceTest extends CloudFoundryAcceptanceTes
|
||||
|
||||
// then the service instance is deleted
|
||||
deleteServiceInstance(SI_NAME);
|
||||
|
||||
// and the backing service is deleted
|
||||
assertThat(listServiceInstances()).doesNotContain(BACKING_SI_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,10 +52,12 @@ import org.cloudfoundry.operations.organizations.Organizations;
|
||||
import org.cloudfoundry.operations.serviceadmin.CreateServiceBrokerRequest;
|
||||
import org.cloudfoundry.operations.serviceadmin.DeleteServiceBrokerRequest;
|
||||
import org.cloudfoundry.operations.serviceadmin.EnableServiceAccessRequest;
|
||||
import org.cloudfoundry.operations.serviceadmin.UpdateServiceBrokerRequest;
|
||||
import org.cloudfoundry.operations.services.CreateServiceInstanceRequest;
|
||||
import org.cloudfoundry.operations.services.DeleteServiceInstanceRequest;
|
||||
import org.cloudfoundry.operations.services.GetServiceInstanceRequest;
|
||||
import org.cloudfoundry.operations.services.ServiceInstance;
|
||||
import org.cloudfoundry.operations.services.ServiceInstanceSummary;
|
||||
import org.cloudfoundry.operations.services.UpdateServiceInstanceRequest;
|
||||
import org.cloudfoundry.operations.spaces.CreateSpaceRequest;
|
||||
import org.cloudfoundry.operations.spaces.SpaceSummary;
|
||||
@@ -114,6 +116,19 @@ public class CloudFoundryService {
|
||||
.doOnError(error -> LOGGER.error("Error creating service broker " + brokerName + ": " + error)));
|
||||
}
|
||||
|
||||
public Mono<Void> updateServiceBroker(String brokerName, String testBrokerAppName) {
|
||||
return getApplicationRoute(testBrokerAppName)
|
||||
.flatMap(url -> cloudFoundryOperations.serviceAdmin()
|
||||
.update(UpdateServiceBrokerRequest.builder()
|
||||
.name(brokerName)
|
||||
.username("user")
|
||||
.password("password")
|
||||
.url(url)
|
||||
.build())
|
||||
.doOnSuccess(item -> LOGGER.info("Updating service broker " + brokerName))
|
||||
.doOnError(error -> LOGGER.error("Error updating service broker " + brokerName + ": " + error)));
|
||||
}
|
||||
|
||||
public Mono<String> getApplicationRoute(String appName) {
|
||||
return cloudFoundryOperations.applications()
|
||||
.get(GetApplicationRequest.builder()
|
||||
@@ -203,6 +218,10 @@ public class CloudFoundryService {
|
||||
.doOnError(error -> LOGGER.error("Error updating service instance " + serviceInstanceName + ": " + error));
|
||||
}
|
||||
|
||||
public Flux<ServiceInstanceSummary> listServiceInstances() {
|
||||
return cloudFoundryOperations.services().listInstances();
|
||||
}
|
||||
|
||||
public Mono<ServiceInstance> getServiceInstance(String serviceInstanceName) {
|
||||
return getServiceInstance(cloudFoundryOperations, serviceInstanceName);
|
||||
}
|
||||
|
||||
@@ -345,6 +345,7 @@ public class AppBrokerAutoConfiguration {
|
||||
*
|
||||
* @param brokeredServices the BrokeredServices bean
|
||||
* @param backingAppDeploymentService the BackingAppDeploymentService bean
|
||||
* @param backingAppManagementService the BackingAppManagementService bean
|
||||
* @param backingServicesProvisionService the BackingServicesProvisionService bean
|
||||
* @param appsParametersTransformationService the BackingApplicationsParametersTransformationService bean
|
||||
* @param servicesParametersTransformationService the BackingServicesParametersTransformationService bean
|
||||
@@ -353,7 +354,9 @@ public class AppBrokerAutoConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
public UpdateServiceInstanceWorkflow appDeploymentUpdateServiceInstanceWorkflow(
|
||||
BrokeredServices brokeredServices, BackingAppDeploymentService backingAppDeploymentService,
|
||||
BrokeredServices brokeredServices,
|
||||
BackingAppDeploymentService backingAppDeploymentService,
|
||||
BackingAppManagementService backingAppManagementService,
|
||||
BackingServicesProvisionService backingServicesProvisionService,
|
||||
BackingApplicationsParametersTransformationService appsParametersTransformationService,
|
||||
BackingServicesParametersTransformationService servicesParametersTransformationService,
|
||||
@@ -362,6 +365,7 @@ public class AppBrokerAutoConfiguration {
|
||||
return new AppDeploymentUpdateServiceInstanceWorkflow(
|
||||
brokeredServices,
|
||||
backingAppDeploymentService,
|
||||
backingAppManagementService,
|
||||
backingServicesProvisionService,
|
||||
appsParametersTransformationService,
|
||||
servicesParametersTransformationService,
|
||||
@@ -373,6 +377,7 @@ public class AppBrokerAutoConfiguration {
|
||||
*
|
||||
* @param brokeredServices the BrokeredServices bean
|
||||
* @param backingAppDeploymentService the BackingAppDeploymentService bean
|
||||
* @param backingAppManagementService the BackingAppManagementService bean
|
||||
* @param backingServicesProvisionService the BackingServicesProvisionService bean
|
||||
* @param credentialProviderService the CredentialProviderService bean
|
||||
* @param targetService the TargetService bean
|
||||
@@ -381,13 +386,16 @@ public class AppBrokerAutoConfiguration {
|
||||
@Bean
|
||||
public DeleteServiceInstanceWorkflow appDeploymentDeleteServiceInstanceWorkflow(
|
||||
BrokeredServices brokeredServices, BackingAppDeploymentService backingAppDeploymentService,
|
||||
BackingAppManagementService backingAppManagementService,
|
||||
BackingServicesProvisionService backingServicesProvisionService,
|
||||
CredentialProviderService credentialProviderService, TargetService targetService) {
|
||||
|
||||
return new AppDeploymentDeleteServiceInstanceWorkflow(
|
||||
brokeredServices,
|
||||
backingAppDeploymentService,
|
||||
backingServicesProvisionService, credentialProviderService,
|
||||
backingAppManagementService,
|
||||
backingServicesProvisionService,
|
||||
credentialProviderService,
|
||||
targetService
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.appbroker.deployer;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class ServicesSpec {
|
||||
|
||||
private String serviceInstanceName;
|
||||
@@ -39,6 +41,30 @@ public class ServicesSpec {
|
||||
return new ServicesSpecBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ServicesSpec that = (ServicesSpec) o;
|
||||
return Objects.equals(serviceInstanceName, that.serviceInstanceName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(serviceInstanceName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServicesSpec{" +
|
||||
"serviceInstanceName='" + serviceInstanceName + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static final class ServicesSpecBuilder {
|
||||
|
||||
private String serviceInstanceName;
|
||||
|
||||
@@ -29,7 +29,9 @@ import org.springframework.cloud.appbroker.deployer.BackingApplication;
|
||||
import org.springframework.cloud.appbroker.deployer.BackingApplications;
|
||||
import org.springframework.cloud.appbroker.deployer.BrokeredService;
|
||||
import org.springframework.cloud.appbroker.deployer.BrokeredServices;
|
||||
import org.springframework.cloud.appbroker.deployer.GetApplicationRequest;
|
||||
import org.springframework.cloud.appbroker.deployer.GetServiceInstanceRequest;
|
||||
import org.springframework.cloud.appbroker.deployer.ServicesSpec;
|
||||
import org.springframework.cloud.appbroker.extensions.targets.TargetService;
|
||||
|
||||
public class BackingAppManagementService {
|
||||
@@ -108,12 +110,42 @@ public class BackingAppManagementService {
|
||||
.then();
|
||||
}
|
||||
|
||||
private Mono<List<BackingApplication>> getBackingApplicationsForService(String serviceInstanceId) {
|
||||
public Mono<BackingApplications> getDeployedBackingApplications(String serviceInstanceId) {
|
||||
return getBackingApplicationsForService(serviceInstanceId)
|
||||
.flatMapMany(Flux::fromIterable)
|
||||
.flatMap(app ->
|
||||
appDeployer
|
||||
.get(GetApplicationRequest.builder()
|
||||
.name(app.getName())
|
||||
.properties(app.getProperties())
|
||||
.build())
|
||||
.flatMap(response -> Flux.fromIterable(response.getServices())
|
||||
.map(serviceName ->
|
||||
ServicesSpec.builder()
|
||||
.serviceInstanceName(serviceName)
|
||||
.build())
|
||||
.collectList()
|
||||
.map(services -> BackingApplication
|
||||
.builder()
|
||||
.name(response.getName())
|
||||
.services(services)
|
||||
.environment(response.getEnvironment())
|
||||
.build()))
|
||||
.doOnRequest(l -> log.debug("Getting deployed backing applications {}", app))
|
||||
.doOnError(exception -> log.error(String.format("Error getting deployed backing application %s " +
|
||||
"with error '%s'", app.getName(), exception.getMessage()), exception))
|
||||
.onErrorResume(exception -> Mono.empty()))
|
||||
.collectList()
|
||||
.map(BackingApplications::new);
|
||||
}
|
||||
|
||||
private Mono<BackingApplications> getBackingApplicationsForService(String serviceInstanceId) {
|
||||
return appDeployer.getServiceInstance(GetServiceInstanceRequest.builder()
|
||||
.serviceInstanceId(serviceInstanceId)
|
||||
.build())
|
||||
.flatMap(response -> findBrokeredService(response.getService(), response.getPlan()))
|
||||
.flatMap(brokeredService -> updateBackingApps(brokeredService, serviceInstanceId));
|
||||
.flatMap(brokeredService -> updateBackingApps(brokeredService, serviceInstanceId))
|
||||
.map(backingApplications -> BackingApplications.builder().backingApplications(backingApplications).build());
|
||||
}
|
||||
|
||||
private Mono<BrokeredService> findBrokeredService(String serviceName, String planName) {
|
||||
|
||||
@@ -22,10 +22,12 @@ import reactor.util.Logger;
|
||||
import reactor.util.Loggers;
|
||||
|
||||
import org.springframework.cloud.appbroker.deployer.BackingAppDeploymentService;
|
||||
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.extensions.credentials.CredentialProviderService;
|
||||
import org.springframework.cloud.appbroker.extensions.targets.TargetService;
|
||||
import org.springframework.cloud.appbroker.manager.BackingAppManagementService;
|
||||
import org.springframework.cloud.appbroker.service.DeleteServiceInstanceWorkflow;
|
||||
import org.springframework.cloud.servicebroker.model.instance.DeleteServiceInstanceRequest;
|
||||
import org.springframework.cloud.servicebroker.model.instance.DeleteServiceInstanceResponse;
|
||||
@@ -41,6 +43,8 @@ public class AppDeploymentDeleteServiceInstanceWorkflow
|
||||
|
||||
private final BackingAppDeploymentService deploymentService;
|
||||
|
||||
private final BackingAppManagementService backingAppManagementService;
|
||||
|
||||
private final CredentialProviderService credentialProviderService;
|
||||
|
||||
private final TargetService targetService;
|
||||
@@ -49,11 +53,13 @@ public class AppDeploymentDeleteServiceInstanceWorkflow
|
||||
|
||||
public AppDeploymentDeleteServiceInstanceWorkflow(BrokeredServices brokeredServices,
|
||||
BackingAppDeploymentService deploymentService,
|
||||
BackingAppManagementService backingAppManagementService,
|
||||
BackingServicesProvisionService backingServicesProvisionService,
|
||||
CredentialProviderService credentialProviderService,
|
||||
TargetService targetService) {
|
||||
super(brokeredServices);
|
||||
this.deploymentService = deploymentService;
|
||||
this.backingAppManagementService = backingAppManagementService;
|
||||
this.credentialProviderService = credentialProviderService;
|
||||
this.targetService = targetService;
|
||||
this.backingServicesProvisionService = backingServicesProvisionService;
|
||||
@@ -61,20 +67,23 @@ public class AppDeploymentDeleteServiceInstanceWorkflow
|
||||
|
||||
@Override
|
||||
public Mono<Void> delete(DeleteServiceInstanceRequest request, DeleteServiceInstanceResponse response) {
|
||||
return undeployBackingApplications(request)
|
||||
.thenMany(deleteBackingServices(request))
|
||||
return deleteBackingServices(request)
|
||||
.thenMany(undeployBackingApplications(request))
|
||||
.then();
|
||||
}
|
||||
|
||||
private Flux<String> deleteBackingServices(DeleteServiceInstanceRequest request) {
|
||||
return getBackingServicesForService(request.getServiceDefinition(), request.getPlan())
|
||||
.flatMapMany(backingServices ->
|
||||
targetService.addToBackingServices(backingServices,
|
||||
getTargetForService(request.getServiceDefinition(), request.getPlan()),
|
||||
request.getServiceInstanceId()))
|
||||
return backingAppManagementService.getDeployedBackingApplications(request.getServiceInstanceId())
|
||||
.flatMapMany(Flux::fromIterable)
|
||||
.flatMap(backingApplication ->
|
||||
Flux.fromIterable(backingApplication.getServices())
|
||||
.map(servicesSpec -> BackingService.builder()
|
||||
.serviceInstanceName(servicesSpec.getServiceInstanceName())
|
||||
.build())
|
||||
.collectList())
|
||||
.doOnEach(backingServices -> log.debug("Deleting backing services {} for {}/{}",
|
||||
backingServices, request.getServiceDefinition().getName(), request.getPlan().getName()))
|
||||
.flatMap(backingServicesProvisionService::deleteServiceInstance)
|
||||
.doOnRequest(l -> log.debug("Deleting backing services for{}/{}",
|
||||
request.getServiceDefinition().getName(), request.getPlan().getName()))
|
||||
.doOnComplete(() -> log.debug("Finished deleting backing services for {}/{}",
|
||||
request.getServiceDefinition().getName(), request.getPlan().getName()))
|
||||
.doOnError(exception -> log.error(String.format("Error deleting backing services for %s/%s with error '%s'",
|
||||
|
||||
@@ -16,17 +16,28 @@
|
||||
|
||||
package org.springframework.cloud.appbroker.workflow.instance;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
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.BrokeredServices;
|
||||
import org.springframework.cloud.appbroker.extensions.parameters.BackingApplicationsParametersTransformationService;
|
||||
import org.springframework.cloud.appbroker.extensions.parameters.BackingServicesParametersTransformationService;
|
||||
import org.springframework.cloud.appbroker.extensions.targets.TargetService;
|
||||
import org.springframework.cloud.appbroker.manager.BackingAppManagementService;
|
||||
import org.springframework.cloud.appbroker.service.UpdateServiceInstanceWorkflow;
|
||||
import org.springframework.cloud.servicebroker.model.instance.UpdateServiceInstanceRequest;
|
||||
import org.springframework.cloud.servicebroker.model.instance.UpdateServiceInstanceResponse;
|
||||
@@ -41,6 +52,8 @@ public class AppDeploymentUpdateServiceInstanceWorkflow extends AppDeploymentIns
|
||||
|
||||
private final BackingAppDeploymentService deploymentService;
|
||||
|
||||
private final BackingAppManagementService backingAppManagementService;
|
||||
|
||||
private final BackingServicesProvisionService backingServicesProvisionService;
|
||||
|
||||
private final BackingApplicationsParametersTransformationService appsParametersTransformationService;
|
||||
@@ -51,12 +64,14 @@ public class AppDeploymentUpdateServiceInstanceWorkflow extends AppDeploymentIns
|
||||
|
||||
public AppDeploymentUpdateServiceInstanceWorkflow(BrokeredServices brokeredServices,
|
||||
BackingAppDeploymentService deploymentService,
|
||||
BackingAppManagementService backingAppManagementService,
|
||||
BackingServicesProvisionService backingServicesProvisionService,
|
||||
BackingApplicationsParametersTransformationService appsParametersTransformationService,
|
||||
BackingServicesParametersTransformationService servicesParametersTransformationService,
|
||||
TargetService targetService) {
|
||||
super(brokeredServices);
|
||||
this.deploymentService = deploymentService;
|
||||
this.backingAppManagementService = backingAppManagementService;
|
||||
this.backingServicesProvisionService = backingServicesProvisionService;
|
||||
this.appsParametersTransformationService = appsParametersTransformationService;
|
||||
this.servicesParametersTransformationService = servicesParametersTransformationService;
|
||||
@@ -79,14 +94,71 @@ public class AppDeploymentUpdateServiceInstanceWorkflow extends AppDeploymentIns
|
||||
.flatMap(backingServices ->
|
||||
servicesParametersTransformationService.transformParameters(backingServices,
|
||||
request.getParameters()))
|
||||
.flatMapMany(backingServicesProvisionService::updateServiceInstance)
|
||||
.flatMap(backingServices -> Flux.fromIterable(backingServices)
|
||||
.collectMap(BackingService::getServiceInstanceName, Function.identity()))
|
||||
.zipWith(getExistingBackingServiceNameMap(request))
|
||||
.flatMapMany(newAndExisting -> {
|
||||
Map<String, BackingService> newServices = newAndExisting.getT1();
|
||||
Map<String, BackingService> existingServices = newAndExisting.getT2();
|
||||
|
||||
Set<String> serviceNamesToUpdate = intersection(newServices.keySet(), existingServices.keySet());
|
||||
Set<String> serviceNamesToCreate = subtract(newServices.keySet(), existingServices.keySet());
|
||||
Set<String> serviceNamesToDelete = subtract(existingServices.keySet(), newServices.keySet());
|
||||
|
||||
List<BackingService> servicesToUpdate = servicesInNameList(newServices, serviceNamesToUpdate);
|
||||
List<BackingService> servicesToCreate = servicesInNameList(newServices, serviceNamesToCreate);
|
||||
List<BackingService> servicesToDelete = servicesInNameList(existingServices,
|
||||
serviceNamesToDelete);
|
||||
|
||||
log.debug("Backing services to update: {}", serviceNamesToUpdate);
|
||||
log.debug("Backing services to create: {}", serviceNamesToCreate);
|
||||
log.debug("Backing services to delete: {}", serviceNamesToDelete);
|
||||
|
||||
return Flux.concat(
|
||||
backingServicesProvisionService.updateServiceInstance(servicesToUpdate),
|
||||
backingServicesProvisionService.createServiceInstance(servicesToCreate),
|
||||
backingServicesProvisionService.deleteServiceInstance(servicesToDelete))
|
||||
.parallel()
|
||||
.runOn(Schedulers.parallel());
|
||||
})
|
||||
.doOnRequest(l -> log.debug("Updating backing services for {}/{}",
|
||||
request.getServiceDefinition().getName(), request.getPlan().getName()))
|
||||
.doOnComplete(() -> log.debug("Finished updating backing services for {}/{}",
|
||||
request.getServiceDefinition().getName(), request.getPlan().getName()))
|
||||
.doOnError(exception -> log.error(String.format("Error updating backing services for %s/%s with error '%s'",
|
||||
request.getServiceDefinition().getName(), request.getPlan().getName(), exception.getMessage()),
|
||||
exception));
|
||||
.doOnError(
|
||||
exception -> log.error(String.format("Error updating backing services for %s/%s with error '%s'",
|
||||
request.getServiceDefinition().getName(), request.getPlan().getName(), exception.getMessage()),
|
||||
exception));
|
||||
}
|
||||
|
||||
private Mono<Map<String, BackingService>> getExistingBackingServiceNameMap(UpdateServiceInstanceRequest request) {
|
||||
return backingAppManagementService.getDeployedBackingApplications(request.getServiceInstanceId())
|
||||
.flatMapMany(Flux::fromIterable)
|
||||
.map(BackingApplication::getServices)
|
||||
.flatMap(Flux::fromIterable)
|
||||
.distinct()
|
||||
.map(servicesSpec -> BackingService.builder()
|
||||
.serviceInstanceName(servicesSpec.getServiceInstanceName())
|
||||
.build())
|
||||
.collectMap(BackingService::getServiceInstanceName, Function.identity());
|
||||
}
|
||||
|
||||
private List<BackingService> servicesInNameList(Map<String, BackingService> services, Set<String> nameList) {
|
||||
List<BackingService> servicesToKeep = new ArrayList<>(services.values());
|
||||
servicesToKeep.removeIf(service -> !nameList.contains(service.getServiceInstanceName()));
|
||||
return servicesToKeep;
|
||||
}
|
||||
|
||||
private Set<String> subtract(Set<String> set1, Set<String> set2) {
|
||||
Set<String> set = new HashSet<>(set1);
|
||||
set.removeAll(set2);
|
||||
return set;
|
||||
}
|
||||
|
||||
private Set<String> intersection(Set<String> set1, Set<String> set2) {
|
||||
Set<String> set = new HashSet<>(set1);
|
||||
set.retainAll(set2);
|
||||
return set;
|
||||
}
|
||||
|
||||
private Flux<String> updateBackingApplications(UpdateServiceInstanceRequest request) {
|
||||
|
||||
@@ -29,13 +29,18 @@ import org.springframework.cloud.appbroker.deployer.BackingApplication;
|
||||
import org.springframework.cloud.appbroker.deployer.BackingApplications;
|
||||
import org.springframework.cloud.appbroker.deployer.BrokeredService;
|
||||
import org.springframework.cloud.appbroker.deployer.BrokeredServices;
|
||||
import org.springframework.cloud.appbroker.deployer.GetApplicationRequest;
|
||||
import org.springframework.cloud.appbroker.deployer.GetApplicationResponse;
|
||||
import org.springframework.cloud.appbroker.deployer.GetServiceInstanceRequest;
|
||||
import org.springframework.cloud.appbroker.deployer.GetServiceInstanceResponse;
|
||||
import org.springframework.cloud.appbroker.deployer.ServicesSpec;
|
||||
import org.springframework.cloud.appbroker.extensions.targets.TargetService;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -311,6 +316,54 @@ class BackingAppManagementServiceTest {
|
||||
verifyNoMoreInteractions(appDeployer, targetService, managementClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void getDeployedBackingApplications() {
|
||||
given(appDeployer.getServiceInstance(any(GetServiceInstanceRequest.class)))
|
||||
.willReturn(Mono.just(GetServiceInstanceResponse.builder()
|
||||
.name("foo-service")
|
||||
.plan("plan1")
|
||||
.service("service1")
|
||||
.build()));
|
||||
|
||||
given(appDeployer.get(any(GetApplicationRequest.class)))
|
||||
.willReturn(Mono.just(GetApplicationResponse.builder()
|
||||
.name("testApp1")
|
||||
.service("service1")
|
||||
.service("service2")
|
||||
.build()),
|
||||
Mono.just(GetApplicationResponse.builder()
|
||||
.name("testApp2")
|
||||
.service("service3")
|
||||
.build()));
|
||||
|
||||
given(targetService.addToBackingApplications(eq(backingApps), any(), eq("foo-service-id")))
|
||||
.willReturn(Mono.just(backingApps));
|
||||
|
||||
StepVerifier.create(backingAppManagementService.getDeployedBackingApplications("foo-service-id"))
|
||||
.expectNext(BackingApplications.builder()
|
||||
.backingApplication(BackingApplication.builder()
|
||||
.name("testApp1")
|
||||
.services(
|
||||
ServicesSpec.builder().serviceInstanceName("service1").build(),
|
||||
ServicesSpec.builder().serviceInstanceName("service2").build())
|
||||
.build())
|
||||
.backingApplication(BackingApplication.builder()
|
||||
.name("testApp2")
|
||||
.services(ServicesSpec.builder().serviceInstanceName("service3").build())
|
||||
.build())
|
||||
.build())
|
||||
.verifyComplete();
|
||||
|
||||
then(targetService).should().addToBackingApplications(eq(backingApps), any(), eq("foo-service-id"));
|
||||
then(appDeployer).should().getServiceInstance(argThat(req -> "foo-service-id".equals(req.getServiceInstanceId())));
|
||||
then(appDeployer).should().get(argThat(req -> "testApp1".equals(req.getName())));
|
||||
then(appDeployer).should().get(argThat(req -> "testApp2".equals(req.getName())));
|
||||
|
||||
verifyNoInteractions(managementClient);
|
||||
verifyNoMoreInteractions(appDeployer, targetService, managementClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
void restageApplications() {
|
||||
given(appDeployer.getServiceInstance(any(GetServiceInstanceRequest.class)))
|
||||
|
||||
@@ -33,15 +33,18 @@ import org.springframework.cloud.appbroker.deployer.BackingServices;
|
||||
import org.springframework.cloud.appbroker.deployer.BackingServicesProvisionService;
|
||||
import org.springframework.cloud.appbroker.deployer.BrokeredService;
|
||||
import org.springframework.cloud.appbroker.deployer.BrokeredServices;
|
||||
import org.springframework.cloud.appbroker.deployer.ServicesSpec;
|
||||
import org.springframework.cloud.appbroker.deployer.TargetSpec;
|
||||
import org.springframework.cloud.appbroker.extensions.credentials.CredentialProviderService;
|
||||
import org.springframework.cloud.appbroker.extensions.targets.TargetService;
|
||||
import org.springframework.cloud.appbroker.manager.BackingAppManagementService;
|
||||
import org.springframework.cloud.appbroker.service.DeleteServiceInstanceWorkflow;
|
||||
import org.springframework.cloud.servicebroker.model.catalog.Plan;
|
||||
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 org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
@@ -52,6 +55,9 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
|
||||
@Mock
|
||||
private BackingAppDeploymentService backingAppDeploymentService;
|
||||
|
||||
@Mock
|
||||
private BackingAppManagementService backingAppManagementService;
|
||||
|
||||
@Mock
|
||||
private TargetService targetService;
|
||||
|
||||
@@ -63,8 +69,6 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
|
||||
|
||||
private BackingApplications backingApps;
|
||||
|
||||
private BackingServices backingServices;
|
||||
|
||||
private TargetSpec targetSpec;
|
||||
|
||||
private DeleteServiceInstanceWorkflow deleteServiceInstanceWorkflow;
|
||||
@@ -85,7 +89,7 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
|
||||
.build())
|
||||
.build();
|
||||
|
||||
backingServices = BackingServices
|
||||
BackingServices backingServices = BackingServices
|
||||
.builder()
|
||||
.backingService(BackingService
|
||||
.builder()
|
||||
@@ -112,7 +116,7 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
|
||||
new AppDeploymentDeleteServiceInstanceWorkflow(
|
||||
brokeredServices,
|
||||
backingAppDeploymentService,
|
||||
backingServicesProvisionService,
|
||||
backingAppManagementService, backingServicesProvisionService,
|
||||
credentialProviderService,
|
||||
targetService
|
||||
);
|
||||
@@ -125,14 +129,45 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
|
||||
|
||||
given(this.backingAppDeploymentService.undeploy(eq(backingApps)))
|
||||
.willReturn(Flux.just("undeployed1", "undeployed2"));
|
||||
given(this.backingAppManagementService.getDeployedBackingApplications(eq(request.getServiceInstanceId())))
|
||||
.willReturn(Mono.just(getExistingBackingAppsWithService("my-service-instance")));
|
||||
given(this.credentialProviderService.deleteCredentials(eq(backingApps), eq(request.getServiceInstanceId())))
|
||||
.willReturn(Mono.just(backingApps));
|
||||
given(this.targetService.addToBackingApplications(eq(backingApps), eq(targetSpec), eq("service-instance-id")))
|
||||
.willReturn(Mono.just(backingApps));
|
||||
given(this.targetService.addToBackingServices(eq(backingServices), eq(targetSpec), eq("service-instance-id")))
|
||||
.willReturn(Mono.just(backingServices));
|
||||
given(this.backingServicesProvisionService.deleteServiceInstance(eq(backingServices)))
|
||||
.willReturn(Flux.just("my-service-instance"));
|
||||
given(this.backingServicesProvisionService.deleteServiceInstance(argThat(backingServices -> {
|
||||
boolean nameMatch = "my-service-instance".equals(backingServices.get(0).getServiceInstanceName());
|
||||
boolean sizeMatch = backingServices.size() == 1;
|
||||
return sizeMatch && nameMatch;
|
||||
}))).willReturn(Flux.just("my-service-instance"));
|
||||
|
||||
StepVerifier
|
||||
.create(deleteServiceInstanceWorkflow.delete(request, response))
|
||||
.expectNext()
|
||||
.expectNext()
|
||||
.verifyComplete();
|
||||
|
||||
verifyNoMoreInteractionsWithServices();
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteServiceInstanceSucceedsWhenBackingServicesDifferFromConfiguration() {
|
||||
DeleteServiceInstanceRequest request = buildRequest("service1", "plan1");
|
||||
DeleteServiceInstanceResponse response = DeleteServiceInstanceResponse.builder().build();
|
||||
|
||||
given(this.backingAppDeploymentService.undeploy(eq(backingApps)))
|
||||
.willReturn(Flux.just("undeployed1", "undeployed2"));
|
||||
given(this.backingAppManagementService.getDeployedBackingApplications(eq(request.getServiceInstanceId())))
|
||||
.willReturn(Mono.just(getExistingBackingAppsWithService("different-service-instance")));
|
||||
given(this.credentialProviderService.deleteCredentials(eq(backingApps), eq(request.getServiceInstanceId())))
|
||||
.willReturn(Mono.just(backingApps));
|
||||
given(this.targetService.addToBackingApplications(eq(backingApps), eq(targetSpec), eq("service-instance-id")))
|
||||
.willReturn(Mono.just(backingApps));
|
||||
given(this.backingServicesProvisionService.deleteServiceInstance(argThat(backingServices -> {
|
||||
boolean nameMatch = "different-service-instance".equals(backingServices.get(0).getServiceInstanceName());
|
||||
boolean sizeMatch = backingServices.size() == 1;
|
||||
return sizeMatch && nameMatch;
|
||||
}))).willReturn(Flux.just("different-service-instance"));
|
||||
|
||||
StepVerifier
|
||||
.create(deleteServiceInstanceWorkflow.delete(request, response))
|
||||
@@ -148,6 +183,9 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
|
||||
DeleteServiceInstanceRequest request = buildRequest("unsupported-service", "plan1");
|
||||
DeleteServiceInstanceResponse response = DeleteServiceInstanceResponse.builder().build();
|
||||
|
||||
given(this.backingAppManagementService.getDeployedBackingApplications(eq(request.getServiceInstanceId())))
|
||||
.willReturn(Mono.just(BackingApplications.builder().build()));
|
||||
|
||||
StepVerifier
|
||||
.create(deleteServiceInstanceWorkflow.delete(request, response))
|
||||
.verifyComplete();
|
||||
@@ -183,4 +221,26 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
|
||||
.build();
|
||||
}
|
||||
|
||||
private BackingApplications getExistingBackingAppsWithService(String serviceInstanceName) {
|
||||
return BackingApplications
|
||||
.builder()
|
||||
.backingApplication(BackingApplication
|
||||
.builder()
|
||||
.name("app1")
|
||||
.services(ServicesSpec
|
||||
.builder()
|
||||
.serviceInstanceName(serviceInstanceName)
|
||||
.build())
|
||||
.build())
|
||||
.backingApplication(BackingApplication
|
||||
.builder()
|
||||
.name("app2")
|
||||
.services(ServicesSpec
|
||||
.builder()
|
||||
.serviceInstanceName(serviceInstanceName)
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.appbroker.workflow.instance;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -36,16 +37,19 @@ import org.springframework.cloud.appbroker.deployer.BackingServices;
|
||||
import org.springframework.cloud.appbroker.deployer.BackingServicesProvisionService;
|
||||
import org.springframework.cloud.appbroker.deployer.BrokeredService;
|
||||
import org.springframework.cloud.appbroker.deployer.BrokeredServices;
|
||||
import org.springframework.cloud.appbroker.deployer.ServicesSpec;
|
||||
import org.springframework.cloud.appbroker.deployer.TargetSpec;
|
||||
import org.springframework.cloud.appbroker.extensions.parameters.BackingApplicationsParametersTransformationService;
|
||||
import org.springframework.cloud.appbroker.extensions.parameters.BackingServicesParametersTransformationService;
|
||||
import org.springframework.cloud.appbroker.extensions.targets.TargetService;
|
||||
import org.springframework.cloud.appbroker.manager.BackingAppManagementService;
|
||||
import org.springframework.cloud.servicebroker.model.catalog.Plan;
|
||||
import org.springframework.cloud.servicebroker.model.catalog.ServiceDefinition;
|
||||
import org.springframework.cloud.servicebroker.model.instance.UpdateServiceInstanceRequest;
|
||||
import org.springframework.cloud.servicebroker.model.instance.UpdateServiceInstanceResponse;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -57,6 +61,9 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
|
||||
@Mock
|
||||
private BackingAppDeploymentService appDeploymentService;
|
||||
|
||||
@Mock
|
||||
private BackingAppManagementService backingAppManagementService;
|
||||
|
||||
@Mock
|
||||
private BackingServicesProvisionService servicesProvisionService;
|
||||
|
||||
@@ -118,6 +125,7 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
|
||||
updateServiceInstanceWorkflow = new AppDeploymentUpdateServiceInstanceWorkflow(
|
||||
brokeredServices,
|
||||
appDeploymentService,
|
||||
backingAppManagementService,
|
||||
servicesProvisionService,
|
||||
appsParametersTransformationService,
|
||||
servicesParametersTransformationService,
|
||||
@@ -125,12 +133,13 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({"unchecked", "UnassignedFluxMonoInstance"})
|
||||
@SuppressWarnings({"UnassignedFluxMonoInstance"})
|
||||
void updateServiceInstanceSucceeds() {
|
||||
UpdateServiceInstanceRequest request = buildRequest("service1", "plan1");
|
||||
UpdateServiceInstanceResponse response = UpdateServiceInstanceResponse.builder().build();
|
||||
|
||||
setupMocks(request);
|
||||
mockNoChangeInBackingServices(request);
|
||||
|
||||
StepVerifier
|
||||
.create(updateServiceInstanceWorkflow.update(request, response))
|
||||
@@ -155,6 +164,7 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
|
||||
UpdateServiceInstanceResponse response = UpdateServiceInstanceResponse.builder().build();
|
||||
|
||||
setupMocks(request);
|
||||
mockNoChangeInBackingServices(request);
|
||||
|
||||
StepVerifier
|
||||
.create(updateServiceInstanceWorkflow.update(request, response))
|
||||
@@ -165,11 +175,48 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
|
||||
verifyNoMoreInteractionsWithServices();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({"UnassignedFluxMonoInstance"})
|
||||
void updateServiceInstanceWithNewBackingServiceSucceeds() {
|
||||
UpdateServiceInstanceRequest request = buildRequest("service1", "plan1");
|
||||
UpdateServiceInstanceResponse response = UpdateServiceInstanceResponse.builder().build();
|
||||
|
||||
setupMocks(request);
|
||||
given(this.backingAppManagementService.getDeployedBackingApplications(eq(request.getServiceInstanceId())))
|
||||
.willReturn(Mono.just(getExistingBackingAppsWithService("existing-service-instance")));
|
||||
given(this.servicesProvisionService.createServiceInstance(any()))
|
||||
.willReturn(Flux.just("my-service-instance"));
|
||||
given(this.servicesProvisionService.deleteServiceInstance(any()))
|
||||
.willReturn(Flux.just("existing-service-instance"));
|
||||
given(this.servicesProvisionService.updateServiceInstance(eq(Collections.emptyList())))
|
||||
.willReturn(Flux.empty());
|
||||
|
||||
StepVerifier
|
||||
.create(updateServiceInstanceWorkflow.update(request, response))
|
||||
.expectNext()
|
||||
.expectNext()
|
||||
.verifyComplete();
|
||||
|
||||
verify(servicesProvisionService).createServiceInstance(backingServices);
|
||||
verify(servicesProvisionService).deleteServiceInstance(BackingServices
|
||||
.builder()
|
||||
.backingService(BackingService
|
||||
.builder()
|
||||
.serviceInstanceName("existing-service-instance")
|
||||
.build())
|
||||
.build());
|
||||
|
||||
verifyNoMoreInteractionsWithServices();
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateServiceInstanceWithNoAppsDoesNothing() {
|
||||
UpdateServiceInstanceRequest request = buildRequest("unsupported-service", "plan1");
|
||||
UpdateServiceInstanceResponse response = UpdateServiceInstanceResponse.builder().build();
|
||||
|
||||
given(this.backingAppManagementService.getDeployedBackingApplications(eq(request.getServiceInstanceId())))
|
||||
.willReturn(Mono.empty());
|
||||
|
||||
StepVerifier
|
||||
.create(updateServiceInstanceWorkflow.update(request, response))
|
||||
.verifyComplete();
|
||||
@@ -180,8 +227,6 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
|
||||
private void setupMocks(UpdateServiceInstanceRequest request) {
|
||||
given(this.appDeploymentService.update(eq(backingApps), eq(request.getServiceInstanceId())))
|
||||
.willReturn(Flux.just("app1", "app2"));
|
||||
given(this.servicesProvisionService.updateServiceInstance(eq(backingServices)))
|
||||
.willReturn(Flux.just("my-service-instance"));
|
||||
|
||||
given(
|
||||
this.appsParametersTransformationService.transformParameters(eq(backingApps), eq(request.getParameters())))
|
||||
@@ -197,6 +242,18 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
|
||||
.willReturn(Mono.just(backingServices));
|
||||
}
|
||||
|
||||
private void mockNoChangeInBackingServices(UpdateServiceInstanceRequest request) {
|
||||
given(this.servicesProvisionService.updateServiceInstance(eq(backingServices)))
|
||||
.willReturn(Flux.just("my-service-instance"));
|
||||
given(this.servicesProvisionService.createServiceInstance(any()))
|
||||
.willReturn(Flux.empty());
|
||||
given(this.servicesProvisionService.deleteServiceInstance(any()))
|
||||
.willReturn(Flux.empty());
|
||||
|
||||
given(this.backingAppManagementService.getDeployedBackingApplications(eq(request.getServiceInstanceId())))
|
||||
.willReturn(Mono.just(getExistingBackingAppsWithService("my-service-instance")));
|
||||
}
|
||||
|
||||
private void verifyNoMoreInteractionsWithServices() {
|
||||
verifyNoMoreInteractions(this.appDeploymentService);
|
||||
verifyNoMoreInteractions(this.servicesProvisionService);
|
||||
@@ -232,4 +289,26 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
|
||||
.build();
|
||||
}
|
||||
|
||||
private BackingApplications getExistingBackingAppsWithService(String serviceInstanceName) {
|
||||
return BackingApplications
|
||||
.builder()
|
||||
.backingApplication(BackingApplication
|
||||
.builder()
|
||||
.name("app1")
|
||||
.services(ServicesSpec
|
||||
.builder()
|
||||
.serviceInstanceName(serviceInstanceName)
|
||||
.build())
|
||||
.build())
|
||||
.backingApplication(BackingApplication
|
||||
.builder()
|
||||
.name("app2")
|
||||
.services(ServicesSpec
|
||||
.builder()
|
||||
.serviceInstanceName(serviceInstanceName)
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -39,6 +40,7 @@ import org.cloudfoundry.AbstractCloudFoundryException;
|
||||
import org.cloudfoundry.UnknownCloudFoundryException;
|
||||
import org.cloudfoundry.client.CloudFoundryClient;
|
||||
import org.cloudfoundry.client.v2.applications.AssociateApplicationRouteRequest;
|
||||
import org.cloudfoundry.client.v2.applications.SummaryApplicationRequest;
|
||||
import org.cloudfoundry.client.v2.organizations.GetOrganizationRequest;
|
||||
import org.cloudfoundry.client.v2.organizations.GetOrganizationResponse;
|
||||
import org.cloudfoundry.client.v2.organizations.ListOrganizationSpacesRequest;
|
||||
@@ -80,7 +82,6 @@ import org.cloudfoundry.operations.applications.ApplicationHealthCheck;
|
||||
import org.cloudfoundry.operations.applications.ApplicationManifest;
|
||||
import org.cloudfoundry.operations.applications.DeleteApplicationRequest;
|
||||
import org.cloudfoundry.operations.applications.Docker;
|
||||
import org.cloudfoundry.operations.applications.GetApplicationRequest;
|
||||
import org.cloudfoundry.operations.applications.PushApplicationManifestRequest;
|
||||
import org.cloudfoundry.operations.applications.Route;
|
||||
import org.cloudfoundry.operations.domains.Domain;
|
||||
@@ -111,6 +112,8 @@ import org.springframework.cloud.appbroker.deployer.DeleteServiceInstanceRespons
|
||||
import org.springframework.cloud.appbroker.deployer.DeployApplicationRequest;
|
||||
import org.springframework.cloud.appbroker.deployer.DeployApplicationResponse;
|
||||
import org.springframework.cloud.appbroker.deployer.DeploymentProperties;
|
||||
import org.springframework.cloud.appbroker.deployer.GetApplicationRequest;
|
||||
import org.springframework.cloud.appbroker.deployer.GetApplicationResponse;
|
||||
import org.springframework.cloud.appbroker.deployer.GetServiceInstanceRequest;
|
||||
import org.springframework.cloud.appbroker.deployer.GetServiceInstanceResponse;
|
||||
import org.springframework.cloud.appbroker.deployer.UndeployApplicationRequest;
|
||||
@@ -165,6 +168,34 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<GetApplicationResponse> get(GetApplicationRequest request) {
|
||||
final String name = request.getName();
|
||||
|
||||
return operationsUtils.getOperations(request.getProperties())
|
||||
.flatMap(cfOperations -> cfOperations.applications()
|
||||
.get(org.cloudfoundry.operations.applications.GetApplicationRequest.builder().name(name).build())
|
||||
.doOnRequest(l -> LOG.debug("Getting application {}", name))
|
||||
.doOnSuccess(response -> LOG.info("Success getting application {} id: {}", name, response.getId()))
|
||||
.doOnError(e -> LOG.warn(String.format("Error getting application %s: %s", name, e.getMessage())))
|
||||
.map(ApplicationDetail::getId)
|
||||
.flatMap(id ->
|
||||
client.applicationsV2()
|
||||
.summary(SummaryApplicationRequest.builder().applicationId(id).build())))
|
||||
.flatMap(summary -> Flux.fromIterable(summary.getServices())
|
||||
.map(org.cloudfoundry.client.v2.serviceinstances.ServiceInstance::getName)
|
||||
.collectList()
|
||||
.map(services -> GetApplicationResponse.builder()
|
||||
.id(summary.getId())
|
||||
.name(summary.getName())
|
||||
.services(services)
|
||||
.environment(summary.getEnvironmentJsons())
|
||||
.build()))
|
||||
.doOnRequest(l -> LOG.debug("Getting application summary for {}", name))
|
||||
.doOnSuccess(item -> LOG.info("Success getting application summary for {}", name))
|
||||
.doOnError(error -> LOG.error("Failed to get application summary for {}", name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<DeployApplicationResponse> deploy(DeployApplicationRequest request) {
|
||||
String appName = request.getName();
|
||||
@@ -200,37 +231,58 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
|
||||
public Mono<UpdateApplicationResponse> update(UpdateApplicationRequest request) {
|
||||
final String name = request.getName();
|
||||
|
||||
return operationsUtils.getOperations(request.getProperties())
|
||||
.flatMap(cfOperations -> cfOperations.applications()
|
||||
.get(GetApplicationRequest.builder().name(name).build())
|
||||
.doOnRequest(l -> LOG.debug("Getting application {}", name))
|
||||
.doOnSuccess(response -> LOG.info("Success getting application {}", name))
|
||||
.doOnError(e -> LOG.warn(String.format("Error getting application %s: %s", name, e.getMessage())))
|
||||
.map(ApplicationDetail::getId)
|
||||
.flatMap(applicationId -> associateHostName(applicationId, request.getProperties()))
|
||||
.flatMap(applicationId -> Mono.zip(Mono.just(applicationId),
|
||||
upgradeApplication(request, applicationId)))
|
||||
.flatMap(tuple2 -> {
|
||||
String packageId = tuple2.getT2();
|
||||
return Mono.zip(Mono.just(tuple2.getT1()), createBuildForPackage(packageId));
|
||||
})
|
||||
.flatMap(tuple2 -> {
|
||||
String buildId = tuple2.getT2();
|
||||
return Mono.zip(Mono.just(tuple2.getT1()), waitForBuildStaged(buildId));
|
||||
}
|
||||
)
|
||||
.map(tuple2 -> tuple2.mapT2((t2) -> t2.getDroplet().getId()))
|
||||
.flatMap(tuple2 -> {
|
||||
String dropletId = tuple2.getT2();
|
||||
String applicationId = tuple2.getT1();
|
||||
return createDeployment(dropletId, applicationId);
|
||||
})
|
||||
.map(CreateDeploymentResponse::getId)
|
||||
.flatMap(this::waitForDeploymentDeployed)
|
||||
.doOnRequest(l -> LOG.debug("Updating application {}", name))
|
||||
.doOnSuccess(item -> LOG.info("Successfully updated application {}", name))
|
||||
.doOnError(error -> LOG.error("Failed to update application {}", name))
|
||||
.thenReturn(UpdateApplicationResponse.builder().name(name).build()));
|
||||
return get(GetApplicationRequest.builder().name(name).properties(request.getProperties()).build())
|
||||
.flatMap(response -> bindNewServices(response, request.getServices(), request.getProperties()))
|
||||
.flatMap(applicationId -> associateHostName(applicationId, request.getProperties()))
|
||||
.flatMap(applicationId -> Mono.zip(Mono.just(applicationId),
|
||||
upgradeApplication(request, applicationId)))
|
||||
.flatMap(tuple2 -> {
|
||||
String packageId = tuple2.getT2();
|
||||
return Mono.zip(Mono.just(tuple2.getT1()), createBuildForPackage(packageId));
|
||||
})
|
||||
.flatMap(tuple2 -> {
|
||||
String buildId = tuple2.getT2();
|
||||
return Mono.zip(Mono.just(tuple2.getT1()), waitForBuildStaged(buildId));
|
||||
}
|
||||
)
|
||||
.map(tuple2 -> tuple2.mapT2((t2) -> t2.getDroplet().getId()))
|
||||
.flatMap(tuple2 -> {
|
||||
String dropletId = tuple2.getT2();
|
||||
String applicationId = tuple2.getT1();
|
||||
return createDeployment(dropletId, applicationId);
|
||||
})
|
||||
.map(CreateDeploymentResponse::getId)
|
||||
.flatMap(this::waitForDeploymentDeployed)
|
||||
.doOnRequest(l -> LOG.debug("Updating application {}", name))
|
||||
.doOnSuccess(item -> LOG.info("Successfully updated application {}", name))
|
||||
.doOnError(error -> LOG.error("Failed to update application {}", name))
|
||||
.thenReturn(UpdateApplicationResponse.builder().name(name).build());
|
||||
}
|
||||
|
||||
private Mono<String> bindNewServices(GetApplicationResponse deployedApp, List<String> services,
|
||||
Map<String, String> properties) {
|
||||
String id = deployedApp.getId();
|
||||
List<String> boundServices = deployedApp.getServices();
|
||||
List<String> servicesToBind = new ArrayList<>(services);
|
||||
servicesToBind.removeAll(boundServices);
|
||||
|
||||
if (servicesToBind.isEmpty()) {
|
||||
return Mono.just(id);
|
||||
}
|
||||
|
||||
String name = deployedApp.getName();
|
||||
return operationsUtils.getOperations(properties)
|
||||
.flatMapMany(cfOperations -> Flux.fromIterable(servicesToBind)
|
||||
.flatMap(service -> cfOperations.services()
|
||||
.bind(BindServiceInstanceRequest.builder()
|
||||
.applicationName(name)
|
||||
.serviceInstanceName(service)
|
||||
.build())
|
||||
.doOnRequest(l -> LOG.debug("Binding application {} to service {}", name, service))
|
||||
.doOnNext(item -> LOG.info("Successfully bind application {} to service {}", name, service))
|
||||
.doOnError(error -> LOG.error("Failed to bind application {} to service {}", name, service))))
|
||||
.then()
|
||||
.thenReturn(id);
|
||||
}
|
||||
|
||||
private Mono<String> associateHostName(String applicationId, Map<String, String> properties) {
|
||||
@@ -799,8 +851,8 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
|
||||
private Duration apiPollingTimeout(Map<String, String> properties) {
|
||||
return Duration.ofSeconds(
|
||||
Optional.ofNullable(properties.get(CloudFoundryDeploymentProperties.API_POLLING_TIMEOUT_PROPERTY_KEY))
|
||||
.map(Long::parseLong)
|
||||
.orElse(this.defaultDeploymentProperties.getApiPollingTimeout()));
|
||||
.map(Long::parseLong)
|
||||
.orElse(this.defaultDeploymentProperties.getApiPollingTimeout()));
|
||||
}
|
||||
|
||||
private Integer instances(Map<String, String> properties) {
|
||||
|
||||
@@ -23,6 +23,8 @@ 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.organizations.GetOrganizationRequest;
|
||||
import org.cloudfoundry.client.v2.organizations.GetOrganizationResponse;
|
||||
import org.cloudfoundry.client.v2.organizations.ListOrganizationSpacesRequest;
|
||||
@@ -38,6 +40,7 @@ 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;
|
||||
import org.cloudfoundry.operations.applications.ApplicationManifest;
|
||||
import org.cloudfoundry.operations.applications.Applications;
|
||||
@@ -69,6 +72,7 @@ import org.springframework.cloud.appbroker.deployer.CreateServiceInstanceRequest
|
||||
import org.springframework.cloud.appbroker.deployer.DeleteServiceInstanceRequest;
|
||||
import org.springframework.cloud.appbroker.deployer.DeployApplicationRequest;
|
||||
import org.springframework.cloud.appbroker.deployer.DeploymentProperties;
|
||||
import org.springframework.cloud.appbroker.deployer.GetApplicationRequest;
|
||||
import org.springframework.cloud.appbroker.deployer.UpdateServiceInstanceRequest;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
@@ -97,7 +101,9 @@ class CloudFoundryAppDeployerTest {
|
||||
private static final String APP_PATH = "test.jar";
|
||||
|
||||
private static final String SERVICE_INSTANCE_ID = "service-instance-id";
|
||||
private static final long DEFAULT_COMPLETION_DURATION = Duration.ofSeconds(DEFAULT_API_POLLING_TIMEOUT_SECONDS).getSeconds();
|
||||
|
||||
private static final long DEFAULT_COMPLETION_DURATION = Duration.ofSeconds(DEFAULT_API_POLLING_TIMEOUT_SECONDS)
|
||||
.getSeconds();
|
||||
|
||||
private static final int EXPECTED_MANIFESTS = 1;
|
||||
|
||||
@@ -121,6 +127,9 @@ class CloudFoundryAppDeployerTest {
|
||||
@Mock
|
||||
private org.cloudfoundry.client.v2.spaces.Spaces clientSpaces;
|
||||
|
||||
@Mock
|
||||
private org.cloudfoundry.client.v2.applications.ApplicationsV2 clientApplications;
|
||||
|
||||
@Mock
|
||||
private org.cloudfoundry.client.v2.organizations.Organizations clientOrganizations;
|
||||
|
||||
@@ -155,6 +164,7 @@ class CloudFoundryAppDeployerTest {
|
||||
given(cloudFoundryClient.serviceInstances()).willReturn(clientServiceInstances);
|
||||
given(cloudFoundryClient.spaces()).willReturn(clientSpaces);
|
||||
given(cloudFoundryClient.organizations()).willReturn(clientOrganizations);
|
||||
given(cloudFoundryClient.applicationsV2()).willReturn(clientApplications);
|
||||
given(operationsUtils.getOperations(anyMap())).willReturn(Mono.just(cloudFoundryOperations));
|
||||
given(operationsUtils.getOperationsForSpace(anyString())).willReturn(Mono.just(cloudFoundryOperations));
|
||||
given(operationsUtils.getOperationsForOrgAndSpace(anyString(), anyString()))
|
||||
@@ -916,6 +926,95 @@ class CloudFoundryAppDeployerTest {
|
||||
then(operationsUtils).shouldHaveNoMoreInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getDeployedAppByName() {
|
||||
given(operationsApplications.get(any(org.cloudfoundry.operations.applications.GetApplicationRequest.class)))
|
||||
.willReturn(Mono.just(ApplicationDetail.builder()
|
||||
.id("foo-id")
|
||||
.name("foo-name")
|
||||
// fields below are required by builder but we don't care
|
||||
.stack("").diskQuota(1).instances(1).memoryLimit(1).requestedState("").runningInstances(1) //
|
||||
.build()));
|
||||
given(clientApplications.summary(any(SummaryApplicationRequest.class)))
|
||||
.willReturn(Mono.just(SummaryApplicationResponse.builder()
|
||||
.id("foo-id")
|
||||
.name("foo-name")
|
||||
.service(org.cloudfoundry.client.v2.serviceinstances.ServiceInstance.builder()
|
||||
.name("foo-service-1")
|
||||
.build())
|
||||
.service(org.cloudfoundry.client.v2.serviceinstances.ServiceInstance.builder()
|
||||
.name("foo-service-2")
|
||||
.build())
|
||||
.environmentJson("foo-key", "foo-value")
|
||||
.build()));
|
||||
|
||||
StepVerifier.create(appDeployer.get(GetApplicationRequest.builder().name("foo-name").build()))
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getName()).isEqualTo("foo-name");
|
||||
assertThat(response.getServices()).hasSize(2);
|
||||
assertThat(response.getServices().get(0)).isEqualTo("foo-service-1");
|
||||
assertThat(response.getServices().get(1)).isEqualTo("foo-service-2");
|
||||
assertThat(response.getEnvironment()).isEqualTo(singletonMap("foo-key", "foo-value"));
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
then(operationsUtils).should().getOperations(argThat(CollectionUtils::isEmpty));
|
||||
then(cloudFoundryOperations).should().applications();
|
||||
then(operationsApplications).should().get(argThat(request -> "foo-name".equals(request.getName())));
|
||||
then(clientApplications).should().summary(argThat(request -> "foo-id".equals(request.getApplicationId())));
|
||||
then(operationsApplications).shouldHaveNoMoreInteractions();
|
||||
then(clientApplications).shouldHaveNoMoreInteractions();
|
||||
then(cloudFoundryOperations).shouldHaveNoMoreInteractions();
|
||||
then(operationsUtils).shouldHaveNoMoreInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getDeployedAppByNameAndSpace() {
|
||||
given(operationsApplications.get(any(org.cloudfoundry.operations.applications.GetApplicationRequest.class)))
|
||||
.willReturn(Mono.just(ApplicationDetail.builder()
|
||||
.id("foo-id")
|
||||
.name("foo-name")
|
||||
// fields below are required by builder but we don't care
|
||||
.stack("").diskQuota(1).instances(1).memoryLimit(1).requestedState("").runningInstances(1) //
|
||||
.build()));
|
||||
given(clientApplications.summary(any(SummaryApplicationRequest.class)))
|
||||
.willReturn(Mono.just(SummaryApplicationResponse.builder()
|
||||
.id("foo-id")
|
||||
.name("foo-name")
|
||||
.service(org.cloudfoundry.client.v2.serviceinstances.ServiceInstance.builder()
|
||||
.name("foo-service-1")
|
||||
.build())
|
||||
.service(org.cloudfoundry.client.v2.serviceinstances.ServiceInstance.builder()
|
||||
.name("foo-service-2")
|
||||
.build())
|
||||
.environmentJson("foo-key", "foo-value")
|
||||
.build()));
|
||||
|
||||
GetApplicationRequest request = GetApplicationRequest.builder()
|
||||
.name("foo-name")
|
||||
.properties(singletonMap(TARGET_PROPERTY_KEY, "foo-space"))
|
||||
.build();
|
||||
StepVerifier.create(appDeployer.get(request))
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getName()).isEqualTo("foo-name");
|
||||
assertThat(response.getServices()).hasSize(2);
|
||||
assertThat(response.getServices().get(0)).isEqualTo("foo-service-1");
|
||||
assertThat(response.getServices().get(1)).isEqualTo("foo-service-2");
|
||||
assertThat(response.getEnvironment()).isEqualTo(singletonMap("foo-key", "foo-value"));
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
then(operationsUtils).should().getOperations(
|
||||
argThat(argument -> "foo-space".equals(argument.get(TARGET_PROPERTY_KEY))));
|
||||
then(cloudFoundryOperations).should().applications();
|
||||
then(operationsApplications).should().get(argThat(req -> "foo-name".equals(req.getName())));
|
||||
then(clientApplications).should().summary(argThat(req -> "foo-id".equals(req.getApplicationId())));
|
||||
then(operationsApplications).shouldHaveNoMoreInteractions();
|
||||
then(clientApplications).shouldHaveNoMoreInteractions();
|
||||
then(cloudFoundryOperations).shouldHaveNoMoreInteractions();
|
||||
then(operationsUtils).shouldHaveNoMoreInteractions();
|
||||
}
|
||||
|
||||
private ApplicationManifest.Builder baseManifest() {
|
||||
return ApplicationManifest.builder()
|
||||
.services(new ArrayList<>());
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.appbroker.deployer.cloudfoundry;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -23,10 +24,13 @@ import org.cloudfoundry.client.CloudFoundryClient;
|
||||
import org.cloudfoundry.client.v2.Metadata;
|
||||
import org.cloudfoundry.client.v2.applications.ApplicationsV2;
|
||||
import org.cloudfoundry.client.v2.applications.AssociateApplicationRouteRequest;
|
||||
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.routes.CreateRouteRequest;
|
||||
import org.cloudfoundry.client.v2.routes.CreateRouteResponse;
|
||||
import org.cloudfoundry.client.v2.routes.Routes;
|
||||
import org.cloudfoundry.client.v2.serviceinstances.ServiceInstance;
|
||||
import org.cloudfoundry.client.v3.BuildpackData;
|
||||
import org.cloudfoundry.client.v3.Lifecycle;
|
||||
import org.cloudfoundry.client.v3.LifecycleType;
|
||||
@@ -57,6 +61,8 @@ import org.cloudfoundry.operations.applications.Applications;
|
||||
import org.cloudfoundry.operations.domains.Domain;
|
||||
import org.cloudfoundry.operations.domains.Domains;
|
||||
import org.cloudfoundry.operations.domains.Status;
|
||||
import org.cloudfoundry.operations.services.BindServiceInstanceRequest;
|
||||
import org.cloudfoundry.operations.services.Services;
|
||||
import org.cloudfoundry.operations.spaces.SpaceDetail;
|
||||
import org.cloudfoundry.operations.spaces.Spaces;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -76,17 +82,22 @@ import org.springframework.cloud.appbroker.deployer.UpdateApplicationRequest;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.springframework.cloud.appbroker.deployer.DeploymentProperties.TARGET_PROPERTY_KEY;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class CloudFoundryAppDeployerUpdateApplicationTest {
|
||||
|
||||
private static final String APP_ID = "app-id";
|
||||
|
||||
private static final String APP_NAME = "test-app";
|
||||
|
||||
private static final String APP_PATH = "test.jar";
|
||||
@@ -96,6 +107,9 @@ class CloudFoundryAppDeployerUpdateApplicationTest {
|
||||
@Mock
|
||||
private Applications operationsApplications;
|
||||
|
||||
@Mock
|
||||
private Services operationsServices;
|
||||
|
||||
@Mock
|
||||
private ApplicationsV2 applicationsV2;
|
||||
|
||||
@@ -142,6 +156,7 @@ class CloudFoundryAppDeployerUpdateApplicationTest {
|
||||
given(operationsApplications.pushManifest(any())).willReturn(Mono.empty());
|
||||
given(resourceLoader.getResource(APP_PATH)).willReturn(new FileSystemResource(APP_PATH));
|
||||
|
||||
given(cloudFoundryOperations.services()).willReturn(operationsServices);
|
||||
given(cloudFoundryOperations.applications()).willReturn(operationsApplications);
|
||||
given(cloudFoundryOperations.domains()).willReturn(domains);
|
||||
given(cloudFoundryOperations.spaces()).willReturn(spaces);
|
||||
@@ -160,6 +175,13 @@ class CloudFoundryAppDeployerUpdateApplicationTest {
|
||||
given(operationsApplications.get(any()))
|
||||
.willReturn(Mono.just(createApplicationDetail()));
|
||||
|
||||
given(applicationsV2.summary(any(SummaryApplicationRequest.class)))
|
||||
.willReturn(Mono.just(SummaryApplicationResponse.builder()
|
||||
.id(APP_ID)
|
||||
.name(APP_NAME)
|
||||
.services(Collections.emptyList())
|
||||
.build()));
|
||||
|
||||
given(packages.create(any()))
|
||||
.willReturn(Mono.just(CreatePackageResponse.builder()
|
||||
.data(BitsData.builder().build())
|
||||
@@ -247,6 +269,27 @@ class CloudFoundryAppDeployerUpdateApplicationTest {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateAppWithTarget() {
|
||||
given(applicationsV2.update(any()))
|
||||
.willReturn(Mono.just(UpdateApplicationResponse.builder()
|
||||
.build()));
|
||||
|
||||
Map<String, String> properties = singletonMap(TARGET_PROPERTY_KEY, "service-instance-id");
|
||||
UpdateApplicationRequest request = UpdateApplicationRequest.builder()
|
||||
.name(APP_NAME)
|
||||
.path(APP_PATH)
|
||||
.properties(properties)
|
||||
.build();
|
||||
|
||||
StepVerifier.create(appDeployer.update(request))
|
||||
.assertNext(response -> assertThat(response.getName()).isEqualTo(APP_NAME))
|
||||
.verifyComplete();
|
||||
|
||||
then(operationsUtils).should().getOperations(properties);
|
||||
then(operationsUtils).shouldHaveNoMoreInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateAppProperties() {
|
||||
ArgumentCaptor<org.cloudfoundry.client.v2.applications.UpdateApplicationRequest> updateApplicationRequestCaptor =
|
||||
@@ -295,7 +338,74 @@ class CloudFoundryAppDeployerUpdateApplicationTest {
|
||||
.assertNext(response -> assertThat(response.getName()).isEqualTo(APP_NAME))
|
||||
.verifyComplete();
|
||||
|
||||
then(applicationsV2).shouldHaveNoInteractions();
|
||||
then(applicationsV2).should().summary(any(SummaryApplicationRequest.class));
|
||||
then(applicationsV2).shouldHaveNoMoreInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateAppWithNoNewServices() {
|
||||
given(applicationsV2.summary(any(SummaryApplicationRequest.class)))
|
||||
.willReturn(Mono.just(SummaryApplicationResponse.builder()
|
||||
.id(APP_ID)
|
||||
.name(APP_NAME)
|
||||
.service(ServiceInstance.builder()
|
||||
.name("service-1")
|
||||
.build())
|
||||
.service(ServiceInstance.builder()
|
||||
.name("service-2")
|
||||
.build())
|
||||
.build()));
|
||||
given(applicationsV2.update(any()))
|
||||
.willReturn(Mono.just(UpdateApplicationResponse.builder()
|
||||
.build()));
|
||||
|
||||
UpdateApplicationRequest request = UpdateApplicationRequest.builder()
|
||||
.name(APP_NAME)
|
||||
.path(APP_PATH)
|
||||
.service("service-1")
|
||||
.service("service-2")
|
||||
.build();
|
||||
|
||||
StepVerifier.create(appDeployer.update(request))
|
||||
.assertNext(response -> assertThat(response.getName()).isEqualTo(APP_NAME))
|
||||
.verifyComplete();
|
||||
|
||||
then(applicationsV2).should().summary(SummaryApplicationRequest.builder().applicationId(APP_ID).build());
|
||||
then(applicationsV2).should().update(argThat(arg -> arg.getApplicationId().equals(APP_ID)));
|
||||
then(operationsServices).shouldHaveNoMoreInteractions();
|
||||
then(applicationsV2).shouldHaveNoMoreInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateAppWithNewServices() {
|
||||
given(operationsServices.bind(any(BindServiceInstanceRequest.class)))
|
||||
.willReturn(Mono.empty());
|
||||
given(applicationsV2.update(any()))
|
||||
.willReturn(Mono.just(UpdateApplicationResponse.builder()
|
||||
.build()));
|
||||
|
||||
UpdateApplicationRequest request = UpdateApplicationRequest.builder()
|
||||
.name(APP_NAME)
|
||||
.path(APP_PATH)
|
||||
.service("service-1")
|
||||
.service("service-2")
|
||||
.build();
|
||||
|
||||
StepVerifier.create(appDeployer.update(request))
|
||||
.assertNext(response -> assertThat(response.getName()).isEqualTo(APP_NAME))
|
||||
.verifyComplete();
|
||||
|
||||
then(applicationsV2).should().summary(SummaryApplicationRequest.builder().applicationId(APP_ID).build());
|
||||
then(operationsServices).should()
|
||||
.bind(BindServiceInstanceRequest.builder().serviceInstanceName("service-1").applicationName(APP_NAME)
|
||||
.build());
|
||||
then(operationsServices).should()
|
||||
.bind(BindServiceInstanceRequest.builder().serviceInstanceName("service-2").applicationName(APP_NAME)
|
||||
.build());
|
||||
then(applicationsV2).should().update(argThat(arg -> arg.getApplicationId().equals(APP_ID)));
|
||||
|
||||
then(operationsServices).shouldHaveNoMoreInteractions();
|
||||
then(applicationsV2).shouldHaveNoMoreInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -342,7 +452,7 @@ class CloudFoundryAppDeployerUpdateApplicationTest {
|
||||
.host("my.host")
|
||||
.build());
|
||||
then(applicationsV2).should().associateRoute(AssociateApplicationRouteRequest.builder()
|
||||
.applicationId("app-id")
|
||||
.applicationId(APP_ID)
|
||||
.routeId("route-id")
|
||||
.build());
|
||||
}
|
||||
@@ -376,9 +486,9 @@ class CloudFoundryAppDeployerUpdateApplicationTest {
|
||||
.assertNext(response -> assertThat(response.getName()).isEqualTo(APP_NAME))
|
||||
.verifyComplete();
|
||||
|
||||
verifyRouteCreatedAndMapped("myDomainComId", "my.host", "space-id", "app-id");
|
||||
verifyRouteCreatedAndMapped("myDomainInternalId", "my.host", "space-id", "app-id");
|
||||
verifyRouteCreatedAndMapped("myDomainDefaultId", "my.host", "space-id", "app-id");
|
||||
verifyRouteCreatedAndMapped("myDomainComId", "my.host", "space-id", APP_ID);
|
||||
verifyRouteCreatedAndMapped("myDomainInternalId", "my.host", "space-id", APP_ID);
|
||||
verifyRouteCreatedAndMapped("myDomainDefaultId", "my.host", "space-id", APP_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -409,8 +519,8 @@ class CloudFoundryAppDeployerUpdateApplicationTest {
|
||||
.assertNext(response -> assertThat(response.getName()).isEqualTo(APP_NAME))
|
||||
.verifyComplete();
|
||||
|
||||
verifyRouteCreatedAndMapped("myDomainInternalId", "my.host", "space-id", "app-id");
|
||||
verifyRouteCreatedAndMapped("myDomainDefaultId", "my.host", "space-id", "app-id");
|
||||
verifyRouteCreatedAndMapped("myDomainInternalId", "my.host", "space-id", APP_ID);
|
||||
verifyRouteCreatedAndMapped("myDomainDefaultId", "my.host", "space-id", APP_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -457,7 +567,7 @@ class CloudFoundryAppDeployerUpdateApplicationTest {
|
||||
.host("my.host")
|
||||
.build());
|
||||
then(applicationsV2).should().associateRoute(AssociateApplicationRouteRequest.builder()
|
||||
.applicationId("app-id")
|
||||
.applicationId(APP_ID)
|
||||
.routeId("route-id")
|
||||
.build());
|
||||
}
|
||||
@@ -508,7 +618,7 @@ class CloudFoundryAppDeployerUpdateApplicationTest {
|
||||
.build());
|
||||
then(applicationsV2).should().associateRoute(AssociateApplicationRouteRequest
|
||||
.builder()
|
||||
.applicationId("app-id")
|
||||
.applicationId(APP_ID)
|
||||
.routeId("route-id")
|
||||
.build());
|
||||
}
|
||||
@@ -572,12 +682,12 @@ class CloudFoundryAppDeployerUpdateApplicationTest {
|
||||
private ApplicationDetail createApplicationDetail() {
|
||||
return ApplicationDetail
|
||||
.builder()
|
||||
.id("app-id")
|
||||
.id(APP_ID)
|
||||
.stack("")
|
||||
.diskQuota(512)
|
||||
.instances(1)
|
||||
.memoryLimit(512)
|
||||
.name("app")
|
||||
.name(APP_NAME)
|
||||
.requestedState("STARTED")
|
||||
.runningInstances(1)
|
||||
.build();
|
||||
|
||||
@@ -32,6 +32,10 @@ public interface AppDeployer {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
default Mono<GetApplicationResponse> get(GetApplicationRequest request) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
default Mono<GetServiceInstanceResponse> getServiceInstance(GetServiceInstanceRequest request) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
public class GetApplicationRequest {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final Map<String, String> properties;
|
||||
|
||||
protected GetApplicationRequest(String name, Map<String, String> properties) {
|
||||
this.name = name;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public static GetDeployedAppRequestBuilder builder() {
|
||||
return new GetDeployedAppRequestBuilder();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Map<String, String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public static final class GetDeployedAppRequestBuilder {
|
||||
|
||||
private String name;
|
||||
|
||||
private final Map<String, String> properties = new HashMap<>();
|
||||
|
||||
private GetDeployedAppRequestBuilder() {
|
||||
}
|
||||
|
||||
public GetDeployedAppRequestBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GetDeployedAppRequestBuilder properties(Map<String, String> properties) {
|
||||
if (!CollectionUtils.isEmpty(properties)) {
|
||||
this.properties.putAll(properties);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GetApplicationRequest build() {
|
||||
return new GetApplicationRequest(name, properties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
public class GetApplicationResponse {
|
||||
|
||||
private final String id;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final Map<String, Object> environment;
|
||||
|
||||
private final List<String> services;
|
||||
|
||||
protected GetApplicationResponse(String id, String name, Map<String, Object> environment, List<String> services) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.environment = environment;
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Map<String, Object> getEnvironment() {
|
||||
return environment;
|
||||
}
|
||||
|
||||
public List<String> getServices() {
|
||||
return services;
|
||||
}
|
||||
|
||||
public static GetApplicationResponseBuilder builder() {
|
||||
return new GetApplicationResponseBuilder();
|
||||
}
|
||||
|
||||
public static final class GetApplicationResponseBuilder {
|
||||
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
private final Map<String, Object> environment = new HashMap<>();
|
||||
|
||||
private final List<String> services = new ArrayList<>();
|
||||
|
||||
private GetApplicationResponseBuilder() {
|
||||
}
|
||||
|
||||
public GetApplicationResponseBuilder id(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GetApplicationResponseBuilder name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GetApplicationResponseBuilder environment(String key, String value) {
|
||||
if (key != null && value != null) {
|
||||
this.environment.put(key, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GetApplicationResponseBuilder environment(Map<String, Object> environment) {
|
||||
if (!CollectionUtils.isEmpty(environment)) {
|
||||
this.environment.putAll(environment);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GetApplicationResponseBuilder service(String service) {
|
||||
if (service != null) {
|
||||
this.services.add(service);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GetApplicationResponseBuilder services(List<String> services) {
|
||||
if (!CollectionUtils.isEmpty(services)) {
|
||||
this.services.addAll(services);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GetApplicationResponse build() {
|
||||
return new GetApplicationResponse(id, name, environment, services);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -331,7 +331,9 @@ Spring Cloud App Broker provides the https://docs.spring.io/spring-cloud-app-bro
|
||||
|
||||
=== Updating a Service Instance
|
||||
|
||||
Spring Cloud App Broker provides the https://docs.spring.io/spring-cloud-app-broker/docs/1.0.0.BUILD-SNAPSHOT/api/org/springframework/cloud/appbroker/workflow/instance/AppDeploymentUpdateServiceInstanceWorkflow.html[`AppDeploymentUpdateServiceInstanceWorkflow`] workflow, which handles updating the configured backing applications and services as illustrated in the previous sections. The service broker application can implement the https://docs.spring.io/spring-cloud-app-broker/docs/1.0.0.BUILD-SNAPSHOT/api/org/springframework/cloud/appbroker/service/UpdateServiceInstanceWorkflow.html[`UpdateServiceInstanceWorkflow`] interface to further modify the deployment. Multiple workflows may be annotated with `@Order` so as to process the workflows in a specific order. Alternatively, the service broker application can implement the `ServiceInstanceService` interface provided by Spring Cloud Open Service Broker. See https://docs.spring.io/spring-cloud-open-service-broker/docs/current/reference/html5/#service-instances[Service Instances] in the https://docs.spring.io/spring-cloud-open-service-broker/docs/current/reference/html5/[Spring Cloud Open Service Broker documentation].
|
||||
Spring Cloud App Broker provides the https://docs.spring.io/spring-cloud-app-broker/docs/1.0.0.BUILD-SNAPSHOT/api/org/springframework/cloud/appbroker/workflow/instance/AppDeploymentUpdateServiceInstanceWorkflow.html[`AppDeploymentUpdateServiceInstanceWorkflow`] workflow, which handles updating the configured backing applications and services as illustrated in the previous sections. If the list of backing services is updated, the default behavior is to create and bind the new backing service instances, and to unbind and delete the existing backing service instances that are no longer listed in the configuration.
|
||||
|
||||
The service broker application can implement the https://docs.spring.io/spring-cloud-app-broker/docs/1.0.0.BUILD-SNAPSHOT/api/org/springframework/cloud/appbroker/service/UpdateServiceInstanceWorkflow.html[`UpdateServiceInstanceWorkflow`] interface to further modify the deployment. Multiple workflows may be annotated with `@Order` so as to process the workflows in a specific order. Alternatively, the service broker application can implement the `ServiceInstanceService` interface provided by Spring Cloud Open Service Broker. See https://docs.spring.io/spring-cloud-open-service-broker/docs/current/reference/html5/#service-instances[Service Instances] in the https://docs.spring.io/spring-cloud-open-service-broker/docs/current/reference/html5/[Spring Cloud Open Service Broker documentation].
|
||||
|
||||
CAUTION: Modifying certain properties, such as disk and memory, when updating an application, may result in downtime.
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.springframework.cloud.appbroker.integration.fixtures.CloudControllerStubFixture.planGuid;
|
||||
import static org.springframework.cloud.appbroker.integration.fixtures.CloudControllerStubFixture.serviceGuid;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@SpringBootTest(
|
||||
@@ -54,12 +56,13 @@ class CatalogComponentTest {
|
||||
.then()
|
||||
.statusCode(HttpStatus.OK.value())
|
||||
.body("services[0].name", equalTo("example"))
|
||||
.body("services[0].id", equalTo(serviceGuid("example")))
|
||||
.body("services[0].description", equalTo("A simple example"))
|
||||
.body("services[0].bindable", equalTo(true))
|
||||
.body("services[0].metadata.size()", is(0))
|
||||
.body("services[0].plan_updateable", equalTo(null))
|
||||
.body("services[0].instances_retrievable", equalTo(null))
|
||||
.body("services[0].plans[0].id", equalTo("standard-plan-id"))
|
||||
.body("services[0].plans[0].id", equalTo(planGuid("standard")))
|
||||
.body("services[0].plans[0].name", equalTo("standard"))
|
||||
.body("services[0].plans[0].metadata", equalTo(null))
|
||||
.body("services[0].plans[0].bindable", equalTo(true))
|
||||
|
||||
@@ -33,10 +33,12 @@ import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.springframework.cloud.appbroker.integration.CreateInstanceWithOAuth2CredentialsComponentTest.APP_NAME_1;
|
||||
import static org.springframework.cloud.appbroker.integration.CreateInstanceWithOAuth2CredentialsComponentTest.APP_NAME_2;
|
||||
import static org.springframework.cloud.appbroker.integration.CreateInstanceWithOAuth2CredentialsComponentTest.PLAN_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.CreateInstanceWithOAuth2CredentialsComponentTest.SERVICE_NAME;
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
"spring.cloud.appbroker.services[0].service-name=example",
|
||||
"spring.cloud.appbroker.services[0].plan-name=standard",
|
||||
"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_1,
|
||||
@@ -69,6 +71,10 @@ class CreateInstanceWithOAuth2CredentialsComponentTest extends WiremockComponent
|
||||
|
||||
protected static final String APP_NAME_2 = "app-with-outh2-credentials2";
|
||||
|
||||
protected static final String SERVICE_NAME = "example";
|
||||
|
||||
protected static final String PLAN_NAME = "standard";
|
||||
|
||||
private static final String SERVICE_INSTANCE_ID = "instance-id";
|
||||
|
||||
@Autowired
|
||||
@@ -122,6 +128,8 @@ class CreateInstanceWithOAuth2CredentialsComponentTest extends WiremockComponent
|
||||
|
||||
@Test
|
||||
void deleteAppWithOAuth2Credentials() {
|
||||
cloudControllerFixture.stubGetServiceInstanceWithNoBinding("instance-id", "instance-name",
|
||||
SERVICE_NAME, PLAN_NAME);
|
||||
cloudControllerFixture.stubAppExists(APP_NAME_1);
|
||||
cloudControllerFixture.stubServiceBindingDoesNotExist(APP_NAME_1);
|
||||
cloudControllerFixture.stubDeleteApp(APP_NAME_1);
|
||||
|
||||
@@ -47,6 +47,8 @@ class CreateInstanceWithOnlyABackingServiceComponentTest extends WiremockCompone
|
||||
|
||||
protected static final String BACKING_SERVICE_NAME = "db-service";
|
||||
|
||||
protected static final String BACKING_PLAN_NAME = "standard";
|
||||
|
||||
@Autowired
|
||||
private OpenServiceBrokerApiFixture brokerFixture;
|
||||
|
||||
@@ -57,7 +59,7 @@ class CreateInstanceWithOnlyABackingServiceComponentTest extends WiremockCompone
|
||||
void createsServicesWhenOnlyBackingServiceIsRequested() {
|
||||
|
||||
// given services are available in the marketplace
|
||||
cloudControllerFixture.stubServiceExists(BACKING_SERVICE_NAME);
|
||||
cloudControllerFixture.stubServiceExists(BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
|
||||
|
||||
// will create the service instance
|
||||
cloudControllerFixture.stubCreateServiceInstance(BACKING_SI_NAME);
|
||||
|
||||
@@ -54,6 +54,8 @@ class CreateInstanceWithServiceInstanceGuidSuffixTargetComponentTest extends Wir
|
||||
|
||||
protected static final String BACKING_SERVICE_NAME = "db-service";
|
||||
|
||||
protected static final String BACKING_PLAN_NAME = "standard";
|
||||
|
||||
@Autowired
|
||||
private OpenServiceBrokerApiFixture brokerFixture;
|
||||
|
||||
@@ -70,7 +72,7 @@ class CreateInstanceWithServiceInstanceGuidSuffixTargetComponentTest extends Wir
|
||||
cloudControllerFixture.stubPushApp(applicationName);
|
||||
|
||||
// given services are available in the marketplace
|
||||
cloudControllerFixture.stubServiceExists(BACKING_SERVICE_NAME);
|
||||
cloudControllerFixture.stubServiceExists(BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
|
||||
|
||||
// will create and bind the service instance
|
||||
cloudControllerFixture.stubCreateServiceInstance(backingServiceInstanceName);
|
||||
|
||||
@@ -51,6 +51,8 @@ class CreateInstanceWithServicesComponentTest extends WiremockComponentTest {
|
||||
|
||||
protected static final String BACKING_SERVICE_NAME = "db-service";
|
||||
|
||||
protected static final String BACKING_PLAN_NAME = "standard";
|
||||
|
||||
@Autowired
|
||||
private OpenServiceBrokerApiFixture brokerFixture;
|
||||
|
||||
@@ -63,7 +65,7 @@ class CreateInstanceWithServicesComponentTest extends WiremockComponentTest {
|
||||
cloudControllerFixture.stubPushApp(APP_NAME);
|
||||
|
||||
// given services are available in the marketplace
|
||||
cloudControllerFixture.stubServiceExists(BACKING_SERVICE_NAME);
|
||||
cloudControllerFixture.stubServiceExists(BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
|
||||
|
||||
// will create and bind the service instance
|
||||
cloudControllerFixture.stubCreateServiceInstance(BACKING_SI_NAME);
|
||||
|
||||
@@ -56,6 +56,8 @@ class CreateInstanceWithServicesParametersComponentTest extends WiremockComponen
|
||||
|
||||
protected static final String BACKING_SERVICE_NAME = "db-service";
|
||||
|
||||
protected static final String BACKING_PLAN_NAME = "standard";
|
||||
|
||||
@Autowired
|
||||
private OpenServiceBrokerApiFixture brokerFixture;
|
||||
|
||||
@@ -68,7 +70,7 @@ class CreateInstanceWithServicesParametersComponentTest extends WiremockComponen
|
||||
cloudControllerFixture.stubPushApp(APP_NAME);
|
||||
|
||||
// given services are available in the marketplace
|
||||
cloudControllerFixture.stubServiceExists(BACKING_SERVICE_NAME);
|
||||
cloudControllerFixture.stubServiceExists(BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
|
||||
|
||||
// will create with filtered parameters and bind the service instance
|
||||
HashMap<String, Object> expectedCreationParameters = new HashMap<>();
|
||||
|
||||
@@ -54,6 +54,8 @@ class CreateInstanceWithSpacePerServiceInstanceTargetComponentTest extends Wirem
|
||||
|
||||
protected static final String BACKING_SERVICE_NAME = "db-service";
|
||||
|
||||
protected static final String BACKING_PLAN_NAME = "standard";
|
||||
|
||||
@Autowired
|
||||
private OpenServiceBrokerApiFixture brokerFixture;
|
||||
|
||||
@@ -69,7 +71,7 @@ class CreateInstanceWithSpacePerServiceInstanceTargetComponentTest extends Wirem
|
||||
cloudControllerFixture.stubPushAppWithHost(APP_NAME, APP_NAME + "-" + serviceInstanceId);
|
||||
|
||||
// given services are available in the marketplace
|
||||
cloudControllerFixture.stubServiceExists(BACKING_SERVICE_NAME);
|
||||
cloudControllerFixture.stubServiceExists(BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
|
||||
|
||||
// will create and bind the service instance
|
||||
cloudControllerFixture.stubCreateServiceInstance(BACKING_SI_NAME);
|
||||
|
||||
@@ -31,10 +31,12 @@ import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.springframework.cloud.appbroker.integration.DeleteInstanceComponentTest.APP_NAME_1;
|
||||
import static org.springframework.cloud.appbroker.integration.DeleteInstanceComponentTest.APP_NAME_2;
|
||||
import static org.springframework.cloud.appbroker.integration.DeleteInstanceComponentTest.PLAN_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.DeleteInstanceComponentTest.SERVICE_NAME;
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
"spring.cloud.appbroker.services[0].service-name=example",
|
||||
"spring.cloud.appbroker.services[0].plan-name=standard",
|
||||
"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_1,
|
||||
"spring.cloud.appbroker.services[0].apps[1].path=classpath:demo.jar",
|
||||
@@ -46,6 +48,10 @@ class DeleteInstanceComponentTest extends WiremockComponentTest {
|
||||
|
||||
protected static final String APP_NAME_2 = "second-app";
|
||||
|
||||
protected static final String SERVICE_NAME = "example";
|
||||
|
||||
protected static final String PLAN_NAME = "standard";
|
||||
|
||||
@Autowired
|
||||
private OpenServiceBrokerApiFixture brokerFixture;
|
||||
|
||||
@@ -54,6 +60,9 @@ class DeleteInstanceComponentTest extends WiremockComponentTest {
|
||||
|
||||
@Test
|
||||
void deleteAppsWhenTheyExist() {
|
||||
cloudControllerFixture.stubGetServiceInstanceWithNoBinding("instance-id", "instance-name",
|
||||
SERVICE_NAME, PLAN_NAME);
|
||||
|
||||
cloudControllerFixture.stubAppExists(APP_NAME_1);
|
||||
cloudControllerFixture.stubAppExists(APP_NAME_2);
|
||||
|
||||
@@ -84,6 +93,9 @@ class DeleteInstanceComponentTest extends WiremockComponentTest {
|
||||
|
||||
@Test
|
||||
void deleteAppsWhenTheyDoNotExist() {
|
||||
cloudControllerFixture.stubGetServiceInstanceWithNoBinding("instance-id", "instance-name",
|
||||
SERVICE_NAME, PLAN_NAME);
|
||||
|
||||
cloudControllerFixture.stubAppDoesNotExist(APP_NAME_1);
|
||||
cloudControllerFixture.stubAppDoesNotExist(APP_NAME_2);
|
||||
|
||||
|
||||
@@ -31,27 +31,36 @@ import static org.hamcrest.Matchers.either;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.springframework.cloud.appbroker.integration.DeleteInstanceWithServicesComponentTest.APP_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.DeleteInstanceWithServicesComponentTest.BACKING_PLAN_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.DeleteInstanceWithServicesComponentTest.BACKING_SERVICE_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.DeleteInstanceWithServicesComponentTest.BACKING_SI_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.DeleteInstanceWithServicesComponentTest.PLAN_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.DeleteInstanceWithServicesComponentTest.SERVICE_NAME;
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
"spring.cloud.appbroker.services[0].service-name=example",
|
||||
"spring.cloud.appbroker.services[0].plan-name=standard",
|
||||
"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=" + BACKING_SI_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].service-instance-name=" + BACKING_SI_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].name=" + BACKING_SERVICE_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].plan=standard"
|
||||
"spring.cloud.appbroker.services[0].services[0].plan=" + BACKING_PLAN_NAME
|
||||
})
|
||||
class DeleteInstanceWithServicesComponentTest extends WiremockComponentTest {
|
||||
|
||||
protected static final String APP_NAME = "app-delete-with-services";
|
||||
|
||||
protected static final String SERVICE_NAME = "example";
|
||||
|
||||
protected static final String PLAN_NAME = "standard";
|
||||
|
||||
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";
|
||||
|
||||
@Autowired
|
||||
private OpenServiceBrokerApiFixture brokerFixture;
|
||||
|
||||
@@ -60,13 +69,15 @@ class DeleteInstanceWithServicesComponentTest extends WiremockComponentTest {
|
||||
|
||||
@Test
|
||||
void deleteAppsAndServicesWhenTheyExist() {
|
||||
cloudControllerFixture.stubAppExists(APP_NAME);
|
||||
cloudControllerFixture.stubGetServiceInstanceWithNoBinding("instance-id", "instance-name",
|
||||
SERVICE_NAME, PLAN_NAME);
|
||||
cloudControllerFixture.stubAppExistsWithBackingService(APP_NAME, BACKING_SI_NAME,
|
||||
BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
|
||||
cloudControllerFixture.stubServiceBindingDoesNotExist(APP_NAME);
|
||||
cloudControllerFixture.stubDeleteApp(APP_NAME);
|
||||
|
||||
cloudControllerFixture.stubServiceInstanceExists(BACKING_SI_NAME);
|
||||
cloudControllerFixture.stubGetBackingServiceInstance(BACKING_SI_NAME, BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
|
||||
|
||||
cloudControllerFixture.stubListServiceBindings(APP_NAME, BACKING_SI_NAME);
|
||||
cloudControllerFixture.stubServiceBindingExists(APP_NAME, BACKING_SI_NAME);
|
||||
cloudControllerFixture.stubDeleteServiceBinding(APP_NAME, BACKING_SI_NAME);
|
||||
cloudControllerFixture.stubDeleteServiceInstance(BACKING_SI_NAME);
|
||||
@@ -92,12 +103,12 @@ class DeleteInstanceWithServicesComponentTest extends WiremockComponentTest {
|
||||
|
||||
@Test
|
||||
void deleteAppsWhenTheyExistAndServicesWhenTheyDoNotExist() {
|
||||
cloudControllerFixture.stubGetServiceInstanceWithNoBinding("instance-id", "instance-name",
|
||||
SERVICE_NAME, PLAN_NAME);
|
||||
cloudControllerFixture.stubAppExists(APP_NAME);
|
||||
cloudControllerFixture.stubServiceBindingDoesNotExist(APP_NAME);
|
||||
cloudControllerFixture.stubDeleteApp(APP_NAME);
|
||||
|
||||
cloudControllerFixture.stubServiceInstanceDoesNotExist(BACKING_SI_NAME);
|
||||
|
||||
// when the service instance is deleted
|
||||
given(brokerFixture.serviceInstanceRequest())
|
||||
.when()
|
||||
@@ -119,10 +130,10 @@ class DeleteInstanceWithServicesComponentTest extends WiremockComponentTest {
|
||||
|
||||
@Test
|
||||
void deleteAppsAndServicesWhenTheyDoNotExist() {
|
||||
cloudControllerFixture.stubGetServiceInstanceWithNoBinding("instance-id", "instance-name",
|
||||
SERVICE_NAME, PLAN_NAME);
|
||||
cloudControllerFixture.stubAppDoesNotExist(APP_NAME);
|
||||
|
||||
cloudControllerFixture.stubServiceInstanceDoesNotExist(BACKING_SI_NAME);
|
||||
|
||||
// when the service instance is deleted
|
||||
given(brokerFixture.serviceInstanceRequest())
|
||||
.when()
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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 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.UpdateInstanceWithNewServiceComponentTest.APP_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithNewServiceComponentTest.NEW_BACKING_PLAN_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithNewServiceComponentTest.NEW_BACKING_SERVICE_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithNewServiceComponentTest.NEW_BACKING_SI_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithNewServiceComponentTest.PLAN_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithNewServiceComponentTest.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
|
||||
})
|
||||
class UpdateInstanceWithNewServiceComponentTest extends WiremockComponentTest {
|
||||
|
||||
protected static final String APP_NAME = "app-update-with-new-service";
|
||||
|
||||
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 updateAppWithNewService() {
|
||||
cloudControllerFixture.stubAppExistsWithBackingService(APP_NAME, BACKING_SI_NAME,
|
||||
BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
|
||||
cloudControllerFixture.stubGetServiceInstanceWithNoBinding("instance-id", "instance-name",
|
||||
SERVICE_NAME, PLAN_NAME);
|
||||
cloudControllerFixture.stubUpdateApp(APP_NAME);
|
||||
|
||||
// will unbind and delete the existing service instance
|
||||
cloudControllerFixture.stubGetBackingServiceInstance(BACKING_SI_NAME, BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
|
||||
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.stubServiceExists(NEW_BACKING_SERVICE_NAME, NEW_BACKING_PLAN_NAME);
|
||||
cloudControllerFixture.stubCreateServiceInstance(NEW_BACKING_SI_NAME);
|
||||
cloudControllerFixture.stubCreateServiceBinding(APP_NAME, NEW_BACKING_SI_NAME);
|
||||
cloudControllerFixture.stubServiceInstanceExists(NEW_BACKING_SI_NAME);
|
||||
|
||||
// when a service instance is updated
|
||||
given(brokerFixture.serviceInstanceRequest())
|
||||
.when()
|
||||
.patch(brokerFixture.createServiceInstanceUrl(), "instance-id")
|
||||
.then()
|
||||
.statusCode(HttpStatus.ACCEPTED.value());
|
||||
|
||||
// when the "last_operation" API is polled
|
||||
given(brokerFixture.serviceInstanceRequest())
|
||||
.when()
|
||||
.get(brokerFixture.getLastInstanceOperationUrl(), "instance-id")
|
||||
.then()
|
||||
.statusCode(HttpStatus.OK.value())
|
||||
.body("state", is(equalTo(OperationState.IN_PROGRESS.toString())));
|
||||
|
||||
String state = brokerFixture.waitForAsyncOperationComplete("instance-id");
|
||||
assertThat(state).isEqualTo(OperationState.SUCCEEDED.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,26 +30,35 @@ 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.UpdateInstanceWithServicesComponentTest.APP_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesComponentTest.SERVICE_INSTANCE_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesComponentTest.BACKING_PLAN_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesComponentTest.BACKING_SERVICE_INSTANCE_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesComponentTest.BACKING_SERVICE_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesComponentTest.PLAN_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesComponentTest.SERVICE_NAME;
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
"spring.cloud.appbroker.services[0].service-name=example",
|
||||
"spring.cloud.appbroker.services[0].plan-name=standard",
|
||||
"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=" + SERVICE_INSTANCE_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].service-instance-name=" + SERVICE_INSTANCE_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].name=" + SERVICE_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].plan=standard"
|
||||
"spring.cloud.appbroker.services[0].apps[0].services[0].service-instance-name=" + BACKING_SERVICE_INSTANCE_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].service-instance-name=" + BACKING_SERVICE_INSTANCE_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].name=" + BACKING_SERVICE_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].plan=" + BACKING_PLAN_NAME
|
||||
})
|
||||
class UpdateInstanceWithServicesComponentTest extends WiremockComponentTest {
|
||||
|
||||
protected static final String APP_NAME = "app-update-with-services";
|
||||
|
||||
protected static final String SERVICE_INSTANCE_NAME = "my-db-service";
|
||||
protected static final String SERVICE_NAME = "example";
|
||||
|
||||
protected static final String SERVICE_NAME = "db-service";
|
||||
protected static final String PLAN_NAME = "standard";
|
||||
|
||||
protected static final String BACKING_SERVICE_INSTANCE_NAME = "my-db-service";
|
||||
|
||||
protected static final String BACKING_SERVICE_NAME = "db-service";
|
||||
|
||||
protected static final String BACKING_PLAN_NAME = "backing-standard";
|
||||
|
||||
@Autowired
|
||||
private OpenServiceBrokerApiFixture brokerFixture;
|
||||
@@ -59,7 +68,10 @@ class UpdateInstanceWithServicesComponentTest extends WiremockComponentTest {
|
||||
|
||||
@Test
|
||||
void updateAppWithServices() {
|
||||
cloudControllerFixture.stubAppExists(APP_NAME);
|
||||
cloudControllerFixture.stubAppExistsWithBackingService(APP_NAME, BACKING_SERVICE_INSTANCE_NAME,
|
||||
BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
|
||||
cloudControllerFixture.stubGetServiceInstanceWithNoBinding("instance-id", "instance-name",
|
||||
SERVICE_NAME, PLAN_NAME);
|
||||
cloudControllerFixture.stubUpdateApp(APP_NAME);
|
||||
|
||||
// when a service instance is updated
|
||||
|
||||
@@ -33,18 +33,21 @@ 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.UpdateInstanceWithServicesParametersComponentTest.APP_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesParametersComponentTest.BACKING_PLAN_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesParametersComponentTest.BACKING_SERVICE_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesParametersComponentTest.BACKING_SI_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesParametersComponentTest.PLAN_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesParametersComponentTest.SERVICE_NAME;
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
"spring.cloud.appbroker.services[0].service-name=example",
|
||||
"spring.cloud.appbroker.services[0].plan-name=standard",
|
||||
"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=" + BACKING_SI_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].service-instance-name=" + BACKING_SI_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].name=" + BACKING_SERVICE_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].plan=standard",
|
||||
"spring.cloud.appbroker.services[0].services[0].plan=" + BACKING_PLAN_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].parameters-transformers[0].name=ParameterMapping",
|
||||
"spring.cloud.appbroker.services[0].services[0].parameters-transformers[0].args.include=paramA,paramC"
|
||||
})
|
||||
@@ -52,10 +55,16 @@ class UpdateInstanceWithServicesParametersComponentTest extends WiremockComponen
|
||||
|
||||
protected static final String APP_NAME = "app-update-services-param";
|
||||
|
||||
protected static final String SERVICE_NAME = "example";
|
||||
|
||||
protected static final String PLAN_NAME = "standard";
|
||||
|
||||
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";
|
||||
|
||||
@Autowired
|
||||
private OpenServiceBrokerApiFixture brokerFixture;
|
||||
|
||||
@@ -64,16 +73,18 @@ class UpdateInstanceWithServicesParametersComponentTest extends WiremockComponen
|
||||
|
||||
@Test
|
||||
void updateAppWithBackingServicesParameters() {
|
||||
cloudControllerFixture.stubAppExists(APP_NAME);
|
||||
cloudControllerFixture.stubAppExistsWithBackingService(APP_NAME, BACKING_SI_NAME, BACKING_SERVICE_NAME,
|
||||
BACKING_PLAN_NAME);
|
||||
cloudControllerFixture.stubGetServiceInstanceWithNoBinding("instance-id", "instance-name",
|
||||
SERVICE_NAME, PLAN_NAME);
|
||||
cloudControllerFixture.stubUpdateApp(APP_NAME);
|
||||
|
||||
cloudControllerFixture.stubServiceInstanceExists(BACKING_SI_NAME);
|
||||
|
||||
// will update with filtered parameters and bind the service instance
|
||||
HashMap<String, Object> expectedCreationParameters = new HashMap<>();
|
||||
expectedCreationParameters.put("paramA", "valueA");
|
||||
expectedCreationParameters.put("paramC", Collections.singletonMap("paramC1", "valueC1"));
|
||||
|
||||
cloudControllerFixture.stubServiceInstanceExists(BACKING_SI_NAME, BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
|
||||
cloudControllerFixture.stubUpdateServiceInstanceWithParameters(BACKING_SI_NAME, expectedCreationParameters);
|
||||
|
||||
// when a service instance is created with parameters
|
||||
|
||||
@@ -30,28 +30,37 @@ 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.UpdateInstanceWithServicesRebindComponentTest.APP_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesRebindComponentTest.BACKING_PLAN_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesRebindComponentTest.BACKING_SERVICE_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesRebindComponentTest.BACKING_SI_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesRebindComponentTest.PLAN_NAME;
|
||||
import static org.springframework.cloud.appbroker.integration.UpdateInstanceWithServicesRebindComponentTest.SERVICE_NAME;
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
"spring.cloud.appbroker.services[0].service-name=example",
|
||||
"spring.cloud.appbroker.services[0].plan-name=standard",
|
||||
"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=" + BACKING_SI_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].service-instance-name=" + BACKING_SI_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].name=" + BACKING_SERVICE_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].plan=standard",
|
||||
"spring.cloud.appbroker.services[0].services[0].plan=" + BACKING_PLAN_NAME,
|
||||
"spring.cloud.appbroker.services[0].services[0].rebind-on-update=true"
|
||||
})
|
||||
class UpdateInstanceWithServicesRebindComponentTest extends WiremockComponentTest {
|
||||
|
||||
protected static final String APP_NAME = "app-update-rebind-with-services";
|
||||
|
||||
protected static final String SERVICE_NAME = "example";
|
||||
|
||||
protected static final String PLAN_NAME = "standard";
|
||||
|
||||
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";
|
||||
|
||||
@Autowired
|
||||
private OpenServiceBrokerApiFixture brokerFixture;
|
||||
|
||||
@@ -60,12 +69,13 @@ class UpdateInstanceWithServicesRebindComponentTest extends WiremockComponentTes
|
||||
|
||||
@Test
|
||||
void updateAppWithServices() {
|
||||
cloudControllerFixture.stubAppExists(APP_NAME);
|
||||
cloudControllerFixture.stubAppExistsWithBackingService(APP_NAME, BACKING_SI_NAME, BACKING_SERVICE_NAME,
|
||||
BACKING_PLAN_NAME);
|
||||
cloudControllerFixture.stubGetServiceInstanceWithNoBinding("instance-id", "instance-name",
|
||||
SERVICE_NAME, PLAN_NAME);
|
||||
cloudControllerFixture.stubUpdateApp(APP_NAME);
|
||||
|
||||
cloudControllerFixture.stubServiceInstanceExists(BACKING_SI_NAME);
|
||||
|
||||
cloudControllerFixture.stubListServiceBindings(APP_NAME, BACKING_SI_NAME);
|
||||
cloudControllerFixture.stubGetBackingServiceInstance(BACKING_SI_NAME, BACKING_SERVICE_NAME, BACKING_PLAN_NAME);
|
||||
cloudControllerFixture.stubServiceBindingExists(APP_NAME, BACKING_SI_NAME);
|
||||
cloudControllerFixture.stubDeleteServiceBinding(APP_NAME, BACKING_SI_NAME);
|
||||
cloudControllerFixture.stubCreateServiceBinding(APP_NAME, BACKING_SI_NAME);
|
||||
|
||||
@@ -208,6 +208,23 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
|
||||
replace("@guid", stackGuid(appName))))));
|
||||
}
|
||||
|
||||
public void stubAppExistsWithBackingService(final String appName, final String serviceInstanceName,
|
||||
final String serviceName, final String planName) {
|
||||
stubAppExists(appName);
|
||||
|
||||
stubFor(get(urlPathEqualTo("/v2/apps/" + appGuid(appName) + "/summary"))
|
||||
.withMetadata(optionalStubMapping())
|
||||
.willReturn(ok()
|
||||
.withBody(cc("get-app-summary-with-backing-service",
|
||||
replace("@name", appName),
|
||||
replace("@service-instance-name", serviceInstanceName),
|
||||
replace("@service-guid", serviceGuid(serviceName)),
|
||||
replace("@plan-guid", planGuid(planName)),
|
||||
replace("@guid", appGuid(appName)),
|
||||
replace("@stack-guid", stackGuid(appName)),
|
||||
replace("@route-guid", routeGuid(appName))))));
|
||||
}
|
||||
|
||||
public void stubPushApp(final String appName, ContentPattern<?>... appMetadataPatterns) {
|
||||
stubCreateAppMetadata(appName, appMetadataPatterns);
|
||||
stubAppAfterCreation(appName, appName);
|
||||
@@ -406,7 +423,9 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
|
||||
stubFor(get(urlPathEqualTo("/v2/spaces/" + TEST_SPACE_GUID + "/services"))
|
||||
.withQueryParam("page", equalTo("1"))
|
||||
.willReturn(ok()
|
||||
.withBody(cc("list-space-services"))));
|
||||
.withBody(cc("list-space-services")
|
||||
.replace("@service-guid", "SERVICE-ID")
|
||||
.replace("@service-name", "db-service"))));
|
||||
|
||||
stubFor(post(urlPathEqualTo("/v2/routes"))
|
||||
.willReturn(ok()
|
||||
@@ -429,6 +448,15 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
|
||||
}
|
||||
|
||||
public void stubServiceInstanceExists(String serviceInstanceName) {
|
||||
stubServiceInstanceExists(serviceInstanceName, "doNotCare", "doNotCare");
|
||||
}
|
||||
|
||||
public void stubServiceInstanceExists(String serviceInstanceName, String serviceName, String planName) {
|
||||
stubServiceInstanceExists(serviceInstanceGuid(serviceInstanceName), serviceInstanceName, serviceName, planName);
|
||||
}
|
||||
|
||||
private void stubServiceInstanceExists(String serviceInstanceId, String serviceInstanceName, String serviceName,
|
||||
String planName) {
|
||||
stubFor(get(urlPathEqualTo("/v2/spaces/" + TEST_SPACE_GUID + "/service_instances"))
|
||||
.withQueryParam("q", equalTo("name:" + serviceInstanceName))
|
||||
.withQueryParam("page", equalTo("1"))
|
||||
@@ -436,31 +464,57 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
|
||||
.willReturn(ok()
|
||||
.withBody(cc("list-space-service_instances",
|
||||
replace("@space-guid", TEST_SPACE_GUID),
|
||||
replace("@service-guid", serviceGuid(serviceName)),
|
||||
replace("@plan-guid", planGuid(planName)),
|
||||
replace("@name", serviceInstanceName),
|
||||
replace("@guid", serviceInstanceName + "-GUID")))));
|
||||
replace("@guid", serviceInstanceId)))));
|
||||
}
|
||||
|
||||
public void stubServiceInstanceDoesNotExist(String serviceInstanceName) {
|
||||
stubFor(get(urlPathEqualTo("/v2/spaces/" + TEST_SPACE_GUID + "/service_instances"))
|
||||
.withQueryParam("q", equalTo("name:" + serviceInstanceName))
|
||||
.withQueryParam("page", equalTo("1"))
|
||||
.withQueryParam("return_user_provided_service_instances", equalTo("true"))
|
||||
public void stubGetServiceInstanceWithNoBinding(String serviceInstanceId, String serviceInstanceName, String serviceName,
|
||||
String planName) {
|
||||
stubFor(get(urlPathEqualTo("/v2/service_instances/" + serviceInstanceId))
|
||||
.willReturn(ok()
|
||||
.withBody(cc("list-space-service_instances-empty"))));
|
||||
.withBody(cc("get-service_instance",
|
||||
replace("@space-guid", TEST_SPACE_GUID),
|
||||
replace("@service-guid", serviceGuid(serviceName)),
|
||||
replace("@plan-guid", planGuid(planName)),
|
||||
replace("@name", serviceInstanceName),
|
||||
replace("@guid", serviceInstanceId)))));
|
||||
|
||||
stubServiceInstanceExists(serviceInstanceId, serviceInstanceName, serviceName, planName);
|
||||
stubFor(get(urlPathEqualTo("/v2/service_bindings"))
|
||||
.withQueryParam("q", equalTo("service_instance_guid:" + serviceInstanceId))
|
||||
.withQueryParam("page", equalTo("1"))
|
||||
.willReturn(ok()
|
||||
.withBody(cc("empty-query-results"))));
|
||||
|
||||
stubGetServiceAndGetPlan(serviceName, planName);
|
||||
}
|
||||
|
||||
public void stubServiceExists(String serviceName) {
|
||||
public void stubGetBackingServiceInstance(String serviceInstanceName, String serviceName, String planName) {
|
||||
String serviceInstanceId = serviceInstanceGuid(serviceInstanceName);
|
||||
stubServiceInstanceExists(serviceInstanceId, serviceInstanceName, serviceName, planName);
|
||||
stubGetServiceAndGetPlan(serviceName, planName);
|
||||
}
|
||||
|
||||
public void stubServiceExists(String serviceName, String planName) {
|
||||
stubFor(get(urlPathEqualTo("/v2/spaces/" + TEST_SPACE_GUID + "/services"))
|
||||
.withQueryParam("q", equalTo("label:" + serviceName))
|
||||
.withQueryParam("page", equalTo("1"))
|
||||
.willReturn(ok()
|
||||
.withBody(cc("list-space-services"))));
|
||||
.withBody(cc("list-space-services")
|
||||
.replace("@service-guid", serviceGuid(serviceName))
|
||||
.replace("@plan-guid", planGuid(planName))
|
||||
.replace("@service-name", serviceName))));
|
||||
|
||||
stubFor(get(urlPathEqualTo("/v2/service_plans"))
|
||||
.withQueryParam("q", equalTo("service_guid:SERVICE-ID"))
|
||||
.withQueryParam("q", equalTo("service_guid:" + serviceGuid(serviceName)))
|
||||
.withQueryParam("page", equalTo("1"))
|
||||
.willReturn(ok()
|
||||
.withBody(cc("list-service-plans"))));
|
||||
.withBody(cc("list-service-plans")
|
||||
.replace("@service-guid", serviceGuid(serviceName))
|
||||
.replace("@plan-name", planName)
|
||||
.replace("@plan-guid", planGuid(planName)))));
|
||||
}
|
||||
|
||||
public void stubCreateServiceInstance(String serviceInstanceName) {
|
||||
@@ -514,7 +568,7 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
|
||||
.willReturn(noContent()));
|
||||
}
|
||||
|
||||
public void stubListServiceBindings(String appName, String serviceInstanceName) {
|
||||
public void stubServiceBindingExists(String appName, String serviceInstanceName) {
|
||||
String serviceInstanceGuid = serviceInstanceGuid(serviceInstanceName);
|
||||
String serviceBindingGuid = serviceBindingGuid(appName, serviceInstanceName);
|
||||
|
||||
@@ -526,11 +580,6 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
|
||||
replace("@guid", serviceBindingGuid),
|
||||
replace("@app-guid", appGuid(appName)),
|
||||
replace("@service-instance-guid", serviceInstanceGuid)))));
|
||||
}
|
||||
|
||||
public void stubServiceBindingExists(String appName, String serviceInstanceName) {
|
||||
String serviceInstanceGuid = serviceInstanceGuid(serviceInstanceName);
|
||||
String serviceBindingGuid = serviceBindingGuid(appName, serviceInstanceName);
|
||||
|
||||
stubFor(get(urlPathEqualTo("/v2/apps/" + appGuid(appName) + "/service_bindings"))
|
||||
.withQueryParam("q", equalTo("service_instance_guid:" + serviceInstanceGuid))
|
||||
@@ -558,6 +607,21 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
|
||||
.willReturn(ok()));
|
||||
}
|
||||
|
||||
private void stubGetServiceAndGetPlan(String serviceName, String planName) {
|
||||
stubFor(get(urlPathEqualTo("/v2/service_plans/" + planGuid(planName)))
|
||||
.willReturn(ok()
|
||||
.withBody(cc("get-service-plan")
|
||||
.replace("@service-guid", serviceGuid(serviceName))
|
||||
.replace("@plan-name", planName)
|
||||
.replace("@plan-guid", planGuid(planName)))));
|
||||
|
||||
stubFor(get(urlPathEqualTo("/v2/services/" + serviceGuid(serviceName)))
|
||||
.willReturn(ok()
|
||||
.withBody(cc("get-service")
|
||||
.replace("@service-guid", serviceGuid(serviceName))
|
||||
.replace("@service-name", serviceName))));
|
||||
}
|
||||
|
||||
private void stubSpaceExists(final String spaceName) {
|
||||
stubFor(get(urlPathEqualTo("/v2/organizations/" + TEST_ORG_GUID + "/spaces"))
|
||||
.withQueryParam("q", equalTo("name:" + spaceName))
|
||||
@@ -576,35 +640,43 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
|
||||
return response;
|
||||
}
|
||||
|
||||
private String appGuid(String appName) {
|
||||
private static String appGuid(String appName) {
|
||||
return appName + "-GUID";
|
||||
}
|
||||
|
||||
private String routeGuid(String appName) {
|
||||
private static String routeGuid(String appName) {
|
||||
return appName + "-ROUTE-GUID";
|
||||
}
|
||||
|
||||
private String stackGuid(String appName) {
|
||||
private static String stackGuid(String appName) {
|
||||
return appName + "-STACK-GUID";
|
||||
}
|
||||
|
||||
private String serviceInstanceGuid(String serviceInstanceName) {
|
||||
return serviceInstanceName + "-GUID";
|
||||
public static String serviceGuid(String serviceName) {
|
||||
return serviceName + "-SERVICE-GUID";
|
||||
}
|
||||
|
||||
private String serviceBindingGuid(String appName, String serviceInstanceName) {
|
||||
public static String planGuid(String planName) {
|
||||
return planName + "-PLAN-GUID";
|
||||
}
|
||||
|
||||
private static String serviceInstanceGuid(String serviceInstanceName) {
|
||||
return serviceInstanceName + "-INSTANCE-GUID";
|
||||
}
|
||||
|
||||
private static String serviceBindingGuid(String appName, String serviceInstanceName) {
|
||||
return appGuid(appName) + "-" + serviceInstanceGuid(serviceInstanceName);
|
||||
}
|
||||
|
||||
private String packageGuid(String appName) {
|
||||
private static String packageGuid(String appName) {
|
||||
return appName + "-PACKAGE-GUID";
|
||||
}
|
||||
|
||||
private String buildGuid(String appName) {
|
||||
private static String buildGuid(String appName) {
|
||||
return appName + "-BUILD-GUID";
|
||||
}
|
||||
|
||||
private String deploymentGuid(String appname) {
|
||||
private static String deploymentGuid(String appname) {
|
||||
return appname + "-DEPLOYMENT-GUID";
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ spring:
|
||||
openservicebroker:
|
||||
catalog:
|
||||
services:
|
||||
- id: example-service-id
|
||||
- id: example-SERVICE-GUID
|
||||
name: example
|
||||
description: A simple example
|
||||
bindable: true
|
||||
@@ -11,7 +11,7 @@ spring:
|
||||
- example
|
||||
tags
|
||||
plans:
|
||||
- id: standard-plan-id
|
||||
- id: standard-PLAN-GUID
|
||||
bindable: true
|
||||
name: standard
|
||||
description: A simple plan
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"guid": "@guid",
|
||||
"name": "@name",
|
||||
"routes": [
|
||||
{
|
||||
"guid": "@route-guid",
|
||||
"host": "@name",
|
||||
"port": null,
|
||||
"path": "",
|
||||
"domain": {
|
||||
"guid": "28e83b6f-3cb4-4df4-a056-4e4ae5d0efad",
|
||||
"name": "apps.example.com"
|
||||
}
|
||||
}
|
||||
],
|
||||
"running_instances": 1,
|
||||
"services": [
|
||||
{
|
||||
"guid": "@service-guid",
|
||||
"name": "@service-instance-name",
|
||||
"bound_app_count": 1,
|
||||
"type": "managed_service_instance"
|
||||
}
|
||||
],
|
||||
"available_domains": [
|
||||
{
|
||||
"guid": "28e83b6f-3cb4-4df4-a056-4e4ae5d0efad",
|
||||
"name": "apps.example.com",
|
||||
"router_group_guid": null,
|
||||
"router_group_type": null
|
||||
}
|
||||
],
|
||||
"production": false,
|
||||
"space_guid": "c32f8285-5127-42dc-a9b9-6d22bb99fa9d",
|
||||
"stack_guid": "@stack-guid",
|
||||
"buildpack": "https://github.com/cloudfoundry/java-buildpack.git#v4.7.1",
|
||||
"detected_buildpack": "",
|
||||
"detected_buildpack_guid": null,
|
||||
"environment_json": {
|
||||
"SPRING_APPLICATION_INDEX": "${vcap.application.instance_index}",
|
||||
"SPRING_CLOUD_APPLICATION_GUID": "${vcap.application.name}:${vcap.application.instance_index}",
|
||||
"SPRING_APPLICATION_JSON": "{}"
|
||||
},
|
||||
"memory": 1024,
|
||||
"instances": 1,
|
||||
"disk_quota": 1024,
|
||||
"state": "STARTED",
|
||||
"version": "54b1eed3-e30c-40ad-8cc6-a7b87fbfb699",
|
||||
"command": null,
|
||||
"console": false,
|
||||
"debug": null,
|
||||
"staging_task_id": "66a74858-c432-446d-bec5-13026d4cbb28",
|
||||
"package_state": "STAGED",
|
||||
"health_check_type": "port",
|
||||
"health_check_timeout": 120,
|
||||
"health_check_http_endpoint": "/health",
|
||||
"staging_failed_reason": null,
|
||||
"staging_failed_description": null,
|
||||
"diego": true,
|
||||
"docker_image": null,
|
||||
"package_updated_at": "2018-08-15T19:33:41Z",
|
||||
"detected_start_command": "JAVA_OPTS=\"-agentpath:$PWD/.java-buildpack/open_jdk_jre/bin/jvmkill-1.12.0_RELEASE=printHeapHistogram=1 -Djava.io.tmpdir=$TMPDIR -Djava.ext.dirs=$PWD/.java-buildpack/container_security_provider:$PWD/.java-buildpack/open_jdk_jre/lib/ext -Djava.security.properties=$PWD/.java-buildpack/java_security/java.security $JAVA_OPTS\" && CALCULATED_MEMORY=$($PWD/.java-buildpack/open_jdk_jre/bin/java-buildpack-memory-calculator-3.10.0_RELEASE -totMemory=$MEMORY_LIMIT -stackThreads=300 -loadedClasses=14289 -poolType=metaspace -vmOptions=\"$JAVA_OPTS\") && echo JVM Memory Configuration: $CALCULATED_MEMORY && JAVA_OPTS=\"$JAVA_OPTS $CALCULATED_MEMORY\" && SERVER_PORT=$PORT eval exec $PWD/.java-buildpack/open_jdk_jre/bin/java $JAVA_OPTS -cp $PWD/. org.springframework.boot.loader.JarLauncher",
|
||||
"enable_ssh": true,
|
||||
"ports": null
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"metadata": {
|
||||
"guid": "@plan-guid",
|
||||
"url": "/v2/service_plans/@plan-guid",
|
||||
"created_at": "2018-01-26T20:48:20Z",
|
||||
"updated_at": "2018-06-11T18:08:05Z"
|
||||
},
|
||||
"entity": {
|
||||
"name": "@plan-name",
|
||||
"free": true,
|
||||
"description": "basic",
|
||||
"service_guid": "@service-guid",
|
||||
"extra": "{}",
|
||||
"public": true,
|
||||
"bindable": true,
|
||||
"active": true,
|
||||
"service_url": "/v2/services/@service-guid",
|
||||
"service_instances_url": "/v2/service_plans/@plan-guid/service_instances",
|
||||
"schemas": {
|
||||
"service_instance": {
|
||||
"create": {
|
||||
"parameters": {}
|
||||
},
|
||||
"update": {
|
||||
"parameters": {}
|
||||
}
|
||||
},
|
||||
"service_binding": {
|
||||
"create": {
|
||||
"parameters": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"metadata": {
|
||||
"guid": "@service-guid",
|
||||
"url": "/v2/services/@service-guid",
|
||||
"created_at": "2018-01-26T20:48:20Z",
|
||||
"updated_at": "2018-06-11T18:08:05Z"
|
||||
},
|
||||
"entity": {
|
||||
"label": "@service-name",
|
||||
"provider": null,
|
||||
"url": null,
|
||||
"description": "My DB Service",
|
||||
"long_description": null,
|
||||
"version": null,
|
||||
"info_url": null,
|
||||
"active": 1,
|
||||
"bindable": 1,
|
||||
"extra": "{}",
|
||||
"tags": [
|
||||
],
|
||||
"requires": [],
|
||||
"documentation_url": null,
|
||||
"plan_updateable": 0,
|
||||
"service_plans_url": "/v2/services/@service-guid/service_plans"
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@
|
||||
"entity": {
|
||||
"name": "@name",
|
||||
"credentials": {},
|
||||
"service_guid": "a14baddf-1ccc-5299-0152-ab9s49de4422",
|
||||
"service_plan_guid": "779d2df0-9cdd-48e8-9781-ea05301cedb1",
|
||||
"service_guid": "@service-guid",
|
||||
"service_plan_guid": "@plan-guid",
|
||||
"space_guid": "@space-guid",
|
||||
"gateway_data": null,
|
||||
"dashboard_url": null,
|
||||
@@ -23,8 +23,8 @@
|
||||
},
|
||||
"tags": [],
|
||||
"space_url": "/v2/spaces/@space-guid",
|
||||
"service_url": "/v2/services/a14baddf-1ccc-5299-0152-ab9s49de4422",
|
||||
"service_plan_url": "/v2/service_plans/779d2df0-9cdd-48e8-9781-ea05301cedb1",
|
||||
"service_url": "/v2/services/@service-guid",
|
||||
"service_plan_url": "/v2/service_plans/@plan-guid",
|
||||
"service_bindings_url": "/v2/service_instances/@guid/service_bindings",
|
||||
"service_keys_url": "/v2/service_instances/@guid/service_keys",
|
||||
"routes_url": "/v2/service_instances/@guid/routes",
|
||||
@@ -32,4 +32,4 @@
|
||||
"shared_to_url": "/v2/service_instances/@guid/shared_to",
|
||||
"service_instance_parameters_url": "/v2/service_instances/@guid/parameters"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,22 +6,22 @@
|
||||
"resources": [
|
||||
{
|
||||
"metadata": {
|
||||
"guid": "SERVICE-PLAN-ID",
|
||||
"url": "/v2/service_plans/SERVICE-PLAN-ID",
|
||||
"guid": "@plan-guid",
|
||||
"url": "/v2/service_plans/@plan-guid",
|
||||
"created_at": "2018-01-26T20:48:20Z",
|
||||
"updated_at": "2018-06-11T18:08:05Z"
|
||||
},
|
||||
"entity": {
|
||||
"name": "standard",
|
||||
"name": "@plan-name",
|
||||
"free": true,
|
||||
"description": "basic",
|
||||
"service_guid": "SERVICE-ID",
|
||||
"service_guid": "@service-guid",
|
||||
"extra": "{}",
|
||||
"public": true,
|
||||
"bindable": true,
|
||||
"active": true,
|
||||
"service_url": "/v2/services/SERVICE-ID",
|
||||
"service_instances_url": "/v2/service_plans/SERVICE-PLAN-ID/service_instances",
|
||||
"service_url": "/v2/services/@service-guid",
|
||||
"service_instances_url": "/v2/service_plans/@plan-guid/service_instances",
|
||||
"schemas": {
|
||||
"service_instance": {
|
||||
"create": {
|
||||
@@ -40,4 +40,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,11 +53,11 @@
|
||||
],
|
||||
"space_url": "/v2/spaces/@space-guid",
|
||||
"stack_url": "/v2/stacks/@stack-guid",
|
||||
"routes_url": "/v2/apps/cae5e4ca-d856-4473-80fd-6f58f9099401/routes",
|
||||
"events_url": "/v2/apps/cae5e4ca-d856-4473-80fd-6f58f9099401/events",
|
||||
"service_bindings_url": "/v2/apps/cae5e4ca-d856-4473-80fd-6f58f9099401/service_bindings",
|
||||
"route_mappings_url": "/v2/apps/cae5e4ca-d856-4473-80fd-6f58f9099401/route_mappings"
|
||||
"routes_url": "/v2/apps/@guid/routes",
|
||||
"events_url": "/v2/apps/@guid/events",
|
||||
"service_bindings_url": "/v2/apps/@guid/service_bindings",
|
||||
"route_mappings_url": "/v2/apps/@guid/route_mappings"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,18 @@
|
||||
"name": "@name",
|
||||
"credentials": {},
|
||||
"space_guid": "@space-guid",
|
||||
"service_guid": "@service-guid",
|
||||
"service_plan_guid": "@plan-guid",
|
||||
"type": "managed_service_instance",
|
||||
"syslog_drain_url": "",
|
||||
"route_service_url": "",
|
||||
"space_url": "/v2/spaces/@space-guid",
|
||||
"service_bindings_url": "/v2/service_instances/@guid/service_bindings",
|
||||
"service_keys_url": "/v2/service_instances/@guid/service_keys",
|
||||
"service_url": "/v2/services/@service-guid",
|
||||
"service_plan_url": "/v2/service_plans/@plan-guid",
|
||||
"routes_url": "/v2/service_instances/@guid/routes"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
"resources": [
|
||||
{
|
||||
"metadata": {
|
||||
"guid": "SERVICE-ID",
|
||||
"url": "/v2/services/SERVICE-ID",
|
||||
"guid": "@service-guid",
|
||||
"url": "/v2/services/@service-guid",
|
||||
"created_at": "2018-01-26T20:48:20Z",
|
||||
"updated_at": "2018-06-11T18:08:05Z"
|
||||
},
|
||||
"entity": {
|
||||
"label": "db-service",
|
||||
"label": "@service-name",
|
||||
"provider": null,
|
||||
"url": null,
|
||||
"description": "My DB Service",
|
||||
@@ -27,8 +27,8 @@
|
||||
"requires": [],
|
||||
"documentation_url": null,
|
||||
"plan_updateable": 0,
|
||||
"service_plans_url": "/v2/services/SERVICE-ID/service_plans"
|
||||
"service_plans_url": "/v2/services/@service-guid/service_plans"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"total_results": 0,
|
||||
"total_pages": 1,
|
||||
"prev_url": null,
|
||||
"next_url": null,
|
||||
"resources": []
|
||||
}
|
||||
Reference in New Issue
Block a user