/**
* Copyright 2010 the original author or authors.
*
* Licensed 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 datameer.awstasks.ssh;
import static org.fest.assertions.Assertions.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import awstasks.com.jcraft.jsch.CachedSession;
import awstasks.com.jcraft.jsch.CipherNone;
import awstasks.com.jcraft.jsch.JSch;
import awstasks.com.jcraft.jsch.Session;
import awstasks.com.jcraft.jsch.jce.DH;
import awstasks.com.jcraft.jsch.jce.TripleDESCBC;
import datameer.awstasks.AbstractTest;
import datameer.awstasks.exec.ShellCommand;
import datameer.awstasks.exec.command.FreeFormCommand;
import datameer.awstasks.exec.handler.ExecCaptureLineHandler;
import datameer.awstasks.exec.handler.ExecCaptureLinesHandler;
import datameer.awstasks.exec.handler.ExecExitCodeHandler;
import datameer.awstasks.testsupport.junit.CheckBefore;
import datameer.awstasks.testsupport.junit.CheckBeforeRunner;
import datameer.awstasks.util.IoUtil;
/**
*
* In order to run this test successfully passwordless access to localhost needs to be enabled on
* your system.
*
*/
@RunWith(CheckBeforeRunner.class)
public class JschRunnerTest extends AbstractTest {
private static final String USER = System.getProperty("user.name");
private static final String HOST = "localhost";
@CheckBefore
public void checkBefore() throws Exception {
JschRunner jschRunner = createJschRunner();
jschRunner.testConnect();
// should be successful if you can connect passphraseless to localhost
}
private JschRunner createJschRunner() {
return createJschRunner(false);
}
private JschRunner createJschRunner(boolean cached) {
JschRunner jschRunner = new JschRunner(USER, HOST, cached);
jschRunner.setKeyfile(JschRunner.findStandardKeyFile(false));
return jschRunner;
}
@Test
public void testConnectWithKeyFileContent() throws Exception {
JschRunner jschRunner = new JschRunner(USER, HOST, true);
File keyFile = JschRunner.findStandardKeyFile(true);
String keyFileContent = readKeyFile(keyFile);
jschRunner.setKeyfileContent(keyFileContent);
ShellCommand<?> command = new ShellCommand<List<String>>(new String[] { "ls", "/" }, true);
jschRunner.execute(command);
// change to wrong password
jschRunner = new JschRunner(USER, HOST, true);
jschRunner.setKeyfileContent(keyFileContent.replaceAll("Y", "K"));
try {
jschRunner.execute(command);
fail("should throw exception");
} catch (Exception e) {
// expected
}
}
private String readKeyFile(File keyFile) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(keyFile)));
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
builder.append("\n");
}
reader.close();
return builder.toString();
}
@Test
public void testUpload() throws Exception {
JschRunner jschRunner = createJschRunner();
File sourceFile = new File("build.xml");
File destFile = new File(_tempFolder.newFolder("folder"), "file");
assertFalse(destFile.exists());
jschRunner.run(new ScpUploadCommand(sourceFile, destFile.getAbsolutePath()));
assertTrue(destFile.exists());
assertEquals(sourceFile.length(), destFile.length());
}
@Test
public void testUploadDownloadWithWhitespaceInName() throws Exception {
JschRunner jschRunner = createJschRunner();
File uploadSrcFile = _tempFolder.newFile("a file");
File uploadDestFolder = _tempFolder.newFolder("upload");
File uploadDestFile = new File(uploadDestFolder, uploadSrcFile.getName());
File downloadDestFolder = _tempFolder.newFolder("download");
File downloadDestFile = new File(downloadDestFolder, uploadSrcFile.getName());
assertFalse(uploadDestFile.exists());
jschRunner.run(new ScpUploadCommand(uploadSrcFile, uploadDestFolder.getAbsolutePath()));
assertTrue(uploadDestFile.exists());
assertEquals(uploadSrcFile.length(), downloadDestFile.length());
jschRunner.run(new ScpDownloadCommand(uploadDestFile.getAbsolutePath(), downloadDestFolder, false));
assertTrue(downloadDestFile.exists());
assertEquals(uploadSrcFile.length(), downloadDestFile.length());
}
@Test
public void testOpenFile() throws Exception {
JschRunner jschRunner = createJschRunner();
InputStream inputStream = jschRunner.openFile(new File("build.xml").getAbsolutePath());
doTestReadOfFile(inputStream);
}
@Test
public void testCreateFile() throws Exception {
File file = _tempFolder.newFile("remoteFile");
file.delete();
String message = "hello world";
JschRunner jschRunner = createJschRunner();
OutputStream outputStream = jschRunner.createFile(file.getAbsolutePath(), message.getBytes().length);
outputStream.write(message.getBytes());
outputStream.close();
BufferedReader reader = new BufferedReader(new FileReader(file));
String line = reader.readLine();
assertEquals(message, line);
assertNull(reader.readLine());
reader.close();
}
@Test
public void testOpen_WhitespaceInName() throws Exception {
File aFile = _tempFolder.newFile("a file");
JschRunner jschRunner = createJschRunner();
InputStream inputStream = jschRunner.openFile(aFile.getAbsolutePath());
inputStream.close();
}
@Test
public void testExecuteShellCommand() throws Exception {
JschRunner jschRunner = createJschRunner();
ShellCommand<?> command = new ShellCommand<List<String>>(new String[] { "ls", "/" }, true);
List<String> result = jschRunner.execute(command, new ExecCaptureLinesHandler());
assertFalse(result.isEmpty());
}
@Test
public void testExecuteShellCommandWithNoEofOutput() throws Exception {
JschRunner jschRunner = createJschRunner();
ShellCommand<List<String>> command = new ShellCommand<List<String>>(new String[] { "ls", "-dl", "/" }, true);
String result = jschRunner.execute(command, new ExecCaptureLineHandler());
assertNotNull(result);
}
@Test
public void testExecuteShellCommandCompleteOutput() throws Exception {
JschRunner jschRunner = createJschRunner();
ShellCommand<List<String>> command = new ShellCommand<List<String>>(new String[] { "ls", "-Al", new File(".").getAbsolutePath() }, true);
List<String> result = jschRunner.execute(command, new ExecCaptureLinesHandler());
assertThat(result.get(0), startsWith("total"));
for (int i = 1; i < result.size(); i++) {
assertEquals(9, split(result.get(i), " ").length);
}
assertNotNull(result);
}
@Test
public void testExecuteShellCommand_LineDoesNotEndWithNewLine() throws Exception {
_tempFolder.newFile("file1");
_tempFolder.newFile("file2");
JschRunner jschRunner = createJschRunner();
FreeFormCommand existsCommand = new FreeFormCommand("ls", "-lA", _tempFolder.getRoot().getAbsolutePath());
existsCommand.setFailOnError(false);
ExecCaptureLinesHandler lineHandler = new ExecCaptureLinesHandler();
int exitCode = jschRunner.execute(existsCommand, new ExecExitCodeHandler(), lineHandler);
assertEquals(0, exitCode);
List<String> lines = lineHandler.getResult(exitCode);
lines.remove(0);// total line
assertEquals(2, lines.size());
for (String line : lines) {
System.out.println("'" + line + "'");
assertFalse(line.endsWith("\n"));
}
}
@Test
public void testExecuteShellCommand_WithoutCachedSession() throws Exception {
JschRunner jschRunner = createJschRunner(false);
ShellCommand<?> command = new ShellCommand<List<String>>(new String[] { "ls", "/" }, true);
assertThat(jschRunner.execute(command, new ExecCaptureLinesHandler())).isNotEmpty();
assertThat(jschRunner.getCreatedSessions()).isEqualTo(1);
assertThat(jschRunner.execute(command, new ExecCaptureLinesHandler())).isNotEmpty();
assertThat(jschRunner.getCreatedSessions()).isEqualTo(2);
}
@Test
public void testExecuteShellCommand_WithCachedSession() throws Exception {
JschRunner jschRunner = createJschRunner(true);
ShellCommand<?> command = new ShellCommand<List<String>>(new String[] { "ls", "/" }, true);
assertThat(jschRunner.execute(command, new ExecCaptureLinesHandler())).isNotEmpty();
assertThat(jschRunner.getCreatedSessions()).isEqualTo(1);
assertThat(jschRunner.execute(command, new ExecCaptureLinesHandler())).isNotEmpty();
assertThat(jschRunner.getCreatedSessions()).isEqualTo(1);
}
@Test
public void testOpenFile_WithoutCachedSession() throws Exception {
JschRunner jschRunner = createJschRunner(false);
doTestReadOfFile(jschRunner.openFile(new File("build.xml").getAbsolutePath()));
assertThat(jschRunner.getCreatedSessions()).isEqualTo(1);
doTestReadOfFile(jschRunner.openFile(new File("build.xml").getAbsolutePath()));
assertThat(jschRunner.getCreatedSessions()).isEqualTo(2);
}
@Test
public void testOpenFile_WithCachedSession() throws Exception {
JschRunner jschRunner = createJschRunner(true);
doTestReadOfFile(jschRunner.openFile(new File("build.xml").getAbsolutePath()));
assertThat(jschRunner.getCreatedSessions()).isEqualTo(1);
doTestReadOfFile(jschRunner.openFile(new File("build.xml").getAbsolutePath()));
assertThat(jschRunner.getCreatedSessions()).isEqualTo(1);
}
private void doTestReadOfFile(InputStream inputStream) throws IOException {
int available = inputStream.available();
ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
assertThat(available, greaterThan(0));
IoUtil.copyBytes(inputStream, byteOutStream);
assertEquals(available, byteOutStream.size());
inputStream.close();
}
@Test
public void testUsingCachedSessionWithIsNotAliveAnymore() throws Exception {
JschRunner jschRunner = createJschRunner(true);
// open cached session
executeTestCommand(jschRunner);
Session session = jschRunner.openSession();
assertThat(session).isInstanceOf(CachedSession.class);
assertThat(session.isConnected()).isTrue();
assertThat(jschRunner.openSession()).isEqualTo(session);
((CachedSession) session).disconnect();
assertThat(session.isConnected()).isTrue();
((CachedSession) session).forcedDisconnect();
assertThat(session.isConnected()).isFalse();
// next use
executeTestCommand(jschRunner);
}
@Test
public void testUsingCachedSession_ConnectedCheckIsNotRelyingOnIsConnectedFlag() throws Exception {
JschRunner jschRunner = createJschRunner(true);
// open cached session
executeTestCommand(jschRunner);
Session session = jschRunner.openSession();
assertThat(session).isInstanceOf(CachedSession.class);
assertThat(session.isConnected()).isTrue();
assertThat(jschRunner.openSession()).isEqualTo(session);
// disconnect session
((CachedSession) session).forcedDisconnect();
session = Mockito.spy(session);
assertThat(session.isConnected()).isFalse();
Mockito.doReturn(true).when(session).isConnected();
assertThat(session.isConnected()).isTrue();
assertThat(JschRunner.isConnected(session)).isFalse();
// next use
assertThat(jschRunner.openSession()).isNotEqualTo(session);
executeTestCommand(jschRunner);
}
private void executeTestCommand(JschRunner jschRunner) throws IOException {
ShellCommand<?> command = new ShellCommand<List<String>>(new String[] { "ls", "/" }, true);
assertThat(jschRunner.execute(command, new ExecCaptureLinesHandler())).isNotEmpty();
}
private static String[] split(String string, String regex) {
List<String> splitList = new ArrayList<String>();
String[] splits = string.split(regex);
for (String split : splits) {
if (split.length() > 0) {
splitList.add(split);
}
}
return splitList.toArray(new String[splitList.size()]);
}
@Test
public void testCheckNotAllowedToChangeAConnectedAndCachedSshSession() throws Exception {
JschRunner jschRunner = createJschRunner(true);
jschRunner.openSession();
try {
jschRunner.setConfig(null);
fail();
} catch (Exception e) {
assertThat(e.getMessage()).isEqualTo("This instance of jsch is already connected please disconnect first.");
}
try {
jschRunner.setKeyfile(null);
fail();
} catch (Exception e) {
assertThat(e.getMessage()).isEqualTo("This instance of jsch is already connected please disconnect first.");
}
try {
jschRunner.setKeyfileContent(null);
fail();
} catch (Exception e) {
assertThat(e.getMessage()).isEqualTo("This instance of jsch is already connected please disconnect first.");
}
try {
jschRunner.setKnownHosts(null);
fail();
} catch (Exception e) {
assertThat(e.getMessage()).isEqualTo("This instance of jsch is already connected please disconnect first.");
}
try {
jschRunner.setPassword(null);
fail();
} catch (Exception e) {
assertThat(e.getMessage()).isEqualTo("This instance of jsch is already connected please disconnect first.");
}
try {
jschRunner.setPort(2);
fail();
} catch (Exception e) {
assertThat(e.getMessage()).isEqualTo("This instance of jsch is already connected please disconnect first.");
}
try {
jschRunner.setProxy(null);
fail();
} catch (Exception e) {
assertThat(e.getMessage()).isEqualTo("This instance of jsch is already connected please disconnect first.");
}
try {
jschRunner.setTrust(true);
fail();
} catch (Exception e) {
assertThat(e.getMessage()).isEqualTo("This instance of jsch is already connected please disconnect first.");
}
try {
jschRunner.setTimeout(1);
fail();
} catch (Exception e) {
assertThat(e.getMessage()).isEqualTo("This instance of jsch is already connected please disconnect first.");
}
jschRunner.disconnect();
}
@Test
public void testCheckAllowedToChangeAConnectedAndNotCachedSshSession() throws Exception {
JschRunner jschRunner = createJschRunner();
Session session = jschRunner.openSession();
try {
jschRunner.setConfig(null);
} catch (IllegalStateException e) {
fail();
}
try {
jschRunner.setKnownHosts(null);
} catch (IllegalStateException e) {
fail();
}
try {
jschRunner.setPort(2);
} catch (Exception e) {
fail();
}
try {
jschRunner.setProxy(null);
} catch (Exception e) {
fail();
}
try {
jschRunner.setTrust(true);
} catch (Exception e) {
fail();
}
try {
jschRunner.setTimeout(1);
} catch (Exception e) {
fail();
}
session.disconnect();
}
@Test
public void testDisconnectOnNonChachedSession_itShouldNotDisconnectSessionBecauseItIsNotKownToRunner() throws Exception {
JschRunner jschRunner = createJschRunner();
Session session = jschRunner.openSession();
assertThat(session.isConnected()).isTrue();
jschRunner.disconnect();
assertThat(session.isConnected()).isTrue();
session.disconnect();
}
@Test
public void testDisconnectOnChachedSession_itShouldDisconnectSessionBecauseItIsNotKownToRunner() throws Exception {
JschRunner jschRunner = createJschRunner(true);
Session session = jschRunner.openSession();
assertThat(session.isConnected()).isTrue();
jschRunner.disconnect();
assertThat(session.isConnected()).isFalse();
}
/**
* We jar jared jsch which configures security algorithms with class names. Make sure we have
* the jar-jared version.
*
* @throws Exception
*/
@Test
public void testCorrectSecurityProviderConfiguration() throws Exception {
assertThat(JSch.getConfig("dh")).isNotNull().isEqualTo(DH.class.getName());
assertThat(JSch.getConfig("3des-cbc")).isNotNull().isEqualTo(TripleDESCBC.class.getName());
assertThat(JSch.getConfig("none")).isNotNull().isEqualTo(CipherNone.class.getName());
}
}