Update backing service instances on update service.

This commit is contained in:
Scott Frederick
2018-12-05 17:46:36 -06:00
parent 5143bae5c1
commit 292e20c18e
21 changed files with 720 additions and 198 deletions

View File

@@ -162,43 +162,56 @@ public class AppBrokerAutoConfiguration {
}
@Bean
public CreateServiceInstanceWorkflow appDeploymentCreateServiceInstanceWorkflow(BrokeredServices brokeredServices,
BackingAppDeploymentService backingAppDeploymentService,
BackingApplicationsParametersTransformationService applicationsParametersTransformationService,
BackingServicesParametersTransformationService servicesParametersTransformationService,
CredentialProviderService credentialProviderService,
TargetService targetService,
BackingServicesProvisionService backingServicesProvisionService) {
public CreateServiceInstanceWorkflow appDeploymentCreateServiceInstanceWorkflow(
BrokeredServices brokeredServices,
BackingAppDeploymentService backingAppDeploymentService,
BackingApplicationsParametersTransformationService appsParametersTransformationService,
BackingServicesParametersTransformationService servicesParametersTransformationService,
CredentialProviderService credentialProviderService,
TargetService targetService,
BackingServicesProvisionService backingServicesProvisionService) {
return new AppDeploymentCreateServiceInstanceWorkflow(
brokeredServices,
backingAppDeploymentService,
applicationsParametersTransformationService,
backingServicesProvisionService,
appsParametersTransformationService,
servicesParametersTransformationService,
credentialProviderService,
targetService,
backingServicesProvisionService);
targetService);
}
@Bean
public DeleteServiceInstanceWorkflow appDeploymentDeleteServiceInstanceWorkflow(BrokeredServices brokeredServices,
BackingAppDeploymentService backingAppDeploymentService,
CredentialProviderService credentialProviderService,
TargetService targetService,
BackingServicesProvisionService backingServicesProvisionService) {
public UpdateServiceInstanceWorkflow appDeploymentUpdateServiceInstanceWorkflow(
BrokeredServices brokeredServices,
BackingAppDeploymentService backingAppDeploymentService,
BackingServicesProvisionService backingServicesProvisionService,
BackingApplicationsParametersTransformationService appsParametersTransformationService,
BackingServicesParametersTransformationService servicesParametersTransformationService,
TargetService targetService) {
return new AppDeploymentUpdateServiceInstanceWorkflow(
brokeredServices,
backingAppDeploymentService,
backingServicesProvisionService,
appsParametersTransformationService,
servicesParametersTransformationService,
targetService);
}
@Bean
public DeleteServiceInstanceWorkflow appDeploymentDeleteServiceInstanceWorkflow(
BrokeredServices brokeredServices,
BackingAppDeploymentService backingAppDeploymentService,
BackingServicesProvisionService backingServicesProvisionService,
CredentialProviderService credentialProviderService,
TargetService targetService) {
return new AppDeploymentDeleteServiceInstanceWorkflow(
brokeredServices,
backingAppDeploymentService,
credentialProviderService,
targetService,
backingServicesProvisionService);
}
@Bean
public UpdateServiceInstanceWorkflow updateServiceInstanceWorkflow(BrokeredServices brokeredServices,
BackingAppDeploymentService backingAppDeploymentService,
BackingApplicationsParametersTransformationService parametersTransformationService,
TargetService targetService) {
return new AppDeploymentUpdateServiceInstanceWorkflow(brokeredServices, backingAppDeploymentService, parametersTransformationService, targetService);
backingServicesProvisionService, credentialProviderService,
targetService
);
}
@Bean

View File

@@ -45,6 +45,21 @@ public class BackingServicesProvisionService {
.sequential();
}
public Flux<String> updateServiceInstance(List<BackingService> backingServices) {
return Flux.fromIterable(backingServices)
.parallel()
.runOn(Schedulers.parallel())
// service instances can be updated with a change to the plan or to parameters
// if the service instance has no parameters, don't update it
.filter(backingService -> !backingService.getParameters().isEmpty())
.flatMap(deployerClient::updateServiceInstance)
.doOnRequest(l -> log.info("Updating backing services {}", backingServices))
.doOnEach(d -> log.info("Finished updating backing service {}", d))
.doOnComplete(() -> log.info("Finished updating backing service {}", backingServices))
.doOnError(e -> log.info("Error updating backing services {} with error {}", backingServices, e))
.sequential();
}
public Flux<String> deleteServiceInstance(List<BackingService> backingServices) {
return Flux.fromIterable(backingServices)
.parallel()
@@ -56,5 +71,4 @@ public class BackingServicesProvisionService {
.doOnError(exception -> log.error("Error deleting backing services {} with error {}", backingServices, exception))
.sequential();
}
}

View File

