Skip to content
Permalink
Browse files

[FIXED JENKINS-24113] - Added a helper to escape property file substi…

…tutions

The commit introduces a Helper class, which is able to substitute variables for paths and property files.
Additional parsers can be added on-demand.

Signed-off-by: Oleg Nenashev <o.v.nenashev@gmail.com>
  • Loading branch information...
oleg-nenashev committed Aug 15, 2014
1 parent 8b09f8b commit 4132e0eaff8aecef68f3d3978cdcc9ab83f2eb5a
@@ -46,6 +46,7 @@
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import jenkins.plugins.customtools.util.envvars.VariablesSubstitutionHelper;

import org.kohsuke.stapler.DataBoundConstructor;

@@ -137,18 +138,22 @@ public boolean hasAdditionalVariables() {

@Override
public CustomTool forEnvironment(EnvVars environment) {
return new CustomTool(getName(), environment.expand(getHome()),
getProperties().toList(), environment.expand(exportedPaths),
String substitutedHomeDir = VariablesSubstitutionHelper.PATH.resolveVariable(getHome(), environment);
String substitutedPath = VariablesSubstitutionHelper.PATH.resolveVariable(exportedPaths, environment);
String substitutedAdditionalVariables = VariablesSubstitutionHelper.PROP_FILE.resolveVariable(additionalVariables, environment);

return new CustomTool(getName(), substitutedHomeDir,
getProperties().toList(), substitutedPath,
LabelSpecifics.substitute(getLabelSpecifics(), environment),
toolVersion, environment.expand(additionalVariables));
toolVersion, substitutedAdditionalVariables);
}

@Override
public @Nonnull CustomTool forNode(Node node, TaskListener log) throws IOException,
InterruptedException {
String substitutedPath = EnvStringParseHelper.resolveExportedPath(exportedPaths, node);
String substitutedHomeDir = EnvStringParseHelper.resolveExportedPath(translateFor(node, log), node);
String substitutedAdditionalVariables = EnvStringParseHelper.resolveExportedPath(additionalVariables, node);
public @Nonnull CustomTool forNode(Node node, TaskListener log)
throws IOException, InterruptedException {
String substitutedHomeDir = VariablesSubstitutionHelper.PATH.resolveVariable(translateFor(node, log), node);
String substitutedPath = VariablesSubstitutionHelper.PATH.resolveVariable(exportedPaths, node);
String substitutedAdditionalVariables = VariablesSubstitutionHelper.PROP_FILE.resolveVariable(additionalVariables, node);

return new CustomTool(getName(), substitutedHomeDir, getProperties().toList(),
substitutedPath, LabelSpecifics.substitute(getLabelSpecifics(), node),
@@ -23,76 +23,23 @@
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import jenkins.plugins.customtools.util.envvars.VariablesSubstitutionHelper;
import jenkins.plugins.customtools.util.envvars.VariablesSubstitutionHelper.SimpleVariablesSubstitutionHelper;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

/**
* Provides parsing of environment variables in input string.
* @author Oleg Nenashev <nenashev@synopsys.com>, Synopsys Inc.
* @since 0.3
*/
@Restricted(NoExternalUse.class)
public class EnvStringParseHelper {
private EnvStringParseHelper() {};

/**
* Resolves tools installation directory using global variables.
* @param environment Collection of environment variables
* @param exportedPaths Input path with macro calls
* @return Raw string
* @since 0.3
*/
public static String resolveExportedPath(@CheckForNull String exportedPaths, @Nonnull EnvVars environment) {
if (exportedPaths == null) return null;
if (!exportedPaths.contains("${")) {
return exportedPaths;
}

// Substitute parameters
// TODO: optimize via parsing of string
String substitutedString = exportedPaths;
for (Map.Entry<String,String> entry : environment.entrySet()) {
substitutedString = substitutedString.replace("${" + entry.getKey() + "}", entry.getValue());
}

return substitutedString;
}

public static String resolveExportedPath(@CheckForNull String exportedPaths, @Nonnull Node node) {
if (exportedPaths == null) return null;
if (!exportedPaths.contains("${")) {
return exportedPaths;
}

// Check node properties
String substitutedString = exportedPaths;
for (NodeProperty<?> entry : node.getNodeProperties()) {
substitutedString = substituteNodeProperty(substitutedString, entry);
}

// Substitute global variables
for (NodeProperty<?> entry : Hudson.getInstance().getGlobalNodeProperties()) {
substitutedString = substituteNodeProperty(substitutedString, entry);
}

return substitutedString;
}

/**
* Substitutes string according to node property.
* @param macroString String to be substituted
* @param property Node property
* @return Substituted string
* @since 0.3
*/
public static String substituteNodeProperty(@CheckForNull String macroString, @CheckForNull NodeProperty<?> property) {
// Get environment variables
if (property != null && property instanceof EnvironmentVariablesNodeProperty) {
EnvironmentVariablesNodeProperty prop = (EnvironmentVariablesNodeProperty)property;
return resolveExportedPath(macroString, prop.getEnvVars());
}

//TODO: add support of other configuration entries or propagate environments
return macroString;
}
private EnvStringParseHelper() {};

private static final SimpleVariablesSubstitutionHelper HELPER = new VariablesSubstitutionHelper.SimpleVariablesSubstitutionHelper();

/**
* Resolves tools installation directory using global variables.
* @param inputString Input path with macro calls
@@ -102,13 +49,18 @@ public static String substituteNodeProperty(@CheckForNull String macroString, @C
*/
public static void checkStringForMacro(@CheckForNull String macroName, @CheckForNull String inputString)
throws CustomToolException {
if (inputString == null) {
return;
}

// Check consistensy and throw errors
if (inputString.contains("${")) {
if (HELPER.hasMacros(inputString)) {
throw new CustomToolException("Can't resolve all variables in "+macroName+" string. Final state: "+inputString);
}
}

@Deprecated
public static String resolveExportedPath(@CheckForNull String exportedPaths, @Nonnull EnvVars environment) {
return HELPER.resolveVariable(exportedPaths, environment);
}

@Deprecated
public static String resolveExportedPath(@CheckForNull String exportedPaths, @Nonnull Node node) {
return HELPER.resolveVariable(exportedPaths, node);
}
}
@@ -26,6 +26,7 @@
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import jenkins.model.Jenkins;
import jenkins.plugins.customtools.util.envvars.VariablesSubstitutionHelper;
import org.kohsuke.stapler.DataBoundConstructor;

/**
@@ -77,17 +78,19 @@ public boolean appliesTo(@Nonnull Node node) {
return l == null || l.contains(node);
}

public @Nonnull LabelSpecifics substitute(EnvVars vars) {
return new LabelSpecifics(label, vars.expand(additionalVars), vars.expand(exportedPaths));
public @Nonnull LabelSpecifics substitute(@Nonnull EnvVars vars) {
return new LabelSpecifics(label,
VariablesSubstitutionHelper.PROP_FILE.resolveVariable(additionalVars, vars),
VariablesSubstitutionHelper.PATH.resolveVariable(exportedPaths, vars));
}

public @Nonnull LabelSpecifics substitute(Node node) {
public @Nonnull LabelSpecifics substitute(@Nonnull Node node) {
return new LabelSpecifics(label,
EnvStringParseHelper.resolveExportedPath(additionalVars, node),
EnvStringParseHelper.resolveExportedPath(exportedPaths, node));
VariablesSubstitutionHelper.PROP_FILE.resolveVariable(additionalVars, node),
VariablesSubstitutionHelper.PATH.resolveVariable(exportedPaths, node));
}

public static @Nonnull LabelSpecifics[] substitute (LabelSpecifics[] specifics, EnvVars vars) {
public static @Nonnull LabelSpecifics[] substitute (LabelSpecifics[] specifics, @Nonnull EnvVars vars) {
LabelSpecifics[] out = new LabelSpecifics[specifics.length];
for (int i=0; i<specifics.length; i++) {
out[i] = specifics[i].substitute(vars);
@@ -0,0 +1,142 @@
/*
* Copyright 2014 Oleg Nenashev <o.v.nenashev@gmail.com>.
*
* 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 jenkins.plugins.customtools.util.envvars;

import hudson.EnvVars;
import hudson.model.Hudson;
import hudson.model.Node;
import hudson.slaves.EnvironmentVariablesNodeProperty;
import hudson.slaves.NodeProperty;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import java.util.Properties;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

/**
* Substitutes variables.
* @since TODO
* @author Oleg Nenashev <o.v.nenashev@gmail.com>
*/
public abstract class VariablesSubstitutionHelper {

public static final VariablesSubstitutionHelper PROP_FILE = new PropFileVariablesSubstitutionHelper();
public static final VariablesSubstitutionHelper PATH = new SimpleVariablesSubstitutionHelper();

/**
* Escapes variable values for the required format.
* @param variableName Name of the variable, which is being substituted
* @param rawValue Input value
* @return Escaped value
*/
public String escapeVariableValue(String variableName, String rawValue) {
return rawValue;
}

/**
* Resolves tools installation directory using global variables.
* @param environment Collection of environment variables
* @param inputValue Input path with macro calls
* @return Raw string
* @since 0.3
*/
public String resolveVariable(@CheckForNull String inputValue, @Nonnull EnvVars environment) {
if (inputValue == null || !hasMacros(inputValue))
return inputValue;

// Substitute parameters
String substitutedString = inputValue;
for (Map.Entry<String,String> entry : environment.entrySet()) {
if (hasMacros(inputValue, entry.getKey())) {
final String escapedValue = escapeVariableValue(entry.getKey(), entry.getValue());
substitutedString = substitutedString.replace("${" + entry.getKey() + "}", escapedValue);
}
}

return substitutedString;
}

public String resolveVariable(@CheckForNull String inputValue, @Nonnull Node node) {
if (!hasMacros(inputValue))
return inputValue;

// Check node properties
String substitutedString = inputValue;
for (NodeProperty<?> entry : node.getNodeProperties()) {
substitutedString = substituteNodeProperty(substitutedString, entry);
}

// Substitute global variables
for (NodeProperty<?> entry : Hudson.getInstance().getGlobalNodeProperties()) {
substitutedString = substituteNodeProperty(substitutedString, entry);
}

return substitutedString;
}

/**
* Substitutes string according to node property.
* @param macroString String to be substituted
* @param property Node property
* @return Substituted string
* @since 0.3
*/
private String substituteNodeProperty(@CheckForNull String macroString, @CheckForNull NodeProperty<?> property) {
// Get environment variables
if (property != null && property instanceof EnvironmentVariablesNodeProperty) {
EnvironmentVariablesNodeProperty prop = (EnvironmentVariablesNodeProperty)property;
return resolveVariable(macroString, prop.getEnvVars());
}

//TODO: add support of other configuration entries or propagate environments
return macroString;
}

public static boolean hasMacros(@CheckForNull String inputString) {
return inputString != null ? inputString.contains("${") : false;
}

public static boolean hasMacros(@CheckForNull String inputString, String macroName) {
return inputString != null ? inputString.contains("${"+macroName+"}") : false;
}

public static class SimpleVariablesSubstitutionHelper extends VariablesSubstitutionHelper {

}

public static class PropFileVariablesSubstitutionHelper extends VariablesSubstitutionHelper {

@Override
public String escapeVariableValue(String variableName, String rawValue) {
OutputStream str= new ByteArrayOutputStream();
Properties prop = new Properties();
prop.setProperty("TMP", rawValue);
try {
prop.store(str,"tmp");
} catch (IOException ex) {
// Fallback to the default behavior
//TODO: Really???
return super.escapeVariableValue(variableName, rawValue);
}

String res = str.toString().split("\n")[2].replaceFirst(".*TMP=", "").trim();
return res;
}
}
}

0 comments on commit 4132e0e

Please sign in to comment.
You can’t perform that action at this time.