Remove dependency on Spring Cloud Deployer. Remove unused code from the deployer abstraction.

This commit is contained in:
Scott Frederick
2018-10-01 16:38:26 -05:00
parent e93a0d0eb9
commit 21a1053cca
23 changed files with 756 additions and 694 deletions

View File

@@ -18,17 +18,15 @@ description = "Spring Cloud App Broker Deployer Cloud Foundry"
ext {
reactorNettyVersion = '0.7.5.RELEASE'
// webflux defaults; must be overridden for org.cloudfoundry:cloudfoundry-client-reactor:3.9.0.RELEASE
// reactorVersion = '3.1.7.RELEASE'
// reactorNettyVersion = '0.7.7.RELEASE'
deployerCFVersion = '1.4.0.RELEASE'
cfJavaClientVersion = "3.9.0.RELEASE"
}
dependencies {
api project(":spring-cloud-app-broker-deployer")
api("org.springframework.cloud:spring-cloud-deployer-cloudfoundry:${deployerCFVersion}")
api("org.cloudfoundry:cloudfoundry-operations:${cfJavaClientVersion}")
api("org.cloudfoundry:cloudfoundry-client-reactor:${cfJavaClientVersion}")
api("org.cloudfoundry:cloudfoundry-util:${cfJavaClientVersion}")
// fix the Boot version to prevent 1.5.x and 2.0.x on the classpath
implementation("org.springframework.boot:spring-boot-starter-validation:${springBootVersion}")

View File

@@ -1,177 +0,0 @@
/*
* 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.cloudfoundry;
import java.io.IOException;
import java.nio.file.Path;
import java.time.Duration;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import org.cloudfoundry.AbstractCloudFoundryException;
import org.cloudfoundry.UnknownCloudFoundryException;
import org.cloudfoundry.util.DelayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.Exceptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.cloud.deployer.spi.app.AppDeployer;
import org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties;
import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest;
import org.springframework.cloud.deployer.spi.core.RuntimeEnvironmentInfo;
import org.springframework.cloud.deployer.spi.util.ByteSizeUtils;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.BUILDPACK_PROPERTY_KEY;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.JAVA_OPTS_PROPERTY_KEY;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.SERVICES_PROPERTY_KEY;
class AbstractCloudFoundryAppDeployer {
private final RuntimeEnvironmentInfo runtimeEnvironmentInfo;
final CloudFoundryDeploymentProperties deploymentProperties;
private final Logger logger = LoggerFactory.getLogger(AbstractCloudFoundryAppDeployer.class);
AbstractCloudFoundryAppDeployer(CloudFoundryDeploymentProperties deploymentProperties, RuntimeEnvironmentInfo runtimeEnvironmentInfo) {
this.deploymentProperties = deploymentProperties;
this.runtimeEnvironmentInfo = runtimeEnvironmentInfo;
}
int memory(AppDeploymentRequest request) {
String withUnit = request.getDeploymentProperties()
.getOrDefault(org.springframework.cloud.deployer.spi.app.AppDeployer.MEMORY_PROPERTY_KEY, this.deploymentProperties.getMemory());
return (int) ByteSizeUtils.parseToMebibytes(withUnit);
}
Set<String> servicesToBind(AppDeploymentRequest request) {
Set<String> services = new HashSet<>();
services.addAll(this.deploymentProperties.getServices());
services.addAll(StringUtils.commaDelimitedListToSet(request.getDeploymentProperties().get(SERVICES_PROPERTY_KEY)));
return services;
}
int diskQuota(AppDeploymentRequest request) {
String withUnit = request.getDeploymentProperties()
.getOrDefault(AppDeployer.DISK_PROPERTY_KEY, this.deploymentProperties.getDisk());
return (int) ByteSizeUtils.parseToMebibytes(withUnit);
}
String buildpack(AppDeploymentRequest request) {
return Optional.ofNullable(request.getDeploymentProperties().get(BUILDPACK_PROPERTY_KEY))
.orElse(this.deploymentProperties.getBuildpack());
}
String javaOpts(AppDeploymentRequest request) {
return Optional.ofNullable(request.getDeploymentProperties().get(JAVA_OPTS_PROPERTY_KEY))
.orElse(this.deploymentProperties.getJavaOpts());
}
Predicate<Throwable> isNotFoundError() {
return t -> t instanceof AbstractCloudFoundryException && ((AbstractCloudFoundryException) t).getStatusCode() == HttpStatus.NOT_FOUND.value();
}
/**
* Return a Docker image identifier if the application Resource is for a Docker image, or {@literal null} otherwise.
*
* @see #getApplication(AppDeploymentRequest)
*/
String getDockerImage(AppDeploymentRequest request) {
try {
String uri = request.getResource().getURI().toString();
if (uri.startsWith("docker:")) {
return uri.substring("docker:".length());
} else {
return null;
}
} catch (IOException e) {
throw Exceptions.propagate(e);
}
}
/**
* Return a Path to the application Resource or {@literal null} if the request is for a Docker image.
*
* @see #getDockerImage(AppDeploymentRequest)
*/
Path getApplication(AppDeploymentRequest request) {
try {
if (!request.getResource().getURI().toString().startsWith("docker:")) {
return request.getResource().getFile().toPath();
} else {
return null;
}
} catch (IOException e) {
throw Exceptions.propagate(e);
}
}
/**
* Return a function usable in {@literal doOnError} constructs that will unwrap unrecognized Cloud Foundry Exceptions
* and log the text payload.
*/
protected Consumer<Throwable> logError(String msg) {
return e -> {
if (e instanceof UnknownCloudFoundryException) {
logger.error(msg + "\nUnknownCloudFoundryException encountered, whose payload follows:\n" + ((UnknownCloudFoundryException)e).getPayload(), e);
} else {
logger.error(msg, e);
}
};
}
/**
* To be used in order to retry the status operation for an application or task.
* @param id The application id or the task id
* @param <T> The type of status object being queried for, usually AppStatus or TaskStatus
* @return The function that executes the retry logic around for determining App or Task Status
*/
<T> Function<Mono<T>, Mono<T>> statusRetry(String id) {
long statusTimeout = this.deploymentProperties.getStatusTimeout();
long requestTimeout = Math.round(statusTimeout * 0.40); // wait 500ms with default status timeout of 2000ms
long initialRetryDelay = Math.round(statusTimeout * 0.10); // wait 200ms with status timeout of 2000ms
if (requestTimeout < 500L) {
logger.info("Computed statusRetry Request timeout = {} ms is below 500ms minimum value. Setting to 500ms", requestTimeout);
requestTimeout = 500L;
}
final long requestTimeoutToUse = requestTimeout;
return m -> m.timeout(Duration.ofMillis(requestTimeoutToUse))
.doOnError(e -> logger.warn(String.format("Error getting status for %s within %sms, Retrying operation.", id, requestTimeoutToUse)))
.retryWhen(DelayUtils.exponentialBackOffError(
Duration.ofMillis(initialRetryDelay), //initial retry delay
Duration.ofMillis(statusTimeout / 2), // max retry delay
Duration.ofMillis(statusTimeout)) // max total retry time
.andThen(retries -> Flux.from(retries).doOnComplete(() ->
logger.info("Successfully retried getStatus operation status [{}] for {}", id))))
.doOnError(e -> logger.error(String.format("Retry operation on getStatus failed for %s. Max retry time %sms", id, statusTimeout)));
}
public RuntimeEnvironmentInfo environmentInfo() {
return runtimeEnvironmentInfo;
}
}