@@ -55,24 +55,39 @@ public class DeployerClient {
}
Mono<String> createServiceInstance(BackingService backingService) {
return appDeployer.createServiceInstance(CreateServiceInstanceRequest.builder()
.serviceInstanceName(backingService.getServiceInstanceName())
.name(backingService.getName())
.plan(backingService.getPlan())
.parameters(backingService.getParameters())
.properties(backingService.getProperties())
.build())
.doOnRequest(l -> log.debug("Creating backing service {}", backingService))
.doOnSuccess(response -> log.debug("Finished creating backing service {}", backingService))
.doOnError(exception -> log.error("Error creating backing service {} with error {}", backingService, exception))
.map(CreateServiceInstanceResponse::getName);
return appDeployer.createServiceInstance(
CreateServiceInstanceRequest.builder()
.serviceInstanceName(backingService.getServiceInstanceName())
.name(backingService.getName())
.plan(backingService.getPlan())
.parameters(backingService.getParameters())
.properties(backingService.getProperties())
.build())
.doOnRequest(l -> log.debug("Creating backing service {}", backingService.getName()))
.doOnSuccess(d -> log.debug("Finished creating backing service {}", backingService.getName()))
.doOnError(e -> log.error("Error creating backing service {} with error {}", backingService.getName(), e))
.map(CreateServiceInstanceResponse::getName);
}
Mono<String> updateServiceInstance(BackingService backingService) {
return appDeployer.updateServiceInstance(
UpdateServiceInstanceRequest.builder()
.serviceInstanceName(backingService.getServiceInstanceName())
.parameters(backingService.getParameters())
.properties(backingService.getProperties())
.build())
.doOnRequest(l -> log.debug("Creating backing service {}", backingService.getName()))
.doOnSuccess(d -> log.debug("Finished creating backing service {}", backingService.getName()))
.doOnError(e -> log.error("Error creating backing service {} with error {}", backingService.getName(), e))
.map(UpdateServiceInstanceResponse::getName);
}
Mono<String> deleteServiceInstance(BackingService backingService) {
return appDeployer.deleteServiceInstance(DeleteServiceInstanceRequest.builder()
.name(backingService.getServiceInstanceName())
.properties(backingService.getProperties())
.build())
.map(DeleteServiceInstanceResponse::getName);
return appDeployer.deleteServiceInstance(
DeleteServiceInstanceRequest.builder()
.serviceInstanceName(backingService.getServiceInstanceName())
.properties(backingService.getProperties())
.build())
.map(DeleteServiceInstanceResponse::getName);
}
}

View File

