Skip to content

Commit

Permalink
[FIXED JENKINS-14407] Allow multi-value default for Scheduled runs
Browse files Browse the repository at this point in the history
  • Loading branch information
imod committed Mar 28, 2013
1 parent 7b44cfb commit e6bdc21
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 62 deletions.
5 changes: 5 additions & 0 deletions pom.xml
Expand Up @@ -114,6 +114,11 @@
<artifactId>token-macro</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>jquery</artifactId>
<version>1.7.2-1</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
Expand Down
Expand Up @@ -12,6 +12,7 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import net.sf.json.JSONArray;
Expand All @@ -37,15 +38,37 @@ public class NodeParameterDefinition extends SimpleParameterDefinition {
public static final String ALL_NODES = "ALL (no restriction)";

public final List<String> allowedSlaves;
public final String defaultValue;
private List<String> defaultSlaves;
@Deprecated
public transient String defaultValue;
private String triggerIfResult;
private boolean allowMultiNodeSelection;
private boolean triggerConcurrentBuilds;

@DataBoundConstructor
private boolean ignoreOfflineNodes;

@DataBoundConstructor
public NodeParameterDefinition(String name, String description, List<String> defaultSlaves, List<String> allowedSlaves, String triggerIfResult, boolean ignoreOfflineNodes) {
super(name, description);
this.allowedSlaves = allowedSlaves;
this.defaultSlaves = defaultSlaves;

if ("multiSelectionDisallowed".equals(triggerIfResult)) {
this.allowMultiNodeSelection = false;
this.triggerConcurrentBuilds = false;
} else if ("allowMultiSelectionForConcurrentBuilds".equals(triggerIfResult)) {
this.allowMultiNodeSelection = true;
this.triggerConcurrentBuilds = true;
} else {
this.allowMultiNodeSelection = true;
this.triggerConcurrentBuilds = false;
}
this.triggerIfResult = triggerIfResult;
this.ignoreOfflineNodes = ignoreOfflineNodes;
}

@Deprecated
public NodeParameterDefinition(String name, String description, String defaultValue, List<String> allowedSlaves, String triggerIfResult) {
super(name, description);
this.defaultValue = defaultValue;
this.allowedSlaves = allowedSlaves;

if (this.allowedSlaves != null && this.allowedSlaves.contains(defaultValue)) {
Expand All @@ -64,14 +87,23 @@ public NodeParameterDefinition(String name, String description, String defaultVa
this.triggerConcurrentBuilds = false;
}
this.triggerIfResult = triggerIfResult;
this.ignoreOfflineNodes = false;
}

public List<String> getDefaultSlaves() {
return defaultSlaves;
}

public boolean isIgnoreOfflineNodes() {
return ignoreOfflineNodes;
}

/**
* e.g. what to show if a build is triggered by hand?
*/
@Override
public NodeParameterValue getDefaultParameterValue() {
return new NodeParameterValue(getName(), getDescription(), defaultValue);
return new NodeParameterValue(getName(), getDefaultSlaves(), isIgnoreOfflineNodes());
}

@Override
Expand All @@ -81,12 +113,7 @@ public ParameterValue createValue(String value) {

@Override
public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValueObj) {
if (defaultValueObj instanceof NodeParameterValue) {
NodeParameterValue value = (NodeParameterValue) defaultValueObj;
return new NodeParameterDefinition(getName(), getDescription(), value.getLabel(), getSlaveNames(), triggerIfResult);
} else {
return this;
}
return this;
}

/**
Expand All @@ -98,11 +125,7 @@ public ParameterDefinition copyWithDefaultValue(ParameterValue defaultValueObj)
public List<String> getAllowedNodesOrAll() {
final List<String> slaves = allowedSlaves == null || allowedSlaves.isEmpty() || allowedSlaves.contains(ALL_NODES) ? getSlaveNames() : allowedSlaves;

Collections.sort(slaves);

// make sure the default is at the first position
slaves.remove(defaultValue);
slaves.add(0, defaultValue);
Collections.sort(slaves, NodeNameComparator.INSTANCE);

return slaves;
}
Expand All @@ -122,6 +145,7 @@ public String getTriggerIfResult() {
*/
public static List<String> getSlaveNamesForSelection() {
List<String> slaveNames = getSlaveNames();
Collections.sort(slaveNames, NodeNameComparator.INSTANCE);
slaveNames.add(0, ALL_NODES);
return slaveNames;
}
Expand All @@ -147,8 +171,21 @@ public static List<String> getSlaveNames() {
}
return test;
}

@Extension

/**
* Comparator preferring the master name
*/
private static final class NodeNameComparator implements Comparator<String> {
public static final NodeNameComparator INSTANCE = new NodeNameComparator();
public int compare(String o1, String o2) {
if("master".endsWith(o1)){
return -1;
}
return o1.compareTo(o2);
}
}

@Extension
public static class DescriptorImpl extends ParameterDescriptor {
@Override
public String getDisplayName() {
Expand Down Expand Up @@ -179,7 +216,7 @@ public ParameterValue createValue(StaplerRequest req, JSONObject jo) {
}
}

NodeParameterValue value = new NodeParameterValue(name, nodes);
NodeParameterValue value = new NodeParameterValue(name, nodes, isIgnoreOfflineNodes());
value.setDescription(getDescription());
return value;
}
Expand All @@ -197,5 +234,18 @@ public boolean getAllowMultiNodeSelection() {
public boolean isTriggerConcurrentBuilds() {
return triggerConcurrentBuilds;
}

/*
* keep backward compatible
*/
public Object readResolve() {
if (defaultValue != null) {
if (defaultSlaves == null) {
defaultSlaves = new ArrayList<String>();
}
defaultSlaves.add(defaultValue);
}
return this;
}

}
Expand Up @@ -4,21 +4,29 @@
package org.jvnet.jenkins.plugins.nodelabelparameter;

import hudson.model.AbstractBuild;
import hudson.model.Computer;
import hudson.model.Node;
import hudson.model.ParameterDefinition;
import hudson.model.ParametersDefinitionProperty;
import hudson.tasks.BuildWrapper;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

import jenkins.model.Jenkins;

import org.apache.commons.lang.StringUtils;
import org.jvnet.jenkins.plugins.nodelabelparameter.wrapper.TriggerNextBuildWrapper;
import org.kohsuke.stapler.DataBoundConstructor;

/**
* @author domi
*
* @author Dominik Bartholdi (imod)
*/
public class NodeParameterValue extends LabelParameterValue {

private static final Logger LOGGER = Logger.getLogger(NodeParameterValue.class.getName());

private static final long serialVersionUID = 1L;
private List<String> nextLabels;
Expand All @@ -30,24 +38,48 @@ public class NodeParameterValue extends LabelParameterValue {
* the name of the parameter
* @param labels
* the node labels to trigger one build after the other with
* @param ignoreOfflineNodes
* if the job should also be triggered on nodes which currently are not available for execution.
*/
@DataBoundConstructor
public NodeParameterValue(String name, List<String> labels) {
public NodeParameterValue(String name, List<String> labels, boolean ignoreOfflineNodes) {
super(name);
if (labels != null && !labels.isEmpty()) {
this.setLabel(labels.get(0).trim());
if (labels.size() > 1) {
final List<String> subList = labels.subList(1, labels.size());
nextLabels = new ArrayList<String>();
for (String l : subList) {
nextLabels.add(l.trim());
}
}
} else {
throw new IllegalArgumentException("at least one label must be given!");

nextLabels = new ArrayList<String>();
if(ignoreOfflineNodes){
for (String nodeName : labels) {
nodeName = nodeName.trim();
if(NodeUtil.isNodeOnline(nodeName)) {
if(getLabel() == null){
this.setLabel(nodeName);
} else {
nextLabels.add(nodeName);
}
} else {
LOGGER.fine("Skipping execution on offline node ["+nodeName+"]");
}
}
} else {
this.setLabel(labels.get(0).trim());
if (labels.size() > 1) {
final List<String> subList = labels.subList(1, labels.size());
for (String l : subList) {
nextLabels.add(l.trim());
}
}
}
}
if(getLabel() == null || getLabel().length() == 0){
// these artificial labels will cause the job to stay in the queue and the user will see this label
if (ignoreOfflineNodes) {
setLabel("Job triggered without a valid online node, given where: "+StringUtils.join(labels, ','));
} else {
setLabel("Job triggered, but no node given");
}
}
}

public NodeParameterValue(String name, String description, String label) {
super(name, description, label);
}
Expand All @@ -57,18 +89,21 @@ public String toString() {
StringBuilder s = new StringBuilder("[NodeParameterValue: ");
s.append(name).append("=").append(getLabel());
if (nextLabels != null && !nextLabels.isEmpty()) {
s.append(", nextNodes=").append(this.nextLabels);
s.append(", nextNodes=").append(StringUtils.join(nextLabels, ','));
}
s.append("]");
return s.toString();
}

/**
* Gets the labels to be used to trigger the next builds with
*
* @return the labels
*/
public List<String> getNextLabels() {
return nextLabels;
return Collections.unmodifiableList(nextLabels == null ? new ArrayList<String>() : nextLabels);
}


/**
* @see hudson.model.ParameterValue#createBuildWrapper(hudson.model.AbstractBuild)
Expand Down Expand Up @@ -116,4 +151,5 @@ public int hashCode() {
result = 31 * result + (nextLabels != null ? nextLabels.hashCode() : 0);
return result;
}

}
@@ -0,0 +1,29 @@
package org.jvnet.jenkins.plugins.nodelabelparameter;

import hudson.model.Computer;
import hudson.model.Node;
import jenkins.model.Jenkins;

public final class NodeUtil {

private NodeUtil() {
}

/**
* checks whether the given node is available for an execution of the job
* @param nodeName the name of the node to check
* @return <code>true</code> if the job is ok to be used
*/
public static boolean isNodeOnline(String nodeName){
if("master".equals(nodeName)){
return true;
}
final Computer c = Jenkins.getInstance().getComputer(nodeName);
if(c != null){
Node n = c.getNode();
return n!=null && c.isOnline() && c.getNumExecutors()>0;
}
return false;
}

}
Expand Up @@ -106,7 +106,7 @@ private void triggerAllBuildsConcurrent(AbstractBuild build, BuildListener liste
for (String nodeName : newBuildNodes) {
final List<String> singleNodeList = new ArrayList<String>();
singleNodeList.add(nodeName);
final NodeParameterValue nodeParameterValue = new NodeParameterValue(parmaName, singleNodeList);
final NodeParameterValue nodeParameterValue = new NodeParameterValue(parmaName, singleNodeList, nodeParameterDefinition.isIgnoreOfflineNodes());
List<ParameterValue> copies = new ArrayList<ParameterValue>(newPrams);
copies.add(nodeParameterValue); // where to do the next build
listener.getLogger().print("schedule build on node " + nodeName);
Expand Down Expand Up @@ -138,7 +138,7 @@ private void triggerBuilds(AbstractBuild build, BuildListener listener) {
NodeParameterValue origNodePram = (NodeParameterValue) parameterValue;
final List<String> nextNodes = origNodePram.getNextLabels();
if (nextNodes != null && !nextNodes.isEmpty() && shouldScheduleNextJob(build.getResult(), triggerIfResult)) {
NodeParameterValue newNodeParam = new NodeParameterValue(origNodePram.getName(), nextNodes);
NodeParameterValue newNodeParam = new NodeParameterValue(origNodePram.getName(), nextNodes, nodeParameterDefinition.isIgnoreOfflineNodes());
newPrams.add(newNodeParam);
final String nextLabel = newNodeParam.getLabel();
if (nextLabel != null) {
Expand Down
Expand Up @@ -31,4 +31,5 @@ AllNodesForLabelBuildParameterFactory.displayName=All Nodes for Label Factory
AllNodesBuildParameterFactory.displayName=Build on every online node
NodeListBuildParameterFactory.displayName=Node-List Factory
NodeListBuildParameterFactory.nodeNotFound=A node with the name {0} could not be found.
NodeListBuildParameterFactory.nodeNotDefined=Please define a node where the job should be triggered on
NodeListBuildParameterFactory.nodeNotDefined=Please define a node where the job should be triggered on
NodeListBuildParameterFactory.skippOfflineNode=Skipping execution on offline node [{0}]

0 comments on commit e6bdc21

Please sign in to comment.