Skip to content
This repository has been archived by the owner on Dec 15, 2021. It is now read-only.

[JENKINS-25550] Hard kill #141

Merged
merged 10 commits into from Oct 26, 2015
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -4,6 +4,7 @@ Only noting significant user-visible or major API changes, not internal code cle

## 1.9 (upcoming)

* [JENKINS-25550](https://issues.jenkins-ci.org/browse/JENKINS-25550): flow builds hung due to a buggy step (and certain erroneous scripts) can now be forcibly stopped by clicking the stop button three times.
* [JENKINS-26860](https://issues.jenkins-ci.org/browse/JENKINS-26860): added _Execute concurrent builds if necessary_ option for Workflow projects.
* [JENKINS-28756](https://issues.jenkins-ci.org/browse/JENKINS-28756): dropdown for _General SCM_ step incorrectly listed SCMs not compatible with Workflow.

Expand Down
Expand Up @@ -25,12 +25,14 @@
package org.jenkinsci.plugins.workflow;

import hudson.model.BallColor;
import hudson.model.Executor;
import hudson.model.Item;
import hudson.model.ParametersAction;
import hudson.model.ParametersDefinitionProperty;
import hudson.model.Result;
import hudson.model.StringParameterDefinition;
import hudson.model.StringParameterValue;
import hudson.model.TaskListener;
import hudson.model.User;
import hudson.model.queue.QueueTaskFuture;
import hudson.security.ACL;
Expand All @@ -45,18 +47,26 @@
import org.jenkinsci.plugins.workflow.cps.CpsFlowExecution;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl;
import org.jenkinsci.plugins.workflow.steps.AbstractStepExecutionImpl;
import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl;
import org.jenkinsci.plugins.workflow.steps.StepContextParameter;
import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep;

import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;
import org.jvnet.hudson.test.recipes.LocalData;
import org.kohsuke.stapler.DataBoundConstructor;

public class WorkflowRunTest {

@ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();
@Rule public JenkinsRule r = new JenkinsRule();

WorkflowJob p;
Expand Down Expand Up @@ -201,4 +211,48 @@ public void contextInjectionOfSubParameters() throws Exception {
r.assertBuildStatusSuccess(JenkinsRuleExt.waitForCompletion(b));
}

@Issue("JENKINS-25550")
@Test public void hardKill() throws Exception {
p = r.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition("zombie()"));
WorkflowRun b = p.scheduleBuild2(0).waitForStart();
JenkinsRuleExt.waitForMessage("undead", b);
Executor ex = b.getExecutor();
assertNotNull(ex);
ex.interrupt();
JenkinsRuleExt.waitForMessage("bwahaha org.jenkinsci.plugins.workflow.steps.FlowInterruptedException #1", b);
ex.interrupt();
JenkinsRuleExt.waitForMessage("bwahaha org.jenkinsci.plugins.workflow.steps.FlowInterruptedException #2", b);
ex.interrupt();
JenkinsRuleExt.waitForMessage("Hard kill!", b);
JenkinsRuleExt.waitForCompletion(b);
r.assertBuildStatus(Result.ABORTED, b);
}
public static class Zombie extends AbstractStepImpl {
Copy link
Member

Choose a reason for hiding this comment

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

add new line. With @Test without new lines this block of code looks very unparsable by eyes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Prefer to keep all the lines relating to this one test case together.

@DataBoundConstructor public Zombie() {}
public static class Execution extends AbstractStepExecutionImpl {
@StepContextParameter private transient TaskListener listener;
int count;
@Override public boolean start() throws Exception {
listener.getLogger().println("undead");
return false;
}
@Override public void stop(Throwable cause) throws Exception {
listener.getLogger().println("bwahaha " + cause + " #" + ++count);
}

}
@TestExtension("hardKill") public static class DescriptorImpl extends AbstractStepDescriptorImpl {
public DescriptorImpl() {
super(Execution.class);
}
@Override public String getFunctionName() {
return "zombie";
}
@Override public String getDisplayName() {
return "zombie";
}
}
}

}
Expand Up @@ -68,6 +68,7 @@
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -202,10 +203,16 @@ public WorkflowRun(WorkflowJob job, File dir) throws IOException {

private AsynchronousExecution sleep() {
final AsynchronousExecution asynchronousExecution = new AsynchronousExecution() {
AtomicInteger interruptionCount = new AtomicInteger();
@Override public void interrupt(boolean forShutdown) {
if (forShutdown) {
return;
}
if (interruptionCount.incrementAndGet() == 3) {
listener.getLogger().println("Hard kill!");
finish(Result.ABORTED);
return;
}
try {
execution.interrupt(Result.ABORTED);
} catch (Exception x) {
Expand Down Expand Up @@ -503,7 +510,7 @@ public boolean hasntStartedYet() {

@Exported
@Override protected boolean isInProgress() {
return execution != null && !execution.isComplete();
return execution != null && !execution.isComplete() && (completed == null || !completed.get());
}

@Override public boolean isLogUpdated() {
Expand Down