mirror of https://github.com/apache/jclouds.git
Issue 861:SSHClient should provide access to input/output streams
This commit is contained in:
parent
38de846947
commit
dd7b16075e
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.jclouds.compute.domain;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.io.Closeables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A current connection to an exec'd command. Please ensure you call {@link ExecChannel#close}
|
||||||
|
*
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
public class ExecChannel implements Closeable {
|
||||||
|
|
||||||
|
private final OutputStream input;
|
||||||
|
private final InputStream output;
|
||||||
|
private final InputStream error;
|
||||||
|
private final Supplier<Integer> exitStatus;
|
||||||
|
private final Closeable closer;
|
||||||
|
|
||||||
|
public ExecChannel(OutputStream input, InputStream output, InputStream error, Supplier<Integer> exitStatus,
|
||||||
|
Closeable closer) {
|
||||||
|
this.input = checkNotNull(input, "input");
|
||||||
|
this.output = checkNotNull(output, "output");
|
||||||
|
this.error = checkNotNull(error, "error");
|
||||||
|
this.exitStatus = checkNotNull(exitStatus, "exitStatus");
|
||||||
|
this.closer = checkNotNull(closer, "closer");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the command's {@code stdin} stream.
|
||||||
|
*/
|
||||||
|
public OutputStream getInput() {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the command's {@code stderr} stream.
|
||||||
|
*/
|
||||||
|
public InputStream getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the command's {@code stdout} stream.
|
||||||
|
*/
|
||||||
|
public InputStream getOutput() {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the exit status of the command if it was received, or {@code null} if this information
|
||||||
|
* was not received.
|
||||||
|
*/
|
||||||
|
public Supplier<Integer> getExitStatus() {
|
||||||
|
return exitStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* closes resources associated with this channel.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
Closeables.closeQuietly(input);
|
||||||
|
Closeables.closeQuietly(output);
|
||||||
|
Closeables.closeQuietly(error);
|
||||||
|
closer.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.ssh;
|
package org.jclouds.ssh;
|
||||||
|
|
||||||
|
import org.jclouds.compute.domain.ExecChannel;
|
||||||
import org.jclouds.compute.domain.ExecResponse;
|
import org.jclouds.compute.domain.ExecResponse;
|
||||||
import org.jclouds.domain.Credentials;
|
import org.jclouds.domain.Credentials;
|
||||||
import org.jclouds.domain.LoginCredentials;
|
import org.jclouds.domain.LoginCredentials;
|
||||||
|
@ -50,9 +51,24 @@ public interface SshClient {
|
||||||
void put(String path, Payload contents);
|
void put(String path, Payload contents);
|
||||||
|
|
||||||
Payload get(String path);
|
Payload get(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a process and block until it is complete
|
||||||
|
*
|
||||||
|
* @param command command line to invoke
|
||||||
|
* @return output of the command
|
||||||
|
*/
|
||||||
ExecResponse exec(String command);
|
ExecResponse exec(String command);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a process and allow the user to interact with it
|
||||||
|
*
|
||||||
|
* @param command command line to invoke
|
||||||
|
* @return reference to the running process
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
ExecChannel execChannel(String command);
|
||||||
|
|
||||||
void connect();
|
void connect();
|
||||||
|
|
||||||
void disconnect();
|
void disconnect();
|
||||||
|
|
|
@ -30,6 +30,7 @@ import static org.jclouds.crypto.CryptoStreams.md5;
|
||||||
import static org.jclouds.crypto.SshKeys.fingerprintPrivateKey;
|
import static org.jclouds.crypto.SshKeys.fingerprintPrivateKey;
|
||||||
import static org.jclouds.crypto.SshKeys.sha1PrivateKey;
|
import static org.jclouds.crypto.SshKeys.sha1PrivateKey;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
|
@ -41,6 +42,7 @@ import javax.inject.Named;
|
||||||
|
|
||||||
import org.apache.commons.io.input.ProxyInputStream;
|
import org.apache.commons.io.input.ProxyInputStream;
|
||||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||||
|
import org.jclouds.compute.domain.ExecChannel;
|
||||||
import org.jclouds.compute.domain.ExecResponse;
|
import org.jclouds.compute.domain.ExecResponse;
|
||||||
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
@ -57,6 +59,7 @@ import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.io.Closeables;
|
import com.google.common.io.Closeables;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.jcraft.jsch.ChannelExec;
|
import com.jcraft.jsch.ChannelExec;
|
||||||
|
@ -125,7 +128,7 @@ public class JschSshClient implements SshClient {
|
||||||
private final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
|
private final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
|
||||||
|
|
||||||
public JschSshClient(BackoffLimitedRetryHandler backoffLimitedRetryHandler, IPSocket socket, int timeout,
|
public JschSshClient(BackoffLimitedRetryHandler backoffLimitedRetryHandler, IPSocket socket, int timeout,
|
||||||
String username, String password, byte[] privateKey) {
|
String username, String password, byte[] privateKey) {
|
||||||
this.host = checkNotNull(socket, "socket").getAddress();
|
this.host = checkNotNull(socket, "socket").getAddress();
|
||||||
checkArgument(socket.getPort() > 0, "ssh port must be greater then zero" + socket.getPort());
|
checkArgument(socket.getPort() > 0, "ssh port must be greater then zero" + socket.getPort());
|
||||||
checkArgument(password != null || privateKey != null, "you must specify a password or a key");
|
checkArgument(password != null || privateKey != null, "you must specify a password or a key");
|
||||||
|
@ -138,10 +141,10 @@ public class JschSshClient implements SshClient {
|
||||||
if (privateKey == null) {
|
if (privateKey == null) {
|
||||||
this.toString = String.format("%s:pw[%s]@%s:%d", username, hex(md5(password.getBytes())), host, port);
|
this.toString = String.format("%s:pw[%s]@%s:%d", username, hex(md5(password.getBytes())), host, port);
|
||||||
} else {
|
} else {
|
||||||
String fingerPrint = fingerprintPrivateKey(new String(privateKey));
|
String fingerPrint = fingerprintPrivateKey(new String(privateKey));
|
||||||
String sha1 = sha1PrivateKey(new String(privateKey));
|
String sha1 = sha1PrivateKey(new String(privateKey));
|
||||||
this.toString = String.format("%s:rsa[fingerprint(%s),sha1(%s)]@%s:%d", username, fingerPrint, sha1, host,
|
this.toString = String.format("%s:rsa[fingerprint(%s),sha1(%s)]@%s:%d", username, fingerPrint, sha1, host,
|
||||||
port);
|
port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +184,8 @@ public class JschSshClient implements SshClient {
|
||||||
} else {
|
} else {
|
||||||
// jsch wipes out your private key
|
// jsch wipes out your private key
|
||||||
if (CredentialUtils.isPrivateKeyEncrypted(privateKey)) {
|
if (CredentialUtils.isPrivateKeyEncrypted(privateKey)) {
|
||||||
throw new IllegalArgumentException("JschSshClientModule does not support private keys that require a passphrase");
|
throw new IllegalArgumentException(
|
||||||
|
"JschSshClientModule does not support private keys that require a passphrase");
|
||||||
}
|
}
|
||||||
jsch.addIdentity(username, Arrays.copyOf(privateKey, privateKey.length), null, emptyPassPhrase);
|
jsch.addIdentity(username, Arrays.copyOf(privateKey, privateKey.length), null, emptyPassPhrase);
|
||||||
}
|
}
|
||||||
|
@ -323,8 +327,8 @@ public class JschSshClient implements SshClient {
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean shouldRetry(Exception from) {
|
boolean shouldRetry(Exception from) {
|
||||||
Predicate<Throwable> predicate = retryAuth ? Predicates.<Throwable>or(retryPredicate, instanceOf(AuthorizationException.class))
|
Predicate<Throwable> predicate = retryAuth ? Predicates.<Throwable> or(retryPredicate,
|
||||||
: retryPredicate;
|
instanceOf(AuthorizationException.class)) : retryPredicate;
|
||||||
if (any(getCausalChain(from), predicate))
|
if (any(getCausalChain(from), predicate))
|
||||||
return true;
|
return true;
|
||||||
if (!retryableMessages.equals(""))
|
if (!retryableMessages.equals(""))
|
||||||
|
@ -343,7 +347,7 @@ public class JschSshClient implements SshClient {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Throwable arg0) {
|
public boolean apply(Throwable arg0) {
|
||||||
return (arg0.toString().indexOf(input) != -1)
|
return (arg0.toString().indexOf(input) != -1)
|
||||||
|| (arg0.getMessage() != null && arg0.getMessage().indexOf(input) != -1);
|
|| (arg0.getMessage() != null && arg0.getMessage().indexOf(input) != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -361,7 +365,7 @@ public class JschSshClient implements SshClient {
|
||||||
if (e.getMessage() != null && e.getMessage().indexOf("Auth fail") != -1)
|
if (e.getMessage() != null && e.getMessage().indexOf("Auth fail") != -1)
|
||||||
throw new AuthorizationException("(" + toString() + ") " + message, e);
|
throw new AuthorizationException("(" + toString() + ") " + message, e);
|
||||||
throw e instanceof SshException ? SshException.class.cast(e) : new SshException(
|
throw e instanceof SshException ? SshException.class.cast(e) : new SshException(
|
||||||
"(" + toString() + ") " + message, e);
|
"(" + toString() + ") " + message, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -467,4 +471,60 @@ public class JschSshClient implements SshClient {
|
||||||
return this.username;
|
return this.username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ExecChannelConnection implements Connection<ExecChannel> {
|
||||||
|
private final String command;
|
||||||
|
private ChannelExec executor = null;
|
||||||
|
|
||||||
|
ExecChannelConnection(String command) {
|
||||||
|
this.command = checkNotNull(command, "command");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
if (executor != null)
|
||||||
|
executor.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecChannel create() throws Exception {
|
||||||
|
checkConnected();
|
||||||
|
String channel = "exec";
|
||||||
|
executor = (ChannelExec) session.openChannel(channel);
|
||||||
|
executor.setPty(true);
|
||||||
|
executor.setCommand(command);
|
||||||
|
ByteArrayOutputStream error = new ByteArrayOutputStream();
|
||||||
|
executor.setErrStream(error);
|
||||||
|
executor.connect();
|
||||||
|
return new ExecChannel(executor.getOutputStream(), executor.getInputStream(), executor.getErrStream(),
|
||||||
|
new Supplier<Integer>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer get() {
|
||||||
|
int exitStatus = executor.getExitStatus();
|
||||||
|
return exitStatus != -1 ? exitStatus : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}, new Closeable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ExecChannel(command=[" + command + "])";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecChannel execChannel(String command) {
|
||||||
|
return acquire(new ExecChannelConnection(command));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,16 @@ package org.jclouds.ssh.jsch;
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
import org.jclouds.compute.domain.ExecChannel;
|
||||||
import org.jclouds.compute.domain.ExecResponse;
|
import org.jclouds.compute.domain.ExecResponse;
|
||||||
import org.jclouds.domain.LoginCredentials;
|
import org.jclouds.domain.LoginCredentials;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
@ -39,6 +43,8 @@ import org.testng.annotations.BeforeGroups;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
|
import com.google.common.io.Closeables;
|
||||||
import com.google.inject.Guice;
|
import com.google.inject.Guice;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
|
|
||||||
|
@ -107,6 +113,22 @@ public class JschSshClientLiveTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecChannel execChannel(String command) {
|
||||||
|
if (command.equals("hostname")) {
|
||||||
|
return new ExecChannel(new ByteArrayOutputStream(), new ByteArrayInputStream(sshHost.getBytes()),
|
||||||
|
new ByteArrayInputStream(new byte[] {}), Suppliers.ofInstance(0), new Closeable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw new RuntimeException("command " + command + " not stubbed");
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
Injector i = Guice.createInjector(new JschSshClientModule(), new SLF4JLoggingModule());
|
Injector i = Guice.createInjector(new JschSshClientModule(), new SLF4JLoggingModule());
|
||||||
|
@ -147,4 +169,16 @@ public class JschSshClientLiveTest {
|
||||||
: sshHost);
|
: sshHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testExecChannelHostname() throws IOException {
|
||||||
|
ExecChannel response = setupClient().execChannel("hostname");
|
||||||
|
try {
|
||||||
|
assertEquals(Strings2.toStringAndClose(response.getError()), "");
|
||||||
|
assertEquals(Strings2.toStringAndClose(response.getOutput()).trim(), "localhost".equals(sshHost) ? InetAddress
|
||||||
|
.getLocalHost().getHostName() : sshHost);
|
||||||
|
} finally {
|
||||||
|
Closeables.closeQuietly(response);
|
||||||
|
}
|
||||||
|
assertEquals(response.getExitStatus().get(), new Integer(0));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -30,6 +30,7 @@ import static org.jclouds.crypto.CryptoStreams.md5;
|
||||||
import static org.jclouds.crypto.SshKeys.fingerprintPrivateKey;
|
import static org.jclouds.crypto.SshKeys.fingerprintPrivateKey;
|
||||||
import static org.jclouds.crypto.SshKeys.sha1PrivateKey;
|
import static org.jclouds.crypto.SshKeys.sha1PrivateKey;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
|
@ -46,6 +47,7 @@ import net.schmizz.sshj.common.IOUtils;
|
||||||
import net.schmizz.sshj.connection.ConnectionException;
|
import net.schmizz.sshj.connection.ConnectionException;
|
||||||
import net.schmizz.sshj.connection.channel.direct.PTYMode;
|
import net.schmizz.sshj.connection.channel.direct.PTYMode;
|
||||||
import net.schmizz.sshj.connection.channel.direct.Session;
|
import net.schmizz.sshj.connection.channel.direct.Session;
|
||||||
|
import net.schmizz.sshj.connection.channel.direct.SessionChannel;
|
||||||
import net.schmizz.sshj.connection.channel.direct.Session.Command;
|
import net.schmizz.sshj.connection.channel.direct.Session.Command;
|
||||||
import net.schmizz.sshj.sftp.SFTPClient;
|
import net.schmizz.sshj.sftp.SFTPClient;
|
||||||
import net.schmizz.sshj.sftp.SFTPException;
|
import net.schmizz.sshj.sftp.SFTPException;
|
||||||
|
@ -56,6 +58,7 @@ import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;
|
||||||
import net.schmizz.sshj.xfer.InMemorySourceFile;
|
import net.schmizz.sshj.xfer.InMemorySourceFile;
|
||||||
|
|
||||||
import org.apache.commons.io.input.ProxyInputStream;
|
import org.apache.commons.io.input.ProxyInputStream;
|
||||||
|
import org.jclouds.compute.domain.ExecChannel;
|
||||||
import org.jclouds.compute.domain.ExecResponse;
|
import org.jclouds.compute.domain.ExecResponse;
|
||||||
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
@ -71,7 +74,9 @@ import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.io.Closeables;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -499,6 +504,59 @@ public class SshjSshClient implements SshClient {
|
||||||
return acquire(new ExecConnection(command));
|
return acquire(new ExecConnection(command));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ExecChannelConnection implements Connection<ExecChannel> {
|
||||||
|
private final String command;
|
||||||
|
private SessionChannel session;
|
||||||
|
|
||||||
|
ExecChannelConnection(String command) {
|
||||||
|
this.command = checkNotNull(command, "command");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
if (session != null)
|
||||||
|
Closeables.closeQuietly(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecChannel create() throws Exception {
|
||||||
|
try {
|
||||||
|
session = SessionChannel.class.cast(acquire(execConnection()));
|
||||||
|
Command output = session.exec(command);
|
||||||
|
output.join(timeoutMillis, TimeUnit.SECONDS);
|
||||||
|
return new ExecChannel(session.getOutputStream(), session.getInputStream(), session.getErrorStream(),
|
||||||
|
new Supplier<Integer>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer get() {
|
||||||
|
return session.getExitStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
}, new Closeable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ExecChannel(command=[" + command + "])";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecChannel execChannel(String command) {
|
||||||
|
return acquire(new ExecChannelConnection(command));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHostAddress() {
|
public String getHostAddress() {
|
||||||
return this.host;
|
return this.host;
|
||||||
|
|
|
@ -20,12 +20,16 @@ package org.jclouds.sshj;
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
import org.jclouds.compute.domain.ExecChannel;
|
||||||
import org.jclouds.compute.domain.ExecResponse;
|
import org.jclouds.compute.domain.ExecResponse;
|
||||||
import org.jclouds.domain.LoginCredentials;
|
import org.jclouds.domain.LoginCredentials;
|
||||||
import org.jclouds.io.Payload;
|
import org.jclouds.io.Payload;
|
||||||
|
@ -39,6 +43,8 @@ import org.testng.annotations.BeforeGroups;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
|
import com.google.common.io.Closeables;
|
||||||
import com.google.inject.Guice;
|
import com.google.inject.Guice;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
|
|
||||||
|
@ -106,7 +112,22 @@ public class SshjSshClientLiveTest {
|
||||||
public void put(String path, String contents) {
|
public void put(String path, String contents) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecChannel execChannel(String command) {
|
||||||
|
if (command.equals("hostname")) {
|
||||||
|
return new ExecChannel(new ByteArrayOutputStream(), new ByteArrayInputStream(sshHost.getBytes()),
|
||||||
|
new ByteArrayInputStream(new byte[] {}), Suppliers.ofInstance(0), new Closeable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw new RuntimeException("command " + command + " not stubbed");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
Injector i = Guice.createInjector(new SshjSshClientModule(), new SLF4JLoggingModule());
|
Injector i = Guice.createInjector(new SshjSshClientModule(), new SLF4JLoggingModule());
|
||||||
|
@ -147,5 +168,16 @@ public class SshjSshClientLiveTest {
|
||||||
assertEquals(response.getOutput().trim(), "localhost".equals(sshHost) ? InetAddress.getLocalHost().getHostName()
|
assertEquals(response.getOutput().trim(), "localhost".equals(sshHost) ? InetAddress.getLocalHost().getHostName()
|
||||||
: sshHost);
|
: sshHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testExecChannelHostname() throws IOException {
|
||||||
|
ExecChannel response = setupClient().execChannel("hostname");
|
||||||
|
try {
|
||||||
|
assertEquals(Strings2.toStringAndClose(response.getError()), "");
|
||||||
|
assertEquals(Strings2.toStringAndClose(response.getOutput()).trim(), "localhost".equals(sshHost) ? InetAddress
|
||||||
|
.getLocalHost().getHostName() : sshHost);
|
||||||
|
} finally {
|
||||||
|
Closeables.closeQuietly(response);
|
||||||
|
}
|
||||||
|
assertEquals(response.getExitStatus().get(), new Integer(0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue