diff --git a/pom.xml b/pom.xml
index 98f06a00..0e1f99c9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -151,6 +151,12 @@ THE SOFTWARE.
matrix-auth
test
+
+ org.jenkins-ci.plugins.pipeline-stage-view
+ pipeline-stage-view
+ 2.13
+ test
+
org.jenkins-ci.plugins.workflow
workflow-basic-steps
diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java
index 4da52f63..597ccfc8 100644
--- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java
+++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java
@@ -41,6 +41,7 @@
import org.jenkinsci.plugins.workflow.graph.StepNode;
import org.jenkinsci.plugins.workflow.graphanalysis.LinearBlockHoppingScanner;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
+import org.jenkinsci.plugins.workflow.support.actions.PauseAction;
import org.jenkinsci.plugins.workflow.support.concurrent.Timeout;
import org.jenkinsci.plugins.workflow.support.steps.ExecutorStepExecution.PlaceholderTask;
@@ -54,14 +55,17 @@ public class ThrottleQueueTaskDispatcher extends QueueTaskDispatcher {
@Deprecated
@Override
public @CheckForNull CauseOfBlockage canTake(Node node, Task task) {
+ CauseOfBlockage cause = null;
if (Jenkins.getAuthentication().equals(ACL.SYSTEM)) {
- return canTakeImpl(node, task);
- }
-
- // Throttle-concurrent-builds requires READ permissions for all projects.
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
- return canTakeImpl(node, task);
+ cause = canTakeImpl(node, task);
+ } else {
+ // Throttle-concurrent-builds requires READ permissions for all projects.
+ try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ cause = canTakeImpl(node, task);
+ }
}
+ updatePauseAction(task, cause);
+ return cause;
}
private CauseOfBlockage canTakeImpl(Node node, Task task) {
@@ -218,14 +222,17 @@ private boolean shouldBeThrottled(@NonNull Task task, @CheckForNull ThrottleJobP
}
private CauseOfBlockage canRun(Task task, ThrottleJobProperty tjp, List pipelineCategories) {
+ CauseOfBlockage cause = null;
if (Jenkins.getAuthentication().equals(ACL.SYSTEM)) {
- return canRunImpl(task, tjp, pipelineCategories);
- }
-
- // Throttle-concurrent-builds requires READ permissions for all projects.
- try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
- return canRunImpl(task, tjp, pipelineCategories);
+ cause = canRunImpl(task, tjp, pipelineCategories);
+ } else {
+ // Throttle-concurrent-builds requires READ permissions for all projects.
+ try (ACLContext ctx = ACL.as(ACL.SYSTEM)) {
+ cause = canRunImpl(task, tjp, pipelineCategories);
+ }
}
+ updatePauseAction(task, cause);
+ return cause;
}
private CauseOfBlockage canRunImpl(Task task, ThrottleJobProperty tjp, List pipelineCategories) {
@@ -695,5 +702,31 @@ private int getMaxConcurrentPerNodeBasedOnMatchingLabels(
return maxConcurrentPerNodeLabeledIfMatch;
}
+ private void updatePauseAction(Task task, CauseOfBlockage cause) {
+ if (task instanceof PlaceholderTask) {
+ PlaceholderTask placeholderTask = (PlaceholderTask) task;
+ try {
+ FlowNode flowNode = placeholderTask.getNode();
+ if (flowNode == null) {
+ return;
+ }
+
+ if (cause != null) {
+ if (PauseAction.getCurrentPause(flowNode) == null) {
+ flowNode.addAction(new PauseAction(cause.getShortDescription()));
+ }
+ } else {
+ if (PauseAction.getCurrentPause(flowNode) != null) {
+ PauseAction.endCurrentPause(flowNode);
+ }
+ }
+ } catch (IOException | InterruptedException e) {
+ LOGGER.log(Level.WARNING, "Error setting pause action on pipeline {0}: {1}", new Object[] {
+ task.getDisplayName(), e
+ });
+ }
+ }
+ }
+
private static final Logger LOGGER = Logger.getLogger(ThrottleQueueTaskDispatcher.class.getName());
}
diff --git a/src/test/java/hudson/plugins/throttleconcurrents/TestUtil.java b/src/test/java/hudson/plugins/throttleconcurrents/TestUtil.java
index 37929815..fd95de0e 100644
--- a/src/test/java/hudson/plugins/throttleconcurrents/TestUtil.java
+++ b/src/test/java/hudson/plugins/throttleconcurrents/TestUtil.java
@@ -8,6 +8,7 @@
import hudson.model.Computer;
import hudson.model.Executor;
import hudson.model.Node;
+import hudson.model.Queue;
import hudson.model.queue.CauseOfBlockage;
import hudson.slaves.DumbSlave;
import hudson.slaves.RetentionStrategy;
@@ -17,6 +18,7 @@
import java.util.Set;
import jenkins.model.queue.CompositeCauseOfBlockage;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
+import org.jenkinsci.plugins.workflow.support.actions.PauseAction;
import org.jenkinsci.plugins.workflow.support.steps.ExecutorStepExecution;
import org.junit.rules.TemporaryFolder;
import org.jvnet.hudson.test.JenkinsRule;
@@ -96,6 +98,12 @@ static Set getBlockageReasons(CauseOfBlockage cob) {
}
}
+ static void hasPauseActionForItem(Queue.Item item) throws Exception {
+ assertTrue(item.task instanceof ExecutorStepExecution.PlaceholderTask);
+ ExecutorStepExecution.PlaceholderTask task = (ExecutorStepExecution.PlaceholderTask) item.task;
+ assertNotNull(task.getNode().getAction(PauseAction.class));
+ }
+
static void hasPlaceholderTaskForRun(Node n, WorkflowRun r) throws Exception {
for (Executor exec : n.toComputer().getExecutors()) {
if (exec.getCurrentExecutable() != null) {
diff --git a/src/test/java/hudson/plugins/throttleconcurrents/ThrottleStepTest.java b/src/test/java/hudson/plugins/throttleconcurrents/ThrottleStepTest.java
index 07cec6d8..0a8c3b3e 100644
--- a/src/test/java/hudson/plugins/throttleconcurrents/ThrottleStepTest.java
+++ b/src/test/java/hudson/plugins/throttleconcurrents/ThrottleStepTest.java
@@ -95,6 +95,7 @@ public void onePerNode() throws Exception {
blockageReasons,
hasItem(Messages._ThrottleQueueTaskDispatcher_MaxCapacityOnNode(1)
.toString()));
+ TestUtil.hasPauseActionForItem(queuedItem);
assertEquals(1, agent.toComputer().countBusy());
TestUtil.hasPlaceholderTaskForRun(agent, firstJobFirstRun);
@@ -176,6 +177,8 @@ public void multipleCategories() throws Exception {
WorkflowRun thirdJobFirstRun = thirdJob.scheduleBuild2(0).waitForStart();
j.waitForMessage("Still waiting to schedule task", thirdJobFirstRun);
assertFalse(j.jenkins.getQueue().isEmpty());
+ Queue.Item queuedItem = j.jenkins.getQueue().getItems()[0];
+ TestUtil.hasPauseActionForItem(queuedItem);
assertEquals(1, firstAgent.toComputer().countBusy());
TestUtil.hasPlaceholderTaskForRun(firstAgent, firstJobFirstRun);
@@ -245,6 +248,9 @@ public void onePerNodeParallel() throws Exception {
j.waitForMessage("Still waiting to schedule task", run1);
j.waitForMessage("Still waiting to schedule task", run2);
+ Queue.Item queuedItem = j.jenkins.getQueue().getItems()[0];
+ TestUtil.hasPauseActionForItem(queuedItem);
+
SemaphoreStep.success("wait-first-branch-a-job/1", null);
SemaphoreStep.waitForStart("wait-first-branch-c-job/1", run1);
assertEquals(1, firstAgent.toComputer().countBusy());
@@ -293,6 +299,8 @@ public void twoTotal() throws Exception {
j.waitForMessage("Still waiting to schedule task", thirdJobFirstRun);
j.jenkins.getQueue().maintain();
assertFalse(j.jenkins.getQueue().isEmpty());
+ Queue.Item queuedItem = j.jenkins.getQueue().getItems()[0];
+ TestUtil.hasPauseActionForItem(queuedItem);
assertEquals(1, firstAgent.toComputer().countBusy());
TestUtil.hasPlaceholderTaskForRun(firstAgent, firstJobFirstRun);
@@ -377,6 +385,8 @@ public boolean perform(AbstractBuild, ?> build, Launcher launcher, BuildListen
WorkflowRun secondJobFirstRun = secondJob.scheduleBuild2(0).waitForStart();
j.waitForMessage("Still waiting to schedule task", secondJobFirstRun);
assertFalse(j.jenkins.getQueue().isEmpty());
+ Queue.Item queuedItem = j.jenkins.getQueue().getItems()[0];
+ TestUtil.hasPauseActionForItem(queuedItem);
assertEquals(1, agent.toComputer().countBusy());
for (Executor e : agent.toComputer().getExecutors()) {
@@ -460,6 +470,7 @@ private String getThrottleScript(String jobName, List categories, String
return "throttle(["
+ StringUtils.join(quoted, ", ")
+ "]) {\n"
+ + "stage('wait') {\n"
+ " echo 'hi there'\n"
+ " node('"
+ label
@@ -468,6 +479,7 @@ private String getThrottleScript(String jobName, List categories, String
+ jobName
+ "-job'\n"
+ " }\n"
+ + "}\n"
+ "}\n";
}