package hudson.plugins.openshift;
import hudson.model.TaskListener;
import hudson.model.Descriptor;
import hudson.remoting.Channel.Listener;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.SlaveComputer;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.openshift.client.OpenShiftException;
public class OpenShiftComputerLauncher extends ComputerLauncher {
private static final Logger LOGGER = Logger
.getLogger(OpenShiftComputerLauncher.class.getName());
@Override
public void launch(SlaveComputer slaveComputer, TaskListener taskListener)
throws IOException, InterruptedException {
LOGGER.info("Launching slave...");
OpenShiftComputer computer = (OpenShiftComputer) slaveComputer;
// If the slave doesn't have a uuid, connect it
if (computer.getNode().getUuid() == null) {
// Don't delay DNS lookup since in this case, Jenkins has probably
// just been restarted and the slave is still running
computer.getNode().connect(true);
}
LOGGER.info("Checking availability of computer " + computer.getNode());
String hostName = computer.getNode().getHostName();
LOGGER.info("Checking SSH access to application " + hostName);
try {
final JSch jsch = new JSch();
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
// Add the private key location
jsch.addIdentity(OpenShiftCloud.get().getPrivateKey()
.getAbsolutePath());
// The user for the SSH connection is the application uuid
String username = computer.getNode().getUuid();
LOGGER.info("Connecting via SSH '" + username + "' '" + hostName + "' '" + OpenShiftCloud.get().getPrivateKey()
.getAbsolutePath() + "'");
final Session sess = jsch.getSession(username, hostName, 22);
sess.setConfig(config);
sess.connect();
LOGGER.info("Connected via SSH.");
PrintStream logger = taskListener.getLogger();
logger.println("Attempting to connect slave...");
logger.println("Transferring slave.jar file...");
// A command for the initial slave setup
StringBuilder execCommand = new StringBuilder();
execCommand.append("mkdir -p $OPENSHIFT_DATA_DIR/jenkins")
.append(" && cd $OPENSHIFT_DATA_DIR/jenkins")
.append(" && rm -f slave.jar").append(" && wget -q --no-check-certificate https://")
.append(getGearDNS(hostName))
.append("/jnlpJars/slave.jar");
String command = execCommand.toString();
LOGGER.info("Exec " + command);
// Open an execution channel that supports SSH agent forwarding
Channel channel = sess.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.connect();
// Wait for the channel to close
while (true) {
if (channel.isClosed()) {
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
}
}
int result = channel.getExitStatus();
if (result != 0) {
LOGGER.warning("Download of slave.jar failed. Return code = " + result);
throw new IOException(
"Download of slave.jar failed. Return code = "
+ result);
}
channel.disconnect();
// Execute the slave.jar to establish a connection
// Make sure to enable SSH agent forwarding
logger.println("Executing slave jar to make connection...");
final Channel slaveChannel = sess.openChannel("exec");
String sshWrapperPath = "/usr/libexec/openshift/cartridges/jenkins/bin/git_ssh_wrapper.sh";
((ChannelExec) slaveChannel).setEnv("GIT_SSH", sshWrapperPath);
((ChannelExec) slaveChannel).setAgentForwarding(true);
String jarCachePath=System.getenv("JENKINS_JAR_CACHE_PATH");
if (jarCachePath==null) {
jarCachePath="$OPENSHIFT_DATA_DIR/.jenkins/cache/jars";
}
//jar-cache parameter needed for jenkins 1.540+
((ChannelExec) slaveChannel)
.setCommand("java -jar $OPENSHIFT_DATA_DIR/jenkins/slave.jar -jar-cache "+jarCachePath);
InputStream serverOutput = slaveChannel.getInputStream();
OutputStream clientInput = slaveChannel.getOutputStream();
slaveChannel.connect();
if (slaveChannel.isClosed()) {
LOGGER.severe("Slave connection terminated early with exit = "
+ channel.getExitStatus());
}
computer.setChannel(serverOutput, clientInput, taskListener,
new Listener() {
public void onClosed(hudson.remoting.Channel channel,
IOException cause) {
slaveChannel.disconnect();
sess.disconnect();
}
});
LOGGER.info("Slave connected.");
logger.flush();
} catch (JSchException e) {
e.printStackTrace();
throw new IOException(e);
}
}
protected String getGearDNS(String hostname) throws IOException {
StringTokenizer tokenizer = new StringTokenizer(hostname, "-");
tokenizer.nextToken();
String currentDns = tokenizer.nextToken();
while (tokenizer.hasMoreTokens()) {
currentDns = currentDns + "-" + tokenizer.nextToken();
}
String gearDns = System.getenv("OPENSHIFT_GEAR_DNS");
tokenizer = new StringTokenizer(gearDns, "-");
currentDns = tokenizer.nextToken() + "-" + currentDns;
return currentDns;
}
public Descriptor<ComputerLauncher> getDescriptor() {
throw new UnsupportedOperationException();
}
}