Skip to content

Commit

Permalink
Merge pull request #5 from oleg-nenashev/master
Browse files Browse the repository at this point in the history
[JENKINS-27243] - Fix the improper Axis values initialization on buildEnvironment() with circular dependencies
  • Loading branch information
oleg-nenashev committed Mar 18, 2015
2 parents bbac467 + 51475b3 commit b873244
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 35 deletions.
Expand Up @@ -24,6 +24,10 @@

import com.google.common.collect.Lists;
import hudson.Util;
import java.util.Arrays;
import java.util.logging.Level;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

/**
* Implements dynamic axis support through a configurable environment variable.
Expand All @@ -34,8 +38,8 @@ public class DynamicAxis extends Axis
{
private static final Logger LOGGER = Logger.getLogger( DynamicAxis.class.getName() );

private String varName = "";
private List<String> axisValues = Lists.newArrayList();
private @CheckForNull String varName = "";
private final @Nonnull List<String> axisValues = Lists.newArrayList();

/**
* Always construct from an axis name and environment variable name.
Expand All @@ -51,9 +55,10 @@ public DynamicAxis( String name, String varName )

/**
* An accessor is required if referenced in the Jelly file.
* @return the name of the variable
* @return Name of the variable. Null values will be replaced
* by empty strings.
*/
public synchronized String getVarName()
public synchronized @Nonnull String getVarName()
{
return varName == null ? "" : varName;
}
Expand All @@ -66,6 +71,7 @@ private void checkForDefaultValues()
{
if( axisValues.isEmpty() )
{
LOGGER.fine( "Axis values list is empty. Adding 'default' value" );
axisValues.add( "default" );
}
}
Expand All @@ -74,9 +80,11 @@ private void checkForDefaultValues()
* Overridden to provide a default value in the event the target environment
* variable cannot be accessed or interpreted.
* @see hudson.matrix.Axis#getValues()
* @return Cached list of axis values from the last
* {@link #rebuild(hudson.matrix.MatrixBuild.MatrixBuildExecution)} call
*/
@Override
public synchronized List<String> getValues()
public synchronized @Nonnull List<String> getValues()
{
checkForDefaultValues();
return axisValues;
Expand All @@ -85,56 +93,58 @@ public synchronized List<String> getValues()
/**
* Overridden to return our environment variable name.
* @see hudson.matrix.Axis#getValueString()
* @return Name of the variable. Null values will be replaced
* by empty strings.
*/
@Override
public synchronized String getValueString()
public synchronized @Nonnull String getValueString()
{
return getVarName();
}

/**
* Override the new rebuild() feature to dynamically evaluate the configured
* environment variable name to get list of axis values to use for the
* current build.
* @see hudson.matrix.Axis#rebuild(hudson.matrix.MatrixBuild.MatrixBuildExecution)
* @return New list of axis values
*/
@Override
public synchronized List<String> rebuild( MatrixBuild.MatrixBuildExecution context )
public synchronized @Nonnull List<String> rebuild( @Nonnull MatrixBuild.MatrixBuildExecution context )
{
// clear any existing values to ensure we do not return old ones
LOGGER.fine( "Rebuilding axis names from variable '" + varName + "'" );
axisValues.clear();
if( context != null )
LOGGER.log( Level.FINE, "Rebuilding axis names from variable ''{0}''", varName);
final List<String> newAxisValues = new ArrayList<String>(axisValues.size());
try
{
try
// attempt to get the current environment variables
final @Nonnull EnvVars vars = context.getBuild().getEnvironment( TaskListener.NULL );

// only spaces are supported as separators, as per the original axis value definition
String varValue = vars.get( varName );
if( varValue != null )
{
// attempt to get the current environment variables
EnvVars vars = context.getBuild().getEnvironment( TaskListener.NULL );
if( vars != null )
{
// only spaces are supported as separators, as per the original axis value definition
String varValue = vars.get( varName );
if( varValue != null )
{
LOGGER.fine( "Variable value is '" + varValue + "'" );
for( String item : Util.tokenize(varValue) )
{
axisValues.add( item );
}
}
}
}
catch( Exception e )
{
LOGGER.severe( "Failed to build list of names: " + e );
LOGGER.log( Level.FINE, "Variable value is ''{0}''", varValue);
newAxisValues.addAll(Arrays.asList(Util.tokenize(varValue)));
}
}
catch( Exception e )
{
LOGGER.log( Level.SEVERE, "Failed to build list of names: {0}", e);
}

// validate result list before returning it
checkForDefaultValues();
LOGGER.fine( "Returning axis list " + axisValues );
// must return a new object because axisValues might change
return new ArrayList<String>(axisValues);
if (newAxisValues.isEmpty()) {
LOGGER.fine( "Axis values list is empty. Adding 'default' value" );
newAxisValues.add( "default" );
}
LOGGER.log( Level.FINE, "Returning axis list {0}", newAxisValues);

// Add values to the cache
axisValues.clear();
axisValues.addAll(newAxisValues);

return newAxisValues;
}

/**
Expand Down
@@ -0,0 +1,89 @@
package ca.silvermaplesolutions.jenkins.plugins.daxis;

import hudson.matrix.Axis;
import hudson.matrix.MatrixBuild;
import hudson.matrix.MatrixProject;
import hudson.model.AbstractBuild;
import hudson.model.ParametersDefinitionProperty;
import hudson.model.StringParameterDefinition;
import java.util.Collection;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.junit.Test;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.FakeChangeLogSCM;
import org.jvnet.hudson.test.HudsonTestCase;

/**
* Tests for {@link DynamicAxis}.
* @author Oleg Nenashev <o.v.nenashev@gmail.com>
*/
public class DynamicAxisTest extends HudsonTestCase {

MatrixProject p;

@Override
protected void setUp() throws Exception {
super.setUp();
p = createMatrixProject();
p.getAxes().add(new DynamicAxis("AXIS", "AXIS_VALUES"));
}

public @Test void testDefaultInjection() throws Exception {
p.addProperty(new ParametersDefinitionProperty(
new StringParameterDefinition("AXIS_VALUES", "1 2 3")));

MatrixBuild run = buildAndAssertSuccess(p);
assertEquals(3, run.getExactRuns().size());
}

@Bug(25660)
public @Test void testInjectionWithEscaping() throws Exception {
checkAxesSetup(new String[]{"1", "\"2 3\""});
}

/**
* Runs the test, when an environment contributor uses axis values
* to build the environment.
* @see AxisValuesUserSCM
*/
@Bug(27243)
public @Test void testInjectionWithAxisValuesUser() throws Exception {
checkAxesSetup(new String[]{"1", "2", "3"});
}

/**
* Sets values from the list and then checks injection results.
* @param values List of values, which should be escaped externally
*/
private void checkAxesSetup(String[] values) throws Exception {
final String valuesString = StringUtils.join(values, " ");
p.addProperty(new ParametersDefinitionProperty(
new StringParameterDefinition("AXIS_VALUES", valuesString)));

// Inject SCM, which implicitly triggers axes rebuild
p.setScm(new AxisValuesUserSCM());

final MatrixBuild run = buildAndAssertSuccess(p);

// No additional values have been injected
assertEquals(values.length, run.getExactRuns().size());
}

/**
* Just a stub {@link SCM}, which rebuilds axes list.
*/
public static class AxisValuesUserSCM extends FakeChangeLogSCM {

@Override
public void buildEnvVars(AbstractBuild<?, ?> build, Map<String, String> env) {
if (build instanceof MatrixBuild) {
final MatrixProject prj = (MatrixProject) build.getParent();
for (Axis axis : prj.getAxes()) {
// Get values for a opeeration
axis.getValues();
}
}
}
}
}

0 comments on commit b873244

Please sign in to comment.