revamped task so that it has no external deps. Added patched SSHExec task

git-svn-id: http://jclouds.googlecode.com/svn/trunk@2581 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2010-01-03 07:00:06 +00:00
parent 7d70031a29
commit 53c6a2376a
6 changed files with 674 additions and 340 deletions

View File

@ -33,11 +33,6 @@
<jclouds.test.listener></jclouds.test.listener> <jclouds.test.listener></jclouds.test.listener>
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.jboss.shrinkwrap</groupId>
<artifactId>shrinkwrap-impl-base</artifactId>
<version>1.0.0-alpha-3</version>
</dependency>
<dependency> <dependency>
<groupId>${project.groupId}</groupId> <groupId>${project.groupId}</groupId>
<artifactId>jclouds-scriptbuilder</artifactId> <artifactId>jclouds-scriptbuilder</artifactId>

View File

@ -22,4 +22,4 @@
- ex. curl http://jclouds.rimuhosting.com/maven2/snapshots/org/jclouds/jclouds-antcontrib/1.0-SNAPSHOT/jclouds-antcontrib-1.0-20091215.023231-1-jar-with-dependencies.jar >jclouds-antcontrib-all.jar - ex. curl http://jclouds.rimuhosting.com/maven2/snapshots/org/jclouds/jclouds-antcontrib/1.0-SNAPSHOT/jclouds-antcontrib-1.0-20091215.023231-1-jar-with-dependencies.jar >jclouds-antcontrib-all.jar
2. invoke ant, adding the library above, and passing the properties 'host' 'username' 'keyfile' which corresponds to your remote credentials 2. invoke ant, adding the library above, and passing the properties 'host' 'username' 'keyfile' which corresponds to your remote credentials
- ex. ant -lib ~/.m2/repository/org/jclouds/jclouds-antcontrib/1.0-SNAPSHOT/jclouds-antcontrib-1.0-SNAPSHOT-jar-with-dependencies.jar -Dhost=67.202.42.237 -Dusername=root -Dkeyfile=$HOME/.ssh/id_dsa - ex. ant -lib ~/.m2/repository/org/jclouds/jclouds-antcontrib/1.0-SNAPSHOT/jclouds-antcontrib-1.0-SNAPSHOT-jar-with-dependencies.jar -Dhost=67.202.42.237 -Dusername=root -Dkeyfile=$HOME/.ssh/id_dsa
- ex. ant -lib jclouds-antcontrib-all.jar -Dhost=67.202.42.237 -Dusername=root -Dkeyfile=$HOME/.ssh/id_dsa - ex. ant -lib ~/.m2/repository/org/jclouds/jclouds-antcontrib/1.0-SNAPSHOT/jclouds-antcontrib-1.0-SNAPSHOT-jar-with-dependencies.jar -Dhost=localhost -Dusername=$USER -Dkeyfile=$HOME/.ssh/id_dsa

View File

@ -0,0 +1,394 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.tools.ant.taskdefs.optional.ssh;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.KeepAliveOutputStream;
import org.apache.tools.ant.util.TeeOutputStream;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
/**
* Executes a command on a remote machine via ssh.
* @since Ant 1.6 (created February 2, 2003)
*/
public class SSHExec extends SSHBase {
private static final int BUFFER_SIZE = 8192;
private static final int RETRY_INTERVAL = 500;
/** the command to execute via ssh */
private String command = null;
/** units are milliseconds, default is 0=infinite */
private long maxwait = 0;
/** for waiting for the command to finish */
private Thread thread = null;
private String outputProperty = null;
private File outputFile = null;
private String errorProperty = null;
private File errorFile = null;
private String resultProperty;
private boolean append = false;
private Resource commandResource = null;
private static final String TIMEOUT_MESSAGE =
"Timeout period exceeded, connection dropped.";
/**
* Constructor for SSHExecTask.
*/
public SSHExec() {
super();
}
/**
* Sets the command to execute on the remote host.
*
* @param command The new command value
*/
public void setCommand(String command) {
this.command = command;
}
/**
* Sets a commandResource from a file
* @param f the value to use.
* @since Ant 1.7.1
*/
public void setCommandResource(String f) {
this.commandResource = new FileResource(new File(f));
}
/**
* The connection can be dropped after a specified number of
* milliseconds. This is sometimes useful when a connection may be
* flaky. Default is 0, which means &quot;wait forever&quot;.
*
* @param timeout The new timeout value in seconds
*/
public void setTimeout(long timeout) {
maxwait = timeout;
}
/**
* If used, stores the output of the command to the given file.
*
* @param output The file to write to.
*/
public void setOutput(File output) {
outputFile = output;
}
/**
* If used, stores the error of the command to the given file.
*
* @param error The file to write to.
*/
public void setError(File error) {
this.errorFile = error;
}
/**
* Determines if the output is appended to the file given in
* <code>setOutput</code>. Default is false, that is, overwrite
* the file.
*
* @param append True to append to an existing file, false to overwrite.
*/
public void setAppend(boolean append) {
this.append = append;
}
/**
* If set, the output of the command will be stored in the given property.
*
* @param property The name of the property in which the command output
* will be stored.
*/
public void setOutputproperty(String property) {
outputProperty = property;
}
/**
* Set the property name whose value should be set to the error of
* the process.
*
* @param property The name of a property in which the standard error of
* the command should be stored.
*/
public void setErrorproperty(String property) {
errorProperty = property;
}
/**
* Set the name of the property in which the return code of the
* command should be stored. Only of interest if failonerror=false.
*
* @param resultProperty name of property.
*
* @since Ant 1.6
*/
public void setResultProperty(String resultProperty) {
this.resultProperty = resultProperty;
}
/**
* Execute the command on the remote host.
*
* @exception BuildException Most likely a network error or bad parameter.
*/
public void execute() throws BuildException {
if (getHost() == null) {
throw new BuildException("Host is required.");
}
if (getUserInfo().getName() == null) {
throw new BuildException("Username is required.");
}
if (getUserInfo().getKeyfile() == null
&& getUserInfo().getPassword() == null) {
throw new BuildException("Password or Keyfile is required.");
}
if (command == null && commandResource == null) {
throw new BuildException("Command or commandResource is required.");
}
Session session = null;
try {
session = openSession();
/* called once */
if (command != null) {
log("cmd : " + command, Project.MSG_INFO);
ExecResponse response = executeCommand(session, command);
if (outputProperty != null) {
getProject().setNewProperty(outputProperty, response.getOutput());
}
if (errorProperty != null) {
getProject().setNewProperty(outputProperty, response.getError());
}
if (resultProperty != null) {
getProject().setNewProperty(resultProperty, response.getCode()+"");
}
} else { // read command resource and execute for each command
try {
BufferedReader br = new BufferedReader(
new InputStreamReader(commandResource.getInputStream()));
String cmd;
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
int lastCode = -1;
while ((cmd = br.readLine()) != null) {
log("cmd : " + cmd, Project.MSG_INFO);
ExecResponse response = executeCommand(session, cmd);
output.append(response.getOutput());
error.append(response.getError());
lastCode = response.getCode();
}
if (outputProperty != null) {
getProject().setNewProperty(outputProperty, output.toString());
}
if (errorProperty != null) {
getProject().setNewProperty(outputProperty, error.toString());
}
if (resultProperty != null) {
getProject().setNewProperty(resultProperty, lastCode+"");
}
FileUtils.close(br);
} catch (IOException e) {
throw new BuildException(e);
}
}
} catch (JSchException e) {
throw new BuildException(e);
} finally {
if (session != null && session.isConnected()) {
session.disconnect();
}
}
}
private static class ExecResponse {
private final String output;
private final String error;
private final int code;
public ExecResponse(String output, String error, int code) {
this.output = output;
this.error = error;
this.code = code;
}
public String getError() {
return error;
}
public String getOutput() {
return output;
}
public int getCode() {
return code;
}
}
private ExecResponse executeCommand(Session session, String cmd)
throws BuildException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
TeeOutputStream tee = new TeeOutputStream(out, new KeepAliveOutputStream(System.out));
ByteArrayOutputStream err = new ByteArrayOutputStream();
TeeOutputStream teeErr = new TeeOutputStream(err, new KeepAliveOutputStream(System.err));
int ec = -1;
try {
final ChannelExec channel;
session.setTimeout((int) maxwait);
/* execute the command */
channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(cmd);
channel.setOutputStream(tee);
channel.setExtOutputStream(tee);
channel.setErrStream(teeErr);
channel.connect();
// wait for it to finish
thread =
new Thread() {
public void run() {
while (!channel.isClosed()) {
if (thread == null) {
return;
}
try {
sleep(RETRY_INTERVAL);
} catch (Exception e) {
// ignored
}
}
}
};
thread.start();
thread.join(maxwait);
if (thread.isAlive()) {
// ran out of time
thread = null;
if (getFailonerror()) {
throw new BuildException(TIMEOUT_MESSAGE);
} else {
log(TIMEOUT_MESSAGE, Project.MSG_ERR);
}
} else {
//success
if (outputFile != null) {
writeToFile(out.toString(), append, outputFile);
}
if (errorFile != null) {
writeToFile(err.toString(), append, errorFile);
}
// this is the wrong test if the remote OS is OpenVMS,
// but there doesn't seem to be a way to detect it.
ec = channel.getExitStatus();
if (ec != 0) {
String msg = "Remote command failed with exit status " + ec;
if (getFailonerror()) {
throw new BuildException(msg);
} else {
log(msg, Project.MSG_ERR);
}
}
}
} catch (BuildException e) {
throw e;
} catch (JSchException e) {
if (e.getMessage().indexOf("session is down") >= 0) {
if (getFailonerror()) {
throw new BuildException(TIMEOUT_MESSAGE, e);
} else {
log(TIMEOUT_MESSAGE, Project.MSG_ERR);
}
} else {
if (getFailonerror()) {
throw new BuildException(e);
} else {
log("Caught exception: " + e.getMessage(),
Project.MSG_ERR);
}
}
} catch (Exception e) {
if (getFailonerror()) {
throw new BuildException(e);
} else {
log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
}
}
return new ExecResponse(out.toString(), err.toString(), ec);
}
/**
* Writes a string to a file. If destination file exists, it may be
* overwritten depending on the "append" value.
*
* @param from string to write
* @param to file to write to
* @param append if true, append to existing file, else overwrite
* @exception Exception most likely an IOException
*/
private void writeToFile(String from, boolean append, File to)
throws IOException {
FileWriter out = null;
try {
out = new FileWriter(to.getAbsolutePath(), append);
StringReader in = new StringReader(from);
char[] buffer = new char[BUFFER_SIZE];
int bytesRead;
while (true) {
bytesRead = in.read(buffer);
if (bytesRead == -1) {
break;
}
out.write(buffer, 0, bytesRead);
}
out.flush();
} finally {
if (out != null) {
out.close();
}
}
}
}