@@ -41,40 +41,52 @@ public class AppDeploymentCreateServiceInstanceWorkflow
private final Logger log = Loggers.getLogger(AppDeploymentCreateServiceInstanceWorkflow.class);
private final BackingAppDeploymentService deploymentService;
private final BackingServicesProvisionService backingServicesProvisionService;
private final BackingApplicationsParametersTransformationService appsParametersTransformationService;
private final BackingServicesParametersTransformationService servicesParametersTransformationService;
private final CredentialProviderService credentialProviderService;
private final TargetService targetService;
private final BackingServicesProvisionService backingServicesProvisionService;
public AppDeploymentCreateServiceInstanceWorkflow(BrokeredServices brokeredServices,
BackingAppDeploymentService deploymentService,
BackingServicesProvisionService backingServicesProvisionService,
BackingApplicationsParametersTransformationService appsParametersTransformationService,
BackingServicesParametersTransformationService servicesParametersTransformationService,
CredentialProviderService credentialProviderService,
TargetService targetService,
BackingServicesProvisionService backingServicesProvisionService) {
TargetService targetService) {
super(brokeredServices);
this.deploymentService = deploymentService;
this.backingServicesProvisionService = backingServicesProvisionService;
this.appsParametersTransformationService = appsParametersTransformationService;
this.servicesParametersTransformationService = servicesParametersTransformationService;
this.credentialProviderService = credentialProviderService;
this.targetService = targetService;
this.backingServicesProvisionService = backingServicesProvisionService;
}
@Override
public Flux<Void> create(CreateServiceInstanceRequest request) {
return
getBackingServicesForService(request.getServiceDefinition(), request.getPlanId())
.flatMap(backingService -> targetService.addToBackingServices(backingService, getTargetForService(request.getServiceDefinition(), request.getPlanId()) , request.getServiceInstanceId()))
.flatMap(backingServices -> servicesParametersTransformationService.transformParameters(backingServices, request.getParameters()))
.flatMap(backingService ->
targetService.addToBackingServices(backingService,
getTargetForService(request.getServiceDefinition(), request.getPlanId()) ,
request.getServiceInstanceId()))
.flatMap(backingServices ->
servicesParametersTransformationService.transformParameters(backingServices,
request.getParameters()))
.flatMapMany(backingServicesProvisionService::createServiceInstance)
.thenMany(
getBackingApplicationsForService(request.getServiceDefinition(), request.getPlanId())
.flatMap(backingApps -> targetService.addToBackingApplications(backingApps, getTargetForService(request.getServiceDefinition(), request.getPlanId()) , request.getServiceInstanceId()))
.flatMap(backingApps -> appsParametersTransformationService.transformParameters(backingApps, request.getParameters()))
.flatMap(backingApplications -> credentialProviderService.addCredentials(backingApplications, request.getServiceInstanceId()))
.flatMap(backingApps ->
targetService.addToBackingApplications(backingApps,
getTargetForService(request.getServiceDefinition(),
request.getPlanId()) , request.getServiceInstanceId()))
.flatMap(backingApps ->
appsParametersTransformationService.transformParameters(backingApps,
request.getParameters()))
.flatMap(backingApplications ->
credentialProviderService.addCredentials(backingApplications,
request.getServiceInstanceId()))
.flatMapMany(deploymentService::deploy)
.doOnRequest(l -> log.debug("Deploying applications {}", brokeredServices))
.doOnEach(response -> log.debug("Finished deploying {}", response))

View File

@@ -45,9 +45,9 @@ public class AppDeploymentDeleteServiceInstanceWorkflow
public AppDeploymentDeleteServiceInstanceWorkflow(BrokeredServices brokeredServices,
BackingAppDeploymentService deploymentService,
BackingServicesProvisionService backingServicesProvisionService,
CredentialProviderService credentialProviderService,
TargetService targetService,
BackingServicesProvisionService backingServicesProvisionService) {
TargetService targetService) {
super(brokeredServices);
this.deploymentService = deploymentService;
this.credentialProviderService = credentialProviderService;

View File

@@ -22,8 +22,10 @@ import reactor.util.Logger;
import reactor.util.Loggers;
import org.springframework.cloud.appbroker.deployer.BackingAppDeploymentService;
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.service.UpdateServiceInstanceWorkflow;
import org.springframework.cloud.servicebroker.model.instance.UpdateServiceInstanceRequest;
@@ -38,30 +40,50 @@ public class AppDeploymentUpdateServiceInstanceWorkflow
private final Logger log = Loggers.getLogger(AppDeploymentUpdateServiceInstanceWorkflow.class);
private final BackingAppDeploymentService deploymentService;
private final BackingApplicationsParametersTransformationService parametersTransformationService;
private final BackingServicesProvisionService backingServicesProvisionService;
private final BackingApplicationsParametersTransformationService appsParametersTransformationService;
private final BackingServicesParametersTransformationService servicesParametersTransformationService;
private final TargetService targetService;
public AppDeploymentUpdateServiceInstanceWorkflow(BrokeredServices brokeredServices,
BackingAppDeploymentService deploymentService,
BackingApplicationsParametersTransformationService parametersTransformationService,
BackingServicesProvisionService backingServicesProvisionService,
BackingApplicationsParametersTransformationService appsParametersTransformationService,
BackingServicesParametersTransformationService servicesParametersTransformationService,
TargetService targetService) {
super(brokeredServices);
this.deploymentService = deploymentService;
this.parametersTransformationService = parametersTransformationService;
this.backingServicesProvisionService = backingServicesProvisionService;
this.appsParametersTransformationService = appsParametersTransformationService;
this.servicesParametersTransformationService = servicesParametersTransformationService;
this.targetService = targetService;
}
public Flux<Void> update(UpdateServiceInstanceRequest request) {
return getBackingApplicationsForService(request.getServiceDefinition(), request.getPlanId())
.flatMap(backingApps -> targetService.addToBackingApplications(backingApps, getTargetForService(request.getServiceDefinition(), request.getPlanId()), request.getServiceInstanceId()))
.flatMap(backingApps ->
parametersTransformationService.transformParameters(backingApps, request.getParameters()))
.flatMapMany(deploymentService::deploy)
.doOnRequest(l -> log.debug("Deploying applications {}", brokeredServices))
.doOnEach(response -> log.debug("Finished deploying {}", response))
.doOnComplete(() -> log.debug("Finished deploying applications {}", brokeredServices))
.doOnError(exception -> log.error("Error deploying applications {} with error {}", brokeredServices, exception))
.flatMap(apps -> Flux.empty());
return
getBackingServicesForService(request.getServiceDefinition(), request.getPlanId())
.flatMap(backingService ->
targetService.addToBackingServices(backingService,
getTargetForService(request.getServiceDefinition(), request.getPlanId()),
request.getServiceInstanceId()))
.flatMap(backingServices ->
servicesParametersTransformationService.transformParameters(backingServices,
request.getParameters()))
.flatMapMany(backingServicesProvisionService::updateServiceInstance)
.thenMany(
getBackingApplicationsForService(request.getServiceDefinition(), request.getPlanId())
.flatMap(backingApps ->
targetService.addToBackingApplications(backingApps,
getTargetForService(request.getServiceDefinition(), request.getPlanId()),
request.getServiceInstanceId()))
.flatMap(backingApps ->
appsParametersTransformationService.transformParameters(backingApps, request.getParameters()))
.flatMapMany(deploymentService::deploy)
.doOnRequest(l -> log.info("Deploying applications {}", brokeredServices))
.doOnEach(s -> log.info("Finished deploying {}", s))
.doOnComplete(() -> log.info("Finished deploying applications {}", brokeredServices))
.doOnError(e -> log.info("Error deploying applications {} with error {}", brokeredServices, e))
.flatMap(apps -> Flux.empty()));
}
@Override

View File

@@ -12,7 +12,9 @@ import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@ExtendWith(MockitoExtension.class)
class BackingServicesProvisionServiceTest {
@@ -37,14 +39,13 @@ class BackingServicesProvisionServiceTest {
.serviceInstanceName("si2")
.name("service2")
.plan("free")
.parameters(Collections.singletonMap("key2", "value2"))
.build())
.build();
}
@Test
@SuppressWarnings("UnassignedFluxMonoInstance")
void shouldCreateServiceInstance() {
void createServiceInstance() {
doReturn(Mono.just("si1"))
.when(deployerClient).createServiceInstance(backingServices.get(0));
doReturn(Mono.just("si2"))
@@ -64,7 +65,20 @@ class BackingServicesProvisionServiceTest {
@Test
@SuppressWarnings("UnassignedFluxMonoInstance")
void shouldDeleteServiceInstance() {
void updateServiceInstance() {
doReturn(Mono.just("si1"))
.when(deployerClient).updateServiceInstance(backingServices.get(0));
StepVerifier.create(backingServicesProvisionService.updateServiceInstance(backingServices))
.assertNext(value -> assertThat(value).isEqualTo("si1"))
.verifyComplete();
verifyNoMoreInteractions(deployerClient);
}
@Test
@SuppressWarnings("UnassignedFluxMonoInstance")
void deleteServiceInstance() {
doReturn(Mono.just("deleted1"))
.when(deployerClient).deleteServiceInstance(backingServices.get(0));
doReturn(Mono.just("deleted2"))

View File

@@ -16,7 +16,6 @@
package org.springframework.cloud.appbroker.deployer;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -37,6 +36,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@SuppressWarnings("UnassignedFluxMonoInstance")
class DeployerClientTest {
private static final String APP_NAME = "helloworld";
@@ -122,7 +122,7 @@ class DeployerClientTest {
.verifyComplete();
verify(appDeployer).deploy(argThat(matchesRequest(APP_NAME, APP_PATH, Collections.emptyMap(),
Collections.emptyMap(), Arrays.asList("my-db-service"))));
Collections.emptyMap(), Collections.singletonList("my-db-service"))));
}
@Test

View File

@@ -56,13 +56,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
class AppDeploymentCreateServiceInstanceWorkflowTest {
@Mock
private BackingAppDeploymentService backingAppDeploymentService;
private BackingAppDeploymentService appDeploymentService;
@Mock
private BackingApplicationsParametersTransformationService backingApplicationsParametersTransformationService;
private BackingServicesProvisionService servicesProvisionService;
@Mock
private BackingServicesParametersTransformationService backingServicesParametersTransformationService;
private BackingApplicationsParametersTransformationService appsParametersTransformationService;
@Mock
private BackingServicesParametersTransformationService servicesParametersTransformationService;
@Mock
private CredentialProviderService credentialProviderService;
@@ -70,9 +73,6 @@ class AppDeploymentCreateServiceInstanceWorkflowTest {
@Mock
private TargetService targetService;
@Mock
private BackingServicesProvisionService backingServicesProvisionService;
private BackingApplications backingApps;
private BackingServices backingServices;
private TargetSpec targetSpec;
@@ -105,7 +105,10 @@ class AppDeploymentCreateServiceInstanceWorkflowTest {
.build())
.build();
targetSpec = TargetSpec.builder().name("TargetSpace").build();
targetSpec = TargetSpec.builder()
.name("TargetSpace")
.build();
BrokeredServices brokeredServices = BrokeredServices
.builder()
.service(BrokeredService
@@ -120,16 +123,16 @@ class AppDeploymentCreateServiceInstanceWorkflowTest {
createServiceInstanceWorkflow = new AppDeploymentCreateServiceInstanceWorkflow(
brokeredServices,
backingAppDeploymentService,
backingApplicationsParametersTransformationService,
backingServicesParametersTransformationService,
appDeploymentService,
servicesProvisionService,
appsParametersTransformationService,
servicesParametersTransformationService,
credentialProviderService,
targetService,
backingServicesProvisionService);
targetService);
}
@Test
@SuppressWarnings("unchecked")
@SuppressWarnings({"unchecked", "UnassignedFluxMonoInstance"})
void createServiceInstanceSucceeds() {
CreateServiceInstanceRequest request = buildRequest("service1", "plan1");
@@ -141,43 +144,8 @@ class AppDeploymentCreateServiceInstanceWorkflowTest {
.expectNext()
.verifyComplete();
verify(backingAppDeploymentService).deploy(backingApps);
verify(backingServicesProvisionService).createServiceInstance(backingServices);
verifyNoMoreInteractionsWithServices();
}
@Test
void createServiceInstanceWithParametersSucceeds() {
CreateServiceInstanceRequest request = buildRequest("service1", "plan1",
singletonMap("ENV_VAR_1", "value from parameters"));
setupMocks(request);
StepVerifier
.create(createServiceInstanceWorkflow.create(request))
.expectNext()
.expectNext()
.verifyComplete();
verify(backingApplicationsParametersTransformationService)
.transformParameters(backingApps, singletonMap("ENV_VAR_1", "value from parameters"));
verifyNoMoreInteractionsWithServices();
}
@Test
void createServiceInstanceWithTargetSucceeds() {
CreateServiceInstanceRequest request = buildRequest("service1", "plan1",
singletonMap("ENV_VAR_1", "value from parameters"));
setupMocks(request);
StepVerifier
.create(createServiceInstanceWorkflow.create(request))
.expectNext()
.expectNext()
.verifyComplete();
verify(appDeploymentService).deploy(backingApps);
verify(servicesProvisionService).createServiceInstance(backingServices);
final String expectedServiceId = "service-instance-id";
verify(targetService).addToBackingServices(backingServices, targetSpec, expectedServiceId);
@@ -186,6 +154,34 @@ class AppDeploymentCreateServiceInstanceWorkflowTest {
verifyNoMoreInteractionsWithServices();
}
@Test
@SuppressWarnings("UnassignedFluxMonoInstance")
void createServiceInstanceWithParametersSucceeds() {
Map<String, Object> parameters = singletonMap("ENV_VAR_1", "value from parameters");
CreateServiceInstanceRequest request = buildRequest("service1", "plan1",
parameters);
setupMocks(request);
StepVerifier
.create(createServiceInstanceWorkflow.create(request))
.expectNext()
.expectNext()
.verifyComplete();
verify(appDeploymentService).deploy(backingApps);
verify(servicesProvisionService).createServiceInstance(backingServices);
verify(appsParametersTransformationService)
.transformParameters(backingApps, parameters);
verify(servicesParametersTransformationService)
.transformParameters(backingServices, parameters);
verifyNoMoreInteractionsWithServices();
}
@Test
void createServiceInstanceWithNoAppsDoesNothing() {
CreateServiceInstanceRequest request = buildRequest("unsupported-service", "plan1");
@@ -198,29 +194,32 @@ class AppDeploymentCreateServiceInstanceWorkflowTest {
}
private void setupMocks(CreateServiceInstanceRequest request) {
given(this.backingAppDeploymentService.deploy(eq(backingApps)))
given(this.appDeploymentService.deploy(eq(backingApps)))
.willReturn(Flux.just("app1", "app2"));
given(this.backingApplicationsParametersTransformationService.transformParameters(eq(backingApps), eq(request.getParameters())))
given(this.servicesProvisionService.createServiceInstance(eq(backingServices)))
.willReturn(Flux.just("my-service-instance"));
given(this.appsParametersTransformationService.transformParameters(eq(backingApps), eq(request.getParameters())))
.willReturn(Mono.just(backingApps));
given(this.backingServicesParametersTransformationService.transformParameters(eq(backingServices), eq(request.getParameters())))
given(this.servicesParametersTransformationService.transformParameters(eq(backingServices), eq(request.getParameters())))
.willReturn(Mono.just(backingServices));
given(this.credentialProviderService.addCredentials(eq(backingApps), eq(request.getServiceInstanceId())))
.willReturn(Mono.just(backingApps));
given(this.targetService.addToBackingApplications(eq(backingApps), eq(targetSpec), eq(request.getServiceInstanceId())))
.willReturn(Mono.just(backingApps));
given(this.targetService.addToBackingServices(eq(backingServices), eq(targetSpec), eq(request.getServiceInstanceId())))
.willReturn(Mono.just(backingServices));
given(this.backingServicesProvisionService.createServiceInstance(eq(backingServices)))
.willReturn(Flux.just("my-service-instance"));
}
private void verifyNoMoreInteractionsWithServices() {
verifyNoMoreInteractions(this.backingAppDeploymentService);
verifyNoMoreInteractions(this.backingApplicationsParametersTransformationService);
verifyNoMoreInteractions(this.backingServicesParametersTransformationService);
verifyNoMoreInteractions(this.appDeploymentService);
verifyNoMoreInteractions(this.servicesProvisionService);
verifyNoMoreInteractions(this.appsParametersTransformationService);
verifyNoMoreInteractions(this.servicesParametersTransformationService);
verifyNoMoreInteractions(this.credentialProviderService);
verifyNoMoreInteractions(this.targetService);
verifyNoMoreInteractions(this.backingServicesProvisionService);
}
private CreateServiceInstanceRequest buildRequest(String serviceName, String planName) {

View File

@@ -109,9 +109,10 @@ class AppDeploymentDeleteServiceInstanceWorkflowTest {
new AppDeploymentDeleteServiceInstanceWorkflow(
brokeredServices,
backingAppDeploymentService,
backingServicesProvisionService,
credentialProviderService,
targetService,
backingServicesProvisionService);
targetService
);
}
@Test

View File

@@ -24,6 +24,10 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.appbroker.deployer.BackingService;
import org.springframework.cloud.appbroker.deployer.BackingServices;
import org.springframework.cloud.appbroker.deployer.BackingServicesProvisionService;
import org.springframework.cloud.appbroker.extensions.parameters.BackingServicesParametersTransformationService;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@@ -43,22 +47,31 @@ import org.springframework.cloud.servicebroker.model.instance.UpdateServiceInsta
import static java.util.Collections.singletonMap;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@ExtendWith(MockitoExtension.class)
class AppDeploymentUpdateServiceInstanceWorkflowTest {
@Mock
private BackingAppDeploymentService backingAppDeploymentService;
private BackingAppDeploymentService appDeploymentService;
@Mock
private BackingApplicationsParametersTransformationService backingApplicationsParametersTransformationService;
private BackingServicesProvisionService servicesProvisionService;
@Mock
private BackingApplicationsParametersTransformationService appsParametersTransformationService;
@Mock
private BackingServicesParametersTransformationService servicesParametersTransformationService;
@Mock
private TargetService targetService;
private BackingApplications backingApps;
private BackingServices backingServices;
private TargetSpec targetSpec;
private AppDeploymentUpdateServiceInstanceWorkflow updateServiceInstanceWorkflow;
@BeforeEach
@@ -75,34 +88,45 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
.build())
.build();
targetSpec = TargetSpec.builder().name("TargetSpace").build();
BrokeredServices brokeredServices = BrokeredServices
backingServices = BackingServices
.builder()
.service(BrokeredService.builder()
.serviceName("service1")
.planName("plan1")
.apps(backingApps)
.target(targetSpec)
.build())
.backingService(BackingService
.builder()
.name("my-service")
.plan("a-plan")
.serviceInstanceName("my-service-instance")
.build())
.build();
updateServiceInstanceWorkflow = new AppDeploymentUpdateServiceInstanceWorkflow(brokeredServices,
backingAppDeploymentService,
backingApplicationsParametersTransformationService,
targetSpec = TargetSpec.builder()
.name("TargetSpace")
.build();
BrokeredServices brokeredServices = BrokeredServices.builder()
.service(BrokeredService.builder()
.serviceName("service1")
.planName("plan1")
.apps(backingApps)
.services(backingServices)
.target(targetSpec)
.build())
.build();
updateServiceInstanceWorkflow = new AppDeploymentUpdateServiceInstanceWorkflow(
brokeredServices,
appDeploymentService,
servicesProvisionService,
appsParametersTransformationService,
servicesParametersTransformationService,
targetService);
}
@Test
@SuppressWarnings("unchecked")
@SuppressWarnings({"unchecked", "UnassignedFluxMonoInstance"})
void updateServiceInstanceSucceeds() {
UpdateServiceInstanceRequest request = buildRequest("service1", "plan1");
given(this.backingAppDeploymentService.deploy(eq(backingApps)))
.willReturn(Flux.just("app1", "app2"));
given(this.targetService.addToBackingApplications(eq(backingApps), eq(targetSpec), eq("service-instance-id")))
.willReturn(Mono.just(backingApps));
given(this.backingApplicationsParametersTransformationService.transformParameters(eq(backingApps), eq(request.getParameters())))
.willReturn(Mono.just(backingApps));
setupMocks(request);
StepVerifier
.create(updateServiceInstanceWorkflow.update(request))
@@ -110,9 +134,14 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
.expectNext()
.verifyComplete();
verifyNoMoreInteractions(this.backingAppDeploymentService);
verifyNoMoreInteractions(this.backingApplicationsParametersTransformationService);
verifyNoMoreInteractions(this.targetService);
verify(appDeploymentService).deploy(backingApps);
verify(servicesProvisionService).updateServiceInstance(backingServices);
final String expectedServiceId = "service-instance-id";
verify(targetService).addToBackingServices(backingServices, targetSpec, expectedServiceId);
verify(targetService).addToBackingApplications(backingApps, targetSpec, expectedServiceId);
verifyNoMoreInteractionsWithServices();
}
@Test
@@ -120,12 +149,7 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
UpdateServiceInstanceRequest request = buildRequest("service1", "plan1",
singletonMap("ENV_VAR_1", "value from parameters"));
given(this.backingAppDeploymentService.deploy(eq(backingApps)))
.willReturn(Flux.just("app1", "app2"));
given(this.backingApplicationsParametersTransformationService.transformParameters(eq(backingApps), eq(request.getParameters())))
.willReturn(Mono.just(backingApps));
given(this.targetService.addToBackingApplications(eq(backingApps), eq(targetSpec), eq("service-instance-id")))
.willReturn(Mono.just(backingApps));
setupMocks(request);
StepVerifier
.create(updateServiceInstanceWorkflow.update(request))
@@ -133,9 +157,7 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
.expectNext()
.verifyComplete();
verifyNoMoreInteractions(this.backingAppDeploymentService);
verifyNoMoreInteractions(this.backingApplicationsParametersTransformationService);
verifyNoMoreInteractions(this.targetService);
verifyNoMoreInteractionsWithServices();
}
@Test
@@ -144,8 +166,31 @@ class AppDeploymentUpdateServiceInstanceWorkflowTest {
.create(updateServiceInstanceWorkflow.update(buildRequest("unsupported-service", "plan1")))
.verifyComplete();
verifyNoMoreInteractions(this.backingAppDeploymentService);
verifyNoMoreInteractions(this.backingApplicationsParametersTransformationService);
verifyNoMoreInteractionsWithServices();
}
private void setupMocks(UpdateServiceInstanceRequest request) {
given(this.appDeploymentService.deploy(eq(backingApps)))
.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())))
.willReturn(Mono.just(backingApps));
given(this.servicesParametersTransformationService.transformParameters(eq(backingServices), eq(request.getParameters())))
.willReturn(Mono.just(backingServices));
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(request.getServiceInstanceId())))
.willReturn(Mono.just(backingServices));
}
private void verifyNoMoreInteractionsWithServices() {
verifyNoMoreInteractions(this.appDeploymentService);
verifyNoMoreInteractions(this.servicesProvisionService);
verifyNoMoreInteractions(this.appsParametersTransformationService);
verifyNoMoreInteractions(this.servicesParametersTransformationService);
verifyNoMoreInteractions(this.targetService);
}

View File

@@ -68,6 +68,8 @@ import org.springframework.cloud.appbroker.deployer.DeployApplicationResponse;
import org.springframework.cloud.appbroker.deployer.DeploymentProperties;
import org.springframework.cloud.appbroker.deployer.UndeployApplicationRequest;
import org.springframework.cloud.appbroker.deployer.UndeployApplicationResponse;
import org.springframework.cloud.appbroker.deployer.UpdateServiceInstanceRequest;
import org.springframework.cloud.appbroker.deployer.UpdateServiceInstanceResponse;
import org.springframework.cloud.appbroker.deployer.util.ByteSizeUtils;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
@@ -218,7 +220,7 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
.name(spaceName)
.build())
.doOnSuccess(response -> logger.info("Created space {}", spaceName))
.doOnError(e -> logger.warn(String.format("Error creating space %s. Exception Message %s", spaceName, e.getMessage())))
.doOnError(e -> logger.warn(String.format("Error creating space %s: %s", spaceName, e.getMessage())))
.onErrorResume(e -> Mono.empty())
.then(Mono.empty()));
return getSpaceIdFromName(spaceName)
@@ -517,7 +519,9 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
.build();
Mono<CreateServiceInstanceResponse> createServiceInstanceResponseMono =
Mono.just(CreateServiceInstanceResponse.builder().name(request.getServiceInstanceName()).build());
Mono.just(CreateServiceInstanceResponse.builder()
.name(request.getServiceInstanceName())
.build());
if (request.getProperties().containsKey(DeploymentProperties.TARGET_PROPERTY_KEY)) {
return createSpace(request.getProperties().get(DeploymentProperties.TARGET_PROPERTY_KEY))
@@ -526,34 +530,49 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
.services()
.createInstance(createServiceInstanceRequest)
.then(createServiceInstanceResponseMono));
} else {
return operations
.services()
.createInstance(createServiceInstanceRequest)
.then(createServiceInstanceResponseMono);
}
return operations
}
@Override
public Mono<UpdateServiceInstanceResponse> updateServiceInstance(UpdateServiceInstanceRequest request) {
org.cloudfoundry.operations.services.UpdateServiceInstanceRequest updateServiceInstanceRequest =
org.cloudfoundry.operations.services.UpdateServiceInstanceRequest
.builder()
.serviceInstanceName(request.getServiceInstanceName())
.parameters(request.getParameters())
.build();
Mono<UpdateServiceInstanceResponse> updateServiceInstanceResponseMono =
Mono.just(UpdateServiceInstanceResponse.builder()
.name(request.getServiceInstanceName())
.build());
return getOperations(request.getProperties())
.services()
.createInstance(createServiceInstanceRequest)
.then(createServiceInstanceResponseMono);
.updateInstance(updateServiceInstanceRequest)
.then(updateServiceInstanceResponseMono);
}
@Override
public Mono<DeleteServiceInstanceResponse> deleteServiceInstance(DeleteServiceInstanceRequest request) {
final String serviceInstanceName = request.getName();
final String serviceInstanceName = request.getServiceInstanceName();
final CloudFoundryOperations operations;
if (request.getProperties().containsKey(DeploymentProperties.TARGET_PROPERTY_KEY)) {
operations = createCloudFoundryOperationsForSpace(request.getProperties().get(DeploymentProperties.TARGET_PROPERTY_KEY));
}
else {
operations = this.operations;
}
return operations
return getOperations(request.getProperties())
.services()
.getInstance(GetServiceInstanceRequest.builder().name(serviceInstanceName).build())
.getInstance(GetServiceInstanceRequest.builder()
.name(serviceInstanceName)
.build())
.map(ServiceInstance::getApplications)
.flatMap((Function<List<String>, Mono<?>>) applications ->
Flux.fromIterable(applications)
.flatMap(
application ->
operations
getOperations(request.getProperties())
.services()
.unbind(
UnbindServiceInstanceRequest
@@ -563,7 +582,7 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
.build())
).collectList())
.then(
operations
getOperations(request.getProperties())
.services()
.deleteInstance(
org.cloudfoundry.operations.services.DeleteServiceInstanceRequest
@@ -573,4 +592,11 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
.then(Mono.just(DeleteServiceInstanceResponse.builder().name(serviceInstanceName).build())));
}
private CloudFoundryOperations getOperations(Map<String, String> properties) {
if (properties.containsKey(DeploymentProperties.TARGET_PROPERTY_KEY)) {
return createCloudFoundryOperationsForSpace(properties.get(DeploymentProperties.TARGET_PROPERTY_KEY));
} else {
return this.operations;
}
}
}

View File

@@ -47,6 +47,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.UpdateServiceInstanceRequest;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.ResourceLoader;
@@ -324,7 +325,7 @@ class CloudFoundryAppDeployerTest {
DeleteServiceInstanceRequest request =
DeleteServiceInstanceRequest.builder()
.name("service-instance-name")
.serviceInstanceName("service-instance-name")
.properties(emptyMap())
.build();
@@ -360,6 +361,27 @@ class CloudFoundryAppDeployerTest {
.verifyComplete();
}
@Test
void updateServiceInstance() {
when(services.updateInstance(
org.cloudfoundry.operations.services.UpdateServiceInstanceRequest.builder()
.serviceInstanceName("service-instance-name")
.parameters(emptyMap())
.build()))
.thenReturn(Mono.empty());
UpdateServiceInstanceRequest request =
UpdateServiceInstanceRequest.builder()
.serviceInstanceName("service-instance-name")
.parameters(emptyMap())
.build();
StepVerifier.create(
appDeployer.updateServiceInstance(request))
.assertNext(response -> assertThat(response.getName()).isEqualTo("service-instance-name"))
.verifyComplete();
}
private ApplicationManifest.Builder baseManifest() {
return ApplicationManifest.builder()
.environmentVariable("SPRING_APPLICATION_INDEX", "${vcap.application.instance_index}")

View File

@@ -32,8 +32,11 @@ public interface AppDeployer {
return Mono.empty();
}
default Mono<DeleteServiceInstanceResponse> deleteServiceInstance(DeleteServiceInstanceRequest request) {
default Mono<UpdateServiceInstanceResponse> updateServiceInstance(UpdateServiceInstanceRequest request) {
return Mono.empty();
}
default Mono<DeleteServiceInstanceResponse> deleteServiceInstance(DeleteServiceInstanceRequest request) {
return Mono.empty();
}
}

View File

@@ -20,11 +20,11 @@ import java.util.Map;
public class DeleteServiceInstanceRequest {
private final String name;
private final String serviceInstanceName;
private final Map<String, String> properties;
DeleteServiceInstanceRequest(String name, Map<String, String> properties) {
this.name = name;
DeleteServiceInstanceRequest(String serviceInstanceName, Map<String, String> properties) {
this.serviceInstanceName = serviceInstanceName;
this.properties = properties;
}
@@ -32,8 +32,8 @@ public class DeleteServiceInstanceRequest {
return new DeleteServiceInstanceRequestBuilder();
}
public String getName() {
return name;
public String getServiceInstanceName() {
return serviceInstanceName;
}
public Map<String, String> getProperties() {
@@ -42,14 +42,14 @@ public class DeleteServiceInstanceRequest {
public static class DeleteServiceInstanceRequestBuilder {
private String name;
private String serviceInstanceName;
private Map<String, String> properties;
DeleteServiceInstanceRequestBuilder() {
}
public DeleteServiceInstanceRequestBuilder name(String name) {
this.name = name;
public DeleteServiceInstanceRequestBuilder serviceInstanceName(String name) {
this.serviceInstanceName = name;
return this;
}
@@ -59,7 +59,7 @@ public class DeleteServiceInstanceRequest {
}
public DeleteServiceInstanceRequest build() {
return new DeleteServiceInstanceRequest(name, properties);
return new DeleteServiceInstanceRequest(serviceInstanceName, properties);
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2016-2018 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
*
* http://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;
public class UpdateServiceInstanceRequest {
private final String serviceInstanceName;
private final Map<String, Object> parameters;
private final Map<String, String> properties;
UpdateServiceInstanceRequest(String serviceInstanceName,
Map<String, Object> parameters,
Map<String, String> properties) {
this.serviceInstanceName = serviceInstanceName;
this.parameters = parameters;
this.properties = properties;
}
public static UpdateServiceInstanceRequestBuilder builder() {
return new UpdateServiceInstanceRequestBuilder();
}
public String getServiceInstanceName() {
return serviceInstanceName;
}
public Map<String, Object> getParameters() {
return parameters;
}
public Map<String, String> getProperties() {
return properties;
}
public static class UpdateServiceInstanceRequestBuilder {
private String serviceInstanceName;
private final Map<String, Object> parameters = new HashMap<>();
private final Map<String, String> properties = new HashMap<>();
UpdateServiceInstanceRequestBuilder() {
}
public UpdateServiceInstanceRequestBuilder serviceInstanceName(String serviceInstanceName) {
this.serviceInstanceName = serviceInstanceName;
return this;
}
public UpdateServiceInstanceRequestBuilder parameters(String key, String value) {
this.parameters.put(key, value);
return this;
}
public UpdateServiceInstanceRequestBuilder parameters(Map<String, Object> parameters) {
if (parameters == null) {
return this;
}
this.parameters.putAll(parameters);
return this;
}
public UpdateServiceInstanceRequestBuilder properties(Map<String, String> properties) {
if (properties == null) {
return this;
}
this.properties.putAll(properties);
return this;
}
public UpdateServiceInstanceRequest build() {
return new UpdateServiceInstanceRequest(serviceInstanceName, parameters, properties);
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2016-2018 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.appbroker.deployer;
public class UpdateServiceInstanceResponse {
private final String name;
UpdateServiceInstanceResponse(String name) {
this.name = name;
}
public static UpdateServiceInstanceResponseBuilder builder() {
return new UpdateServiceInstanceResponseBuilder();
}
public String getName() {
return name;
}
public static class UpdateServiceInstanceResponseBuilder {
private String name;
UpdateServiceInstanceResponseBuilder() {
}
public UpdateServiceInstanceResponseBuilder name(String name) {
this.name = name;
return this;
}
public UpdateServiceInstanceResponse build() {
return new UpdateServiceInstanceResponse(name);
}
}
}

View File

@@ -41,8 +41,8 @@ import static org.springframework.cloud.appbroker.sample.UpdateInstanceComponent
})
class UpdateInstanceComponentTest extends WiremockComponentTest {
static final String APP_NAME_1 = "first-app";
static final String APP_NAME_2 = "second-app";
static final String APP_NAME_1 = "update-first-app";
static final String APP_NAME_2 = "update-second-app";
@Autowired
private OpenServiceBrokerApiFixture brokerFixture;

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2016-2018 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
*
* http://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.sample;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.appbroker.sample.fixtures.CloudControllerStubFixture;
import org.springframework.cloud.appbroker.sample.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.sample.UpdateInstanceWithServicesComponentTest.APP_NAME;
import static org.springframework.cloud.appbroker.sample.UpdateInstanceWithServicesComponentTest.SERVICE_NAME;
import static org.springframework.cloud.appbroker.sample.UpdateInstanceWithServicesComponentTest.SERVICE_INSTANCE_NAME;
@TestPropertySource(properties = {
"spring.cloud.appbroker.services[0].service-name=example",
"spring.cloud.appbroker.services[0].plan-name=standard",
"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"
})
class UpdateInstanceWithServicesComponentTest extends WiremockComponentTest {
static final String APP_NAME = "app-update-with-services";
static final String SERVICE_INSTANCE_NAME = "my-db-service";
static final String SERVICE_NAME = "db-service";
@Autowired
private OpenServiceBrokerApiFixture brokerFixture;
@Autowired
private CloudControllerStubFixture cloudControllerFixture;
@Test
void updateAppWithServicesWhenServicesExist() {
cloudControllerFixture.stubAppExists(APP_NAME);
cloudControllerFixture.stubUpdateApp(APP_NAME);
cloudControllerFixture.stubServiceInstanceExists(SERVICE_INSTANCE_NAME);
cloudControllerFixture.stubCreateServiceBinding(APP_NAME, SERVICE_INSTANCE_NAME);
// when a service instance is created
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());
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2016-2018 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
*
* http://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.sample;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.appbroker.sample.fixtures.CloudControllerStubFixture;
import org.springframework.cloud.appbroker.sample.fixtures.OpenServiceBrokerApiFixture;
import org.springframework.cloud.servicebroker.model.instance.OperationState;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.TestPropertySource;
import java.util.Collections;
import java.util.HashMap;
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.sample.UpdateInstanceWithServicesParametersComponentTest.APP_NAME;
import static org.springframework.cloud.appbroker.sample.UpdateInstanceWithServicesParametersComponentTest.SERVICE_NAME;
import static org.springframework.cloud.appbroker.sample.UpdateInstanceWithServicesParametersComponentTest.SERVICE_INSTANCE_NAME;
@TestPropertySource(properties = {
"spring.cloud.appbroker.services[0].service-name=example",
"spring.cloud.appbroker.services[0].plan-name=standard",
"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].services[0].parameters-transformers[0].name=ParameterMapping",
"spring.cloud.appbroker.services[0].services[0].parameters-transformers[0].args.include=paramA,paramC"
})
class UpdateInstanceWithServicesParametersComponentTest extends WiremockComponentTest {
static final String APP_NAME = "app-update-services-param";
static final String SERVICE_INSTANCE_NAME = "my-db-service";
static final String SERVICE_NAME = "db-service";
@Autowired
private OpenServiceBrokerApiFixture brokerFixture;
@Autowired
private CloudControllerStubFixture cloudControllerFixture;
@Test
void updateAppWithBackingServicesParameters() {
cloudControllerFixture.stubAppExists(APP_NAME);
cloudControllerFixture.stubUpdateApp(APP_NAME);
cloudControllerFixture.stubServiceInstanceExists(SERVICE_INSTANCE_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.stubUpdateServiceInstanceWithParameters(SERVICE_INSTANCE_NAME, expectedCreationParameters);
cloudControllerFixture.stubCreateServiceBinding(APP_NAME, SERVICE_INSTANCE_NAME);
// when a service instance is created with parameters
HashMap<String, Object> creationParameters = new HashMap<>();
creationParameters.put("paramA", "valueA");
creationParameters.put("paramB", "valueB");
creationParameters.put("paramC", Collections.singletonMap("paramC1", "valueC1"));
given(brokerFixture.serviceInstanceRequest(creationParameters))
.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());
}
}

View File

@@ -328,6 +328,13 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
.willReturn(ok()));
}
public void stubUpdateServiceInstanceWithParameters(String serviceInstanceName, Map<String, Object> params) {
String serviceInstanceGuid = serviceInstanceName + "-GUID";
stubFor(put(urlEqualTo("/v2/service_instances/" + serviceInstanceGuid + "?accepts_incomplete=true"))
.withRequestBody(matchingJsonPath("$.[?(@.parameters == " + new JSONObject(params) + ")]"))
.willReturn(ok()));
}
public void stubCreateServiceBinding(String appName, String serviceInstanceName) {
String serviceInstanceGuid = serviceInstanceName + "-GUID";
String serviceBindingGuid = appGuid(appName) + "-" + serviceInstanceGuid;