View File

@@ -16,87 +16,84 @@
package org.springframework.cloud.appbroker.deployer.cloudfoundry;
import java.io.IOException;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.cloudfoundry.AbstractCloudFoundryException;
import org.cloudfoundry.UnknownCloudFoundryException;
import org.cloudfoundry.operations.CloudFoundryOperations;
import org.cloudfoundry.operations.applications.ApplicationDetail;
import org.cloudfoundry.operations.applications.ApplicationHealthCheck;
import org.cloudfoundry.operations.applications.ApplicationManifest;
import org.cloudfoundry.operations.applications.ApplicationSummary;
import org.cloudfoundry.operations.applications.DeleteApplicationRequest;
import org.cloudfoundry.operations.applications.Docker;
import org.cloudfoundry.operations.applications.GetApplicationRequest;
import org.cloudfoundry.operations.applications.InstanceDetail;
import org.cloudfoundry.operations.applications.PushApplicationManifestRequest;
import org.cloudfoundry.operations.applications.Route;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
import reactor.core.publisher.Flux;
import org.springframework.cloud.appbroker.deployer.util.ByteSizeUtils;
import org.springframework.http.HttpStatus;
import reactor.core.Exceptions;
import reactor.core.publisher.Mono;
import org.springframework.cloud.appbroker.deployer.AppDeployer;
import org.springframework.cloud.appbroker.deployer.DeployApplicationRequest;
import org.springframework.cloud.appbroker.deployer.DeployApplicationResponse;
import org.springframework.cloud.appbroker.deployer.GetApplicationStatusRequest;
import org.springframework.cloud.appbroker.deployer.GetApplicationStatusResponse;
import org.springframework.cloud.appbroker.deployer.UndeployApplicationRequest;
import org.springframework.cloud.appbroker.deployer.UndeployApplicationResponse;
import org.springframework.cloud.deployer.spi.app.AppStatus;
import org.springframework.cloud.deployer.spi.app.DeploymentState;
import org.springframework.cloud.deployer.spi.cloudfoundry.AppNameGenerator;
import org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryAppInstanceStatus;
import org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties;
import org.springframework.cloud.deployer.spi.core.AppDefinition;
import org.springframework.cloud.deployer.spi.core.AppDeploymentRequest;
import org.springframework.cloud.deployer.spi.core.RuntimeEnvironmentInfo;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.DOMAIN_PROPERTY;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.HEALTHCHECK_HTTP_ENDPOINT_PROPERTY_KEY;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.HEALTHCHECK_PROPERTY_KEY;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.HEALTHCHECK_TIMEOUT_PROPERTY_KEY;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.HOST_PROPERTY;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.NO_ROUTE_PROPERTY;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.ROUTES_PROPERTY;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.ROUTE_PATH_PROPERTY;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.ROUTE_PROPERTY;
import static org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties.USE_SPRING_APPLICATION_JSON_KEY;
import static org.springframework.cloud.appbroker.deployer.DeploymentProperties.COUNT_PROPERTY_KEY;
import static org.springframework.cloud.appbroker.deployer.DeploymentProperties.DISK_PROPERTY_KEY;
import static org.springframework.cloud.appbroker.deployer.DeploymentProperties.GROUP_PROPERTY_KEY;
import static org.springframework.cloud.appbroker.deployer.DeploymentProperties.MEMORY_PROPERTY_KEY;
import static org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties.BUILDPACK_PROPERTY_KEY;
import static org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties.DOMAIN_PROPERTY;
import static org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties.HEALTHCHECK_HTTP_ENDPOINT_PROPERTY_KEY;
import static org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties.HEALTHCHECK_PROPERTY_KEY;
import static org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties.HEALTHCHECK_TIMEOUT_PROPERTY_KEY;
import static org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties.HOST_PROPERTY;
import static org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties.JAVA_OPTS_PROPERTY_KEY;
import static org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties.NO_ROUTE_PROPERTY;
import static org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties.ROUTES_PROPERTY;
import static org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties.ROUTE_PATH_PROPERTY;
import static org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties.ROUTE_PROPERTY;
import static org.springframework.cloud.appbroker.deployer.cloudfoundry.CloudFoundryDeploymentProperties.USE_SPRING_APPLICATION_JSON_KEY;
public class CloudFoundryAppDeployer extends AbstractCloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware {
public class CloudFoundryAppDeployer implements AppDeployer, ResourceLoaderAware {
private static final Logger logger = LoggerFactory.getLogger(CloudFoundryAppDeployer.class);
private static final String SPRING_CLOUD_DEPLOYER_CLOUDFOUNDRY_SERVICES_KEY = "spring.cloud.deployer.cloudfoundry.services";
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private final AppNameGenerator applicationNameGenerator;
private final CloudFoundryDeploymentProperties defaultDeploymentProperties;
private final CloudFoundryOperations operations;
private ResourceLoader resourceLoader;
public CloudFoundryAppDeployer(AppNameGenerator applicationNameGenerator,
CloudFoundryDeploymentProperties deploymentProperties,
public CloudFoundryAppDeployer(CloudFoundryDeploymentProperties deploymentProperties,
CloudFoundryOperations operations,
RuntimeEnvironmentInfo runtimeEnvironmentInfo,
ResourceLoader resourceLoader) {
super(deploymentProperties, runtimeEnvironmentInfo);
this.defaultDeploymentProperties = deploymentProperties;
this.operations = operations;
this.applicationNameGenerator = applicationNameGenerator;
this.resourceLoader = resourceLoader;
}
@@ -106,180 +103,147 @@ public class CloudFoundryAppDeployer extends AbstractCloudFoundryAppDeployer imp
}
@Override
public Mono<DeployApplicationResponse> deploy(DeployApplicationRequest deployApplicationRequest) {
AppDeploymentRequest request = createAppDeploymentRequest(deployApplicationRequest);
public Mono<DeployApplicationResponse> deploy(DeployApplicationRequest request) {
String appName = request.getName();
Resource appResource = getAppResource(request);
Map<String, String> deploymentProperties = request.getProperties();
logger.trace("Entered deploy: Deploying AppDeploymentRequest: AppDefinition = {}, Resource = {}, Deployment Properties = {}",
request.getDefinition(), request.getResource(), request.getDeploymentProperties());
String deploymentId = deploymentId(request);
logger.trace("Deploying application: request={}, resource={}",
appName, appResource);
logger.trace("deploy: Pushing application");
return pushApplication(deploymentId, request)
.timeout(Duration.ofSeconds(this.deploymentProperties.getApiTimeout()))
.doOnSuccess(item -> logger.info("Successfully deployed {}", deploymentId))
return pushApplication(request, deploymentProperties, appResource)
.timeout(Duration.ofSeconds(this.defaultDeploymentProperties.getApiTimeout()))
.doOnSuccess(item -> logger.info("Successfully deployed {}", appName))
.doOnError(error -> {
if (isNotFoundError().test(error)) {
logger.warn("Unable to deploy application. It may have been destroyed before start completed: " + error.getMessage());
}
else {
logError(String.format("Failed to deploy %s", deploymentId)).accept(error);
logError(String.format("Failed to deploy %s", appName)).accept(error);
}
})
.thenReturn(DeployApplicationResponse.builder()
.name(deploymentId)
.name(appName)
.build());
}
private Mono<Void> pushApplication(String deploymentId, AppDeploymentRequest request) {
ApplicationManifest manifest = buildAppManifest(deploymentId, request);
private Mono<Void> pushApplication(DeployApplicationRequest request,
Map<String, String> deploymentProperties,
Resource appResource) {
ApplicationManifest manifest = buildAppManifest(request, deploymentProperties, appResource);
logger.debug("Pushing manifest" + manifest.toString());
return requestPushApplication(
PushApplicationManifestRequest.builder()
.manifest(manifest)
.stagingTimeout(this.deploymentProperties.getStagingTimeout())
.startupTimeout(this.deploymentProperties.getStartupTimeout())
.stagingTimeout(this.defaultDeploymentProperties.getStagingTimeout())
.startupTimeout(this.defaultDeploymentProperties.getStartupTimeout())
.build())
.doOnSuccess(v -> logger.info("Done uploading bits for {}", deploymentId))
.doOnError(e -> logger.error(String.format("Error creating app %s. Exception Message %s", deploymentId, e.getMessage())));
.doOnSuccess(v -> logger.info("Done uploading bits for {}", request.getName()))
.doOnError(e -> logger.error(String.format("Error creating app %s. Exception Message %s",
request.getName(), e.getMessage())));
}
private ApplicationManifest buildAppManifest(String deploymentId, AppDeploymentRequest request) {
private ApplicationManifest buildAppManifest(DeployApplicationRequest request,
Map<String, String> deploymentProperties,
Resource appResource) {
ApplicationManifest.Builder manifest = ApplicationManifest.builder()
.path(getApplication(request)) // Only one of the two is non-null
.disk(diskQuota(request))
.environmentVariables(getEnvironmentVariables(deploymentId, request))
.healthCheckType(healthCheck(request))
.healthCheckHttpEndpoint(healthCheckEndpoint(request))
.timeout(healthCheckTimeout(request))
.instances(instances(request))
.memory(memory(request))
.name(deploymentId)
.noRoute(toggleNoRoute(request))
.services(servicesToBind(request));
.name(request.getName())
.path(getApplication(appResource))
.environmentVariables(getEnvironmentVariables(request.getEnvironment()))
.services(servicesToBind(request.getServices()))
.disk(diskQuota(deploymentProperties))
.healthCheckType(healthCheck(deploymentProperties))
.healthCheckHttpEndpoint(healthCheckEndpoint(deploymentProperties))
.timeout(healthCheckTimeout(deploymentProperties))
.instances(instances(deploymentProperties))
.memory(memory(deploymentProperties))
.noRoute(toggleNoRoute(deploymentProperties));
Optional.ofNullable(host(request)).ifPresent(manifest::host);
Optional.ofNullable(domain(request)).ifPresent(manifest::domain);
Optional.ofNullable(routePath(request)).ifPresent(manifest::routePath);
Optional.ofNullable(host(deploymentProperties)).ifPresent(manifest::host);
Optional.ofNullable(domain(deploymentProperties)).ifPresent(manifest::domain);
Optional.ofNullable(routePath(deploymentProperties)).ifPresent(manifest::routePath);
if (route(request) != null) {
manifest.route(Route.builder().route(route(request)).build());
if (route(deploymentProperties) != null) {
manifest.route(Route.builder().route(route(deploymentProperties)).build());
}
if (!routes(request).isEmpty()) {
Set<Route> routes = routes(request).stream()
if (!routes(deploymentProperties).isEmpty()) {
Set<Route> routes = routes(deploymentProperties).stream()
.map(r -> Route.builder().route(r).build())
.collect(Collectors.toSet());
manifest.routes(routes);
}
if (getDockerImage(request) != null) {
manifest.docker(Docker.builder().image(getDockerImage(request)).build());
if (getDockerImage(appResource) != null) {
manifest.docker(Docker.builder().image(getDockerImage(appResource)).build());
} else {
manifest.buildpack(buildpack(request));
manifest.buildpack(buildpack(deploymentProperties));
}
return manifest.build();
}
private DeploymentState mapShallowAppState(ApplicationSummary applicationSummary) {
if (applicationSummary.getRunningInstances().equals(applicationSummary.getInstances())) {
return DeploymentState.deployed;
}
else if (applicationSummary.getInstances() > 0) {
return DeploymentState.partial;
} else {
return DeploymentState.undeployed;
}
}
@Override
public Mono<GetApplicationStatusResponse> status(GetApplicationStatusRequest request) {
return getStatus(request.getName())
.doOnSuccess(v -> logger.info("Successfully computed status [{}] for {}", v, request.getName()))
.doOnError(logError(String.format("Failed to compute status for %s", request.getName())))
.timeout(Duration.ofMillis(this.deploymentProperties.getStatusTimeout()))
.onErrorResume(t -> {
logger.error("Caught exception while querying for status of {}", request.getName(), t);
return Mono.just(createErrorAppStatus(request.getName()));
})
.flatMap(status -> Mono.just(GetApplicationStatusResponse.builder()
.deploymentId(status.getDeploymentId())
.status(status.toString())
.build()));
private Mono<Void> requestPushApplication(PushApplicationManifestRequest request) {
return this.operations.applications()
.pushManifest(request);
}
@Override
public Mono<UndeployApplicationResponse> undeploy(UndeployApplicationRequest request) {
return getStatus(request.getName())
.doOnNext(status -> assertApplicationExists(request.getName(), status))
.timeout(Duration.ofSeconds(this.deploymentProperties.getApiTimeout()))
.then(requestDeleteApplication(request.getName())
.timeout(Duration.ofSeconds(this.deploymentProperties.getApiTimeout()))
.doOnSuccess(v -> logger.info("Successfully undeployed app {}", request.getName()))
.doOnError(logError(String.format("Failed to undeploy app %s", request.getName()))))
.then(getStatus(request.getName()))
//TODO: do we wait for deletion here? maybe we don't need status.
.flatMap(status -> Mono.just(UndeployApplicationResponse.builder()
.status(status.getState().toString())
.build()));
String appName = request.getName();
logger.trace("Undeploying application: request={}", request);
return requestGetApplication(appName)
.timeout(Duration.ofSeconds(this.defaultDeploymentProperties.getApiTimeout()))
.then(requestDeleteApplication(appName)
.timeout(Duration.ofSeconds(this.defaultDeploymentProperties.getApiTimeout()))
.doOnSuccess(v -> logger.info("Successfully undeployed app {}", appName))
.doOnError(logError(String.format("Failed to undeploy app %s", appName)))
.then(Mono.just(UndeployApplicationResponse.builder()
.name(appName)
.build())));
}
private void assertApplicationExists(String deploymentId, AppStatus status) {
DeploymentState state = status.getState();
if (state == DeploymentState.unknown) {
throw new IllegalStateException(String.format("App %s is not in a deployed state", deploymentId));
}
private Mono<ApplicationDetail> requestGetApplication(String name) {
return this.operations.applications()
.get(GetApplicationRequest.builder()
.name(name)
.build());
}
private AppStatus createAppStatus(ApplicationDetail applicationDetail, String deploymentId) {
logger.trace("Gathering instances for " + applicationDetail);
logger.trace("InstanceDetails: " + applicationDetail.getInstanceDetails());
private Mono<Void> requestDeleteApplication(String name) {
return this.operations.applications()
.delete(DeleteApplicationRequest.builder()
.deleteRoutes(defaultDeploymentProperties.isDeleteRoutes())
.name(name)
.build());
}
AppStatus.Builder builder = AppStatus.of(deploymentId);
private Map<String, String> getEnvironmentVariables(Map<String, String> environment) {
Map<String, String> envVariables = new HashMap<>(getApplicationEnvironment(environment));
int i = 0;
for (InstanceDetail instanceDetail : applicationDetail.getInstanceDetails()) {
builder.with(new CloudFoundryAppInstanceStatus(applicationDetail, instanceDetail, i++));
}
for (; i < applicationDetail.getInstances(); i++) {
builder.with(new CloudFoundryAppInstanceStatus(applicationDetail, null, i));
String javaOpts = javaOpts(environment);
if (StringUtils.hasText(javaOpts)) {
envVariables.put("JAVA_OPTS", javaOpts(environment));
}
return builder.build();
String group = environment.get(GROUP_PROPERTY_KEY);
if (StringUtils.hasText(group)) {
envVariables.put("SPRING_CLOUD_APPLICATION_GROUP", group);
}
envVariables.put("SPRING_CLOUD_APPLICATION_GUID", "${vcap.application.name}:${vcap.application.instance_index}");
envVariables.put("SPRING_APPLICATION_INDEX", "${vcap.application.instance_index}");
return envVariables;
}
private AppStatus createEmptyAppStatus(String deploymentId) {
return AppStatus.of(deploymentId)
.build();
}
private Map<String, String> getApplicationEnvironment(Map<String, String> environment) {
Map<String, String> applicationProperties = getSanitizedApplicationEnvironment(environment);
private AppStatus createErrorAppStatus(String deploymentId) {
return AppStatus.of(deploymentId)
.generalState(DeploymentState.error)
.build();
}
private String deploymentId(AppDeploymentRequest request) {
String prefix = Optional.ofNullable(request.getDeploymentProperties().get(org.springframework.cloud.deployer.spi.app.AppDeployer.GROUP_PROPERTY_KEY))
.map(group -> String.format("%s-", group))
.orElse("");
String appName = String.format("%s%s", prefix, request.getDefinition().getName());
return this.applicationNameGenerator.generateAppName(appName);
}
private String domain(AppDeploymentRequest request) {
return Optional.ofNullable(request.getDeploymentProperties().get(DOMAIN_PROPERTY))
.orElse(this.deploymentProperties.getDomain());
}
private Map<String, String> getApplicationProperties(String deploymentId, AppDeploymentRequest request) {
Map<String, String> applicationProperties = getSanitizedApplicationProperties(deploymentId, request);
if (!useSpringApplicationJson(request)) {
if (!useSpringApplicationJson(environment)) {
return applicationProperties;
}
@@ -290,127 +254,40 @@ public class CloudFoundryAppDeployer extends AbstractCloudFoundryAppDeployer imp
}
}
private Map<String, String> getCommandLineArguments(AppDeploymentRequest request) {
if (request.getCommandlineArguments().isEmpty()) {
return Collections.emptyMap();
}
String argumentsAsString = request.getCommandlineArguments().stream()
.collect(Collectors.joining(" "));
String yaml = new Yaml().dump(Collections.singletonMap("arguments", argumentsAsString));
return Collections.singletonMap("JBP_CONFIG_JAVA_MAIN", yaml);
}
private Map<String, String> getEnvironmentVariables(String deploymentId, AppDeploymentRequest request) {
Map<String, String> envVariables = new HashMap<>();
envVariables.putAll(getApplicationProperties(deploymentId, request));
envVariables.putAll(getCommandLineArguments(request));
String javaOpts = javaOpts(request);
if (StringUtils.hasText(javaOpts)) {
envVariables.put("JAVA_OPTS", javaOpts(request));
}
String group = request.getDeploymentProperties().get(org.springframework.cloud.deployer.spi.app.AppDeployer.GROUP_PROPERTY_KEY);
if (StringUtils.hasText(group)) {
envVariables.put("SPRING_CLOUD_APPLICATION_GROUP", group);
}
envVariables.put("SPRING_CLOUD_APPLICATION_GUID", "${vcap.application.name}:${vcap.application.instance_index}");
envVariables.put("SPRING_APPLICATION_INDEX", "${vcap.application.instance_index}");
return envVariables;
}
private Map<String, String> getSanitizedApplicationProperties(String deploymentId, AppDeploymentRequest request) {
Map<String, String> applicationProperties = new HashMap<>(request.getDefinition().getProperties());
private Map<String, String> getSanitizedApplicationEnvironment(Map<String, String> environment) {
Map<String, String> applicationProperties = new HashMap<>(environment);
// Remove server.port as CF assigns a port for us, and we don't want to override that
Optional.ofNullable(applicationProperties.remove("server.port"))
.ifPresent(port -> logger.warn("Ignoring 'server.port={}' for app {}, as Cloud Foundry will assign a local dynamic port. Route to the app will use port 80.", port, deploymentId));
.ifPresent(port -> logger.warn("Ignoring 'server.port={}', " +
"as Cloud Foundry will assign a local dynamic port. " +
"Route to the app will use port 80.", port));
return applicationProperties;
}
private Mono<AppStatus> getStatus(String deploymentId) {
return requestGetApplication(deploymentId)
.map(applicationDetail -> createAppStatus(applicationDetail, deploymentId))
.onErrorResume(IllegalArgumentException.class, t -> {
logger.debug("Application for {} does not exist.", deploymentId);
return Mono.just(createEmptyAppStatus(deploymentId));
})
.transform(statusRetry(deploymentId))
.onErrorReturn(createErrorAppStatus(deploymentId));
private boolean useSpringApplicationJson(Map<String, String> environment) {
return Optional.ofNullable(environment.get(USE_SPRING_APPLICATION_JSON_KEY))
.map(Boolean::valueOf)
.orElse(this.defaultDeploymentProperties.isUseSpringApplicationJson());
}
private ApplicationHealthCheck healthCheck(AppDeploymentRequest request) {
return Optional.ofNullable(request.getDeploymentProperties().get(HEALTHCHECK_PROPERTY_KEY))
private Set<String> servicesToBind(List<String> services) {
Set<String> allServices = new HashSet<>();
allServices.addAll(this.defaultDeploymentProperties.getServices());
allServices.addAll(services);
return allServices;
}
private String domain(Map<String, String> properties) {
return Optional.ofNullable(properties.get(DOMAIN_PROPERTY))
.orElse(this.defaultDeploymentProperties.getDomain());
}
private ApplicationHealthCheck healthCheck(Map<String, String> properties) {
return Optional.ofNullable(properties.get(HEALTHCHECK_PROPERTY_KEY))
.map(this::toApplicationHealthCheck)
.orElse(this.deploymentProperties.getHealthCheck());
}
private String healthCheckEndpoint(AppDeploymentRequest request) {
return Optional.ofNullable(request.getDeploymentProperties().get(HEALTHCHECK_HTTP_ENDPOINT_PROPERTY_KEY))
.orElse(this.deploymentProperties.getHealthCheckHttpEndpoint());
}
private Integer healthCheckTimeout(AppDeploymentRequest request) {
String timeoutString = request.getDeploymentProperties()
.getOrDefault(HEALTHCHECK_TIMEOUT_PROPERTY_KEY, this.deploymentProperties.getHealthCheckTimeout());
return Integer.parseInt(timeoutString);
}
private String host(AppDeploymentRequest request) {
return Optional.ofNullable(request.getDeploymentProperties().get(HOST_PROPERTY))
.orElse(this.deploymentProperties.getHost());
}
private int instances(AppDeploymentRequest request) {
return Optional.ofNullable(request.getDeploymentProperties().get(org.springframework.cloud.deployer.spi.app.AppDeployer.COUNT_PROPERTY_KEY))
.map(Integer::parseInt)
.orElse(this.deploymentProperties.getInstances());
}
private Mono<Void> requestDeleteApplication(String id) {
return this.operations.applications()
.delete(DeleteApplicationRequest.builder()
.deleteRoutes(deploymentProperties.isDeleteRoutes())
.name(id)
.build());
}
private Mono<ApplicationDetail> requestGetApplication(String id) {
return this.operations.applications()
.get(GetApplicationRequest.builder()
.name(id)
.build());
}
private Mono<Void> requestPushApplication(PushApplicationManifestRequest request) {
return this.operations.applications()
.pushManifest(request);
}
private Flux<ApplicationSummary> requestSummary() {
return this.operations.applications().list();
}
private String routePath(AppDeploymentRequest request) {
String routePath = request.getDeploymentProperties().get(ROUTE_PATH_PROPERTY);
if (StringUtils.hasText(routePath) && !routePath.startsWith("/")) {
throw new IllegalArgumentException(
"Cloud Foundry routes must start with \"/\". Route passed = [" + routePath + "].");
}
return routePath;
}
private String route(AppDeploymentRequest request) {
return request.getDeploymentProperties().get(ROUTE_PROPERTY);
}
private Set<String> routes(AppDeploymentRequest request) {
Set<String> routes = new HashSet<>();
routes.addAll(this.deploymentProperties.getRoutes());
routes.addAll(StringUtils.commaDelimitedListToSet(request.getDeploymentProperties().get(ROUTES_PROPERTY)));
return routes;
.orElse(this.defaultDeploymentProperties.getHealthCheck());
}
private ApplicationHealthCheck toApplicationHealthCheck(String raw) {
@@ -418,41 +295,136 @@ public class CloudFoundryAppDeployer extends AbstractCloudFoundryAppDeployer imp
return ApplicationHealthCheck.from(raw);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(String.format("Unsupported health-check value '%s'. Available values are %s", raw,
StringUtils.arrayToCommaDelimitedString(ApplicationHealthCheck.values())), e);
StringUtils.arrayToCommaDelimitedString(ApplicationHealthCheck.values())), e);
}
}
private Boolean toggleNoRoute(AppDeploymentRequest request) {
return Optional.ofNullable(request.getDeploymentProperties().get(NO_ROUTE_PROPERTY))
private String healthCheckEndpoint(Map<String, String> properties) {
return Optional.ofNullable(properties.get(HEALTHCHECK_HTTP_ENDPOINT_PROPERTY_KEY))
.orElse(this.defaultDeploymentProperties.getHealthCheckHttpEndpoint());
}
private Integer healthCheckTimeout(Map<String, String> properties) {
String timeoutString = properties
.getOrDefault(HEALTHCHECK_TIMEOUT_PROPERTY_KEY, this.defaultDeploymentProperties.getHealthCheckTimeout());
return Integer.parseInt(timeoutString);
}
private int instances(Map<String, String> properties) {
return Optional.ofNullable(properties.get(COUNT_PROPERTY_KEY))
.map(Integer::parseInt)
.orElse(this.defaultDeploymentProperties.getInstances());
}
private String host(Map<String, String> properties) {
return Optional.ofNullable(properties.get(HOST_PROPERTY))
.orElse(this.defaultDeploymentProperties.getHost());
}
private String routePath(Map<String, String> properties) {
String routePath = properties.get(ROUTE_PATH_PROPERTY);
if (StringUtils.hasText(routePath) && !routePath.startsWith("/")) {
throw new IllegalArgumentException(
"Cloud Foundry routes must start with \"/\". Route passed = [" + routePath + "].");
}
return routePath;
}
private String route(Map<String, String> properties) {
return properties.get(ROUTE_PROPERTY);
}
private Set<String> routes(Map<String, String> properties) {
Set<String> routes = new HashSet<>();
routes.addAll(this.defaultDeploymentProperties.getRoutes());
routes.addAll(StringUtils.commaDelimitedListToSet(properties.get(ROUTES_PROPERTY)));
return routes;
}
private Boolean toggleNoRoute(Map<String, String> properties) {
return Optional.ofNullable(properties.get(NO_ROUTE_PROPERTY))
.map(Boolean::valueOf)
.orElse(null);
}
private boolean useSpringApplicationJson(AppDeploymentRequest request) {
return Optional.ofNullable(request.getDeploymentProperties().get(USE_SPRING_APPLICATION_JSON_KEY))
.map(Boolean::valueOf)
.orElse(this.deploymentProperties.isUseSpringApplicationJson());
private int memory(Map<String, String> properties) {
String withUnit = properties
.getOrDefault(MEMORY_PROPERTY_KEY, this.defaultDeploymentProperties.getMemory());
return (int) ByteSizeUtils.parseToMebibytes(withUnit);
}
private AppDeploymentRequest createAppDeploymentRequest(DeployApplicationRequest request) {
AppDefinition appDefinition = new AppDefinition(request.getName(), request.getEnvironment());
Resource resource = resourceLoader.getResource(request.getPath());
Map<String, String> deploymentProperties = createDeploymentProperties(request);
return new AppDeploymentRequest(appDefinition, resource, deploymentProperties);
private int diskQuota(Map<String, String> properties) {
String withUnit = properties
.getOrDefault(DISK_PROPERTY_KEY, this.defaultDeploymentProperties.getDisk());
return (int) ByteSizeUtils.parseToMebibytes(withUnit);
}
private Map<String, String> createDeploymentProperties(DeployApplicationRequest request) {
Map<String, String> deploymentProperties = new HashMap<>();
if (request.getProperties() != null) {
deploymentProperties.putAll(request.getProperties());
private String buildpack(Map<String, String> properties) {
return Optional.ofNullable(properties.get(BUILDPACK_PROPERTY_KEY))
.orElse(this.defaultDeploymentProperties.getBuildpack());
}
private String javaOpts(Map<String, String> properties) {
return Optional.ofNullable(properties.get(JAVA_OPTS_PROPERTY_KEY))
.orElse(this.defaultDeploymentProperties.getJavaOpts());
}
private Predicate<Throwable> isNotFoundError() {
return t -> t instanceof AbstractCloudFoundryException && ((AbstractCloudFoundryException) t).getStatusCode() == HttpStatus.NOT_FOUND.value();
}
/**
* Return a Docker image identifier if the application Resource is for a Docker image, or {@literal null} otherwise.
*
* @see #getApplication(Resource)
*/
private String getDockerImage(Resource resource) {
try {
String uri = resource.getURI().toString();
if (uri.startsWith("docker:")) {
return uri.substring("docker:".length());
} else {
return null;
}
} catch (IOException e) {
throw Exceptions.propagate(e);
}
if (request.getServices() != null) {
Map<String, String> services = Collections.singletonMap(SPRING_CLOUD_DEPLOYER_CLOUDFOUNDRY_SERVICES_KEY,
StringUtils.collectionToCommaDelimitedString(request.getServices()));
deploymentProperties.putAll(services);
}
return deploymentProperties;
}
private Resource getAppResource(DeployApplicationRequest request) {
return resourceLoader.getResource(request.getPath());
}
/**
* Return a Path to the application Resource or {@literal null} if the request is for a Docker image.
*
* @see #getDockerImage(Resource)
* @param resource the resource representing the app bits
*/
private Path getApplication(Resource resource) {
try {
if (!resource.getURI().toString().startsWith("docker:")) {
return resource.getFile().toPath();
} else {
return null;
}
} catch (IOException e) {
throw Exceptions.propagate(e);
}
}
/**
* Return a function usable in {@literal doOnError} constructs that will unwrap unrecognized Cloud Foundry Exceptions
* and log the text payload.
*/
private Consumer<Throwable> logError(String msg) {
return e -> {
if (e instanceof UnknownCloudFoundryException) {
logger.error(msg + "\nUnknownCloudFoundryException encountered, whose payload follows:\n"
+ ((UnknownCloudFoundryException)e).getPayload(), e);
} else {
logger.error(msg, e);
}
};
}
}

View File

@@ -0,0 +1,320 @@
/*
* 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.cloudfoundry;
import org.cloudfoundry.operations.applications.ApplicationHealthCheck;
import org.springframework.beans.factory.annotation.Value;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;
/**
* Holds configuration properties for specifying what resources and services an app deployed to a Cloud Foundry runtime
* will get.
*
* @author Eric Bottard
* @author Greg Turnquist
* @author Ilayaperumal Gopinathan
*/
public class CloudFoundryDeploymentProperties {
public static final String CLOUDFOUNDRY_PROPERTIES = "spring.cloud.deployer.cloudfoundry";
public static final String HEALTHCHECK_PROPERTY_KEY = CLOUDFOUNDRY_PROPERTIES + ".health-check";
public static final String HEALTHCHECK_HTTP_ENDPOINT_PROPERTY_KEY = CLOUDFOUNDRY_PROPERTIES + ".health-check-http-endpoint";
public static final String HEALTHCHECK_TIMEOUT_PROPERTY_KEY = CLOUDFOUNDRY_PROPERTIES + ".health-check-timeout";
public static final String ROUTE_PATH_PROPERTY = CLOUDFOUNDRY_PROPERTIES + ".route-path";
public static final String ROUTE_PROPERTY = CLOUDFOUNDRY_PROPERTIES + ".route";
public static final String ROUTES_PROPERTY = CLOUDFOUNDRY_PROPERTIES + ".routes";
public static final String NO_ROUTE_PROPERTY = CLOUDFOUNDRY_PROPERTIES + ".no-route";
public static final String HOST_PROPERTY = CLOUDFOUNDRY_PROPERTIES + ".host";
public static final String DOMAIN_PROPERTY = CLOUDFOUNDRY_PROPERTIES + ".domain";
public static final String BUILDPACK_PROPERTY_KEY = CLOUDFOUNDRY_PROPERTIES + ".buildpack";
public static final String JAVA_OPTS_PROPERTY_KEY = CLOUDFOUNDRY_PROPERTIES + ".javaOpts";
public static final String USE_SPRING_APPLICATION_JSON_KEY = CLOUDFOUNDRY_PROPERTIES + ".use-spring-application-json";
/**
* The names of services to bind to all applications deployed as a module.
* This should typically contain a service capable of playing the role of a binding transport.
*/
private Set<String> services = new HashSet<>();
/**
* The host name to use as part of the route. Defaults to hostname derived by Cloud Foundry.
*/
private String host = null;
/**
* The domain to use when mapping routes for applications.
*/
private String domain;
/**
* The routes that the application should be bound to.
* Mutually exclusive with host and domain.
*/
private Set<String> routes = new HashSet<>();
/**
* The buildpack to use for deploying the application.
*/
private String buildpack = "https://github.com/cloudfoundry/java-buildpack.git#v4.7.1";
/**
* The amount of memory to allocate, if not overridden per-app. Default unit is mebibytes, 'M' and 'G" suffixes supported.
*/
private String memory = "1024m";
/**
* 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";
/**
* The type of health check to perform on deployed application, if not overridden per-app. Defaults to PORT
*/
private ApplicationHealthCheck healthCheck = ApplicationHealthCheck.PORT;
/**
* The path that the http health check will use, defaults to @{code /health}
*/
private String healthCheckHttpEndpoint = "/health";
/**
* The timeout value for health checks in seconds. Defaults to 120 seconds.
*/
private String healthCheckTimeout = "120";
/**
* The number of instances to run.
*/
private int instances = 1;
/**
* Flag to enable prefixing the app name with a random prefix.
*/
private boolean enableRandomAppNamePrefix = true;
/**
* Timeout for blocking API calls, in seconds.
*/
private long apiTimeout = 360L;
/**
* Timeout for name API operations in milliseconds
*/
private long statusTimeout = 5_000L;
/**
* Flag to indicate whether application properties are fed into SPRING_APPLICATION_JSON or ENVIRONMENT VARIABLES.
*/
private boolean useSpringApplicationJson = true;
/**
* If set, override the timeout allocated for staging the app by the client.
*/
private Duration stagingTimeout = Duration.ofMinutes(15L);
/**
* If set, override the timeout allocated for starting the app by the client.
*/
private Duration startupTimeout = Duration.ofMinutes(5L);
/**
* String to use as prefix for name of deployed app. Defaults to spring.application.name.
*/
@Value("${spring.application.name:}")
private String appNamePrefix;
/**
* Whether to also delete routes when un-deploying an application.
*/
private boolean deleteRoutes = true;
private String javaOpts;
public Set<String> getServices() {
return services;
}
public void setServices(Set<String> services) {
this.services = services;
}
public String getBuildpack() {
return buildpack;
}
public void setBuildpack(String buildpack) {
this.buildpack = buildpack;
}
public String getMemory() {
return memory;
}
public void setMemory(String memory) {
this.memory = memory;
}
public String getDisk() {
return disk;
}
public void setDisk(String disk) {
this.disk = disk;
}
public int getInstances() {
return instances;
}
public void setInstances(int instances) {
this.instances = instances;
}
public boolean isEnableRandomAppNamePrefix() {
return enableRandomAppNamePrefix;
}
public void setEnableRandomAppNamePrefix(boolean enableRandomAppNamePrefix) {
this.enableRandomAppNamePrefix = enableRandomAppNamePrefix;
}
public String getAppNamePrefix() {
return appNamePrefix;
}
public void setAppNamePrefix(String appNamePrefix) {
this.appNamePrefix = appNamePrefix;
}
public long getApiTimeout() {
return apiTimeout;
}
public void setApiTimeout(long apiTimeout) {
this.apiTimeout = apiTimeout;
}
public boolean isUseSpringApplicationJson() {
return useSpringApplicationJson;
}
public void setUseSpringApplicationJson(boolean useSpringApplicationJson) {
this.useSpringApplicationJson = useSpringApplicationJson;
}
public ApplicationHealthCheck getHealthCheck() {
return healthCheck;
}
public void setHealthCheck(ApplicationHealthCheck healthCheck) {
this.healthCheck = healthCheck;
}
public String getHealthCheckHttpEndpoint() {
return healthCheckHttpEndpoint;
}
public void setHealthCheckHttpEndpoint(String healthCheckHttpEndpoint) {
this.healthCheckHttpEndpoint = healthCheckHttpEndpoint;
}
public String getHealthCheckTimeout() {
return healthCheckTimeout;
}
public void setHealthCheckTimeout(String healthCheckTimeout) {
this.healthCheckTimeout = healthCheckTimeout;
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public Set<String> getRoutes() {
return routes;
}
public void setRoutes(Set<String> routes) {
this.routes = routes;
}
public Duration getStagingTimeout() {
return stagingTimeout;
}
public void setStagingTimeout(Duration stagingTimeout) {
this.stagingTimeout = stagingTimeout;
}
public Duration getStartupTimeout() {
return startupTimeout;
}
public void setStartupTimeout(Duration startupTimeout) {
this.startupTimeout = startupTimeout;
}
public long getStatusTimeout() {
return statusTimeout;
}
public void setStatusTimeout(long statusTimeout) {
this.statusTimeout = statusTimeout;
}
public boolean isDeleteRoutes() {
return deleteRoutes;
}
public void setDeleteRoutes(boolean deleteRoutes) {
this.deleteRoutes = deleteRoutes;
}
public String getJavaOpts() {
return javaOpts;
}
public void setJavaOpts(String javaOpts) {
this.javaOpts = javaOpts;
}
}

View File

@@ -1,28 +0,0 @@
/*
* 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.cloudfoundry;
import org.springframework.cloud.deployer.spi.cloudfoundry.AppNameGenerator;
public class NoOpAppNameGenerator implements AppNameGenerator {
@Override
public String generateAppName(String appName) {
return appName;
}
}

View File

@@ -33,7 +33,6 @@ import reactor.test.StepVerifier;
import org.springframework.cloud.appbroker.deployer.AppDeployer;
import org.springframework.cloud.appbroker.deployer.DeployApplicationRequest;
import org.springframework.cloud.deployer.spi.cloudfoundry.CloudFoundryDeploymentProperties;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.ResourceLoader;
@@ -64,7 +63,6 @@ class CloudFoundryAppDeployerTest {
@BeforeEach
void setUp() {
NoOpAppNameGenerator applicationNameGenerator = new NoOpAppNameGenerator();
CloudFoundryDeploymentProperties deploymentProperties = new CloudFoundryDeploymentProperties();
deploymentProperties.setDisk(APP_MEMORY_STRING);
@@ -72,8 +70,8 @@ class CloudFoundryAppDeployerTest {
when(cloudFoundryOperations.applications()).thenReturn(applications);
when(resourceLoader.getResource(APP_PATH)).thenReturn(new FileSystemResource(APP_PATH));
appDeployer = new CloudFoundryAppDeployer(applicationNameGenerator,
deploymentProperties, cloudFoundryOperations, null, resourceLoader);
appDeployer = new CloudFoundryAppDeployer(
deploymentProperties, cloudFoundryOperations, resourceLoader);
}
@Test

View File

@@ -1,33 +0,0 @@
/*
* 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.cloudfoundry;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.deployer.spi.cloudfoundry.AppNameGenerator;
import static org.assertj.core.api.Assertions.assertThat;
class NoOpAppNameGeneratorTest {
private AppNameGenerator appNameGenerator = new NoOpAppNameGenerator();
@Test
void shouldNoOp() {
assertThat(appNameGenerator.generateAppName("app-name")).isEqualTo("app-name");
}
}