View File

@ -21,324 +21,201 @@ package org.jclouds.tools.ant;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.scriptbuilder.domain.Statements.exec; import static org.jclouds.scriptbuilder.domain.Statements.exec;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.security.SecureRandom;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.List; import java.util.List;
import java.util.zip.ZipFile;
import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Location;
import org.apache.tools.ant.Project; import org.apache.tools.ant.Project;
import org.apache.tools.ant.Target;
import org.apache.tools.ant.Task; import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Java; import org.apache.tools.ant.taskdefs.Java;
import org.apache.tools.ant.taskdefs.optional.ssh.SSHExec;
import org.apache.tools.ant.taskdefs.optional.ssh.SSHUserInfo; import org.apache.tools.ant.taskdefs.optional.ssh.SSHUserInfo;
import org.apache.tools.ant.taskdefs.optional.ssh.Scp;
import org.apache.tools.ant.types.CommandlineJava; import org.apache.tools.ant.types.CommandlineJava;
import org.apache.tools.ant.types.Environment; import org.apache.tools.ant.types.Environment;
import org.apache.tools.ant.util.KeepAliveOutputStream; import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.util.TeeOutputStream; import org.apache.tools.ant.types.Path;
import org.jboss.shrinkwrap.api.Archives;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.importer.ExplodedImporter;
import org.jboss.shrinkwrap.api.importer.ZipImporter;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.scriptbuilder.domain.StatementList; import org.jclouds.scriptbuilder.domain.StatementList;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.io.Closeables;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
/** /**
* Ported from Jsch SSHExec task. * Version of the Java task that executes over ssh.
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class JavaOverSsh extends Java { public class JavaOverSsh extends Java {
/** Default listen port for SSH daemon */ private final SSHExec exec;
private static final int SSH_PORT = 22; private final Scp scp;
private final SSHUserInfo userInfo;
private String jvm = "/usr/bin/java"; private String jvm = "/usr/bin/java";
private File localDirectory; private File localDirectory;
private File remoteDirectory; private File remoteDirectory;
private Environment env = new Environment(); private Environment env = new Environment();
private String host;
private String knownHosts;
private int port = SSH_PORT;
private SSHUserInfo userInfo = new SSHUserInfo();
private OsFamily osFamily = OsFamily.UNIX; private OsFamily osFamily = OsFamily.UNIX;
/** units are milliseconds, default is 0=infinite */
private long maxwait = 0;
/** for waiting for the command to finish */
private Thread watchDog = null;
private File outputFile;
private String outputProperty;
private String resultProperty;
private File errorFile; private File errorFile;
private String errorProperty; private String errorProperty;
private File outputFile;
private String outputProperty;
private boolean append; private boolean append;
private static final String TIMEOUT_MESSAGE = "Timeout period exceeded, connection dropped.";
public JavaOverSsh() { public JavaOverSsh() {
super(); super();
setFork(true); setFork(true);
exec = new SSHExec();
scp = new Scp();
userInfo = new SSHUserInfo();
} }
public JavaOverSsh(Task owner) { public JavaOverSsh(Task owner) {
super(owner); this();
setFork(true); bindToOwner(owner);
} }
@Override @Override
public int executeJava() throws BuildException { public int executeJava() throws BuildException {
String command = convertJavaToScript(getCommandLine());
InputStream classpathJar = (getCommandLine().getClasspath() != null) ? makeClasspathJarOrNull(getCommandLine()
.getClasspath().list())
: null;
InputStream bootClasspathJar = (getCommandLine().getBootclasspath() != null) ? makeClasspathJarOrNull(getCommandLine()
.getBootclasspath().list())
: null;
InputStream currentDirectoryZip = Archives.create("cwd.zip", ZipExporter.class).as(
ExplodedImporter.class).importDirectory(localDirectory).as(ZipExporter.class)
.exportZip();
if (getHost() == null) {
throw new BuildException("Host is required.");
}
if (getUserInfo().getName() == null) {
throw new BuildException("Username is required.");
}
if (getUserInfo().getKeyfile() == null && getUserInfo().getPassword() == null) {
throw new BuildException("Password or Keyfile is required.");
}
Session session = null;
try {
// execute the command
session = openSession();
session.setTimeout((int) maxwait);
ChannelSftp sftp = null;
sftp = (ChannelSftp) session.openChannel("sftp");
sftp.connect();
sftp.put(currentDirectoryZip, remoteDirectory + "/cwd.zip");
Closeables.closeQuietly(currentDirectoryZip);
if (classpathJar != null || bootClasspathJar != null) {
if (classpathJar != null) {
sftp.put(classpathJar, remoteDirectory + "/classpath.jar");
Closeables.closeQuietly(classpathJar);
}
if (bootClasspathJar != null) {
sftp.put(classpathJar, remoteDirectory + "/boot-classpath.jar");
Closeables.closeQuietly(classpathJar);
}
}
final ChannelExec channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(command);
ByteArrayOutputStream out = new ByteArrayOutputStream();
TeeOutputStream teeOut = new TeeOutputStream(out, new KeepAliveOutputStream(System.out));
ByteArrayOutputStream err = new ByteArrayOutputStream();
TeeOutputStream teeErr = new TeeOutputStream(err, new KeepAliveOutputStream(System.err));
channel.setOutputStream(teeOut);
channel.setExtOutputStream(teeOut);
channel.setErrStream(teeErr);
channel.connect();
// wait for it to finish
watchDog = new Thread() {
public void run() {
while (!channel.isEOF()) {
if (watchDog == null) {
return;
}
try {
sleep(500);
} catch (Exception e) {
// ignored
}
}
}
};
watchDog.start();
watchDog.join(maxwait);
if (watchDog.isAlive()) {
// ran out of time
throw new BuildException(TIMEOUT_MESSAGE);
} else {
// completed successfully
return writeOutputAndReturnExitStatus(channel, out, err);
}
} catch (BuildException e) {
throw e;
} catch (JSchException e) {
if (e.getMessage().indexOf("session is down") >= 0) {
throw new BuildException(TIMEOUT_MESSAGE, e);
} else {
throw new BuildException(e);
}
} catch (Exception e) {
throw new BuildException(e);
} finally {
if (session != null && session.isConnected()) {
session.disconnect();
}
}
}
InputStream makeClasspathJarOrNull(String... paths) {
if (paths != null && paths.length > 0) {
JavaArchive classpathArchive = Archives.create("classpath.jar", JavaArchive.class);
for (String path : paths) {
File file = new File(path);
if (file.exists()) {
if (file.isFile()) {
try {
classpathArchive.as(ZipImporter.class).importZip(
new ZipFile(file.getAbsolutePath()));
} catch (IOException e) {
throw new BuildException(e);
}
} else {
classpathArchive.as(ExplodedImporter.class).importDirectory(file);
}
}
}
return classpathArchive.as(ZipExporter.class).exportZip();
}
return null;
}
private int writeOutputAndReturnExitStatus(final ChannelExec channel, ByteArrayOutputStream out,
ByteArrayOutputStream err) throws IOException {
if (outputProperty != null) {
getProject().setProperty(outputProperty, out.toString());
}
if (outputFile != null) {
writeToFile(err.toString(), append, outputFile);
}
if (errorProperty != null) {
getProject().setProperty(errorProperty, err.toString());
}
if (errorFile != null) {
writeToFile(out.toString(), append, errorFile);
}
if (resultProperty != null) {
getProject().setProperty(resultProperty, channel.getExitStatus() + "");
}
return channel.getExitStatus();
}
/**
* Writes a string to a file. If destination file exists, it may be overwritten depending on the
* "append" value.
*
* @param from
* string to write
* @param to
* file to write to
* @param append
* if true, append to existing file, else overwrite
* @exception Exception
* most likely an IOException
*/
private void writeToFile(String from, boolean append, File to) throws IOException {
FileWriter out = null;
try {
out = new FileWriter(to.getAbsolutePath(), append);
StringReader in = new StringReader(from);
char[] buffer = new char[8192];
int bytesRead;
while (true) {
bytesRead = in.read(buffer);
if (bytesRead == -1) {
break;
}
out.write(buffer, 0, bytesRead);
}
out.flush();
} finally {
if (out != null) {
out.close();
}
}
}
String convertJavaToScript(CommandlineJava commandLine) {
checkNotNull(jvm, "jvm must be set"); checkNotNull(jvm, "jvm must be set");
checkNotNull(localDirectory, "dir must be set");
checkNotNull(remoteDirectory, "remotedir must be set"); checkNotNull(remoteDirectory, "remotedir must be set");
// must copy the files over first as we are changing the system properties based on this.
if (localDirectory != null) {
FileSet cwd = new FileSet();
cwd.setDir(localDirectory);
mkdirAndCopyTo(remoteDirectory.getAbsolutePath(), ImmutableList.of(cwd));
}
if (getCommandLine().getClasspath() != null) {
copyPathTo(getCommandLine().getClasspath(), remoteDirectory.getAbsolutePath()
+ "/classpath");
}
if (getCommandLine().getBootclasspath() != null) {
copyPathTo(getCommandLine().getBootclasspath(), remoteDirectory.getAbsolutePath()
+ "/bootclasspath");
}
String command = convertJavaToScriptNormalizingPaths(getCommandLine());
String random = new SecureRandom().nextInt() + "";
exec.setResultProperty(random);
exec.setFailonerror(false);
exec.setCommand(command);
exec.setError(errorFile);
exec.setErrorproperty(errorProperty);
exec.setOutput(outputFile);
exec.setOutputproperty(outputProperty);
exec.setAppend(append);
exec.execute();
return Integer.parseInt(getProject().getProperty(random));
}
private void mkdirAndCopyTo(String destination, Iterable<FileSet> sets) {
scp.init();
exec.setCommand(exec("{md} " + destination).render(osFamily));
exec.execute();
String scpDestination = getScpDir(destination);
System.out.println("Sending to: " + scpDestination);
for (FileSet set : sets)
scp.addFileset(set);
scp.setTodir(scpDestination);
scp.execute();
}
private String getScpDir(String path) {
return String.format("%s:%s@%s:%s", userInfo.getName(),
userInfo.getKeyfile() == null ? userInfo.getPassword() : userInfo.getPassphrase(),
scp.getHost(), path);
}
void resetPathToUnderPrefixIfExistsAndIsFileIfNotExistsAddAsIs(Path path, String prefix,
StringBuilder destination) {
if (path == null)
return;
String[] paths = path.list();
if (paths != null && paths.length > 0) {
for (int i = 0; i < paths.length; i++) {
File file = new File(paths[i]);
if (file.exists()) {
// directories are flattened under the prefix anyway, so there's no need to add them
// to the path
if (file.isFile())
destination.append("{ps}").append(file.getName());
} else {
// if the file doesn't exist, it is probably a "forward reference" to something that
// is already on the remote machine
destination.append("{ps}").append(file.getAbsolutePath());
}
}
}
}
void copyPathTo(Path path, String destination) {
List<FileSet> filesets = Lists.newArrayList();
if (path.list() != null && path.list().length > 0) {
for (String filepath : path.list()) {
File file = new File(filepath);
if (file.exists()) {
FileSet fileset = new FileSet();
if (file.isFile()) {
fileset.setFile(file);
} else {
fileset.setDir(file);
}
filesets.add(fileset);
}
}
}
mkdirAndCopyTo(destination, filesets);
}
String convertJavaToScriptNormalizingPaths(CommandlineJava commandLine) {
List<Statement> statements = Lists.newArrayList(); List<Statement> statements = Lists.newArrayList();
String[] environment = env.getVariables(); String[] environment = env.getVariables();
if (environment != null) { if (environment != null) {
for (int i = 0; i < environment.length; i++) { for (int i = 0; i < environment.length; i++) {
log("Setting environment variable: " + environment[i], Project.MSG_VERBOSE); log("Setting environment variable: " + environment[i], Project.MSG_VERBOSE);
statements.add(exec("{export} " + environment[i])); String[] keyValue = environment[i].split("=");
statements.add(exec(String.format("{export} %s={vq}%s{vq}", keyValue[0], keyValue[1])));
} }
} }
statements.add(exec("{cd} " + remoteDirectory)); statements.add(exec("{cd} " + remoteDirectory));
statements.add(exec("jar -xf cwd.zip"));
StringBuilder commandBuilder = new StringBuilder(jvm); StringBuilder commandBuilder = new StringBuilder(jvm);
if (commandLine.getBootclasspath() != null if (getCommandLine().getBootclasspath() != null) {
&& commandLine.getBootclasspath().list().length > 0) { commandBuilder.append(" -Xbootclasspath:bootclasspath");
commandBuilder.append(" -Xbootclasspath:boot-classpath.jar"); resetPathToUnderPrefixIfExistsAndIsFileIfNotExistsAddAsIs(commandLine.getBootclasspath(),
"bootclasspath", commandBuilder);
} }
if (commandLine.getVmCommand().getArguments() != null if (commandLine.getVmCommand().getArguments() != null
&& commandLine.getVmCommand().getArguments().length > 0) { && commandLine.getVmCommand().getArguments().length > 0) {
commandBuilder.append(" "); commandBuilder.append(" ").append(
String[] variables = commandLine.getVmCommand().getArguments(); Joiner.on(' ').join(commandLine.getVmCommand().getArguments()));
for (int i = 0; i < variables.length; i++) {
commandBuilder.append(variables[i]);
if (i + 1 < variables.length)
commandBuilder.append(" ");
}
}
if (commandLine.getClasspath() != null && commandLine.getClasspath().list().length > 0) {
commandBuilder.append(" -cp classpath.jar");
} }
commandBuilder.append(" -cp classpath");
resetPathToUnderPrefixIfExistsAndIsFileIfNotExistsAddAsIs(commandLine.getClasspath(),
"classpath", commandBuilder);
if (commandLine.getSystemProperties() != null if (commandLine.getSystemProperties() != null
&& commandLine.getSystemProperties().getVariables() != null && commandLine.getSystemProperties().getVariables() != null
&& commandLine.getSystemProperties().getVariables().length > 0) { && commandLine.getSystemProperties().getVariables().length > 0) {
commandBuilder.append(" "); commandBuilder.append(" ").append(
String[] variables = commandLine.getSystemProperties().getVariables(); Joiner.on(' ').join(commandLine.getSystemProperties().getVariables()));
for (int i = 0; i < variables.length; i++) {
commandBuilder.append(variables[i]);
if (i + 1 < variables.length)
commandBuilder.append(" ");
}
} }
commandBuilder.append(" ").append(commandLine.getClassname()); commandBuilder.append(" ").append(commandLine.getClassname());
if (commandLine.getJavaCommand().getArguments() != null if (commandLine.getJavaCommand().getArguments() != null
&& commandLine.getJavaCommand().getArguments().length > 0) { && commandLine.getJavaCommand().getArguments().length > 0) {
commandBuilder.append(" "); commandBuilder.append(" ").append(
String[] variables = commandLine.getJavaCommand().getArguments(); Joiner.on(' ').join(commandLine.getJavaCommand().getArguments()));
for (int i = 0; i < variables.length; i++) {
commandBuilder.append(variables[i]);
if (i + 1 < variables.length)
commandBuilder.append(" ");
}
} }
statements.add(exec(commandBuilder.toString())); statements.add(exec(commandBuilder.toString()));
@ -351,6 +228,10 @@ public class JavaOverSsh extends Java {
env.addVariable(var); env.addVariable(var);
} }
/**
* Note that if the {@code dir} property is set, this will be copied recursively to the remote
* host.
*/
@Override @Override
public void setDir(File localDir) { public void setDir(File localDir) {
this.localDirectory = checkNotNull(localDir, "dir"); this.localDirectory = checkNotNull(localDir, "dir");
@ -378,7 +259,8 @@ public class JavaOverSsh extends Java {
* The new host value * The new host value
*/ */
public void setHost(String host) { public void setHost(String host) {
this.host = host; exec.setHost(host);
scp.setHost(host);
} }
/** /**
@ -387,7 +269,7 @@ public class JavaOverSsh extends Java {
* @return the host * @return the host
*/ */
public String getHost() { public String getHost() {
return host; return exec.getHost();
} }
/** /**
@ -397,6 +279,8 @@ public class JavaOverSsh extends Java {
* The new username value * The new username value
*/ */
public void setUsername(String username) { public void setUsername(String username) {
exec.setUsername(username);
scp.setUsername(username);
userInfo.setName(username); userInfo.setName(username);
} }
@ -407,6 +291,8 @@ public class JavaOverSsh extends Java {
* The new password value * The new password value
*/ */
public void setPassword(String password) { public void setPassword(String password) {
exec.setPassword(password);
scp.setPassword(password);
userInfo.setPassword(password); userInfo.setPassword(password);
} }
@ -417,7 +303,11 @@ public class JavaOverSsh extends Java {
* The new keyfile value * The new keyfile value
*/ */
public void setKeyfile(String keyfile) { public void setKeyfile(String keyfile) {
exec.setKeyfile(keyfile);
scp.setKeyfile(keyfile);
userInfo.setKeyfile(keyfile); userInfo.setKeyfile(keyfile);
if (userInfo.getPassphrase() == null)
userInfo.setPassphrase("");
} }
/** /**
@ -427,6 +317,8 @@ public class JavaOverSsh extends Java {
* The new passphrase value * The new passphrase value
*/ */
public void setPassphrase(String passphrase) { public void setPassphrase(String passphrase) {
exec.setPassphrase(passphrase);
scp.setPassphrase(passphrase);
userInfo.setPassphrase(passphrase); userInfo.setPassphrase(passphrase);
} }
@ -439,7 +331,8 @@ public class JavaOverSsh extends Java {
* a path to the known hosts file. * a path to the known hosts file.
*/ */
public void setKnownhosts(String knownHosts) { public void setKnownhosts(String knownHosts) {
this.knownHosts = knownHosts; exec.setKnownhosts(knownHosts);
scp.setKnownhosts(knownHosts);
} }
/** /**
@ -449,6 +342,8 @@ public class JavaOverSsh extends Java {
* if true trust the identity of unknown hosts. * if true trust the identity of unknown hosts.
*/ */
public void setTrust(boolean yesOrNo) { public void setTrust(boolean yesOrNo) {
exec.setTrust(yesOrNo);
scp.setTrust(yesOrNo);
userInfo.setTrust(yesOrNo); userInfo.setTrust(yesOrNo);
} }
@ -459,7 +354,8 @@ public class JavaOverSsh extends Java {
* port number of remote host. * port number of remote host.
*/ */
public void setPort(int port) { public void setPort(int port) {
this.port = port; exec.setPort(port);
scp.setPort(port);
} }
/** /**
@ -468,7 +364,7 @@ public class JavaOverSsh extends Java {
* @return the port * @return the port
*/ */
public int getPort() { public int getPort() {
return port; return exec.getPort();
} }
/** /**
@ -479,42 +375,8 @@ public class JavaOverSsh extends Java {
*/ */
public void init() throws BuildException { public void init() throws BuildException {
super.init(); super.init();
this.knownHosts = System.getProperty("user.home") + "/.ssh/known_hosts"; exec.init();
this.port = SSH_PORT; scp.init();
}
/**
* Open an ssh seession.
*
* @return the opened session
* @throws JSchException
* on error
*/
protected Session openSession() throws JSchException {
JSch jsch = new JSch();
if (null != userInfo.getKeyfile()) {
jsch.addIdentity(userInfo.getKeyfile());
}
if (!userInfo.getTrust() && knownHosts != null) {
log("Using known hosts: " + knownHosts, Project.MSG_DEBUG);
jsch.setKnownHosts(knownHosts);
}
Session session = jsch.getSession(userInfo.getName(), host, port);
session.setUserInfo(userInfo);
log("Connecting to " + host + ":" + port);
session.connect();
return session;
}
/**
* Get the user information.
*
* @return the user information
*/
protected SSHUserInfo getUserInfo() {
return userInfo;
} }
/** /**
@ -525,36 +387,85 @@ public class JavaOverSsh extends Java {
* The new timeout value in seconds * The new timeout value in seconds
*/ */
public void setTimeout(long timeout) { public void setTimeout(long timeout) {
maxwait = timeout; exec.setTimeout(timeout);
}
/**
* Set the verbose flag.
*
* @param verbose
* if true output more verbose logging
* @since Ant 1.6.2
*/
public void setVerbose(boolean verbose) {
exec.setVerbose(verbose);
scp.setVerbose(verbose);
} }
@Override @Override
public void setError(File out) { public void setError(File error) {
this.errorFile = out; this.errorFile = error;
} }
@Override @Override
public void setErrorProperty(String errorProp) { public void setErrorProperty(String property) {
this.errorProperty = errorProp; errorProperty = property;
} }
@Override @Override
public void setOutput(File out) { public void setOutput(File out) {
this.outputFile = out; outputFile = out;
} }
@Override @Override
public void setOutputproperty(String outputProp) { public void setOutputproperty(String outputProp) {
this.outputProperty = outputProp; outputProperty = outputProp;
}
@Override
public void setResultProperty(String resultProperty) {
this.resultProperty = resultProperty;
} }
@Override @Override
public void setAppend(boolean append) { public void setAppend(boolean append) {
this.append = append; this.append = append;
} }
@Override
public void setProject(Project project) {
super.setProject(project);
exec.setProject(project);
scp.setProject(project);
}
@Override
public void setOwningTarget(Target target) {
super.setOwningTarget(target);
exec.setOwningTarget(target);
scp.setOwningTarget(target);
}
@Override
public void setTaskName(String taskName) {
super.setTaskName(taskName);
exec.setTaskName(taskName);
scp.setTaskName(taskName);
}
@Override
public void setDescription(String description) {
super.setDescription(description);
exec.setDescription(description);
scp.setDescription(description);
}
@Override
public void setLocation(Location location) {
super.setLocation(location);
exec.setLocation(location);
scp.setLocation(location);
}
@Override
public void setTaskType(String type) {
super.setTaskType(type);
exec.setTaskType(type);
scp.setTaskType(type);
}
} }

View File

@ -24,33 +24,39 @@ import java.io.File;
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 java.net.UnknownHostException;
import java.util.Map.Entry;
import org.apache.tools.ant.Project; import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Java;
import org.apache.tools.ant.types.Environment; import org.apache.tools.ant.types.Environment;
import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Environment.Variable; import org.apache.tools.ant.types.Environment.Variable;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.Iterables;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "unit", testName = "jclouds.JavaOverSshTest") @Test(groups = "unit", testName = "jclouds.JavaOverSshTest")
public class JavaOverSshTest { public class JavaOverSshTest {
public static final Entry<String, String> LAST_ENV = Iterables.getLast(System.getenv()
.entrySet());
// TODO, this test will break in windows
public void testFull() throws SecurityException, NoSuchMethodException { public void testFull() throws SecurityException, NoSuchMethodException {
JavaOverSsh task = createTask(); JavaOverSsh task = makeJavaOverSsh();
assertEquals( String expected = String
String .format(
.format( "export %s=\"%s\"%ncd /tmp/foo\n%s -Xms16m -Xmx32m -cp classpath -Dfooble=baz -Dfoo=bar org.jclouds.tools.ant.TestClass %s hello world\n",
"cd /tmp/foo\njar -xf cwd.zip\n%s -Xms256 -cp classpath.jar -Dfooble=baz -Dfoo=bar org.jclouds.tools.ant.TestClass hello world\n", LAST_ENV.getKey(), LAST_ENV.getValue(), System.getProperty("java.home")
System.getProperty("java.home") + "/bin/java", System + "/bin/java", LAST_ENV.getKey());
.getProperty("user.dir")), task.convertJavaToScript(task assertEquals(task.convertJavaToScriptNormalizingPaths(task.getCommandLine()), expected);
.getCommandLine()));
} }
private JavaOverSsh createTask() { private Java populateTask(Java task) {
Project p = new Project(); Project p = new Project();
JavaOverSsh task = new JavaOverSsh();
task.setProject(p); task.setProject(p);
task.setClassname(TestClass.class.getName()); task.setClassname(TestClass.class.getName());
task.createClasspath().add(new Path(p, "target/test-classes")); task.createClasspath().add(new Path(p, "target/test-classes"));
@ -62,20 +68,43 @@ public class JavaOverSshTest {
Variable prop2 = new Environment.Variable(); Variable prop2 = new Environment.Variable();
prop2.setKey("foo"); prop2.setKey("foo");
prop2.setValue("bar"); prop2.setValue("bar");
task.addSysproperty(prop2); task.addSysproperty(prop2);
task.createJvmarg().setValue("-Xms256"); task.createJvmarg().setValue("-Xms16m");
task.createJvmarg().setValue("-Xmx32m");
Variable env = new Environment.Variable();
env.setKey(LAST_ENV.getKey());
env.setValue(LAST_ENV.getValue());
task.addEnv(env);
task.createArg().setValue(env.getKey());
task.createArg().setValue("hello"); task.createArg().setValue("hello");
task.createArg().setValue("world"); task.createArg().setValue("world");
task.setDir(new File(System.getProperty("user.dir"))); task.setDir(new File(System.getProperty("user.dir")));
task.setRemotedir(new File("/tmp/foo"));
task.setFork(true); task.setFork(true);
task.setJvm(System.getProperty("java.home") + "/bin/java"); task.setJvm(System.getProperty("java.home") + "/bin/java");
task.setOutputproperty("out");
task.setErrorProperty("err");
task.setResultProperty("result");
return task; return task;
} }
@Test(enabled = false, groups = { "live" }) @Test(enabled = false, groups = { "live" })
public void testSsh() throws NumberFormatException, FileNotFoundException, IOException { public void testSsh() throws NumberFormatException, FileNotFoundException, IOException {
Java java = makeJava();
java.execute();
JavaOverSsh javaOverSsh = makeJavaOverSsh();
addDestinationTo(javaOverSsh);
javaOverSsh.execute();
assertEquals(javaOverSsh.getProject().getProperty("out"), javaOverSsh.getProject()
.getProperty("out"));
assertEquals(javaOverSsh.getProject().getProperty("err"), javaOverSsh.getProject()
.getProperty("err"));
assertEquals(javaOverSsh.getProject().getProperty("result"), javaOverSsh.getProject()
.getProperty("result"));
}
private void addDestinationTo(JavaOverSsh javaOverSsh) throws UnknownHostException {
String sshHost = System.getProperty("jclouds.test.ssh.host"); String sshHost = System.getProperty("jclouds.test.ssh.host");
String sshPort = System.getProperty("jclouds.test.ssh.port"); String sshPort = System.getProperty("jclouds.test.ssh.port");
String sshUser = System.getProperty("jclouds.test.ssh.username"); String sshUser = System.getProperty("jclouds.test.ssh.username");
@ -85,23 +114,26 @@ public class JavaOverSshTest {
int port = (sshPort != null) ? Integer.parseInt(sshPort) : 22; int port = (sshPort != null) ? Integer.parseInt(sshPort) : 22;
InetAddress host = (sshHost != null) ? InetAddress.getByName(sshHost) : InetAddress InetAddress host = (sshHost != null) ? InetAddress.getByName(sshHost) : InetAddress
.getLocalHost(); .getLocalHost();
javaOverSsh.setHost(host.getHostAddress());
JavaOverSsh task = createTask(); javaOverSsh.setPort(port);
task.setHost(host.getHostAddress()); javaOverSsh.setUsername(sshUser);
task.setPort(port);
task.setTrust(true);
task.setUsername(sshUser);
if (sshKeyFile != null && !sshKeyFile.trim().equals("")) { if (sshKeyFile != null && !sshKeyFile.trim().equals("")) {
task.setKeyfile(sshKeyFile); javaOverSsh.setKeyfile(sshKeyFile);
} else { } else {
task.setPassword(sshPass); javaOverSsh.setPassword(sshPass);
} }
task.setOutputproperty("out"); }
task.setErrorProperty("err");
task.setResultProperty("result"); private JavaOverSsh makeJavaOverSsh() {
task.execute(); JavaOverSsh task = new JavaOverSsh();
assertEquals(task.getProject().getProperty("out"), "[hello, world]\n"); populateTask(task);
assertEquals(task.getProject().getProperty("err"), "err\n"); task.setRemotedir(new File("/tmp/foo"));
assertEquals(task.getProject().getProperty("result"), "3"); task.setVerbose(true);
task.setTrust(true);
return task;
}
private Java makeJava() {
return populateTask(new Java());
} }
} }

View File

@ -5,6 +5,8 @@ import java.util.Arrays;
public class TestClass { public class TestClass {
public static void main(String... args) { public static void main(String... args) {
System.out.println("env:");
System.out.println(System.getenv(args[0]));
File cwd = new File(System.getProperty("user.dir")); File cwd = new File(System.getProperty("user.dir"));
System.out.println("children:"); System.out.println("children:");
for (File child : cwd.listFiles()) for (File child : cwd.listFiles())