/*******************************************************************************
* Copyright (c) 2015, 2017 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cloudfoundry;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFCloudDomain;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFStack;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.v2.CFRoute;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.deployment.CloudApplicationDeploymentProperties;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.deployment.DeploymentProperties;
import org.springframework.ide.eclipse.boot.util.Log;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.Yaml;
import com.google.common.collect.ImmutableList;
/**
* Reads and creates manifest.yml content from a specific location relative to
* an {@link IProject}.
*
*/
public class ApplicationManifestHandler {
public static final String RANDOM_VAR = "${random}"; //$NON-NLS-1$
public static final String APPLICATIONS_PROP = "applications";
public static final String NAME_PROP = "name";
public static final String MEMORY_PROP = "memory";
public static final String INSTANCES_PROP = "instances";
public static final String SUB_DOMAIN_PROP = "host";
public static final String SUB_DOMAINS_PROP = "hosts";
public static final String DOMAIN_PROP = "domain";
public static final String DOMAINS_PROP = "domains";
public static final String NO_ROUTE_PROP = "no-route";
public static final String NO_HOSTNAME_PROP = "no-hostname";
public static final String RANDOM_ROUTE_PROP = "random-route";
public static final String SERVICES_PROP = "services";
public static final String LABEL_PROP = "label";
public static final String PROVIDER_PROP = "provider";
public static final String VERSION_PROP = "version";
public static final String PLAN_PROP = "plan";
public static final String PATH_PROP = "path";
public static final String BUILDPACK_PROP = "buildpack";
public static final String ENV_PROP = "env";
public static final String DISK_QUOTA_PROP = "disk_quota";
public static final String INHERIT_PROP = "inherit";
public static final String TIMEOUT_PROP = "timeout";
public static final String HEALTH_CHECK_TYPE_PROP = "health-check-type";
public static final String COMMAND_PROP = "command";
public static final String STACK_PROP = "stack";
public static final String ROUTES_PROP = "routes";
public static final String ROUTE_PROP = "route";
private final IProject project;
private final IFile manifestFile;
private final Map<String, Object> cloudData;
public ApplicationManifestHandler(IProject project, Map<String, Object> cloudData) {
this(project, cloudData, null);
}
public ApplicationManifestHandler(IProject project, Map<String, Object> cloudData, IFile manifestFile) {
this.project = project;
this.manifestFile = manifestFile;
this.cloudData = cloudData;
}
@SuppressWarnings("unchecked")
public static List<CFCloudDomain> getCloudDomains(Map<String, Object> cloudData) {
Object obj = cloudData == null ? null : cloudData.get(DOMAINS_PROP);
return obj instanceof List ? (List<CFCloudDomain>) obj : Collections.<CFCloudDomain>emptyList();
}
@SuppressWarnings("unchecked")
public static List<CFStack> getCloudStacks(Map<String, Object> cloudData) {
Object obj = cloudData == null ? null : cloudData.get(STACK_PROP);
return obj instanceof List ? (List<CFStack>) obj : Collections.<CFStack>emptyList();
}
public static String getDefaultBuildpack(Map<String, Object> cloudData) {
return getDefaultValue(cloudData, BUILDPACK_PROP, String.class);
}
@SuppressWarnings("unchecked")
public static <T> T getDefaultValue(Map<String, Object> cloudData, String key, Class<T> type) {
Object obj = cloudData == null ? null : cloudData.get(key);
return obj != null && type.isAssignableFrom(obj.getClass()) ? (T) obj : null;
}
protected InputStream getInputStream() throws Exception {
File file = getManifestFile();
if (file != null && file.exists()) {
return new FileInputStream(file);
} else {
throw ExceptionUtil.coreException("No manifest.yml file found in project: " + project.getName());
}
}
/**
*
* @return manifest file if it exists. Null otherwise
*/
public File getManifestFile() {
if (manifestFile != null) {
URI locationURI = manifestFile.getLocationURI();
if (locationURI != null) {
File file = new File(locationURI);
return file.exists() ? file : null;
}
}
return null;
}
public boolean hasManifest() {
File file = getManifestFile();
return file != null && file.exists();
}
/**
*
* @param applicationName
* name of application to lookup in the manifest file.
* @param propertyName
* String value property to retrieve from manifest for given
* application entry.
* @return Value of property, or null if not found, or entry for application
* in manifest does not exist.
*/
public String getApplicationProperty(String appName, String propertyName, IProgressMonitor monitor) {
try {
Map<?, ?> appMap = getApplicationMap(appName, monitor);
if (appMap != null) {
return getValue(appMap, propertyName, String.class);
}
} catch (Exception e) {
Log.log(e);
}
return null;
}
public Map<?, ?> getApplicationMap(String appName, IProgressMonitor monitor) throws Exception {
Map<Object, Object> allResults = parseManifestFromFile();
List<Map<?, ?>> appMaps = getApplications(allResults);
for (Map<?, ?> appMap : appMaps) {
String existingAppName = getValue(appMap, NAME_PROP, String.class);
if (existingAppName != null && existingAppName.equals(appName)) {
return appMap;
}
}
return null;
}
/**
*
* @param containerMap
* @param propertyName
* @return map of values for the given property name, or null if it cannot
* be resolved
*/
@SuppressWarnings("unchecked")
protected Map<?, ?> getContainingPropertiesMap(Map<?, ?> containerMap, String propertyName) {
if (containerMap == null || propertyName == null) {
return null;
}
Object yamlElementObj = containerMap.get(propertyName);
if (yamlElementObj instanceof Map) {
return (Map<Object, Object>) yamlElementObj;
} else {
return null;
}
}
// protected String getStringValue(Map<?, ?> containingMap, String propertyName) {
//
// if (containingMap == null) {
// return null;
// }
//
// Object valObj = containingMap.get(propertyName);
//
// if (valObj instanceof String) {
// return (String) valObj;
// }
// return null;
// }
//
@SuppressWarnings("unchecked")
protected static <T> T getValue(Map<?, ?> containingMap, String propertyName, Class<T> type) {
if (containingMap == null) {
return null;
}
Object valObj = containingMap.get(propertyName);
if (valObj != null && type.isAssignableFrom(valObj.getClass())) {
return (T) valObj;
}
return null;
}
protected static boolean hasValue(Map<?, ?> containingMap, String propertyName) {
return containingMap != null && containingMap.containsKey(propertyName);
}
public static List<Map<?, ?>> getApplications(Map<?, ?> results) throws CoreException {
Object applicationsObj = results.get(APPLICATIONS_PROP);
if (!(applicationsObj instanceof List<?>)) {
return null;
}
List<?> applicationsList = (List<?>) applicationsObj;
List<Map<?, ?>> applications = new ArrayList<>();
// Use only the first application entry
if (!applicationsList.isEmpty()) {
for (Object val : applicationsList) {
if (val instanceof Map<?, ?>) {
applications.add((Map<?, ?>) val);
}
}
}
return applications;
}
protected CloudApplicationDeploymentProperties getDeploymentProperties(Map<?, ?> appMap,
Map<Object, Object> allResults, IProgressMonitor monitor) throws Exception {
CloudApplicationDeploymentProperties properties = new CloudApplicationDeploymentProperties();
String appName = getValue(appMap, NAME_PROP, String.class);
properties.setAppName(appName);
properties.setProject(project);
properties.setManifestFile(manifestFile);
readMemory(appMap, allResults, properties);
readDiskQuota(appMap, allResults, properties);
readApplicationURL(appMap, allResults, properties);
readBuildpack(appMap, allResults, properties);
readEnvVars(appMap, allResults, properties);
readServices(appMap, allResults, properties);
readInstances(appMap, allResults, properties);
readTimeout(appMap, allResults, properties);
readHealthCheckType(appMap, allResults, properties);
readCommand(appMap, allResults, properties);
readStack(appMap, allResults, properties);
return properties;
}
public List<CloudApplicationDeploymentProperties> load(IProgressMonitor monitor) throws Exception {
SubMonitor subMonitor = SubMonitor.convert(monitor);
subMonitor.beginTask("Loading manifest.yml", 6);
try {
Map<Object, Object> allResults = parseManifestFromFile();
if (allResults == null || allResults.isEmpty()) {
throw ExceptionUtil
.coreException("No content found in manifest.yml. Make sure the manifest is valid.");
}
List<Map<?, ?>> appMaps = getApplications(allResults);
List<CloudApplicationDeploymentProperties> properties = new ArrayList<>();
if (appMaps == null) {
CloudApplicationDeploymentProperties props = getDeploymentProperties(allResults, allResults, subMonitor);
if (props != null) {
properties.add(props);
}
} else {
for (Map<?, ?> app : appMaps) {
CloudApplicationDeploymentProperties props = getDeploymentProperties(app, allResults, subMonitor);
if (props != null) {
properties.add(props);
}
}
}
return properties;
} finally {
subMonitor.done();
}
}
/**
* Creates a new manifest.yml file. If one already exists, the existing one
* will not be replaced.
*
* @return true if new file created with content. False otherwise
* @throws Exception
* if error occurred during file creation or serialising
* manifest content
*/
public boolean create(IProgressMonitor monitor, CloudApplicationDeploymentProperties properties) throws Exception {
if (properties == null) {
return false;
}
File file = getManifestFile();
if (file != null) {
Log.warn(
"Manifest.yml file already found at: " + manifestFile.getFullPath() + ". New content will not be written.");
return false;
}
Map<Object, Object> deploymentInfoYaml = toYaml(properties, cloudData);
DumperOptions options = new DumperOptions();
options.setExplicitStart(true);
options.setCanonical(false);
options.setPrettyFlow(true);
options.setDefaultFlowStyle(FlowStyle.BLOCK);
Yaml yaml = new Yaml(options);
String manifestValue = yaml.dump(deploymentInfoYaml);
if (manifestValue == null) {
throw ExceptionUtil.coreException("Failed to generate manifesty.yml for: " + properties.getAppName()
+ " Unknown problem trying to serialise content of manifest into: " + deploymentInfoYaml);
}
createFile(project, manifestFile, manifestValue, monitor);
return true;
}
public static Map<Object, Object> toYaml(DeploymentProperties properties, Map<String, Object> cloudData) {
return toYaml(properties, cloudData, false);
}
@SuppressWarnings("unchecked")
public static Map<Object, Object> toYaml(DeploymentProperties properties, Map<String, Object> cloudData, boolean legacyHostDomain) {
Map<Object, Object> deploymentInfoYaml = new LinkedHashMap<>();
Object applicationsObj = deploymentInfoYaml.get(APPLICATIONS_PROP);
List<Map<Object, Object>> applicationsList = null;
if (applicationsObj == null) {
applicationsList = new ArrayList<>();
deploymentInfoYaml.put(APPLICATIONS_PROP, applicationsList);
} else if (applicationsObj instanceof List<?>) {
applicationsList = (List<Map<Object, Object>>) applicationsObj;
}
Map<Object, Object> application = new LinkedHashMap<>();
applicationsList.add(application);
application.put(NAME_PROP, properties.getAppName());
String memory = getMemoryAsString(properties.getMemory());
if (memory != null) {
application.put(MEMORY_PROP, memory);
}
String diskQuota = getMemoryAsString(properties.getDiskQuota());
if (diskQuota != null && properties.getDiskQuota() != DeploymentProperties.DEFAULT_MEMORY) {
application.put(DISK_QUOTA_PROP, diskQuota);
}
if (properties.getInstances() != DeploymentProperties.DEFAULT_INSTANCES) {
application.put(ApplicationManifestHandler.INSTANCES_PROP, properties.getInstances());
}
if (properties.getTimeout() != null) {
application.put(ApplicationManifestHandler.TIMEOUT_PROP, properties.getTimeout());
}
String healthCheck = properties.getHealthCheckType();
if (healthCheck != null) {
application.put(ApplicationManifestHandler.HEALTH_CHECK_TYPE_PROP, healthCheck);
}
if (properties.getCommand() != null) {
application.put(ApplicationManifestHandler.COMMAND_PROP, properties.getCommand());
}
if (properties.getStack() != null) {
application.put(ApplicationManifestHandler.STACK_PROP, properties.getStack());
}
if (properties.getServices() != null && !properties.getServices().isEmpty()) {
application.put(SERVICES_PROP, properties.getServices());
}
if (properties.getEnvironmentVariables() != null && !properties.getEnvironmentVariables().isEmpty()) {
application.put(ENV_PROP, properties.getEnvironmentVariables());
}
if (properties.getBuildpack() != null) {
application.put(ApplicationManifestHandler.BUILDPACK_PROP, properties.getBuildpack());
}
if (legacyHostDomain) {
Set<String> hosts = new LinkedHashSet<>();
Set<String> domains = new LinkedHashSet<>();
List<CFCloudDomain> cloudDomains = getCloudDomains(cloudData);
extractHostsAndDomains(properties.getUris(), cloudDomains, hosts, domains);
for (String uri : properties.getUris()) {
try {
// Find the first valid URL
CFRoute route = CFRoute.builder().from(uri, cloudDomains).build();
if (route.getHost() != null) {
hosts.add(route.getHost());
}
if (route.getDomain() != null) {
domains.add(route.getDomain());
}
} catch (Exception e) {
// ignore
}
}
if (hosts.isEmpty() && domains.isEmpty()) {
application.put(NO_ROUTE_PROP, true);
} else {
if (hosts.isEmpty()) {
application.put(NO_HOSTNAME_PROP, true);
} else if (hosts.size() == 1) {
String host = hosts.iterator().next();
if (!properties.getAppName().equals(host)) {
application.put(SUB_DOMAIN_PROP, host);
}
} else {
application.put(SUB_DOMAINS_PROP, new ArrayList<>(hosts));
}
if (domains.size() == 1) {
application.put(DOMAIN_PROP, domains.iterator().next());
} else if (domains.size() > 1) {
application.put(DOMAINS_PROP, new ArrayList<>(domains));
}
}
} else {
Set<String> uris = properties.getUris();
if (uris == null || uris.isEmpty()) {
application.put(NO_ROUTE_PROP, true);
} else {
application.put(ApplicationManifestHandler.ROUTES_PROP, uris.stream().map(uri -> {
Map<Object, Object> route = new LinkedHashMap<>();
route.put(ApplicationManifestHandler.ROUTE_PROP, uri);
return route;
}).collect(Collectors.toList()));
}
}
return deploymentInfoYaml;
}
public static void extractHostsAndDomains(Collection<String> uris, List<CFCloudDomain> cloudDomains, Set<String> hostsSet, Set<String> domainsSet) {
for (String uri : uris) {
try {
// Find the first valid URL
CFRoute route = CFRoute.builder().from(uri, cloudDomains).build();
if (route.getHost() != null) {
hostsSet.add(route.getHost());
}
if (route.getDomain() != null) {
domainsSet.add(route.getDomain());
}
} catch (Exception e) {
// ignore
}
}
}
public void createFile(IProject project, IFile file, String data, IProgressMonitor monitor) throws CoreException {
file.create(new ByteArrayInputStream(data.getBytes()), true, monitor);
project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
}
protected void readEnvVars(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties) {
Map<Object, Object> propertiesMap = new LinkedHashMap<>();
Map<?, ?> map = getContainingPropertiesMap(allResults, ENV_PROP);
if (map != null) {
propertiesMap.putAll(map);
}
map = getContainingPropertiesMap(application, ENV_PROP);
if (map != null) {
propertiesMap.putAll(map);
}
if (propertiesMap.isEmpty()) {
return;
}
Map<String, String> loadedVars = new HashMap<>();
for (Entry<?, ?> entry : propertiesMap.entrySet()) {
if ((entry.getKey() instanceof String)) {
String varName = (String) entry.getKey();
String varValue = null;
if (entry.getValue() instanceof String) {
varValue = (String) entry.getValue();
} else if (entry.getValue() instanceof Integer) {
varValue = Integer.toString((Integer) entry.getValue());
}
if (varName != null && varValue != null) {
loadedVars.put(varName, varValue);
}
}
}
properties.setEnvironmentVariables(loadedVars);
}
protected void readServices(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties) {
Object yamlElementObj = allResults.get(SERVICES_PROP);
List<String> cloudServices = new ArrayList<>();
if (yamlElementObj instanceof List<?>) {
addServices((List<?>) yamlElementObj, cloudServices);
}
yamlElementObj = application.get(SERVICES_PROP);
if (yamlElementObj instanceof List<?>) {
addServices((List<?>) yamlElementObj, cloudServices);
}
properties.setServices(cloudServices);
}
protected void addServices(List<?> servicesToAdd, List<String> cloudServices) {
for (Object servNameObj : servicesToAdd) {
if (servNameObj instanceof String && !cloudServices.contains(servNameObj)) {
String serviceName = (String) servNameObj;
cloudServices.add(serviceName);
}
}
}
protected void readInstances(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties) {
Integer instances = getValue(application, INSTANCES_PROP, Integer.class);
if (instances == null) {
instances = getValue(allResults, INSTANCES_PROP, Integer.class);
}
if (instances != null) {
properties.setInstances(instances);
}
}
protected void readTimeout(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties) {
Integer timeout = getValue(application, TIMEOUT_PROP, Integer.class);
if (timeout == null) {
timeout = getValue(allResults, TIMEOUT_PROP, Integer.class);
}
if (timeout != null) {
properties.setTimeout(timeout);
}
}
private void readHealthCheckType(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties) {
String hc = getValue(application, HEALTH_CHECK_TYPE_PROP, String.class);
if (hc == null) {
hc = getValue(allResults, HEALTH_CHECK_TYPE_PROP, String.class);
}
if (hc != null) {
properties.setHealthCheckType(hc);
}
}
protected void readBuildpack(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties) {
String buildpack = getValue(application, BUILDPACK_PROP, String.class);
if (buildpack == null) {
buildpack = getValue(allResults, BUILDPACK_PROP, String.class);
}
if (buildpack != null) {
properties.setBuildpack(buildpack);
}
}
protected void readCommand(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties) {
String command = getValue(application, COMMAND_PROP, String.class);
if (command == null) {
command = getValue(allResults, COMMAND_PROP, String.class);
}
if (command != null) {
properties.setCommand(command);
}
}
protected void readStack(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties) {
String stack = getValue(application, STACK_PROP, String.class);
if (stack == null) {
stack = getValue(allResults, STACK_PROP, String.class);
}
if (stack != null && isStackValid(stack, getCloudStacks(cloudData))) {
properties.setStack(stack);
}
}
private boolean noRoute(Map<?, ?> application, Map<Object, Object> allResults) {
Boolean noRoute = getValue(application, NO_ROUTE_PROP, Boolean.class);
if (noRoute == null) {
noRoute = getValue(allResults, NO_ROUTE_PROP, Boolean.class);
}
return Boolean.TRUE.equals(noRoute);
}
/**
*
* @param application
* @param allResults
* @param properties
* @param randomRoute true if random host should be generated
* @return non-null list of URIs parsed from domains and hosts. May be empty
*/
@SuppressWarnings("unchecked")
private List<String> fromDomainsAndHosts(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties, boolean randomRoute) {
List<CFCloudDomain> domains = getCloudDomains(cloudData);
HashSet<String> hostsSet = new LinkedHashSet<>();
HashSet<String> domainsSet = new LinkedHashSet<>();
/*
* Gather domains from app node and root node from 'domain' and 'domains' attributes
*/
String domain = getValue(application, DOMAIN_PROP, String.class);
if (domain == null) {
domain = getValue(allResults, DOMAIN_PROP, String.class);
}
if (domain != null && isDomainValid(domain, domains)) {
domainsSet.add(domain);
}
List<String> domainList = (List<String>) getValue(allResults, DOMAINS_PROP, List.class);
if (domainList != null) {
for (String d : domainList) {
if (isDomainValid(d, domains)) {
domainsSet.add(d);
}
}
}
domainList = (List<String>) getValue(application, DOMAINS_PROP, List.class);
if (domainList != null) {
for (String d : domainList) {
if (isDomainValid(d, domains)) {
domainsSet.add(d);
}
}
}
/*
* Gather domains from app node and root node from 'host' and 'hosts'
* attributes. Account for ${random} in host's name
*/
String host = getValue(application, SUB_DOMAIN_PROP, String.class);
if (host == null) {
host = getValue(allResults, SUB_DOMAIN_PROP, String.class);
}
if (host != null) {
hostsSet.add(host);
}
List<String> hostList = (List<String>) getValue(allResults, SUB_DOMAINS_PROP, List.class);
if (hostList != null) {
hostsSet.addAll(hostList);
}
hostList = (List<String>) getValue(application, SUB_DOMAINS_PROP, List.class);
if (hostList != null) {
hostsSet.addAll(hostList);
}
/*
* If no host names found check for "random-route: true" and
* "no-hostname: true" otherwise take app name as the host name
*/
if (hostsSet.isEmpty()) {
if (randomRoute) {
hostsSet.add(extractHost("${random}", 10));
domainsSet.clear();
domainsSet.add(domains.get(0).getName());
} else {
Boolean noHostName = getValue(application, NO_HOSTNAME_PROP, Boolean.class);
if (noHostName == null) {
noHostName = getValue(allResults, NO_HOSTNAME_PROP, Boolean.class);
}
if (!Boolean.TRUE.equals(noHostName)) {
/*
* Assumes name is set before URIs are processed
*/
hostsSet.add(properties.getAppName());
}
}
}
/*
* Set a domain if they are still empty
*/
if (domainsSet.isEmpty()) {
domainsSet.add(domains.get(0).getName());
}
/*
* Compose URIs for application based on hosts and domains
*/
List<String> uris = new ArrayList<>(hostsSet.isEmpty() ? 1 : hostsSet.size() * domainsSet.size());
for (String d : domainsSet) {
if (hostsSet.isEmpty()) {
uris.add(CFRoute.builder().domain(d).build().getRoute());
} else {
for (String h : hostsSet) {
uris.add(CFRoute.builder().host(h).domain(d).build().getRoute());
}
}
}
return uris;
}
protected void readApplicationURL(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties) {
Boolean randomRoute = getValue(application, RANDOM_ROUTE_PROP, Boolean.class);
if (randomRoute == null) {
randomRoute = getValue(allResults, RANDOM_ROUTE_PROP, Boolean.class);
}
boolean useRandomRoute = Boolean.TRUE.equals(randomRoute);
properties.setRandomRoute(useRandomRoute);
/*
* Check for "no-route: true". If set then uris list should be empty
*/
if (!noRoute(application, allResults)) {
// Manifest documentation states:
// "The routes attribute cannot be used in conjunction with the
// following
// attributes: host, hosts, domain, domains, and no-hostname.
// An error will result."
// If only routes are available, then only parse routes. Do NOT
// also create a default URI from domain and app name if routes are available.
// This appears to be consistent with cf CLI behaviour as well.
// Otherwise fall back to parsing from domains and hosts
List<String> uris = null;
if (hasRoutesProperty(application, allResults)) {
uris = fromRoutesProperty(application, allResults, properties);
} else {
uris = fromDomainsAndHosts(application, allResults, properties, useRandomRoute);
}
properties.setUris(uris);
}
}
private List<String> fromRoutesProperty(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties) {
Set<String> uris = new LinkedHashSet<>();
List<CFCloudDomain> domains = getCloudDomains(cloudData);
List<?> routes = getValue(application, ROUTES_PROP, List.class);
List<?> rootRoutes = getValue(allResults, ROUTES_PROP, List.class);
if (routes != null || rootRoutes != null) {
routes = routes == null ? Collections.emptyList() : routes;
rootRoutes = rootRoutes == null ? Collections.emptyList() : rootRoutes;
return ImmutableList.copyOf(Stream.concat(routes.stream(), rootRoutes.stream())
.filter(o -> o instanceof Map<?,?>)
.map(o -> (Map<?,?>) o)
.map(routeMap -> routeMap.get(ROUTE_PROP))
.filter(Objects::nonNull)
.filter(route -> route instanceof String)
.map(route -> {
String url = (String) route;
CFRoute rt = CFRoute.builder().from(url, domains).build();
return rt.getRoute();
})
.collect(Collectors.toSet()));
}
return ImmutableList.copyOf(uris);
}
protected boolean hasRoutesProperty(Map<?, ?> application, Map<Object, Object> allResults) {
return hasValue(application, ROUTES_PROP) || hasValue(allResults, ROUTES_PROP);
}
public static boolean isDomainValid(String domain, List<CFCloudDomain> domains) {
for (CFCloudDomain cloudDomain : domains) {
if (cloudDomain.getName().equals(domain)) {
return true;
}
}
return false;
}
public static boolean isStackValid(String stack, List<CFStack> stacks) {
for (CFStack cloudStack : stacks) {
if (cloudStack.getName().equals(stack)) {
return true;
}
}
return false;
}
private String extractHost(String subdomain, int length) {
// Check for random word
int varIndex = subdomain.indexOf(RANDOM_VAR);
while (varIndex >= 0) {
String randomWord = RandomStringUtils.randomAlphabetic(length);
subdomain = subdomain.replace(subdomain.substring(varIndex, RANDOM_VAR.length()), randomWord);
varIndex = subdomain.indexOf(RANDOM_VAR);
}
return subdomain;
}
protected void readMemory(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties) throws Exception {
int memoryValue = readMemoryValue(application, allResults, MEMORY_PROP);
if (memoryValue >= 0) {
properties.setMemory(memoryValue);
}
}
protected void readDiskQuota(Map<?, ?> application, Map<Object, Object> allResults,
CloudApplicationDeploymentProperties properties) throws Exception {
int memoryValue = readMemoryValue(application, allResults, DISK_QUOTA_PROP);
if (memoryValue >= 0) {
properties.setDiskQuota(memoryValue);
}
}
protected int readMemoryValue(Map<?, ?> application, Map<Object, Object> allResults,
String propertyKey) throws Exception {
// Check if value in integer form
Integer memoryVal = getValue(application, propertyKey, Integer.class);
if (memoryVal != null) {
return memoryVal.intValue();
}
// If not in Integer form, try String as the memory may end in with a 'G' or 'M'
String memoryStringVal = getValue(application, propertyKey, String.class);
if (memoryStringVal == null) {
// Check if there is memory property set for all apps if nothing set for the app specifically
memoryVal = getValue(allResults, propertyKey, Integer.class);
if (memoryVal == null) {
// Not in integer form
memoryStringVal = getValue(allResults, propertyKey, String.class);
} else {
// Integer form? Return the value right away
return memoryVal.intValue();
}
}
// Should only get here if memory value not in integer form
if (memoryStringVal != null && memoryStringVal.length() > 0) {
// Parse non-integer memory value
return convertMemory(memoryStringVal);
}
// No memory property specified? Assume the default
return DeploymentProperties.DEFAULT_MEMORY;
}
public static int convertMemory(String memoryStringVal) throws CoreException {
String memoryIndicator[] = { "m", "g", "mb", "gb" };
int gIndex = -1;
boolean gb = false;
for (String indicator : memoryIndicator) {
int beginIndex = memoryStringVal.length() - indicator.length();
if (beginIndex >= 0) {
if (indicator.equalsIgnoreCase(memoryStringVal.substring(beginIndex))) {
gIndex = beginIndex;
gb = indicator.charAt(0) == 'g';
break;
}
}
}
// There has to be a number before the 'G' or 'M', if 'G' or 'M'
// is used, or its not a valid
// memory
if (gIndex > 0) {
memoryStringVal = memoryStringVal.substring(0, gIndex);
} else if (gIndex == 0) {
throw ExceptionUtil.coreException("Failed to read memory value. Invalid memory: " + memoryStringVal);
}
try {
return Integer.valueOf(memoryStringVal) * (gb ? 1024 : 1);
} catch (NumberFormatException e) {
throw ExceptionUtil.coreException("Failed to parse memory due to: " + e.getMessage());
}
}
/**
*
* @return map of parsed manifest file, if the file exists. If the file does
* not exist, return null.
* @throws CoreException
* if manifest file exists, but error occurred that prevents a
* map to be generated.
*/
@SuppressWarnings("unchecked")
protected Map<Object, Object> parseManifestFromFile() throws Exception {
InputStream inputStream = getInputStream();
if (inputStream != null) {
Yaml yaml = new Yaml();
try {
Object results = yaml.load(inputStream);
if (results instanceof Map) {
return (Map<Object, Object>) results;
} else {
String source = manifestFile == null ? "entered manifest" : "file " + manifestFile.getFullPath();
throw ExceptionUtil.coreException("Expected a map of values for "
+ source + ". Unable to load manifest content. Actual results: " + results);
}
} finally {
try {
inputStream.close();
} catch (IOException e) {
// Ignore
}
}
}
return null;
}
static protected String getMemoryAsString(int memory) {
if (memory < 1) {
return null;
}
return memory + "M";
}
}