Skip to content

Commit 4df5f87

Browse files
committed
Cancel triggered build on InterruptException
When a triggering project is cancelled by interrupting its Executor thread and a triggered project is waiting in the build queue (via `waitForStart()`) the InterruptedException is not handled. The tiggered project is properly cancelled, but the triggering project is left in the queue and will eventually run when a slot is available. Catch InterruptException while waiting for the triggered project to start and cancel the queued run. Stacktrace with parameterized-trigger-2.44: org.jenkinsci.plugins.postbuildscript.PostBuildScriptException: java.lang.InterruptedException at org.jenkinsci.plugins.postbuildscript.processor.Processor.processBuildSteps(Processor.java:190) at org.jenkinsci.plugins.postbuildscript.processor.Processor.processScripts(Processor.java:91) at org.jenkinsci.plugins.postbuildscript.processor.Processor.process(Processor.java:79) at org.jenkinsci.plugins.postbuildscript.processor.Processor.process(Processor.java:73) at org.jenkinsci.plugins.postbuildscript.PostBuildScript.perform(PostBuildScript.java:116) at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20) at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:803) at hudson.model.AbstractBuild$AbstractBuildExecution.performAllBuildSteps(AbstractBuild.java:752) at hudson.model.Build$BuildExecution.post2(Build.java:177) at hudson.model.AbstractBuild$AbstractBuildExecution.post(AbstractBuild.java:697) at hudson.model.Run.execute(Run.java:1932) at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43) at hudson.model.ResourceController.execute(ResourceController.java:97) at hudson.model.Executor.run(Executor.java:429) Caused by: java.lang.InterruptedException at java.base/java.lang.Object.wait(Native Method) at java.base/java.lang.Object.wait(Object.java:328) at hudson.remoting.AsyncFutureImpl.get(AsyncFutureImpl.java:79) at hudson.model.queue.FutureImpl.waitForStart(FutureImpl.java:68) at hudson.plugins.parameterizedtrigger.TriggerBuilder.perform(TriggerBuilder.java:146) at org.jenkinsci.plugins.postbuildscript.processor.Processor.processBuildSteps(Processor.java:180) ... 13 more https://phabricator.wikimedia.org/T282893
1 parent 9a306fc commit 4df5f87

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

src/main/java/hudson/plugins/parameterizedtrigger/TriggerBuilder.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,15 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
148148
try {
149149
if (future != null ) {
150150
listener.getLogger().println("Waiting for the completion of " + HyperlinkNote.encodeTo('/'+ p.getUrl(), p.getFullDisplayName()));
151-
Run startedRun = future.waitForStart();
151+
Run startedRun;
152+
try {
153+
startedRun = future.waitForStart();
154+
} catch (InterruptedException x) {
155+
listener.getLogger().println( "Build aborting: cancelling queued project " + HyperlinkNote.encodeTo('/'+ p.getUrl(), p.getFullDisplayName()) );
156+
future.cancel(true);
157+
throw x; // rethrow so that the triggering project get flagged as cancelled
158+
}
159+
152160
listener.getLogger().println(HyperlinkNote.encodeTo('/' + startedRun.getUrl(), startedRun.getFullDisplayName()) + " started.");
153161

154162
Run completedRun = future.get();

src/test/java/hudson/plugins/parameterizedtrigger/test/TriggerBuilderTest.java

+32
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252

5353

5454
import java.util.ArrayList;
55+
import java.util.concurrent.Future;
5556
import java.util.Collections;
5657
import java.util.List;
5758
import java.util.ListIterator;
@@ -70,10 +71,13 @@
7071
import static org.junit.Assert.assertEquals;
7172
import static org.junit.Assert.assertFalse;
7273
import static org.junit.Assert.assertNotNull;
74+
import static org.junit.Assert.assertNull;
75+
import static org.junit.Assert.assertArrayEquals;
7376
import static org.junit.Assert.assertTrue;
7477
import static org.mockito.ArgumentMatchers.any;
7578
import static org.mockito.Mockito.when;
7679

80+
7781
public class TriggerBuilderTest {
7882

7983
@Rule
@@ -224,6 +228,34 @@ public void testNonBlockingTrigger() throws Exception {
224228
"Triggering projects: project1, project2, project3");
225229
}
226230

231+
@Test
232+
public void testCancelsDownstreamBuildWhenInterrupted() throws Exception{
233+
r.jenkins.setNumExecutors(1); // the downstream-project would be in the build queue
234+
235+
FreeStyleProject triggerProject = r.createFreeStyleProject("upstream-project");
236+
FreeStyleProject downstreamProject = r.createFreeStyleProject("downstream-project");
237+
238+
TriggerBuilder triggerBuilder = new TriggerBuilder(createTriggerConfig("downstream-project"));
239+
240+
triggerProject.getBuildersList().add(triggerBuilder);
241+
242+
QueueTaskFuture<FreeStyleBuild> parentBuild = triggerProject.scheduleBuild2(0);
243+
parentBuild.waitForStart();
244+
245+
// Now cancel the trigger project and let it finishes
246+
triggerProject.getLastBuild().getExecutor().interrupt();
247+
parentBuild.get();
248+
249+
assertLines(triggerProject.getLastBuild(),
250+
"Waiting for the completion of downstream-project",
251+
"Build aborting: cancelling queued project downstream-project",
252+
"Build was aborted",
253+
"Finished: ABORTED"
254+
);
255+
assertNull("No downstream build has been run", downstreamProject.getLastBuild());
256+
assertEquals("No build left in queue", 0, r.jenkins.getQueue().countBuildableItems());
257+
}
258+
227259
@Test
228260
public void testConsoleOutputWithCounterParameters() throws Exception{
229261
r.createFreeStyleProject("project1");

0 commit comments

Comments
 (0)