Remove hard-coded defaults from CF deployment properties to allow platform defaults when no values are specified by the broker configuration.

This commit is contained in:
Scott Frederick
2018-10-08 11:38:52 -05:00
parent efd9ff7455
commit 6c0ddfc000
8 changed files with 211 additions and 64 deletions

View File

@@ -151,12 +151,12 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
.path(getApplication(appResource))
.environmentVariables(getEnvironmentVariables(request.getEnvironment()))
.services(request.getServices())
.instances(instances(deploymentProperties))
.memory(memory(deploymentProperties))
.disk(diskQuota(deploymentProperties))
.healthCheckType(healthCheck(deploymentProperties))
.healthCheckHttpEndpoint(healthCheckEndpoint(deploymentProperties))
.timeout(healthCheckTimeout(deploymentProperties))
.instances(instances(deploymentProperties))
.memory(memory(deploymentProperties))
.noRoute(toggleNoRoute(deploymentProperties));
Optional.ofNullable(host(deploymentProperties)).ifPresent(manifest::host);
@@ -252,7 +252,7 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
return new DefaultApplications(
((DefaultCloudFoundryOperations) this.operations).getCloudFoundryClientPublisher(),
((DefaultCloudFoundryOperations) this.operations).getDopplerClientPublisher(),
(this.operations).spaces().get(GetSpaceRequest.builder().name(space).build()).map(SpaceDetail::getId));
this.operations.spaces().get(GetSpaceRequest.builder().name(space).build()).map(SpaceDetail::getId));
}
private DefaultSpaces createSpaceOperations() {
@@ -321,8 +321,8 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
private ApplicationHealthCheck healthCheck(Map<String, String> properties) {
return Optional.ofNullable(properties.get(CloudFoundryDeploymentProperties.HEALTHCHECK_PROPERTY_KEY))
.map(this::toApplicationHealthCheck)
.orElse(this.defaultDeploymentProperties.getHealthCheck());
.map(this::toApplicationHealthCheck)
.orElse(this.defaultDeploymentProperties.getHealthCheck());
}
private ApplicationHealthCheck toApplicationHealthCheck(String raw) {
@@ -336,19 +336,19 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
private String healthCheckEndpoint(Map<String, String> properties) {
return Optional.ofNullable(properties.get(CloudFoundryDeploymentProperties.HEALTHCHECK_HTTP_ENDPOINT_PROPERTY_KEY))
.orElse(this.defaultDeploymentProperties.getHealthCheckHttpEndpoint());
.orElse(this.defaultDeploymentProperties.getHealthCheckHttpEndpoint());
}
private Integer healthCheckTimeout(Map<String, String> properties) {
String timeoutString = properties
.getOrDefault(CloudFoundryDeploymentProperties.HEALTHCHECK_TIMEOUT_PROPERTY_KEY, this.defaultDeploymentProperties.getHealthCheckTimeout());
return Integer.parseInt(timeoutString);
return Optional.ofNullable(properties.get(CloudFoundryDeploymentProperties.HEALTHCHECK_TIMEOUT_PROPERTY_KEY))
.map(Integer::parseInt)
.orElse(this.defaultDeploymentProperties.getHealthCheckTimeout());
}
private int instances(Map<String, String> properties) {
private Integer instances(Map<String, String> properties) {
return Optional.ofNullable(properties.get(DeploymentProperties.COUNT_PROPERTY_KEY))
.map(Integer::parseInt)
.orElse(this.defaultDeploymentProperties.getInstances());
.map(Integer::parseInt)
.orElse(this.defaultDeploymentProperties.getInstances());
}
private String host(Map<String, String> properties) {
@@ -382,30 +382,31 @@ public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware
.orElse(null);
}
private int memory(Map<String, String> properties) {
String withUnit = properties
.getOrDefault(DeploymentProperties.MEMORY_PROPERTY_KEY, this.defaultDeploymentProperties.getMemory());
return (int) ByteSizeUtils.parseToMebibytes(withUnit);
private Integer memory(Map<String, String> properties) {
return Optional.ofNullable(properties.get(DeploymentProperties.MEMORY_PROPERTY_KEY))
.map(ByteSizeUtils::parseToMebibytes)
.orElse(ByteSizeUtils.parseToMebibytes(defaultDeploymentProperties.getMemory()));
}
private int diskQuota(Map<String, String> properties) {
String withUnit = properties
.getOrDefault(DeploymentProperties.DISK_PROPERTY_KEY, this.defaultDeploymentProperties.getDisk());
return (int) ByteSizeUtils.parseToMebibytes(withUnit);
private Integer diskQuota(Map<String, String> properties) {
return Optional.ofNullable(properties.get(DeploymentProperties.DISK_PROPERTY_KEY))
.map(ByteSizeUtils::parseToMebibytes)
.orElse(ByteSizeUtils.parseToMebibytes(defaultDeploymentProperties.getDisk()));
}
private String buildpack(Map<String, String> properties) {
return Optional.ofNullable(properties.get(CloudFoundryDeploymentProperties.BUILDPACK_PROPERTY_KEY))
.orElse(this.defaultDeploymentProperties.getBuildpack());
.orElse(this.defaultDeploymentProperties.getBuildpack());
}
private String javaOpts(Map<String, String> properties) {
return Optional.ofNullable(properties.get(CloudFoundryDeploymentProperties.JAVA_OPTS_PROPERTY_KEY))
.orElse(this.defaultDeploymentProperties.getJavaOpts());
.orElse(this.defaultDeploymentProperties.getJavaOpts());
}
private Predicate<Throwable> isNotFoundError() {
return t -> t instanceof AbstractCloudFoundryException && ((AbstractCloudFoundryException) t).getStatusCode() == HttpStatus.NOT_FOUND.value();
return t -> t instanceof AbstractCloudFoundryException &&
((AbstractCloudFoundryException) t).getStatusCode() == HttpStatus.NOT_FOUND.value();
}
/**

View File

@@ -76,37 +76,37 @@ public class CloudFoundryDeploymentProperties {
/**
* The buildpack to use for deploying the application.
*/
private String buildpack = "https://github.com/cloudfoundry/java-buildpack.git#v4.7.1";
private String buildpack;
/**
* The amount of memory to allocate, if not overridden per-app. Default unit is mebibytes, 'M' and 'G" suffixes supported.
*/
private String memory = "1024m";
private String memory;
/**
* The amount of disk space to allocate, if not overridden per-app. Default unit is mebibytes, 'M' and 'G" suffixes supported.
*/
private String disk = "1024m";
private String disk;
/**
* The type of health check to perform on deployed application, if not overridden per-app. Defaults to PORT
*/
private ApplicationHealthCheck healthCheck = ApplicationHealthCheck.PORT;
private ApplicationHealthCheck healthCheck;
/**
* The path that the http health check will use, defaults to @{code /health}
*/
private String healthCheckHttpEndpoint = "/health";
private String healthCheckHttpEndpoint;
/**
* The timeout value for health checks in seconds. Defaults to 120 seconds.
*/
private String healthCheckTimeout = "120";
private Integer healthCheckTimeout;
/**
* The number of instances to run.
*/
private int instances = 1;
private Integer instances;
/**
* Flag to enable prefixing the app name with a random prefix.
@@ -179,7 +179,7 @@ public class CloudFoundryDeploymentProperties {
this.disk = disk;
}
public int getInstances() {
public Integer getInstances() {
return instances;
}
@@ -235,11 +235,11 @@ public class CloudFoundryDeploymentProperties {
this.healthCheckHttpEndpoint = healthCheckHttpEndpoint;
}
public String getHealthCheckTimeout() {
public Integer getHealthCheckTimeout() {
return healthCheckTimeout;
}
public void setHealthCheckTimeout(String healthCheckTimeout) {
public void setHealthCheckTimeout(Integer healthCheckTimeout) {
this.healthCheckTimeout = healthCheckTimeout;
}

View File

@@ -16,9 +16,11 @@
package org.springframework.cloud.appbroker.deployer.cloudfoundry;
import java.util.Collections;
import java.io.File;
import java.util.ArrayList;
import org.cloudfoundry.operations.CloudFoundryOperations;
import org.cloudfoundry.operations.applications.ApplicationHealthCheck;
import org.cloudfoundry.operations.applications.ApplicationManifest;
import org.cloudfoundry.operations.applications.Applications;
import org.cloudfoundry.operations.applications.PushApplicationManifestRequest;
@@ -28,6 +30,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.appbroker.deployer.DeploymentProperties;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@@ -45,8 +48,6 @@ import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class CloudFoundryAppDeployerTest {
private static final int APP_MEMORY = 1024;
private static final String APP_MEMORY_STRING = "1024M";
private static final String APP_NAME = "test-app";
private static final String APP_PATH = "test.jar";
@@ -60,43 +61,185 @@ class CloudFoundryAppDeployerTest {
@Mock
private ResourceLoader resourceLoader;
private CloudFoundryDeploymentProperties deploymentProperties;
@BeforeEach
void setUp() {
CloudFoundryDeploymentProperties deploymentProperties = new CloudFoundryDeploymentProperties();
deploymentProperties.setDisk(APP_MEMORY_STRING);
deploymentProperties = new CloudFoundryDeploymentProperties();
when(applications.pushManifest(any())).thenReturn(Mono.empty());
when(cloudFoundryOperations.applications()).thenReturn(applications);
when(resourceLoader.getResource(APP_PATH)).thenReturn(new FileSystemResource(APP_PATH));
when(applications.pushManifest(any()))
.thenReturn(Mono.empty());
when(cloudFoundryOperations.applications())
.thenReturn(applications);
when(resourceLoader.getResource(APP_PATH))
.thenReturn(new FileSystemResource(APP_PATH));
appDeployer = new CloudFoundryAppDeployer(
deploymentProperties, cloudFoundryOperations, resourceLoader);
}
@Test
void shouldDeployAppWithNameAndPath() {
StepVerifier.create(appDeployer.deploy(DeployApplicationRequest.builder()
void deployAppWithPlatformDefaults() {
DeployApplicationRequest request = DeployApplicationRequest.builder()
.name(APP_NAME)
.properties(Collections.emptyMap())
.path(APP_PATH)
.build()))
.build();
StepVerifier.create(appDeployer.deploy(request))
.assertNext(response -> assertThat(response.getName()).isEqualTo(APP_NAME))
.verifyComplete();
verify(applications).pushManifest(argThat(matchesExpectedManifest()));
ApplicationManifest expectedManifest = baseManifest()
.name(APP_NAME)
.path(new File(APP_PATH).toPath())
.build();
verify(applications).pushManifest(argThat(matchesManifest(expectedManifest)));
}
private ArgumentMatcher<PushApplicationManifestRequest> matchesExpectedManifest() {
return request -> {
if (request.getManifests().size() == 1) {
ApplicationManifest manifest = request.getManifests().get(0);
return APP_NAME.equals(manifest.getName())
&& APP_PATH.equals(manifest.getPath().toString())
&& manifest.getDisk() == APP_MEMORY;
@Test
void deployAppWithPropertiesInRequest() {
DeployApplicationRequest request = DeployApplicationRequest.builder()
.name(APP_NAME)
.path(APP_PATH)
.property(DeploymentProperties.COUNT_PROPERTY_KEY, "3")
.property(DeploymentProperties.MEMORY_PROPERTY_KEY, "2G")
.property(DeploymentProperties.DISK_PROPERTY_KEY, "3G")
.property(CloudFoundryDeploymentProperties.HEALTHCHECK_PROPERTY_KEY, "http")
.property(CloudFoundryDeploymentProperties.HEALTHCHECK_HTTP_ENDPOINT_PROPERTY_KEY, "/healthcheck")
.property(CloudFoundryDeploymentProperties.BUILDPACK_PROPERTY_KEY, "buildpack")
.property(CloudFoundryDeploymentProperties.DOMAIN_PROPERTY, "domain")
.property(CloudFoundryDeploymentProperties.HOST_PROPERTY, "host")
.property(CloudFoundryDeploymentProperties.NO_ROUTE_PROPERTY, "true")
.build();
StepVerifier.create(appDeployer.deploy(request))
.assertNext(response -> assertThat(response.getName()).isEqualTo(APP_NAME))
.verifyComplete();
ApplicationManifest expectedManifest = baseManifest()
.name(APP_NAME)
.path(new File(APP_PATH).toPath())
.instances(3)
.memory(2048)
.disk(3072)
.healthCheckType(ApplicationHealthCheck.HTTP)
.healthCheckHttpEndpoint("/healthcheck")
.buildpack("buildpack")
.domain("domain")
.host("host")
.noRoute(true)
.build();
verify(applications).pushManifest(argThat(matchesManifest(expectedManifest)));
}
@Test
void deployAppWithDefaultProperties() {
deploymentProperties.setInstances(3);
deploymentProperties.setMemory("2G");
deploymentProperties.setDisk("3G");
deploymentProperties.setBuildpack("buildpack");
deploymentProperties.setHealthCheck(ApplicationHealthCheck.HTTP);
deploymentProperties.setHealthCheckHttpEndpoint("/healthcheck");
deploymentProperties.setDomain("domain");
deploymentProperties.setHost("host");
DeployApplicationRequest request = DeployApplicationRequest.builder()
.name(APP_NAME)
.path(APP_PATH)
.build();
StepVerifier.create(appDeployer.deploy(request))
.assertNext(response -> assertThat(response.getName()).isEqualTo(APP_NAME))
.verifyComplete();
ApplicationManifest expectedManifest = baseManifest()
.name(APP_NAME)
.path(new File(APP_PATH).toPath())
.instances(3)
.memory(2048)
.disk(3072)
.healthCheckType(ApplicationHealthCheck.HTTP)
.healthCheckHttpEndpoint("/healthcheck")
.buildpack("buildpack")
.domain("domain")
.host("host")
.build();
verify(applications).pushManifest(argThat(matchesManifest(expectedManifest)));
}
@Test
void deployAppWithRequestOverridingDefaultProperties() {
deploymentProperties.setInstances(3);
deploymentProperties.setMemory("2G");
deploymentProperties.setDisk("3G");
deploymentProperties.setBuildpack("buildpack1");
deploymentProperties.setHealthCheck(ApplicationHealthCheck.HTTP);
deploymentProperties.setHealthCheckHttpEndpoint("/healthcheck1");
deploymentProperties.setDomain("domain1");
deploymentProperties.setHost("host1");
DeployApplicationRequest request = DeployApplicationRequest.builder()
.name(APP_NAME)
.path(APP_PATH)
.property(DeploymentProperties.COUNT_PROPERTY_KEY, "5")
.property(DeploymentProperties.MEMORY_PROPERTY_KEY, "4G")
.property(DeploymentProperties.DISK_PROPERTY_KEY, "5G")
.property(CloudFoundryDeploymentProperties.HEALTHCHECK_PROPERTY_KEY, "port")
.property(CloudFoundryDeploymentProperties.HEALTHCHECK_HTTP_ENDPOINT_PROPERTY_KEY, "/healthcheck2")
.property(CloudFoundryDeploymentProperties.BUILDPACK_PROPERTY_KEY, "buildpack2")
.property(CloudFoundryDeploymentProperties.DOMAIN_PROPERTY, "domain2")
.property(CloudFoundryDeploymentProperties.HOST_PROPERTY, "host2")
.property(CloudFoundryDeploymentProperties.NO_ROUTE_PROPERTY, "true")
.build();
StepVerifier.create(appDeployer.deploy(request))
.assertNext(response -> assertThat(response.getName()).isEqualTo(APP_NAME))
.verifyComplete();
ApplicationManifest expectedManifest = baseManifest()
.name(APP_NAME)
.path(new File(APP_PATH).toPath())
.instances(5)
.memory(4096)
.disk(5120)
.healthCheckType(ApplicationHealthCheck.PORT)
.healthCheckHttpEndpoint("/healthcheck2")
.buildpack("buildpack2")
.domain("domain2")
.host("host2")
.noRoute(true)
.build();
verify(applications).pushManifest(argThat(matchesManifest(expectedManifest)));
}
private ApplicationManifest.Builder baseManifest() {
return ApplicationManifest.builder()
.environmentVariable("SPRING_APPLICATION_INDEX", "${vcap.application.instance_index}")
.environmentVariable("SPRING_CLOUD_APPLICATION_GUID", "${vcap.application.name}:${vcap.application.instance_index}")
.environmentVariable("SPRING_APPLICATION_JSON", "{}")
.services(new ArrayList<>());
}
private ArgumentMatcher<PushApplicationManifestRequest> matchesManifest(ApplicationManifest expectedManifest) {
return new ArgumentMatcher<PushApplicationManifestRequest>() {
@Override
public boolean matches(PushApplicationManifestRequest request) {
if (request.getManifests().size() == 1) {
return request.getManifests().get(0).equals(expectedManifest);
}
return false;
}
return false;
@Override
public String toString() {
return expectedManifest.toString();
}
};
}