Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.424</version>
<version>1.480</version>
</parent>

<artifactId>throttle-concurrents</artifactId>
<packaging>hpi</packaging>
<name>Jenkins Throttle Concurrent Builds Plug-in</name>
<version>1.8</version>
<version>2.0-KALRAY-SNAPSHOT</version>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes in pom.xml should be removed

<url>http://wiki.jenkins-ci.org/display/JENKINS/Throttle+Concurrent+Builds+Plugin</url>
<description>Plugin to throttle the number of concurrent builds of a single job per node.</description>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import hudson.util.ListBoxModel;
import hudson.Util;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;

Expand All @@ -20,6 +21,8 @@
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;

import org.apache.commons.lang.StringUtils;

public class ThrottleJobProperty extends JobProperty<AbstractProject<?,?>> {
// Moving category to categories, to support, well, multiple categories per job.
@Deprecated transient String category;
Expand All @@ -30,6 +33,9 @@ public class ThrottleJobProperty extends JobProperty<AbstractProject<?,?>> {
private boolean throttleEnabled;
private String throttleOption;

private final String combinationFilter;
private String[] matchParamsArray;

/**
* Store a config version so we're able to migrate config on various
* functionality upgrades.
Expand All @@ -41,14 +47,24 @@ public ThrottleJobProperty(Integer maxConcurrentPerNode,
Integer maxConcurrentTotal,
List<String> categories,
boolean throttleEnabled,
String throttleOption) {
String throttleOption,
String combinationFilter) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change the tabulation to spaces

this.maxConcurrentPerNode = maxConcurrentPerNode == null ? 0 : maxConcurrentPerNode;
this.maxConcurrentTotal = maxConcurrentTotal == null ? 0 : maxConcurrentTotal;
this.categories = categories;
this.throttleEnabled = throttleEnabled;
this.throttleOption = throttleOption;
}
this.combinationFilter = combinationFilter;
this.matchParamsArray = StringUtils.split(this.combinationFilter, ",");

}
public ThrottleJobProperty(Integer maxConcurrentPerNode,
Integer maxConcurrentTotal,
List<String> categories,
boolean throttleEnabled,
String throttleOption){
this(maxConcurrentPerNode, maxConcurrentTotal, categories, throttleEnabled, throttleOption, "");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usage of null together with @CheckForNull annotation is preferable

}

/**
* Migrates deprecated/obsolete data
Expand Down Expand Up @@ -91,7 +107,9 @@ public String getThrottleOption() {
public List<String> getCategories() {
return categories;
}

public String getCombinationFilter(){
return combinationFilter;
}
public Integer getMaxConcurrentPerNode() {
if (maxConcurrentPerNode == null)
maxConcurrentPerNode = 0;
Expand All @@ -106,6 +124,14 @@ public Integer getMaxConcurrentTotal() {
return maxConcurrentTotal;
}

public ArrayList<String> getMatchParamsArray(){
if(this.matchParamsArray != null){
return new ArrayList<String>(Arrays.asList(this.matchParamsArray));
} else {
return new ArrayList<String>();
}
}

@Extension
public static final class DescriptorImpl extends JobPropertyDescriptor {
private List<ThrottleCategory> categories;
Expand Down Expand Up @@ -195,7 +221,23 @@ public ListBoxModel doFillCategoryItems() {

return m;
}


/**
* Check whether the configuring model is parameterized. Called from jelly.
*
* Note: Caller should pass it for the model is not bound to
* {@link StaplerRequest#findAncestorObject(Class)}
* when called via hetelo-list.
*
* @param it
* @return true if the target model is {@link AbstractProject} is parameterized.
*/
public boolean isParameterizedProject(Object it) {
if ((it == null) || ! (it instanceof AbstractProject))
return false;
AbstractProject p = (AbstractProject) it;
return p.isParameterized();
}
}

public static final class ThrottleCategory extends AbstractDescribableImpl<ThrottleCategory> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@
import hudson.Extension;
import hudson.matrix.MatrixConfiguration;
import hudson.matrix.MatrixProject;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Computer;
import hudson.model.Executor;
import hudson.model.Hudson;
import hudson.model.Node;
import hudson.model.Queue;
import hudson.model.Queue.BuildableItem;
import hudson.model.Queue.Task;
import hudson.model.labels.LabelAtom;
import hudson.model.queue.CauseOfBlockage;
import hudson.model.queue.QueueTaskDispatcher;
import hudson.model.ParametersAction;
import hudson.model.ParameterValue;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -23,20 +27,21 @@
public class ThrottleQueueTaskDispatcher extends QueueTaskDispatcher {

@Override
public CauseOfBlockage canTake(Node node, Task task) {
public CauseOfBlockage canTake(Node node, BuildableItem item) {
Task task = item.task;
if (task instanceof MatrixConfiguration) {
return null;
}

ThrottleJobProperty tjp = getThrottleJobProperty(task);
if (tjp!=null && tjp.getThrottleEnabled()) {
CauseOfBlockage cause = canRun(task, tjp);
CauseOfBlockage cause = canRun(item, tjp);
if (cause != null) return cause;

if (tjp.getThrottleOption().equals("project")) {
if (tjp.getMaxConcurrentPerNode().intValue() > 0) {
int maxConcurrentPerNode = tjp.getMaxConcurrentPerNode().intValue();
int runCount = buildsOfProjectOnNode(node, task);
int runCount = buildsOfProjectOnNode(node, item.task, item, tjp.getMatchParamsArray());

// This would mean that there are as many or more builds currently running than are allowed.
if (runCount >= maxConcurrentPerNode) {
Expand Down Expand Up @@ -66,7 +71,7 @@ else if (tjp.getThrottleOption().equals("category")) {
if (Hudson.getInstance().getQueue().isPending(catProj)) {
return CauseOfBlockage.fromMessage(Messages._ThrottleQueueTaskDispatcher_BuildPending());
}
runCount += buildsOfProjectOnNode(node, catProj);
runCount += buildsOfProjectOnNode(node, catProj, item, tjp.getMatchParamsArray());
}
// This would mean that there are as many or more builds currently running than are allowed.
if (runCount >= maxConcurrentPerNode) {
Expand All @@ -87,12 +92,13 @@ else if (tjp.getThrottleOption().equals("category")) {
public CauseOfBlockage canRun(Queue.Item item) {
ThrottleJobProperty tjp = getThrottleJobProperty(item.task);
if (tjp!=null && tjp.getThrottleEnabled()) {
return canRun(item.task, tjp);
return canRun(item, tjp);
}
return null;
}

public CauseOfBlockage canRun(Task task, ThrottleJobProperty tjp) {
public CauseOfBlockage canRun(Queue.Item item, ThrottleJobProperty tjp) {
Task task = item.task;
if (task instanceof MatrixConfiguration) {
return null;
}
Expand All @@ -102,7 +108,7 @@ public CauseOfBlockage canRun(Task task, ThrottleJobProperty tjp) {
if (tjp.getThrottleOption().equals("project")) {
if (tjp.getMaxConcurrentTotal().intValue() > 0) {
int maxConcurrentTotal = tjp.getMaxConcurrentTotal().intValue();
int totalRunCount = buildsOfProjectOnAllNodes(task);
int totalRunCount = buildsOfProjectOnAllNodes(item.task, item, tjp.getMatchParamsArray());

if (totalRunCount >= maxConcurrentTotal) {
return CauseOfBlockage.fromMessage(Messages._ThrottleQueueTaskDispatcher_MaxCapacityTotal(totalRunCount));
Expand Down Expand Up @@ -130,7 +136,7 @@ else if (tjp.getThrottleOption().equals("category")) {
if (Hudson.getInstance().getQueue().isPending(catProj)) {
return CauseOfBlockage.fromMessage(Messages._ThrottleQueueTaskDispatcher_BuildPending());
}
totalRunCount += buildsOfProjectOnAllNodes(catProj);
totalRunCount += buildsOfProjectOnAllNodes(catProj, item, tjp.getMatchParamsArray());
}

if (totalRunCount >= maxConcurrentTotal) {
Expand Down Expand Up @@ -159,45 +165,73 @@ private ThrottleJobProperty getThrottleJobProperty(Task task) {
return null;
}

private int buildsOfProjectOnNode(Node node, Task task) {

private int buildsOnExecutor(Task taskToMatch, Queue.Item queuedItem, Executor exec, ArrayList<String> matchParams) {
int runCount = 0;
if (exec.getCurrentExecutable() != null
&& exec.getCurrentExecutable().getParent() == taskToMatch) {
if(matchParams.isEmpty()){
runCount++;
} else {
/* We need to check if the params actually match */
exec.getCurrentExecutable().getParent();
if(! (exec.getCurrentExecutable() instanceof AbstractBuild<?,?>)){
LOGGER.warning("Something is wrong, the run is not actually a build !?");
return 0;
}
ParametersAction queuedAction = queuedItem.getAction(ParametersAction.class);

AbstractBuild<?,?> running = (AbstractBuild<?,?>)exec.getCurrentExecutable();
ParametersAction runningAction = running.getAction(ParametersAction.class);

int incr = 1;
for(String param_name : matchParams){
ParameterValue runningVal = runningAction == null ? null : runningAction.getParameter(param_name);
ParameterValue newVal = queuedAction == null ? null : queuedAction.getParameter(param_name);
if(runningVal == null && newVal == null){
/* This is OK */
} else {
if(runningVal == null || !runningVal.equals(newVal))
incr = 0;
}
}
runCount += incr;
}
}

return runCount;
}
private int buildsOfProjectOnNode(Node node, Task taskToMatch, Queue.Item queuedItem, ArrayList<String> matchParams) {
int runCount = 0;
LOGGER.fine("Checking for builds of " + task.getName() + " on node " + node.getDisplayName());

// I think this'll be more reliable than job.getBuilds(), which seemed to not always get
// a build right after it was launched, for some reason.
Computer computer = node.toComputer();
if (computer != null) { //Not all nodes are certain to become computers, like nodes with 0 executors.
for (Executor e : computer.getExecutors()) {
runCount += buildsOnExecutor(task, e);
runCount += buildsOnExecutor(taskToMatch, queuedItem, e, matchParams);
}
if (task instanceof MatrixProject) {
if (taskToMatch instanceof MatrixProject) {
for (Executor e : computer.getOneOffExecutors()) {
runCount += buildsOnExecutor(task, e);
runCount += buildsOnExecutor(taskToMatch, queuedItem, e, matchParams);
}
}
}

return runCount;
}

private int buildsOfProjectOnAllNodes(Task task) {
int totalRunCount = buildsOfProjectOnNode(Hudson.getInstance(), task);

private int buildsOfProjectOnAllNodes(Task taskToMatch, Queue.Item queuedItem, ArrayList<String> matchParams) {
int totalRunCount = buildsOfProjectOnNode(Hudson.getInstance(), taskToMatch, queuedItem, matchParams);

for (Node node : Hudson.getInstance().getNodes()) {
totalRunCount += buildsOfProjectOnNode(node, task);
totalRunCount += buildsOfProjectOnNode(node, taskToMatch, queuedItem, matchParams);
}
return totalRunCount;
}

private int buildsOnExecutor(Task task, Executor exec) {
int runCount = 0;
if (exec.getCurrentExecutable() != null
&& exec.getCurrentExecutable().getParent() == task) {
runCount++;
}

return runCount;
}

private List<AbstractProject<?,?>> getCategoryProjects(String category) {
List<AbstractProject<?,?>> categoryProjects = new ArrayList<AbstractProject<?,?>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
field="maxConcurrentPerNode">
<f:textbox />
</f:entry>
<j:if test="${descriptor.isParameterizedProject(it)}">
<f:entry title="${%Only account builds that match these parameters}"
field="combinationFilter">
<f:textbox />
</f:entry>
</j:if>
<j:if test="${!empty(descriptor.categories)}">
<f:entry title="${%Multi-Project Throttle Category}">
<j:forEach var="cat" items="${descriptor.categories}">
Expand Down