diff --git a/core/src/main/java/hudson/slaves/CommandLauncher.java b/core/src/main/java/hudson/slaves/CommandLauncher.java index eceb307f0bcac49665681c5f1c2802b39381da2f..66cfaaf05636a18ae4e6ad53f0a9cc56d9442ae6 100644 --- a/core/src/main/java/hudson/slaves/CommandLauncher.java +++ b/core/src/main/java/hudson/slaves/CommandLauncher.java @@ -130,14 +130,7 @@ public class CommandLauncher extends ComputerLauncher { computer.setChannel(proc.getInputStream(), proc.getOutputStream(), listener.getLogger(), new Channel.Listener() { @Override public void onClosed(Channel channel, IOException cause) { - try { - int exitCode = proc.exitValue(); - if (exitCode!=0) { - listener.error("Process terminated with exit code "+exitCode); - } - } catch (IllegalThreadStateException e) { - // hasn't terminated yet - } + reportProcessTerminated(proc, listener); try { ProcessTree.get().killAll(proc, cookie); @@ -167,12 +160,23 @@ public class CommandLauncher extends ComputerLauncher { LOGGER.log(Level.SEVERE, msg, e); e.printStackTrace(listener.error(msg)); - if(_proc!=null) + if(_proc!=null) { + reportProcessTerminated(_proc, listener); try { ProcessTree.get().killAll(_proc, _cookie); } catch (InterruptedException x) { x.printStackTrace(listener.error(Messages.ComputerLauncher_abortedLaunch())); } + } + } + } + + private static void reportProcessTerminated(Process proc, TaskListener listener) { + try { + int exitCode = proc.exitValue(); + listener.error("Process terminated with exit code " + exitCode); + } catch (IllegalThreadStateException e) { + // hasn't terminated yet } } diff --git a/test/src/test/java/hudson/slaves/CommandLauncherTest.java b/test/src/test/java/hudson/slaves/CommandLauncherTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e2e215f163ec2631636c64e7a912a75e302dc43a --- /dev/null +++ b/test/src/test/java/hudson/slaves/CommandLauncherTest.java @@ -0,0 +1,88 @@ +/* + * The MIT License + * + * Copyright (c) 2015 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package hudson.slaves; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; +import hudson.Functions; +import hudson.model.Node; + +import java.util.Collections; + +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +public class CommandLauncherTest { + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Test + public void commandFails() throws Exception { + assumeTrue(!Functions.isWindows()); + DumbSlave slave = createSlave("false"); + + String log = slave.toComputer().getLog(); + assertTrue(log, slave.toComputer().isOffline()); + assertThat(log, containsString("ERROR: Process terminated with exit code")); + assertThat(log, not(containsString("ERROR: Process terminated with exit code 0"))); + } + + @Test + public void commandSuceedsWithoutChannel() throws Exception { + assumeTrue(!Functions.isWindows()); + DumbSlave slave = createSlave("true"); + + String log = slave.toComputer().getLog(); + assertTrue(log, slave.toComputer().isOffline()); + assertThat(log, containsString("ERROR: Process terminated with exit code 0")); + } + + public DumbSlave createSlave(String command) throws Exception { + DumbSlave slave; + synchronized (j.jenkins) { + slave = new DumbSlave( + "dummy", + "dummy", + j.createTmpDir().getPath(), + "1", + Node.Mode.NORMAL, + "", + new CommandLauncher(command), + RetentionStrategy.NOOP, + Collections.EMPTY_LIST + ); + j.jenkins.addNode(slave); + } + + Thread.sleep(100); + + return slave; + } +}