Store binding credentials in CredHub when available

Resolves #119
This commit is contained in:
rclarkson
2018-12-07 10:52:56 -05:00
committed by Scott Frederick
parent e9010c2851
commit 255a9cad2b
11 changed files with 442 additions and 73 deletions

View File

@@ -48,6 +48,7 @@ configure(allprojects) {
springVersion = project.findProperty("springVersion") ?: "5.0.10.RELEASE"
reactorVersion = project.findProperty("reactorVersion") ?: "Bismuth-SR14"
openServiceBrokerVersion = "3.0.0.BUILD-SNAPSHOT"
springCredhubVersion = "2.0.0.BUILD-SNAPSHOT"
cfJavaClientVersion = "3.13.0.RELEASE"
mockitoVersion = "2.23.4"
immutablesVersion = "2.7.3"

View File

@@ -6,5 +6,5 @@ include "spring-cloud-app-broker-core"
include "spring-cloud-app-broker-autoconfigure"
include "spring-cloud-app-broker-sample"
include "spring-cloud-app-broker-acceptance-tests"
include "spring-cloud-app-broker-security-credhub"
include 'spring-cloud-starter-app-broker'

View File

@@ -26,20 +26,29 @@ dependencies {
implementation project(":spring-cloud-app-broker-core")
implementation project(":spring-cloud-app-broker-deployer")
implementation project(":spring-cloud-app-broker-deployer-cloudfoundry")
implementation project(":spring-cloud-app-broker-security-credhub")
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.cloudfoundry:cloudfoundry-client-reactor:${cfJavaClientVersion}")
implementation("org.cloudfoundry:cloudfoundry-operations:${cfJavaClientVersion}")
implementation("io.projectreactor:reactor-core")
implementation("io.projectreactor.ipc:reactor-netty")
api("org.immutables:value:${immutablesVersion}")
compileOnly("org.cloudfoundry:cloudfoundry-client-reactor:${cfJavaClientVersion}")
compileOnly("org.cloudfoundry:cloudfoundry-operations:${cfJavaClientVersion}")
compileOnly("org.springframework.credhub:spring-credhub-starter:${springCredhubVersion}")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.boot:spring-boot-starter-webflux")
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("org.mockito:mockito-junit-jupiter:${mockitoVersion}")
testCompileOnly("org.cloudfoundry:cloudfoundry-client-reactor:${cfJavaClientVersion}")
testCompileOnly("org.cloudfoundry:cloudfoundry-operations:${cfJavaClientVersion}")
testCompileOnly("org.springframework.credhub:spring-credhub-starter:${springCredhubVersion}")
}
compileJava.dependsOn(processResources)

View File

@@ -211,13 +211,13 @@ public class AppBrokerAutoConfiguration {
@Bean
public WorkflowServiceInstanceBindingService serviceInstanceBindingService(
ServiceInstanceBindingStateRepository stateRepository,
@Autowired(required = false) List<CreateServiceInstanceAppBindingWorkflow> createServiceInstanceAppBindingWorkflows,
@Autowired(required = false) List<CreateServiceInstanceRouteBindingWorkflow> createServiceInstanceRouteBindingWorkflows,
@Autowired(required = false) List<DeleteServiceInstanceBindingWorkflow> deleteServiceInstanceBindingWorkflows) {
ServiceInstanceBindingStateRepository stateRepository,
@Autowired(required = false) List<CreateServiceInstanceAppBindingWorkflow> createServiceInstanceAppBindingWorkflows,
@Autowired(required = false) List<CreateServiceInstanceRouteBindingWorkflow> createServiceInstanceRouteBindingWorkflows,
@Autowired(required = false) List<DeleteServiceInstanceBindingWorkflow> deleteServiceInstanceBindingWorkflows) {
return new WorkflowServiceInstanceBindingService(stateRepository,
createServiceInstanceAppBindingWorkflows,
createServiceInstanceRouteBindingWorkflows,
deleteServiceInstanceBindingWorkflows);
createServiceInstanceAppBindingWorkflows,
createServiceInstanceRouteBindingWorkflows,
deleteServiceInstanceBindingWorkflows);
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.autoconfigure;
import org.springframework.cloud.appbroker.workflow.binding.CredHubPersistingCreateServiceInstanceAppBindingWorkflow;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.cloud.appbroker.service.CreateServiceInstanceAppBindingWorkflow;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.credhub.core.ReactiveCredHubOperations;
@Configuration
@AutoConfigureBefore(AppBrokerAutoConfiguration.class)
public class CredHubAutoConfiguration {
@Value("${spring.application.name}")
private String appName;
@Bean
@ConditionalOnBean(ReactiveCredHubOperations.class)
public CreateServiceInstanceAppBindingWorkflow credhubPersistingCreateServiceInstanceAppBindingWorkflow(ReactiveCredHubOperations credHubOperations) {
return new CredHubPersistingCreateServiceInstanceAppBindingWorkflow(credHubOperations, appName);
}
}

View File

@@ -1,3 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.appbroker.autoconfigure.CloudFoundryAppDeployerAutoConfiguration,\
org.springframework.cloud.appbroker.autoconfigure.CredHubAutoConfiguration,\
org.springframework.cloud.appbroker.autoconfigure.AppBrokerAutoConfiguration

View File

@@ -17,8 +17,11 @@
package org.springframework.cloud.appbroker.autoconfigure;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.appbroker.workflow.binding.CredHubPersistingCreateServiceInstanceAppBindingWorkflow;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.appbroker.deployer.BackingAppDeploymentService;
import org.springframework.cloud.appbroker.deployer.BackingApplications;
@@ -35,6 +38,7 @@ import org.springframework.cloud.appbroker.extensions.parameters.ParameterMappin
import org.springframework.cloud.appbroker.extensions.parameters.PropertyMappingParametersTransformerFactory;
import org.springframework.cloud.appbroker.extensions.targets.SpacePerServiceInstance;
import org.springframework.cloud.appbroker.extensions.targets.TargetService;
import org.springframework.cloud.appbroker.service.CreateServiceInstanceAppBindingWorkflow;
import org.springframework.cloud.appbroker.service.WorkflowServiceInstanceBindingService;
import org.springframework.cloud.appbroker.service.WorkflowServiceInstanceService;
import org.springframework.cloud.appbroker.state.ServiceInstanceBindingStateRepository;
@@ -42,17 +46,47 @@ import org.springframework.cloud.appbroker.state.ServiceInstanceStateRepository;
import org.springframework.cloud.appbroker.workflow.instance.AppDeploymentCreateServiceInstanceWorkflow;
import org.springframework.cloud.appbroker.workflow.instance.AppDeploymentDeleteServiceInstanceWorkflow;
import org.springframework.cloud.appbroker.workflow.instance.AppDeploymentUpdateServiceInstanceWorkflow;
import org.springframework.context.annotation.Bean;
import org.springframework.credhub.core.ReactiveCredHubOperations;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
class AppBrokerAutoConfigurationTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(AppBrokerAutoConfiguration.class));
.withConfiguration(AutoConfigurations.of(AppBrokerAutoConfiguration.class, CredHubAutoConfiguration.class));
@Test
void servicesAreCreatedWithCloudFoundryConfigured() {
configuredContext()
.run(this::assertContext);
}
@Test
void servicesAreCreatedWithCloudFoundryAndCredHubConfigured() {
configuredContext()
.withUserConfiguration(CredHubConfiguration.class)
.run((context) -> {
assertContext(context);
assertThat(context)
.hasSingleBean(CreateServiceInstanceAppBindingWorkflow.class)
.getBean(CreateServiceInstanceAppBindingWorkflow.class)
.isExactlyInstanceOf(CredHubPersistingCreateServiceInstanceAppBindingWorkflow.class);
});
}
@Test
void servicesAreNotCreatedWithoutDeployerConfiguration() {
this.contextRunner
.run((context) -> {
assertThat(context).doesNotHaveBean(BackingApplications.class);
assertThat(context).doesNotHaveBean(DeployerClient.class);
});
}
private ApplicationContextRunner configuredContext() {
return this.contextRunner
.withConfiguration(AutoConfigurations.of(CloudFoundryAppDeployerAutoConfiguration.class))
.withPropertyValues(
"spring.cloud.appbroker.services[0].service-name=service1",
@@ -76,70 +110,70 @@ class AppBrokerAutoConfigurationTest {
"spring.cloud.appbroker.deployer.cloudfoundry.api-host=https://api.example.com",
"spring.cloud.appbroker.deployer.cloudfoundry.username=user",
"spring.cloud.appbroker.deployer.cloudfoundry.password=secret"
)
.run((context) -> {
assertThat(context).hasSingleBean(BrokeredServices.class);
BrokeredServices brokeredServices = context.getBean(BrokeredServices.class);
assertThat(brokeredServices).hasSize(2);
assertThat(brokeredServices.get(0).getServiceName()).isEqualTo("service1");
assertThat(brokeredServices.get(0).getPlanName()).isEqualTo("service1-plan1");
assertThat(brokeredServices.get(0).getApps().get(0).getName()).isEqualTo("app1");
assertThat(brokeredServices.get(0).getApps().get(0).getPath()).isEqualTo("classpath:app1.jar");
assertThat(brokeredServices.get(0).getApps().get(0).getProperties().get("memory")).isEqualTo("1G");
assertThat(brokeredServices.get(0).getApps().get(0).getProperties().get("instances")).isNull();
assertThat(brokeredServices.get(0).getApps().get(1).getName()).isEqualTo("app2");
assertThat(brokeredServices.get(0).getApps().get(1).getPath()).isEqualTo("classpath:app2.jar");
assertThat(brokeredServices.get(0).getApps().get(1).getProperties().get("memory")).isEqualTo("2G");
assertThat(brokeredServices.get(0).getApps().get(1).getProperties().get("instances")).isEqualTo("2");
assertThat(brokeredServices.get(1).getServiceName()).isEqualTo("service2");
assertThat(brokeredServices.get(1).getPlanName()).isEqualTo("service2-plan1");
assertThat(brokeredServices.get(1).getApps().get(0).getName()).isEqualTo("app3");
assertThat(brokeredServices.get(1).getApps().get(0).getPath()).isEqualTo("classpath:app3.jar");
assertThat(context).hasSingleBean(DeployerClient.class);
assertThat(context).hasSingleBean(BrokeredServices.class);
assertThat(context).hasSingleBean(ServiceInstanceStateRepository.class);
assertThat(context).hasSingleBean(ServiceInstanceBindingStateRepository.class);
assertThat(context).hasSingleBean(BackingAppDeploymentService.class);
assertThat(context).hasSingleBean(BackingServicesProvisionService.class);
assertThat(context).hasSingleBean(BackingApplicationsParametersTransformationService.class);
assertThat(context).hasSingleBean(EnvironmentMappingParametersTransformerFactory.class);
assertThat(context).hasSingleBean(PropertyMappingParametersTransformerFactory.class);
assertThat(context).hasSingleBean(ParameterMappingParametersTransformerFactory.class);
assertThat(context).hasSingleBean(BackingServicesParametersTransformationService.class);
assertThat(context).hasSingleBean(CredentialProviderService.class);
assertThat(context).hasSingleBean(SpringSecurityBasicAuthCredentialProviderFactory.class);
assertThat(context).hasSingleBean(SpringSecurityOAuth2CredentialProviderFactory.class);
assertThat(context).hasSingleBean(TargetService.class);
assertThat(context).hasSingleBean(SpacePerServiceInstance.class);
assertThat(context).hasSingleBean(WorkflowServiceInstanceService.class);
assertThat(context).hasSingleBean(WorkflowServiceInstanceBindingService.class);
assertThat(context).hasSingleBean(AppDeploymentCreateServiceInstanceWorkflow.class);
assertThat(context).hasSingleBean(AppDeploymentDeleteServiceInstanceWorkflow.class);
assertThat(context).hasSingleBean(AppDeploymentUpdateServiceInstanceWorkflow.class);
});
);
}
@Test
void servicesAreNotCreatedWithoutDeployerConfiguration() {
this.contextRunner
.run((context) -> {
assertThat(context).doesNotHaveBean(BackingApplications.class);
assertThat(context).doesNotHaveBean(DeployerClient.class);
});
private void assertContext(AssertableApplicationContext context) {
assertThat(context).hasSingleBean(BrokeredServices.class);
BrokeredServices brokeredServices = context.getBean(BrokeredServices.class);
assertThat(brokeredServices).hasSize(2);
assertThat(brokeredServices.get(0).getServiceName()).isEqualTo("service1");
assertThat(brokeredServices.get(0).getPlanName()).isEqualTo("service1-plan1");
assertThat(brokeredServices.get(0).getApps().get(0).getName()).isEqualTo("app1");
assertThat(brokeredServices.get(0).getApps().get(0).getPath()).isEqualTo("classpath:app1.jar");
assertThat(brokeredServices.get(0).getApps().get(0).getProperties().get("memory")).isEqualTo("1G");
assertThat(brokeredServices.get(0).getApps().get(0).getProperties().get("instances")).isNull();
assertThat(brokeredServices.get(0).getApps().get(1).getName()).isEqualTo("app2");
assertThat(brokeredServices.get(0).getApps().get(1).getPath()).isEqualTo("classpath:app2.jar");
assertThat(brokeredServices.get(0).getApps().get(1).getProperties().get("memory")).isEqualTo("2G");
assertThat(brokeredServices.get(0).getApps().get(1).getProperties().get("instances")).isEqualTo("2");
assertThat(brokeredServices.get(1).getServiceName()).isEqualTo("service2");
assertThat(brokeredServices.get(1).getPlanName()).isEqualTo("service2-plan1");
assertThat(brokeredServices.get(1).getApps().get(0).getName()).isEqualTo("app3");
assertThat(brokeredServices.get(1).getApps().get(0).getPath()).isEqualTo("classpath:app3.jar");
assertThat(context).hasSingleBean(DeployerClient.class);
assertThat(context).hasSingleBean(BrokeredServices.class);
assertThat(context).hasSingleBean(ServiceInstanceStateRepository.class);
assertThat(context).hasSingleBean(ServiceInstanceBindingStateRepository.class);
assertThat(context).hasSingleBean(BackingAppDeploymentService.class);
assertThat(context).hasSingleBean(BackingServicesProvisionService.class);
assertThat(context).hasSingleBean(BackingApplicationsParametersTransformationService.class);
assertThat(context).hasSingleBean(EnvironmentMappingParametersTransformerFactory.class);
assertThat(context).hasSingleBean(PropertyMappingParametersTransformerFactory.class);
assertThat(context).hasSingleBean(ParameterMappingParametersTransformerFactory.class);
assertThat(context).hasSingleBean(BackingServicesParametersTransformationService.class);
assertThat(context).hasSingleBean(CredentialProviderService.class);
assertThat(context).hasSingleBean(SpringSecurityBasicAuthCredentialProviderFactory.class);
assertThat(context).hasSingleBean(SpringSecurityOAuth2CredentialProviderFactory.class);
assertThat(context).hasSingleBean(TargetService.class);
assertThat(context).hasSingleBean(SpacePerServiceInstance.class);
assertThat(context).hasSingleBean(WorkflowServiceInstanceService.class);
assertThat(context).hasSingleBean(WorkflowServiceInstanceBindingService.class);
assertThat(context).hasSingleBean(AppDeploymentCreateServiceInstanceWorkflow.class);
assertThat(context).hasSingleBean(AppDeploymentDeleteServiceInstanceWorkflow.class);
assertThat(context).hasSingleBean(AppDeploymentUpdateServiceInstanceWorkflow.class);
}
@TestConfiguration
public static class CredHubConfiguration {
@Bean
public ReactiveCredHubOperations credHubOperations() {
return mock(ReactiveCredHubOperations.class);
}
}
}

View File

@@ -1,3 +1,6 @@
spring:
application:
name: foo-app
logging:
level:
cloudfoundry-client: DEBUG

View File

@@ -0,0 +1,38 @@
/*
* 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.
*/
description = "Spring Cloud App Broker Deployer Security CredHub"
dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-dependencies:${springBootVersion}"
}
}
dependencies {
implementation project(":spring-cloud-app-broker-core")
implementation("io.projectreactor:reactor-core")
implementation("org.springframework.cloud:spring-cloud-open-service-broker-core:${openServiceBrokerVersion}")
implementation("org.springframework.credhub:spring-credhub-starter:${springCredhubVersion}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testImplementation("org.springframework.boot:spring-boot-starter-webflux")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("io.projectreactor:reactor-test")
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("org.mockito:mockito-junit-jupiter:${mockitoVersion}")
}

View File

@@ -0,0 +1,93 @@
/*
* 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.workflow.binding;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.Logger;
import reactor.util.Loggers;
import org.springframework.cloud.appbroker.service.CreateServiceInstanceAppBindingWorkflow;
import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceAppBindingResponse;
import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceAppBindingResponse.CreateServiceInstanceAppBindingResponseBuilder;
import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceBindingRequest;
import org.springframework.core.annotation.Order;
import org.springframework.credhub.core.ReactiveCredHubOperations;
import org.springframework.credhub.support.json.JsonCredentialRequest;
import org.springframework.util.CollectionUtils;
@Order(50)
public class CredHubPersistingCreateServiceInstanceAppBindingWorkflow implements CreateServiceInstanceAppBindingWorkflow {
private static final Logger LOG = Loggers.getLogger(CredHubPersistingCreateServiceInstanceAppBindingWorkflow.class);
private static final String CREDENTIALS_KEY = "credhub-ref";
private static final String CREDENTIALS_VALUE_TEMPLATE = "/c/%s/%s/%s/credentials-json";
private final String appName;
private final ReactiveCredHubOperations credHubOperations;
public CredHubPersistingCreateServiceInstanceAppBindingWorkflow(ReactiveCredHubOperations credHubOperations, String appName) {
this.credHubOperations = credHubOperations;
this.appName = appName;
}
@Override
public Flux<Void> create(CreateServiceInstanceBindingRequest request) {
return Flux.empty();
}
@Override
public Mono<CreateServiceInstanceAppBindingResponseBuilder> buildResponse(CreateServiceInstanceBindingRequest request,
CreateServiceInstanceAppBindingResponseBuilder responseBuilder) {
return Mono.just(responseBuilder.build())
.flatMap(response -> {
if (!CollectionUtils.isEmpty(response.getCredentials())) {
return persistBindingCredentials(request, response)
.doOnRequest(l -> LOG.debug("Storing binding credentials in CredHub"))
.doOnSuccess(r -> LOG.debug("Finished storing binding credentials in CredHub"))
.doOnError(exception -> LOG.debug("Error storing binding credentials in CredHub with error {}", exception));
}
return Mono.just(responseBuilder);
});
}
private Mono<CreateServiceInstanceAppBindingResponseBuilder> persistBindingCredentials(CreateServiceInstanceBindingRequest request,
CreateServiceInstanceAppBindingResponse response) {
return credHubOperations
.credentials()
.write(JsonCredentialRequest
.builder()
.value(response.getCredentials())
.build())
.thenReturn(CreateServiceInstanceAppBindingResponse
.builder()
.async(response.isAsync())
.bindingExisted(response.isBindingExisted())
.credentials(CREDENTIALS_KEY, formatCredentials(request))
.operation(response.getOperation())
.syslogDrainUrl(response.getSyslogDrainUrl())
.volumeMounts(response.getVolumeMounts()));
}
private String formatCredentials(CreateServiceInstanceBindingRequest request) {
return String.format(CREDENTIALS_VALUE_TEMPLATE, this.appName, request.getServiceDefinitionId(), request.getBindingId());
}
}

View File

@@ -0,0 +1,148 @@
/*
* 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.workflow.binding;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceAppBindingResponse;
import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceAppBindingResponse.CreateServiceInstanceAppBindingResponseBuilder;
import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceBindingRequest;
import org.springframework.cloud.servicebroker.model.binding.VolumeMount;
import org.springframework.credhub.core.ReactiveCredHubOperations;
import org.springframework.credhub.core.credential.ReactiveCredHubCredentialOperations;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@ExtendWith(MockitoExtension.class)
class CredHubPersistingCreateServiceInstanceAppBindingWorkflowTest {
@Mock
private ReactiveCredHubOperations credHubOperations;
@Mock
private ReactiveCredHubCredentialOperations credHubCredentialOperations;
private CredHubPersistingCreateServiceInstanceAppBindingWorkflow workflow;
@BeforeEach
void setUp() {
this.workflow = new CredHubPersistingCreateServiceInstanceAppBindingWorkflow(credHubOperations, "test-app-name");
}
@Test
void noBindingCredentials() {
CreateServiceInstanceBindingRequest request = CreateServiceInstanceBindingRequest
.builder()
.bindingId("foo-binding-id")
.serviceInstanceId("foo-instance-id")
.serviceDefinitionId("foo-definition-id")
.build();
CreateServiceInstanceAppBindingResponseBuilder responseBuilder = CreateServiceInstanceAppBindingResponse
.builder()
.bindingExisted(true)
.syslogDrainUrl("https://logs.example.com")
.volumeMounts(VolumeMount.builder().build())
.volumeMounts(VolumeMount.builder().build())
.volumeMounts(Arrays.asList(
VolumeMount.builder().build(),
VolumeMount.builder().build()
));
StepVerifier
.create(this.workflow.buildResponse(request, responseBuilder))
.assertNext(createServiceInstanceAppBindingResponseBuilder -> {
CreateServiceInstanceAppBindingResponse response = createServiceInstanceAppBindingResponseBuilder.build();
assertThat(response.isBindingExisted()).isEqualTo(true);
assertThat(response.getCredentials()).hasSize(0);
assertThat(response.getSyslogDrainUrl()).isEqualTo("https://logs.example.com");
assertThat(response.getVolumeMounts()).hasSize(4);
})
.verifyComplete();
verifyZeroInteractions(this.credHubCredentialOperations);
}
@Test
@SuppressWarnings("serial")
void storeCredentialsInCredHub() {
CreateServiceInstanceBindingRequest request = CreateServiceInstanceBindingRequest
.builder()
.bindingId("foo-binding-id")
.serviceInstanceId("foo-instance-id")
.serviceDefinitionId("foo-definition-id")
.build();
Map<String, Object> credentials = new HashMap<String, Object>() {{
put("credential4", "value4");
put("credential5", "value5");
}};
CreateServiceInstanceAppBindingResponseBuilder responseBuilder = CreateServiceInstanceAppBindingResponse
.builder()
.bindingExisted(true)
.credentials("credential1", "value1")
.credentials("credential2", 2)
.credentials("credential3", true)
.credentials(credentials)
.syslogDrainUrl("https://logs.example.com")
.volumeMounts(VolumeMount.builder().build())
.volumeMounts(VolumeMount.builder().build())
.volumeMounts(Arrays.asList(
VolumeMount.builder().build(),
VolumeMount.builder().build()
));
given(this.credHubOperations.credentials())
.willReturn(credHubCredentialOperations);
given(this.credHubCredentialOperations.write(any()))
.willReturn(Mono.empty());
StepVerifier
.create(this.workflow.buildResponse(request, responseBuilder))
.assertNext(createServiceInstanceAppBindingResponseBuilder -> {
CreateServiceInstanceAppBindingResponse response = createServiceInstanceAppBindingResponseBuilder.build();
assertThat(response.isBindingExisted()).isEqualTo(true);
assertThat(response.getCredentials()).hasSize(1);
assertThat(response.getCredentials().get("credhub-ref")).isEqualTo("/c/test-app-name/foo-definition-id/foo-binding-id/credentials-json");
assertThat(response.getSyslogDrainUrl()).isEqualTo("https://logs.example.com");
assertThat(response.getVolumeMounts()).hasSize(4);
})
.verifyComplete();
verify(this.credHubCredentialOperations).write(any());
verifyNoMoreInteractions(this.credHubCredentialOperations);
}
}