diff --git a/compute/src/main/java/org/jclouds/compute/domain/ExecResponse.java b/compute/src/main/java/org/jclouds/compute/domain/ExecResponse.java index 345d6da656..b74729b376 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/ExecResponse.java +++ b/compute/src/main/java/org/jclouds/compute/domain/ExecResponse.java @@ -22,14 +22,16 @@ import com.google.common.base.Objects; public class ExecResponse implements CustomizationResponse { + public static final int DEFAULT_EXIT_STATUS = -1; + private final String output; private final String error; private final int exitStatus; - public ExecResponse(String output, String error, int exitStatus) { + public ExecResponse(String output, String error, Integer exitStatus) { this.output = output; this.error = error; - this.exitStatus = exitStatus; + this.exitStatus = exitStatus != null ? exitStatus : DEFAULT_EXIT_STATUS; } public String getError() { diff --git a/compute/src/test/clojure/org/jclouds/ssh_test.clj b/compute/src/test/clojure/org/jclouds/ssh_test.clj index defc024885..494c936d5e 100644 --- a/compute/src/test/clojure/org/jclouds/ssh_test.clj +++ b/compute/src/test/clojure/org/jclouds/ssh_test.clj @@ -46,9 +46,9 @@ "Default exec function - replies to ./runscript status by returning 1" [cmd] (merge - {:exit 0 :err "stderr" :out "stdout"} + {:exit (Integer. 0) :err "stderr" :out "stdout"} (condp = cmd - "/tmp/init-bootstrap status" {:exit 1 :out "[]"} + "/tmp/init-bootstrap status" {:exit (Integer. 1) :out "[]"} {}))) diff --git a/drivers/sshj/src/main/java/org/jclouds/sshj/SshjSshClient.java b/drivers/sshj/src/main/java/org/jclouds/sshj/SshjSshClient.java index b07b0e2e6d..32632acb59 100644 --- a/drivers/sshj/src/main/java/org/jclouds/sshj/SshjSshClient.java +++ b/drivers/sshj/src/main/java/org/jclouds/sshj/SshjSshClient.java @@ -460,9 +460,8 @@ public class SshjSshClient implements SshClient { Command output = session.exec(checkNotNull(command, "command")); String outputString = IOUtils.readFully(output.getInputStream()).toString(); output.join(sshClientConnection.getSessionTimeout(), TimeUnit.MILLISECONDS); - int errorStatus = output.getExitStatus(); String errorString = IOUtils.readFully(output.getErrorStream()).toString(); - return new ExecResponse(outputString, errorString, errorStatus); + return new ExecResponse(outputString, errorString, output.getExitStatus()); } finally { clear(); } diff --git a/drivers/sshj/src/test/java/org/jclouds/sshj/SshjSshClientTest.java b/drivers/sshj/src/test/java/org/jclouds/sshj/SshjSshClientTest.java index eb2875b4fa..1d2e826e50 100644 --- a/drivers/sshj/src/test/java/org/jclouds/sshj/SshjSshClientTest.java +++ b/drivers/sshj/src/test/java/org/jclouds/sshj/SshjSshClientTest.java @@ -22,20 +22,28 @@ import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; +import static org.testng.Assert.assertEquals; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.net.ConnectException; import java.net.SocketTimeoutException; import java.util.Properties; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.common.SSHException; import net.schmizz.sshj.connection.ConnectionException; +import net.schmizz.sshj.connection.channel.direct.PTYMode; +import net.schmizz.sshj.connection.channel.direct.Session; +import net.schmizz.sshj.connection.channel.direct.Session.Command; import net.schmizz.sshj.sftp.SFTPException; import net.schmizz.sshj.transport.TransportException; import net.schmizz.sshj.userauth.UserAuthException; +import org.jclouds.compute.domain.ExecResponse; import org.jclouds.domain.LoginCredentials; import org.jclouds.logging.BufferLogger; import org.jclouds.logging.BufferLogger.Record; @@ -47,12 +55,14 @@ import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; +import com.google.common.collect.ImmutableMap; import com.google.common.net.HostAndPort; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; + @Test public class SshjSshClientTest { @@ -205,5 +215,38 @@ public class SshjSshClientTest { logcheck.assertLogDoesntContain("attempt 2 of 5"); Assert.assertEquals(Level.INFO, r.getLevel()); } + + public void testExecResponseHasDefaultExitStatusIfDriverReturnsNullExitStatus() throws Exception { + SSHClientConnection mockConnection = createMock(SSHClientConnection.class); + net.schmizz.sshj.SSHClient mockClient = createMock(net.schmizz.sshj.SSHClient.class); + Session session = createMock(Session.class); + Command command = createMock(Command.class); + SshjSshClient client = createClient(); + InputStream is = new ByteArrayInputStream( new byte[0] ); + + mockConnection.getSessionTimeout(); expectLastCall().andReturn(0); + mockClient.isConnected(); expectLastCall().andReturn(true); + mockClient.startSession(); expectLastCall().andReturn(session); + session.allocatePTY("vt100", 80, 24, 0, 0, ImmutableMap. of()); + expectLastCall(); + session.exec("some-command"); expectLastCall().andReturn( command ); + session.close(); expectLastCall(); + command.join(0, TimeUnit.MILLISECONDS); expectLastCall(); + command.getInputStream(); expectLastCall().andReturn(is); + command.getErrorStream(); expectLastCall().andReturn(is); + command.getExitStatus(); expectLastCall().andReturn(null); + replay(mockConnection); + replay(mockClient); + replay(session); + replay(command); + + mockConnection.ssh = mockClient; + client.sshClientConnection = mockConnection; + + ExecResponse response = client.exec( "some-command" ); + + assertEquals(response.getExitStatus(), ExecResponse.DEFAULT_EXIT_STATUS); + verify(mockConnection, mockClient, session, command); + } }