package org.cloudifysource.esc.installer; /******************************************************************************* * Copyright (c) 2012 GigaSpaces Technologies Ltd. All rights reserved * * 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. *******************************************************************************/ import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.cloudifysource.domain.cloud.ScriptLanguages; import org.cloudifysource.dsl.internal.CloudifyConstants; import org.cloudifysource.dsl.utils.IPUtils; /******* * A simple wrapper around a StringBuilder. Used to generate the contents of the cloudify environment file that is * injected into the remote cloudify installation * * @author dank, barakme * @since 2.5.0 * */ public class EnvironmentFileBuilder { private static final String CYGDRIVE = "/cygdrive/"; private static final java.util.logging.Logger logger = java.util.logging.Logger .getLogger(EnvironmentFileBuilder.class.getName()); private static final String CIFS_ABSOLUTE_PATH_WITH_DRIVE_REGEX = "/[a-zA-Z][$]/.*"; private static Pattern pattern; private final StringBuilder sb = new StringBuilder(); private String newline; private final ScriptLanguages scriptLanguage; private final Map<String, String> externalEnvVars; private final Set<String> appendedExternalEnvVars = new HashSet<String>(); private static final String GSA_MODE_ENV = "GSA_MODE"; private static final String NO_WEB_SERVICES_ENV = "NO_WEB_SERVICES"; private static final String NO_MANAGEMENT_SPACE_ENV = "NO_MANAGEMENT_SPACE"; private static final String NO_MANAGEMENT_SPACE_CONTAINER_ENV = "NO_MANAGEMENT_SPACE_CONTAINER"; private static final String LUS_IP_ADDRESS_ENV = "LUS_IP_ADDRESS"; private static final String WORKING_HOME_DIRECTORY_ENV = "WORKING_HOME_DIRECTORY"; private static final String CLOUD_FILE = "CLOUD_FILE"; private static final String AUTO_RESTART_AGENT = "AUTO_RESTART_AGENT"; /******** * Constructor. * * @param mode * the execution mode. */ public EnvironmentFileBuilder(final ScriptLanguages scriptLanguage, final Map<String, String> externalEnvVars) { this.scriptLanguage = scriptLanguage; this.externalEnvVars = externalEnvVars; switch (scriptLanguage) { case LINUX_SHELL: this.newline = "\n"; break; case WINDOWS_BATCH: this.newline = "\r\n"; break; default: throw new UnsupportedOperationException( "Unsupported script language: " + scriptLanguage); } } private String createSpringProfilesString(final InstallationDetails details) { final String securityProfile = details.getSecurityProfile(); final String storageProfile = (details.isPersistent() ? CloudifyConstants.PERSISTENCE_PROFILE_PERSISTENT : CloudifyConstants.PERSISTENCE_PROFILE_TRANSIENT); return securityProfile + "," + storageProfile; } /********* * 8 Loads the environment file from the given installation details. * * @param details * the installation details. */ public void loadEnvironmentFileFromDetails(final InstallationDetails details) { final EnvironmentFileBuilder builder = this; String remoteDirectory = details.getRemoteDir(); if (remoteDirectory.endsWith("/")) { remoteDirectory = remoteDirectory.substring(0, remoteDirectory.length() - 1); } if (details.isManagement()) { // add the relative path to the cloud file location remoteDirectory = remoteDirectory + "/" + details.getRelativeLocalDir(); } String authGroups = null; if (details.getAuthGroups() != null) { // authgroups should be a strongly typed object convertible into a // String authGroups = details.getAuthGroups(); } final String springProfiles = createSpringProfilesString(details); String safePublicIpAddress = IPUtils.getSafeIpAddress(details.getPublicIp()); String safePrivateIpAddress = IPUtils.getSafeIpAddress(details.getPrivateIp()); builder.exportVar(LUS_IP_ADDRESS_ENV, details.getLocator()) .exportVar(GSA_MODE_ENV, details.isManagement() ? "lus" : "agent") .exportVar(CloudifyConstants.SPRING_ACTIVE_PROFILE_ENV_VAR, springProfiles) .exportVar(NO_WEB_SERVICES_ENV, details.isNoWebServices() ? "true" : "false") .exportVar(NO_MANAGEMENT_SPACE_ENV, details.isNoManagementSpace() ? "true" : "false") .exportVar(NO_MANAGEMENT_SPACE_CONTAINER_ENV, details.isNoManagementSpaceContainer() ? "true" : "false") .exportVar( CloudifyConstants.CLOUDIFY_CLOUD_MACHINE_IP_ADDRESS_ENV, details.isBindToPrivateIp() ? safePrivateIpAddress : safePublicIpAddress) .exportVar(CloudifyConstants.CLOUDIFY_LINK_ENV, details.getCloudifyUrl()) .exportVar(CloudifyConstants.CLOUDIFY_OVERRIDES_LINK_ENV, details.getOverridesUrl()) .exportVar(WORKING_HOME_DIRECTORY_ENV, remoteDirectory) .exportVar(AUTO_RESTART_AGENT, details.getAutoRestartAgent()) .exportVar(CloudifyConstants.GIGASPACES_AUTH_GROUPS, authGroups) .exportVar(CloudifyConstants.GIGASPACES_AGENT_ENV_PRIVATE_IP, safePrivateIpAddress) .exportVar(CloudifyConstants.GIGASPACES_AGENT_ENV_PUBLIC_IP, safePublicIpAddress) .exportVar(CloudifyConstants.GIGASPACES_CLOUD_TEMPLATE_NAME, details.getTemplateName()) .exportVar(CloudifyConstants.GIGASPACES_CLOUD_MACHINE_ID, details.getMachineId()) .exportVar(CloudifyConstants.CLOUDIFY_CLOUD_MACHINE_ID, details.getMachineId()) // maintain backwards compatibility for pre 2.3.0 .exportVar(CloudifyConstants.CLOUDIFY_AGENT_ENV_PRIVATE_IP, safePrivateIpAddress) .exportVar(CloudifyConstants.CLOUDIFY_CLOUD_LOCATION_ID, details.getLocationId()) .exportVar(CloudifyConstants.USM_ATTRIBUTES_STORE_DISCOVERY_TIMEOUT_ENV_VAR, details.getAttributesStoreDiscoveryTimeout().toString()) .exportVar(CloudifyConstants.CLOUDIFY_AGENT_ENV_PUBLIC_IP, safePublicIpAddress); if (details.isManagement()) { String remotePath = details.getRemoteDir(); if (!remotePath.endsWith("/")) { remotePath += "/"; } builder.exportVar(CLOUD_FILE, remotePath + details.getCloudFile().getName()); logger.log(Level.FINE, "Setting ESM/GSM/LUS java options."); builder.exportVar("ESM_JAVA_OPTIONS", details.getEsmCommandlineArgs()); builder.exportVar("LUS_JAVA_OPTIONS", details.getLusCommandlineArgs()); builder.exportVar("GSM_JAVA_OPTIONS", details.getGsmCommandlineArgs()); logger.log(Level.FINE, "Setting gsc lrmi port-range and custom rest/webui ports."); builder.exportVar(CloudifyConstants.GSC_LRMI_PORT_RANGE_ENVIRONMENT_VAR, details.getGscLrmiPortRange()); builder.exportVar(CloudifyConstants.REST_PORT_ENV_VAR, details.getRestPort().toString()); builder.exportVar(CloudifyConstants.REST_MAX_MEMORY_ENVIRONMENT_VAR, details.getRestMaxMemory()); builder.exportVar(CloudifyConstants.WEBUI_PORT_ENV_VAR, details.getWebuiPort().toString()); builder.exportVar(CloudifyConstants.WEBUI_MAX_MEMORY_ENVIRONMENT_VAR, details.getWebuiMaxMemory()); } logger.log(Level.FINE, "Setting GSA java options."); builder.exportVar("GSA_JAVA_OPTIONS", details.getGsaCommandlineArgs()); if (details.getUsername() != null) { builder.exportVar("USERNAME", details.getUsername()); } if (details.getPassword() != null) { builder.exportVar("PASSWORD", details.getPassword()); } builder.exportVar(CloudifyConstants.SPRING_SECURITY_CONFIG_FILE_ENV_VAR, details.getRemoteDir() + "/" + CloudifyConstants.SECURITY_FILE_NAME); if (StringUtils.isNotBlank(details.getKeystorePassword())) { builder.exportVar(CloudifyConstants.KEYSTORE_FILE_ENV_VAR, details.getRemoteDir() + "/" + CloudifyConstants.KEYSTORE_FILE_NAME); builder.exportVar(CloudifyConstants.KEYSTORE_PASSWORD_ENV_VAR, details.getKeystorePassword()); } if (details.getOpenFilesLimit() != null) { builder.exportVar(CloudifyConstants.CLOUDIFY_OPEN_FILES_LIMIT, details.getOpenFilesLimit()); } } private String normalizeWindowsPaths(final String original) { final String cifsNormalized = normalizeCifsPath(original); final String cygwinNormalized = normalizeCygwinPath(cifsNormalized); return cygwinNormalized; } /**************** * Given a path of the type /C$/PATH - indicating an absolute cifs path, returns /PATH. If the string does not * match, returns the original unmodified string. * * @param str * the input path. * @return the input path, adjusted to remove the cifs drive letter, if it exists, or the original path if the drive * letter is not present. */ public static String normalizeCifsPath(final String str) { final String expression = CIFS_ABSOLUTE_PATH_WITH_DRIVE_REGEX; if (pattern == null) { pattern = Pattern.compile(expression); } if (str == null) { return null; } if (pattern.matcher(str).matches()) { final char drive = str.charAt(1); return drive + ":\\" + str.substring("/c$/".length()).replace('/', '\\'); } return str; } /******** * Normalizes a cygwin path to a standard windows path, where a cygwin path is any string that starts with * '/cygwin/'. Other strings are not changed. * * @param str * the original value. * @return the normalized string. */ public static String normalizeCygwinPath(final String str) { if (str == null) { return null; } if (!str.startsWith(CYGDRIVE)) { return str; } final String pathWithoutCygdrive = str.substring(CYGDRIVE.length()); final String pathWithDriveLetter = pathWithoutCygdrive.replaceFirst("/", ":/"); final String pathWithBackslash = pathWithDriveLetter.replace("/", "\\"); return pathWithBackslash; } /** * Change all \ char to / from a linux path * @param str * @return */ public static String normalizeLinuxPath (final String str) { String linuxPath=str; if (str.contains("\\")) linuxPath = str.replace("\\", "/"); if (!str.startsWith("/")) linuxPath = "/"+linuxPath; if (str.endsWith("/")) linuxPath = linuxPath.substring(0, linuxPath.length()-1); return linuxPath; } /** * Normalizes the path and returns a standard windows absolute path. * * @param remoteDirectory * The remote path. * @return a normalized absolute windows path. */ public static String normalizeLocalAbsolutePath(final String remoteDirectory) { if (remoteDirectory.startsWith("/") && remoteDirectory.indexOf("$") == 2) { // '$' is a special char. final String quoteReplacement = Matcher.quoteReplacement("$"); return remoteDirectory.replaceFirst(quoteReplacement, ":").replaceFirst("/", ""); } return remoteDirectory; } /******** * same as org.cloudifysource.esc.installer.EnvironmentFileBuilder.exportVar(String, String, boolean) with append * field set to false. * * @param name * name of env var. * @param value * value of env var. * @return he builder. */ public EnvironmentFileBuilder exportVar(final String name, final String value) { return exportVar(name, value, false); } /********* * Adds an environment variable to the command line. * * @param name * variable name. * @param value * variable value. * @param append * true if the variable value should be appended to the current one, false otherwise. * @return this. */ public EnvironmentFileBuilder exportVar(final String name, final String value, final boolean append) { String actualValue = value; if (value == null) { actualValue = ""; } if (append) { actualValue = appendValue(name, value); } // appends the external value, if exists, to the end of the env var. // External values override any values that were added via exportVar. actualValue = appendExternalValue(name, actualValue); return addValue(name, actualValue); } private String appendExternalValue(final String name, final String actualValue) { String externalValue = externalEnvVars.get(name); String finalValue = actualValue; if (!StringUtils.isEmpty(externalValue)) { // remove surrounding quotes if (externalValue.startsWith("\'") && externalValue.endsWith("\'")) { externalValue = externalValue.substring(1, externalValue.length() - 1); } finalValue = actualValue + ' ' + externalValue; } appendedExternalEnvVars.add(name); return finalValue; } private EnvironmentFileBuilder addValue(final String name, final String value) { String actualValue = value; switch (this.scriptLanguage) { case LINUX_SHELL: sb.append("export ").append('"').append(name).append("=").append(actualValue).append('"'); break; case WINDOWS_BATCH: // remove surrounding quotes if (actualValue.startsWith("\"") && actualValue.endsWith("\"")) { actualValue = actualValue.substring(1, actualValue.length() - 1); } // If the value of a variable includes an ampersand, it will cause the command to // be interpreted as two different commands. To avoid this, escape the ampersand with a caret // and wrap the entire command with double quotes. Yes, that is how batch files do it. String normalizedValue = normalizeWindowsPaths(actualValue); if (normalizedValue.contains("&") || normalizedValue.contains("%")) { normalizedValue = normalizedValue.replace("&", "^&"); normalizedValue = normalizedValue.replace("%", "%%"); } sb.append("SET \"").append(name).append("=").append(normalizedValue).append("\""); break; default: throw new IllegalArgumentException("Unsupported script language: " + this.scriptLanguage); } sb.append(newline); return this; } private String appendValue(final String name, final String value) { switch (this.scriptLanguage) { case LINUX_SHELL: return "${" + name + "} " + value; case WINDOWS_BATCH: return "%" + name + "% " + value; default: throw new IllegalArgumentException("Unsupported script language: " + this.scriptLanguage); } } /*********** * Prepares the final environment output. Should only be called after all other values have been set. * * @return this instance. */ public EnvironmentFileBuilder build() { // add only environment vars that were not appended to existing vars. for (Map.Entry<String, String> entry : externalEnvVars.entrySet()) { String name = entry.getKey(); if (!appendedExternalEnvVars.contains(name)) { String value = entry.getValue(); if (!StringUtils.isEmpty(value)) { addValue(name, value); } this.appendedExternalEnvVars.add(name); } } return this; } @Override public String toString() { return sb.toString(); } /******* * Returns the file name for the environment file for the current script language. * * @return the environment file name. */ public String getEnvironmentFileName() { switch (this.scriptLanguage) { case LINUX_SHELL: return "cloudify_env.sh"; case WINDOWS_BATCH: return "cloudify_env.bat"; default: throw new UnsupportedOperationException("Unexpected script language: " + this.scriptLanguage); } } }