Skip to content

Commit

Permalink
Merge pull request #57 from jglick/exitStatus-JENKINS-48300
Browse files Browse the repository at this point in the history
[JENKINS-48300] Add an overload for exitStatus taking TaskListener
  • Loading branch information
svanoort committed Jan 26, 2018
2 parents 7f57bb2 + bc0e235 commit 7c12b3a
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 54 deletions.
Expand Up @@ -207,8 +207,8 @@ private FilePath pidFile(FilePath ws) throws IOException, InterruptedException {
return controlDir(ws).child("pid");
}

@Override public Integer exitStatus(FilePath workspace, Launcher launcher) throws IOException, InterruptedException {
Integer status = super.exitStatus(workspace, launcher);
@Override public Integer exitStatus(FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
Integer status = super.exitStatus(workspace, launcher, listener);
if (status != null) {
LOGGER.log(Level.FINE, "found exit code {0} in {1}", new Object[] {status, controlDir});
return status;
Expand All @@ -221,17 +221,18 @@ private FilePath pidFile(FilePath ws) throws IOException, InterruptedException {
lastCheck = now;
long currentTimestamp = getLogFile(workspace).lastModified();
if (currentTimestamp == 0) {
LOGGER.log(Level.FINE, "apparently never started in {0}", controlDir);
listener.getLogger().println("process apparently never started in " + controlDir);
return recordExitStatus(workspace, -2);
} else if (checkedTimestamp > 0) {
if (currentTimestamp < checkedTimestamp) {
LOGGER.log(Level.WARNING, "apparent clock skew in {0}", controlDir);
listener.getLogger().println("apparent clock skew in " + controlDir);
} else if (currentTimestamp < checkedTimestamp + TimeUnit.SECONDS.toMillis(HEARTBEAT_MINIMUM_DELTA)) {
FilePath pidFile = pidFile(workspace);
if (pidFile.exists()) {
LOGGER.log(Level.FINE, "still have {0} so heartbeat checks unreliable; process may or may not be alive", pidFile);
listener.getLogger().println("still have " + pidFile + " so heartbeat checks unreliable; process may or may not be alive");
} else {
LOGGER.log(Level.FINE, "heartbeat touches apparently not running in {0}", controlDir);
listener.getLogger().println("wrapper script does not seem to be touching the log file in " + controlDir);
listener.getLogger().println("(JENKINS-48300: if on a laggy filesystem, consider -Dorg.jenkinsci.plugins.durabletask.BourneShellScript.HEARTBEAT_CHECK_INTERVAL=300)");
return recordExitStatus(workspace, -1);
}
}
Expand Down
19 changes: 15 additions & 4 deletions src/main/java/org/jenkinsci/plugins/durabletask/Controller.java
Expand Up @@ -27,6 +27,7 @@
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.TaskListener;
import hudson.util.LogTaskListener;
import java.io.IOException;
import java.io.OutputStream;
Expand Down Expand Up @@ -55,17 +56,27 @@ public abstract class Controller implements Serializable {
* Checks whether the task has finished.
* @param workspace the workspace in use
* @param launcher a way to start processes
* @param logger a way to report special messages
* @return an exit code (zero is successful), or null if the task appears to still be running
*/
public @CheckForNull Integer exitStatus(FilePath workspace, Launcher launcher) throws IOException, InterruptedException {
if (Util.isOverridden(Controller.class, getClass(), "exitStatus", FilePath.class)) {
public @CheckForNull Integer exitStatus(FilePath workspace, Launcher launcher, TaskListener logger) throws IOException, InterruptedException {
if (Util.isOverridden(Controller.class, getClass(), "exitStatus", FilePath.class, Launcher.class)) {
return exitStatus(workspace, launcher);
} else if (Util.isOverridden(Controller.class, getClass(), "exitStatus", FilePath.class)) {
return exitStatus(workspace);
} else {
throw new AbstractMethodError("implement exitStatus(FilePath, Launcher)");
throw new AbstractMethodError("implement exitStatus(FilePath, Launcher, TaskListener)");
}
}

/** @deprecated use {@link #exitStatus(FilePath, Launcher)} instead */
/** @deprecated use {@link #exitStatus(FilePath, Launcher, TaskListener)} instead */
@Deprecated
public @CheckForNull Integer exitStatus(FilePath workspace, Launcher launcher) throws IOException, InterruptedException {
return exitStatus(workspace, launcher, TaskListener.NULL);
}

/** @deprecated use {@link #exitStatus(FilePath, Launcher, TaskListener)} instead */
@Deprecated
public @CheckForNull Integer exitStatus(FilePath workspace) throws IOException, InterruptedException {
return exitStatus(workspace, createLauncher(workspace));
}
Expand Down
Expand Up @@ -33,11 +33,13 @@
import hudson.remoting.RemoteOutputStream;
import hudson.remoting.VirtualChannel;
import hudson.slaves.WorkspaceList;
import hudson.util.StreamTaskListener;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
Expand Down Expand Up @@ -159,7 +161,7 @@ private static class WriteLog extends MasterToSlaveFileCallable<Long> {
}

// TODO would be more efficient to allow API to consolidate writeLog with exitStatus (save an RPC call)
@Override public Integer exitStatus(FilePath workspace, Launcher launcher) throws IOException, InterruptedException {
@Override public Integer exitStatus(FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
FilePath status = getResultFile(workspace);
if (status.exists()) {
try {
Expand Down Expand Up @@ -237,11 +239,12 @@ public FilePath getOutputFile(FilePath workspace) throws IOException, Interrupte
VirtualChannel channel = cd.getChannel();
String node = (channel instanceof Channel) ? ((Channel) channel).getName() : null;
String location = node != null ? cd.getRemote() + " on " + node : cd.getRemote();
Integer code = exitStatus(workspace, launcher);
StringWriter w = new StringWriter();
Integer code = exitStatus(workspace, launcher, new StreamTaskListener(w));
if (code != null) {
return "completed process (code " + code + ") in " + location;
return w + "completed process (code " + code + ") in " + location;
} else {
return "awaiting process completion in " + location;
return w + "awaiting process completion in " + location;
}
}

Expand Down
Expand Up @@ -82,12 +82,12 @@ public class BourneShellScriptTest {
@Test
public void smokeTest() throws Exception {
Controller c = new BourneShellScript("echo hello world").launch(new EnvVars(), ws, launcher, listener);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws,baos);
assertEquals(0, c.exitStatus(ws, launcher).intValue());
assertEquals(0, c.exitStatus(ws, launcher, listener).intValue());
assertTrue(baos.toString().contains("hello world"));
c.cleanup(ws);
}
Expand All @@ -99,14 +99,14 @@ public void smokeTest() throws Exception {
Controller c = new BourneShellScript("trap 'echo got SIGCHLD' CHLD; trap 'echo got SIGTERM' TERM; trap 'echo exiting; exit 99' EXIT; sleep 999").launch(new EnvVars(), ws, launcher, listener);
Thread.sleep(1000);
c.stop(ws, launcher);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws, baos);
String log = baos.toString();
System.out.println(log);
assertEquals(99, c.exitStatus(ws, launcher).intValue());
assertEquals(99, c.exitStatus(ws, launcher, listener).intValue());
assertTrue(log.contains("sleep 999"));
assertTrue(log.contains("got SIG"));
c.cleanup(ws);
Expand All @@ -117,37 +117,37 @@ public void smokeTest() throws Exception {
Thread.sleep(1000);
launcher.kill(Collections.singletonMap("killemall", "true"));
c.getResultFile(ws).delete();
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws, baos);
String log = baos.toString();
System.out.println(log);
assertEquals(Integer.valueOf(-1), c.exitStatus(ws, launcher));
assertEquals(Integer.valueOf(-1), c.exitStatus(ws, launcher, listener));
assertTrue(log.contains("sleep 999"));
c.cleanup(ws);
}

@Test public void justSlow() throws Exception {
Controller c = new BourneShellScript("sleep 60").launch(new EnvVars(), ws, launcher, listener);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
c.writeLog(ws, System.out);
assertEquals(0, c.exitStatus(ws, launcher).intValue());
assertEquals(0, c.exitStatus(ws, launcher, listener).intValue());
c.cleanup(ws);
}

@Issue("JENKINS-27152")
@Test public void cleanWorkspace() throws Exception {
Controller c = new BourneShellScript("touch stuff && echo ---`ls -1a`---").launch(new EnvVars(), ws, launcher, listener);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws, baos);
assertEquals(0, c.exitStatus(ws, launcher).intValue());
assertEquals(0, c.exitStatus(ws, launcher, listener).intValue());
assertThat(baos.toString(), containsString("---. .. stuff---"));
c.cleanup(ws);
}
Expand All @@ -157,12 +157,12 @@ public void smokeTest() throws Exception {
DurableTask task = new BourneShellScript("echo 42");
task.captureOutput();
Controller c = task.launch(new EnvVars(), ws, launcher, listener);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws, baos);
assertEquals(0, c.exitStatus(ws, launcher).intValue());
assertEquals(0, c.exitStatus(ws, launcher, listener).intValue());
assertThat(baos.toString(), containsString("+ echo 42"));
assertEquals("42\n", new String(c.getOutput(ws, launcher)));
c.cleanup(ws);
Expand All @@ -171,24 +171,24 @@ public void smokeTest() throws Exception {
@Issue("JENKINS-40734")
@Test public void envWithShellChar() throws Exception {
Controller c = new BourneShellScript("echo \"value=$MYNEWVAR\"").launch(new EnvVars("MYNEWVAR", "foo$$bar"), ws, launcher, listener);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws,baos);
assertEquals(0, c.exitStatus(ws, launcher).intValue());
assertEquals(0, c.exitStatus(ws, launcher, listener).intValue());
assertThat(baos.toString(), containsString("value=foo$$bar"));
c.cleanup(ws);
}

@Test public void shebang() throws Exception {
Controller c = new BourneShellScript("#!/bin/cat\nHello, world!").launch(new EnvVars(), ws, launcher, listener);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws, new TeeOutputStream(baos, System.out));
assertEquals(0, c.exitStatus(ws, launcher).intValue());
assertEquals(0, c.exitStatus(ws, launcher, listener).intValue());
assertThat(baos.toString(), containsString("Hello, world!"));
c.cleanup(ws);
}
Expand All @@ -209,12 +209,12 @@ private void runOnDocker(DumbSlave s) throws Exception {
FilePath dockerWS = s.getWorkspaceRoot();
Launcher dockerLauncher = s.createLauncher(listener);
Controller c = new BourneShellScript("echo hello world; sleep 10").launch(new EnvVars(), dockerWS, dockerLauncher, listener);
while (c.exitStatus(dockerWS, dockerLauncher) == null) {
while (c.exitStatus(dockerWS, dockerLauncher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(dockerWS, baos);
assertEquals(0, c.exitStatus(dockerWS, dockerLauncher).intValue());
assertEquals(0, c.exitStatus(dockerWS, dockerLauncher, listener).intValue());
assertTrue(baos.toString().contains("hello world"));
c.cleanup(dockerWS);
do {
Expand Down
Expand Up @@ -100,12 +100,12 @@ public class PowershellScriptTest {
Controller c = new PowershellScript("Write-Output \"Hello, World!\"; exit 1;").launch(new EnvVars(), ws, launcher, listener);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
TeeOutputStream tos = new TeeOutputStream(baos, System.err);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
c.writeLog(ws, tos);
Thread.sleep(100);
}
c.writeLog(ws, tos);
assertEquals(Integer.valueOf(1), c.exitStatus(ws, launcher));
assertEquals(Integer.valueOf(1), c.exitStatus(ws, launcher, listener));
String log = baos.toString();
assertTrue(log, log.contains("Hello, World!"));
c.cleanup(ws);
Expand All @@ -115,12 +115,12 @@ public class PowershellScriptTest {
Controller c = new PowershellScript("Write-Output \"Success!\";").launch(new EnvVars(), ws, launcher, listener);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
TeeOutputStream tos = new TeeOutputStream(baos, System.err);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
c.writeLog(ws, tos);
Thread.sleep(100);
}
c.writeLog(ws, tos);
assertEquals(Integer.valueOf(0), c.exitStatus(ws, launcher));
assertEquals(Integer.valueOf(0), c.exitStatus(ws, launcher, listener));
String log = baos.toString();
assertTrue(log, log.contains("Success!"));
c.cleanup(ws);
Expand All @@ -130,25 +130,25 @@ public class PowershellScriptTest {
Controller c = new PowershellScript("MyBogus-Cmdlet").launch(new EnvVars(), ws, launcher, listener);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
TeeOutputStream tos = new TeeOutputStream(baos, System.err);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
c.writeLog(ws, tos);
Thread.sleep(100);
}
c.writeLog(ws, tos);
assertTrue(c.exitStatus(ws, launcher).intValue() != 0);
assertTrue(c.exitStatus(ws, launcher, listener).intValue() != 0);
c.cleanup(ws);
}

@Test public void explicitError() throws Exception {
DurableTask task = new PowershellScript("Write-Output \"Hello, World!\"; throw \"explicit error\";");
task.captureOutput();
Controller c = task.launch(new EnvVars(), ws, launcher, listener);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws, baos);
assertTrue(c.exitStatus(ws, launcher).intValue() != 0);
assertTrue(c.exitStatus(ws, launcher, listener).intValue() != 0);
assertThat(baos.toString(), containsString("explicit error"));
c.cleanup(ws);
}
Expand All @@ -157,24 +157,24 @@ public class PowershellScriptTest {
DurableTask task = new PowershellScript("$VerbosePreference = \"Continue\"; Write-Verbose \"Hello, World!\"");
task.captureOutput();
Controller c = task.launch(new EnvVars(), ws, launcher, listener);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws, baos);
assertEquals(0, c.exitStatus(ws, launcher).intValue());
assertEquals(0, c.exitStatus(ws, launcher, listener).intValue());
assertThat(baos.toString(), containsString("Hello, World!"));
c.cleanup(ws);
}

@Test public void echoEnvVar() throws Exception {
Controller c = new PowershellScript("echo envvar=$env:MYVAR").launch(new EnvVars("MYVAR", "power$hell"), ws, launcher, listener);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
Thread.sleep(100);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
c.writeLog(ws,baos);
assertEquals(0, c.exitStatus(ws, launcher).intValue());
assertEquals(0, c.exitStatus(ws, launcher, listener).intValue());
assertThat(baos.toString(), containsString("envvar=power$hell"));
c.cleanup(ws);
}
Expand All @@ -183,12 +183,12 @@ public class PowershellScriptTest {
Controller c = new PowershellScript("Write-Output \"Helló, Wõrld ®\";").launch(new EnvVars(), ws, launcher, listener);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
TeeOutputStream tos = new TeeOutputStream(baos, System.err);
while (c.exitStatus(ws, launcher) == null) {
while (c.exitStatus(ws, launcher, listener) == null) {
c.writeLog(ws, tos);
Thread.sleep(100);
}
c.writeLog(ws, tos);
assertEquals(Integer.valueOf(0), c.exitStatus(ws, launcher));
assertEquals(Integer.valueOf(0), c.exitStatus(ws, launcher, listener));
String log = baos.toString("UTF-8");
assertTrue(log, log.contains("Helló, Wõrld ®"));
c.cleanup(ws);
Expand Down

0 comments on commit 7c12b3a

Please sign in to comment.