package hudson.util; import static org.junit.Assert.*; import static org.junit.Assume.*; import java.io.File; import java.io.IOException; import hudson.EnvVars; import hudson.Functions; import hudson.Launcher; import hudson.Proc; import hudson.model.AbstractBuild; import hudson.model.BuildListener; import hudson.model.FreeStyleBuild; import hudson.model.FreeStyleProject; import hudson.tasks.Maven; import hudson.tasks.Shell; import hudson.util.ProcessTreeRemoting.IOSProcess; import org.junit.After; import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.ExtractResourceSCM; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.TestBuilder; import org.jvnet.hudson.test.TestExtension; import com.google.common.collect.ImmutableMap; public class ProcessTreeKillerTest { @Rule public JenkinsRule j = new JenkinsRule(); private Process process; @After public void tearDown() throws Exception { if (null != process) process.destroy(); } @Test public void manualAbortProcess() throws Exception { ProcessTree.enabled = true; FreeStyleProject project = j.createFreeStyleProject(); // this contains a maven project with a single test that sleeps 5s. project.setScm(new ExtractResourceSCM(getClass().getResource( "ProcessTreeKiller-test-project.jar"))); project.getBuildersList().add(new Maven("install", "maven")); // build the project, wait until tests are running, then cancel. project.scheduleBuild2(0).waitForStart(); FreeStyleBuild b = project.getLastBuild(); b.doStop(); Thread.sleep(1000); // will fail (at least on windows) if test process is still running b.getWorkspace().deleteRecursive(); } @Test @Issue("JENKINS-22641") public void processProperlyKilledUnix() throws Exception { ProcessTree.enabled = true; Assume.assumeFalse("This test does not involve windows", Functions.isWindows()); FreeStyleProject sleepProject = j.createFreeStyleProject(); FreeStyleProject processJob = j.createFreeStyleProject(); sleepProject.getBuildersList().add(new Shell("nohup sleep 100000 &")); j.assertBuildStatusSuccess(sleepProject.scheduleBuild2(0).get()); processJob.getBuildersList().add(new Shell("ps -ef | grep sleep")); j.assertLogNotContains("sleep 100000", processJob.scheduleBuild2(0).get()); } @Test public void doNotKillProcessWithCookie() throws Exception { Assume.assumeFalse("This test does not involve windows", Functions.isWindows()); ProcessTree.enabled = true; SpawnBuilder spawner = new SpawnBuilder(); FreeStyleProject p = j.createFreeStyleProject(); p.getBuildersList().add(spawner); try { j.buildAndAssertSuccess(p); assertTrue("Process should be running", spawner.proc.isAlive()); } finally { spawner.proc.kill(); assertTrue("Process should be dead", !spawner.proc.isAlive()); } } public static final class SpawnBuilder extends TestBuilder { private Proc proc; @Override public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { EnvVars envvars = build.getEnvironment(listener); envvars.addLine("BUILD_ID=dontKillMe"); proc = launcher.launch().envs(envvars).cmds("nohup", "sleep", "100000").start(); return true; } } @Test @Issue("JENKINS-9104") public void considersKillingVetos() throws Exception { // on some platforms where we fail to list any processes, this test will // just not work assumeTrue(ProcessTree.get() != ProcessTree.DEFAULT); // kick off a process we (shouldn't) kill ProcessBuilder pb = new ProcessBuilder(); pb.environment().put("cookie", "testKeepDaemonsAlive"); if (File.pathSeparatorChar == ';') { pb.command("cmd"); } else { pb.command("sleep", "5m"); } process = pb.start(); ProcessTree processTree = ProcessTree.get(); processTree.killAll(ImmutableMap.of("cookie", "testKeepDaemonsAlive")); try { process.exitValue(); fail("Process should have been excluded from the killing"); } catch (IllegalThreadStateException e) { // Means the process is still running } } @TestExtension("considersKillingVetos") public static class VetoAllKilling extends ProcessKillingVeto { @Override public VetoCause vetoProcessKilling(IOSProcess p) { return new VetoCause("Peace on earth"); } } }