/*
* The MIT License
*
* Copyright 2010 Monty Taylor
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.plugins.jclouds;
import com.google.common.base.Predicate;
import com.trilead.ssh2.Connection;
import com.trilead.ssh2.Session;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.model.TaskListener;
import hudson.remoting.Channel;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.SlaveComputer;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.InetAddress;
import java.util.concurrent.TimeUnit;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.domain.Credentials;
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
import org.jclouds.net.IPSocket;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.ssh.SshClient;
import org.jclouds.ssh.jsch.JschSshClient;
import org.jclouds.ssh.jsch.predicates.InetSocketAddressConnect;
/**
*
* @author Monty Taylor
*/
public class JCloudCompuerLauncher extends ComputerLauncher {
private final int FAILED=-1;
private final int SAMEUSER=0;
private final int RECONNECT = -2;
@Override
public void launch(SlaveComputer _computer, TaskListener listener) {
SshClient sshClient = null;
boolean successful = false;
try {
JCloudComputer computer = (JCloudComputer) _computer;
PrintStream logger = listener.getLogger();
logger.println("Waiting for " + computer.getName() + "to launch");
OUTER:
while (true) {
switch (computer.getState()) {
case PENDING:
Thread.sleep(5000); // check every 5 secs
continue OUTER;
case RUNNING:
break OUTER;
case TERMINATED:
// abort
logger.println("The instance " + computer.getInstanceId() + " appears to be shut down. Aborting launch.");
return;
}
}
logger.println(computer.getName() + " launched. Initializing hudson");
// TODO: parse the version number. maven-enforcer-plugin might help
logger.println("Verifying that java exists");
if(computer.executeScript("java -fullversion", logger) != 0) {
logger.println("Java not found! Please install java as part of your node initialization");
}
// TODO: on Windows with ec2-sshd, this scp command ends up just putting slave.jar as c:\tmp
// bug in ec2-sshd?
logger.println("Copying slave.jar");
IPSocket socket = new IPSocket(computer.describeNode().getPublicAddresses().toArray(new String[0])[0], 22);
Predicate<IPSocket> socketOpen = new RetryablePredicate<IPSocket>(
new InetSocketAddressConnect(), 180, 5, TimeUnit.SECONDS);
socketOpen.apply(socket);
Credentials instanceCredentials = computer.describeNode().getCredentials();
sshClient = new JschSshClient(new BackoffLimitedRetryHandler(), socket, 60000,
instanceCredentials.account, "", instanceCredentials.key.getBytes());
sshClient.connect();
sshClient.put("/tmp/slave.jar", new ByteArrayInputStream(Hudson.getInstance().getJnlpJars("slave.jar").readFully()));
sshClient.disconnect();
logger.println("Copied jar");
logger.println("Launching slave agent");
final Connection conn = connectToSsh(computer.describeNode());
final Session sess = conn.openSession();
// @TODO jvmopts: sess.execCommand("java " + computer.getNode().jvmopts + " -jar /tmp/slave.jar");
sess.execCommand("java -jar /tmp/slave.jar");
computer.setChannel(sess.getStdout(), sess.getStdin(), logger, new Channel.Listener() {
@Override
public void onClosed(Channel channel, IOException cause) {
sess.close();
conn.close();
}
});
successful = true;
} catch (IOException e) {
e.printStackTrace(listener.error(e.getLocalizedMessage()));
} catch (InterruptedException e) {
e.printStackTrace(listener.error(e.getMessage()));
} finally {
if (sshClient != null && !successful) {
sshClient.disconnect();
}
}
}
private Connection connectToSsh(NodeMetadata inst) throws InterruptedException {
while(true) {
try {
Connection conn = new Connection(inst.getPublicAddresses().toArray()[0].toString(),22);
conn.connect();
return conn; // successfully connected
} catch (IOException e) {
// keep retrying until SSH comes up
Thread.sleep(5000);
}
}
}
@Override
public Descriptor<ComputerLauncher> getDescriptor() {
throw new UnsupportedOperationException();
}
}