Add tests for CredHub integration.

This commit is contained in:
Scott Frederick
2019-01-07 12:52:50 -06:00
parent 7cb8d85b4b
commit ec39297419
15 changed files with 344 additions and 37 deletions

View File

@@ -47,7 +47,7 @@ configure(allprojects) {
springVersion = project.findProperty("springVersion") ?: "5.1.3.RELEASE"
reactorVersion = project.findProperty("reactorVersion") ?: "Californium-SR3"
openServiceBrokerVersion = "3.0.0.M3"
springCredhubVersion = "2.0.0.RC1"
springCredhubVersion = "2.0.0.BUILD-SNAPSHOT"
cfJavaClientVersion = "3.14.0.RELEASE"
mockitoVersion = "2.23.4"
immutablesVersion = "2.7.3"

View File

@@ -43,12 +43,7 @@ dependencies {
testImplementation("org.assertj:assertj-core:${assertjVersion}")
testImplementation("com.revinate:assertj-json:1.2.0")
testImplementation("org.springframework.boot:spring-boot-starter-webflux")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.cloudfoundry:cloudfoundry-client-reactor:${cfJavaClientVersion}")
testImplementation("org.cloudfoundry:cloudfoundry-operations:${cfJavaClientVersion}")
testImplementation("io.projectreactor:reactor-core")
testImplementation("io.projectreactor.ipc:reactor-netty")
}
// build the test broker from /src into a jar that the tests can deploy

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.appbroker.autoconfigure;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -25,10 +26,12 @@ import org.springframework.cloud.appbroker.service.CreateServiceInstanceAppBindi
import org.springframework.cloud.appbroker.workflow.binding.CredHubPersistingCreateServiceInstanceAppBindingWorkflow;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.credhub.autoconfig.CredHubTemplateAutoConfiguration;
import org.springframework.credhub.core.ReactiveCredHubOperations;
@Configuration
@AutoConfigureBefore(AppBrokerAutoConfiguration.class)
@AutoConfigureAfter(CredHubTemplateAutoConfiguration.class)
@ConditionalOnClass(ReactiveCredHubOperations.class)
@ConditionalOnBean(ReactiveCredHubOperations.class)
public class CredHubAutoConfiguration {

View File

@@ -26,8 +26,6 @@ dependencies {
implementation project(":spring-cloud-app-broker-deployer")
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}")
implementation("org.springframework.boot:spring-boot-starter-validation")

View File

@@ -0,0 +1,107 @@
/*
* 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.integration;
import org.junit.jupiter.api.Disabled;
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.CredHubStubFixture;
import org.springframework.cloud.appbroker.integration.fixtures.OpenServiceBrokerApiFixture;
import org.springframework.cloud.appbroker.integration.fixtures.UaaStubFixture;
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.CreateInstanceWithBasicAuthCredentialsComponentTest.APP_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].credential-providers[0].name=SpringSecurityBasicAuth",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.length=14",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-uppercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-lowercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-numeric=false",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-special=false",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].name=SpringSecurityOAuth2",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.registration=example-app-client",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.client-id=test-client",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.grant-types=[\"client_credentials\"]",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.length=12",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-uppercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-lowercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-numeric=false",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-special=false",
"spring.credhub.url=http://localhost:8888"
})
class CreateInstanceWithCredHubCredentialsComponentTest extends WiremockComponentTest {
static final String APP_NAME = "app-with-credentials";
private static final String SERVICE_INSTANCE_ID = "instance-id";
@Autowired
private OpenServiceBrokerApiFixture brokerFixture;
@Autowired
private CloudControllerStubFixture cloudControllerFixture;
@Autowired
private UaaStubFixture uaaStubFixture;
@Autowired
private CredHubStubFixture credHubFixture;
@Test
@Disabled
void pushAppWithCredentials() {
cloudControllerFixture.stubAppDoesNotExist(APP_NAME);
cloudControllerFixture.stubPushApp(APP_NAME);
uaaStubFixture.stubCreateClient();
credHubFixture.stubGenerateUser(APP_NAME, SERVICE_INSTANCE_ID, "basic", 14);
credHubFixture.stubGeneratePassword(APP_NAME, SERVICE_INSTANCE_ID, "oauth2", 12);
// when a service instance is created
given(brokerFixture.serviceInstanceRequest())
.when()
.put(brokerFixture.createServiceInstanceUrl(), SERVICE_INSTANCE_ID)
.then()
.statusCode(HttpStatus.ACCEPTED.value());
// when the "last_operation" API is polled
given(brokerFixture.serviceInstanceRequest())
.when()
.get(brokerFixture.getLastInstanceOperationUrl(), SERVICE_INSTANCE_ID)
.then()
.statusCode(HttpStatus.OK.value())
.body("state", is(equalTo(OperationState.IN_PROGRESS.toString())));
String state = brokerFixture.waitForAsyncOperationComplete(SERVICE_INSTANCE_ID);
assertThat(state).isEqualTo(OperationState.SUCCEEDED.toString());
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.integration;
import org.junit.jupiter.api.Disabled;
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.CredHubStubFixture;
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.DeleteInstanceWithCredHubCredentialsComponentTest.APP_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].credential-providers[0].name=SpringSecurityBasicAuth",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.length=14",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-uppercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-lowercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-numeric=false",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[0].args.include-special=false",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].name=SpringSecurityOAuth2",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.registration=example-app-client",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.client-id=test-client",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.grant-types=[\"client_credentials\"]",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.length=12",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-uppercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-lowercase-alpha=true",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-numeric=false",
"spring.cloud.appbroker.services[0].apps[0].credential-providers[1].args.include-special=false",
"spring.credhub.url=http://localhost:8080"
})
class DeleteInstanceWithCredHubCredentialsComponentTest extends WiremockComponentTest {
static final String APP_NAME = "first-app";
private static final String SERVICE_INSTANCE_ID = "instance-id";
@Autowired
private OpenServiceBrokerApiFixture brokerFixture;
@Autowired
private CloudControllerStubFixture cloudControllerFixture;
@Autowired
private CredHubStubFixture credHubFixture;
@Test
@Disabled
void deleteAppWithCredentials() {
cloudControllerFixture.stubAppExists(APP_NAME);
cloudControllerFixture.stubServiceBindingDoesNotExist(APP_NAME);
cloudControllerFixture.stubDeleteApp(APP_NAME);
credHubFixture.stubDeleteCredential(APP_NAME, SERVICE_INSTANCE_ID, "basic");
credHubFixture.stubDeleteCredential(APP_NAME, SERVICE_INSTANCE_ID, "oauth2");
// when the service instance is deleted
given(brokerFixture.serviceInstanceRequest())
.when()
.delete(brokerFixture.deleteServiceInstanceUrl(), SERVICE_INSTANCE_ID)
.then()
.statusCode(HttpStatus.ACCEPTED.value());
// when the "last_operation" API is polled
given(brokerFixture.serviceInstanceRequest())
.when()
.get(brokerFixture.getLastInstanceOperationUrl(), SERVICE_INSTANCE_ID)
.then()
.statusCode(HttpStatus.OK.value())
.body("state", is(equalTo(OperationState.IN_PROGRESS.toString())));
String state = brokerFixture.waitForAsyncOperationComplete(SERVICE_INSTANCE_ID);
assertThat(state).isEqualTo(OperationState.SUCCEEDED.toString());
}
}

View File

@@ -25,6 +25,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.appbroker.integration.fixtures.CloudControllerStubFixture;
import org.springframework.cloud.appbroker.integration.fixtures.CredHubStubFixture;
import org.springframework.cloud.appbroker.integration.fixtures.OpenServiceBrokerApiFixture;
import org.springframework.cloud.appbroker.integration.fixtures.UaaStubFixture;
import org.springframework.cloud.appbroker.integration.fixtures.WiremockServerFixture;
@@ -38,7 +39,8 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
WiremockServerFixture.class,
OpenServiceBrokerApiFixture.class,
CloudControllerStubFixture.class,
UaaStubFixture.class},
UaaStubFixture.class,
CredHubStubFixture.class},
properties = {
"spring.cloud.appbroker.deployer.cloudfoundry.api-host=localhost",
"spring.cloud.appbroker.deployer.cloudfoundry.api-port=8080",

View File

@@ -33,7 +33,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.noContent;
import static com.github.tomakehurst.wiremock.client.WireMock.ok;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.put;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
@@ -44,6 +43,10 @@ public class CloudControllerStubFixture extends WiremockStubFixture {
private static final String TEST_ORG_GUID = "TEST-ORG-GUID";
private static final String TEST_QUOTA_DEFINITION_GUID = "TEST-QUOTA-DEFINITION-GUID";
protected CloudControllerStubFixture() {
super(8080);
}
public void stubCommonCloudControllerRequests() {
stubGetPlatformInfo();
stubFindTestOrg();

View File

@@ -0,0 +1,70 @@
/*
* 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.integration.fixtures;
import org.springframework.boot.test.context.TestComponent;
import static com.github.tomakehurst.wiremock.client.WireMock.delete;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath;
import static com.github.tomakehurst.wiremock.client.WireMock.noContent;
import static com.github.tomakehurst.wiremock.client.WireMock.ok;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
@TestComponent
public class CredHubStubFixture extends WiremockStubFixture {
protected CredHubStubFixture() {
super(8888);
}
public void stubGenerateUser(String appId, String serviceInstanceId,
String descriptor, int length) {
stubGenerate(appId, serviceInstanceId, descriptor, length, "user", "post-data-user");
}
public void stubGeneratePassword(String appId, String serviceInstanceId,
String descriptor, int length) {
stubGenerate(appId, serviceInstanceId, descriptor, length, "password", "post-data-password");
}
private void stubGenerate(String appId, String serviceInstanceId,
String descriptor, int length,
String type, String responseBody) {
stubFor(post(urlPathEqualTo("/api/v1/data"))
.withRequestBody(matchingJsonPath("$.[?(@.name == '" +
"/" + appId +
"/" + serviceInstanceId +
"/" + descriptor + "')]"))
.withRequestBody(matchingJsonPath("$.[?(@.type == '" + type + "')]"))
.withRequestBody(matchingJsonPath("$.[?(@.parameters.length == '" + length + "')]"))
.willReturn(ok()
.withHeader("Content-type", "application/json")
.withBody(credhub(responseBody))));
}
public void stubDeleteCredential(String appId, String serviceInstanceId, String descriptor) {
String credentialName = "/" + appId + "/" + serviceInstanceId + "/" + descriptor;
stubFor(delete(urlPathEqualTo("/api/v1/data"))
.withQueryParam("name", equalTo(credentialName))
.willReturn(noContent()));
}
private String credhub(String fileRoot) {
return readResponseFromFile(fileRoot, "credhub");
}
}

View File

@@ -22,11 +22,14 @@ import static com.github.tomakehurst.wiremock.client.WireMock.delete;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.ok;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
@TestComponent
public class UaaStubFixture extends WiremockStubFixture {
protected UaaStubFixture() {
super(8080);
}
public void stubCommonUaaRequests() {
stubRetrieveAccessToken();
stubRetrieveTokenKeys();

View File

@@ -26,7 +26,6 @@ import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import static com.github.tomakehurst.wiremock.client.WireMock.recordSpec;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.assertj.core.api.Assertions.assertThat;
@@ -35,48 +34,38 @@ public class WiremockServerFixture {
@Value("${spring.cloud.appbroker.deployer.cloudfoundry.api-port}")
private int cfApiPort;
@Value("${wiremock.record:false}")
private boolean wiremockRecord;
@Value("${wiremock.cloudfoundry.api-url:}")
private String cfApiUrl;
private WireMockServer wiremockServer;
private WireMockServer ccUaaWiremockServer;
private WireMockServer credHubWiremockServer;
public void startWiremock() {
wiremockServer = new WireMockServer(wireMockConfig()
ccUaaWiremockServer = new WireMockServer(wireMockConfig()
.port(cfApiPort)
.usingFilesUnderClasspath("recordings"));
ccUaaWiremockServer.start();
if (wiremockRecord) {
wiremockServer.startRecording(
recordSpec()
.forTarget(cfApiUrl)
);
}
wiremockServer.start();
credHubWiremockServer = new WireMockServer(wireMockConfig()
.port(8888)
.usingFilesUnderClasspath("recordings"));
credHubWiremockServer.start();
}
public void stopWiremock() {
if (wiremockRecord) {
wiremockServer.stopRecording();
}
wiremockServer.stop();
ccUaaWiremockServer.stop();
credHubWiremockServer.stop();
}
public void resetWiremock() {
wiremockServer.resetAll();
ccUaaWiremockServer.resetAll();
credHubWiremockServer.resetAll();
}
public void verifyAllRequiredStubsUsed() {
Set<UUID> servedStubIds = wiremockServer.getServeEvents().getRequests().stream()
Set<UUID> servedStubIds = ccUaaWiremockServer.getServeEvents().getRequests().stream()
.filter(event -> event.getStubMapping() != null)
.map(event -> event.getStubMapping().getId())
.collect(Collectors.toSet());
List<StubMapping> unusedStubs = wiremockServer.listAllStubMappings().getMappings().stream()
List<StubMapping> unusedStubs = ccUaaWiremockServer.listAllStubMappings().getMappings().stream()
.filter(stub -> !servedStubIds.contains(stub.getId()))
.filter(this::stubIsOptional)
.collect(Collectors.toList());

View File

@@ -16,7 +16,10 @@
package org.springframework.cloud.appbroker.integration.fixtures;
import com.github.tomakehurst.wiremock.client.MappingBuilder;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.common.Metadata;
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
@@ -29,9 +32,25 @@ import java.util.stream.Collectors;
public class WiremockStubFixture {
private static final String RESPONSES_RESOURCE_PATH = "classpath:/responses/";
protected WireMock wireMock;
@Autowired
private ResourceLoader resourceLoader;
protected WiremockStubFixture(int port) {
wireMock = WireMock.create()
.port(port)
.build();
}
protected StubMapping stubFor(MappingBuilder mappingBuilder) {
return givenThat(mappingBuilder);
}
protected StubMapping givenThat(MappingBuilder mappingBuilder) {
return wireMock.register(mappingBuilder);
}
CloudControllerStubFixture.StringReplacementPair replace(String regex, String replacement) {
return new CloudControllerStubFixture.StringReplacementPair(regex, replacement);
}

View File

@@ -0,0 +1,7 @@
{
"type": "password",
"version_created_at": "2017-01-05T01:01:01Z",
"id": "67fc3def-bbfb-4953-83f8-4ab0682ad675",
"name": "/example-password",
"value": "mRPZBbAfblRpac"
}

View File

@@ -0,0 +1,10 @@
{
"type": "user",
"version_created_at": "2017-01-05T01:01:01Z",
"id": "67fc3def-bbfb-4953-83f8-4ab0682ad675",
"name": "/example-user",
"value": {
"username": "FQnwWoxgSrDuqD",
"password": "mRPZBbAfblRpac"
}
}

View File

@@ -29,7 +29,6 @@ dependencies {
api("org.cloudfoundry:cloudfoundry-client-reactor:${cfJavaClientVersion}")
api("org.cloudfoundry:cloudfoundry-operations:${cfJavaClientVersion}")
api("io.projectreactor.ipc:reactor-netty")
}
install {