mirror of https://github.com/jenkinsci/jenkins.git
Creating a package to host slave related code.
git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@9394 71c3de6d-444a-0410-be80-ed276b4c234a
This commit is contained in:
parent
375cca85ed
commit
9c546d6d7c
|
|
@ -1,12 +1,13 @@
|
|||
package hudson.model;
|
||||
|
||||
import hudson.EnvVars;
|
||||
import hudson.FilePath;
|
||||
import hudson.Launcher;
|
||||
import hudson.Launcher.RemoteLauncher;
|
||||
import hudson.Util;
|
||||
import hudson.slaves.SlaveStartMethod;
|
||||
import hudson.slaves.SlaveAvailabilityStrategy;
|
||||
import hudson.slaves.CommandStartMethod;
|
||||
import hudson.slaves.JNLPStartMethod;
|
||||
import hudson.maven.agent.Main;
|
||||
import hudson.maven.agent.PluginManagerInterceptor;
|
||||
import hudson.model.Descriptor.FormException;
|
||||
|
|
@ -19,13 +20,10 @@ import hudson.tasks.DynamicLabeler;
|
|||
import hudson.tasks.LabelFinder;
|
||||
import hudson.util.ClockDifference;
|
||||
import hudson.util.NullStream;
|
||||
import hudson.util.ProcessTreeKiller;
|
||||
import hudson.util.RingBufferLogHandler;
|
||||
import hudson.util.StreamCopyThread;
|
||||
import hudson.util.StreamTaskListener;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
import org.kohsuke.stapler.DataBoundConstructor;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
|
@ -44,8 +42,6 @@ import java.util.logging.Level;
|
|||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sf.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Information about a Hudson slave node.
|
||||
*
|
||||
|
|
@ -592,116 +588,6 @@ public final class Slave implements Node, Serializable {
|
|||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
public static class JNLPStartMethod extends SlaveStartMethod {
|
||||
|
||||
@Override
|
||||
public boolean isLaunchSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void launch(ComputerImpl computer, StreamTaskListener listener) {
|
||||
// do nothing as we cannot self start
|
||||
}
|
||||
|
||||
//@DataBoundConstructor
|
||||
public JNLPStartMethod() {
|
||||
}
|
||||
|
||||
public Descriptor<SlaveStartMethod> getDescriptor() {
|
||||
return DESCRIPTOR;
|
||||
}
|
||||
|
||||
public static final Descriptor<SlaveStartMethod> DESCRIPTOR = new Descriptor<SlaveStartMethod>(JNLPStartMethod.class) {
|
||||
public String getDisplayName() {
|
||||
return "Launch slave agents via JNLP";
|
||||
}
|
||||
|
||||
public SlaveStartMethod newInstance(StaplerRequest req, JSONObject formData) throws FormException {
|
||||
return new JNLPStartMethod();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class CommandStartMethod extends SlaveStartMethod {
|
||||
|
||||
/**
|
||||
* Command line to launch the agent, like
|
||||
* "ssh myslave java -jar /path/to/hudson-remoting.jar"
|
||||
*/
|
||||
private String agentCommand;
|
||||
|
||||
@DataBoundConstructor
|
||||
public CommandStartMethod(String command) {
|
||||
this.agentCommand = command;
|
||||
}
|
||||
|
||||
public String getCommand() {
|
||||
return agentCommand;
|
||||
}
|
||||
|
||||
public Descriptor<SlaveStartMethod> getDescriptor() {
|
||||
return DESCRIPTOR;
|
||||
}
|
||||
|
||||
public static final Descriptor<SlaveStartMethod> DESCRIPTOR = new Descriptor<SlaveStartMethod>(CommandStartMethod.class) {
|
||||
public String getDisplayName() {
|
||||
return "Launch slave via execution of command on the Master";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the formatted current time stamp.
|
||||
*/
|
||||
private static String getTimestamp() {
|
||||
return String.format("[%1$tD %1$tT]", new Date());
|
||||
}
|
||||
|
||||
public void launch(ComputerImpl computer, final StreamTaskListener listener) {
|
||||
try {
|
||||
listener.getLogger().println(Messages.Slave_Launching(getTimestamp()));
|
||||
listener.getLogger().println("$ " + getCommand());
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder(Util.tokenize(getCommand()));
|
||||
final EnvVars cookie = ProcessTreeKiller.createCookie();
|
||||
pb.environment().putAll(cookie);
|
||||
final Process proc = pb.start();
|
||||
|
||||
// capture error information from stderr. this will terminate itself
|
||||
// when the process is killed.
|
||||
new StreamCopyThread("stderr copier for remote agent on " + computer.getDisplayName(),
|
||||
proc.getErrorStream(), listener.getLogger()).start();
|
||||
|
||||
computer.setChannel(proc.getInputStream(), proc.getOutputStream(), listener.getLogger(), new Listener() {
|
||||
public void onClosed(Channel channel, IOException cause) {
|
||||
if (cause != null) {
|
||||
cause.printStackTrace(
|
||||
listener.error(Messages.Slave_Terminated(getTimestamp())));
|
||||
}
|
||||
ProcessTreeKiller.get().kill(proc, cookie);
|
||||
}
|
||||
});
|
||||
|
||||
LOGGER.info("slave agent launched for " + computer.getDisplayName());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace(listener.error("aborted"));
|
||||
} catch (IOException e) {
|
||||
Util.displayIOException(e, listener);
|
||||
|
||||
String msg = Util.getWin32ErrorMessage(e);
|
||||
if (msg == null) {
|
||||
msg = "";
|
||||
} else {
|
||||
msg = " : " + msg;
|
||||
}
|
||||
msg = Messages.Slave_UnableToLaunch(computer.getDisplayName(), msg);
|
||||
LOGGER.log(Level.SEVERE, msg, e);
|
||||
e.printStackTrace(listener.error(msg));
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CommandStartMethod.class.getName());
|
||||
}
|
||||
|
||||
//
|
||||
// backwrad compatibility
|
||||
//
|
||||
|
|
@ -728,8 +614,8 @@ public final class Slave implements Node, Serializable {
|
|||
private transient String agentCommand;
|
||||
|
||||
static {
|
||||
SlaveStartMethod.LIST.add(Slave.JNLPStartMethod.DESCRIPTOR);
|
||||
SlaveStartMethod.LIST.add(Slave.CommandStartMethod.DESCRIPTOR);
|
||||
SlaveStartMethod.LIST.add(JNLPStartMethod.DESCRIPTOR);
|
||||
SlaveStartMethod.LIST.add(CommandStartMethod.DESCRIPTOR);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
package hudson.model;
|
||||
|
||||
import hudson.util.DescriptorList;
|
||||
import hudson.ExtensionPoint;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
import net.sf.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Slave availability strategy
|
||||
*/
|
||||
public abstract class SlaveAvailabilityStrategy implements Describable<SlaveAvailabilityStrategy>, ExtensionPoint {
|
||||
|
||||
/**
|
||||
* This method will be called periodically to allow this strategy to decide what to do with it's owning slave.
|
||||
* The default implementation takes the slave on-line every time it's off-line.
|
||||
*
|
||||
* @param slave The slave that owns this strategy, i.e. {@code slave.getAvailabilityStrategy() == this}
|
||||
* @param state Some state information that may be useful in deciding what to do.
|
||||
* @return The number of minutes after which the strategy would like to be checked again. The strategy may be
|
||||
* rechecked earlier or later that this!
|
||||
*/
|
||||
public long check(Slave slave, State state) {
|
||||
Slave.ComputerImpl c = slave.getComputer();
|
||||
if (c != null && c.isOffline() && c.isLaunchSupported())
|
||||
c.tryReconnect();
|
||||
return 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* All registered {@link SlaveAvailabilityStrategy} implementations.
|
||||
*/
|
||||
public static final DescriptorList<SlaveAvailabilityStrategy> LIST = new DescriptorList<SlaveAvailabilityStrategy>(
|
||||
Always.DESCRIPTOR
|
||||
);
|
||||
|
||||
public static class State {
|
||||
private final boolean jobWaiting;
|
||||
private final boolean jobRunning;
|
||||
|
||||
public State(boolean jobWaiting, boolean jobRunning) {
|
||||
this.jobWaiting = jobWaiting;
|
||||
this.jobRunning = jobRunning;
|
||||
}
|
||||
|
||||
public boolean isJobWaiting() {
|
||||
return jobWaiting;
|
||||
}
|
||||
|
||||
public boolean isJobRunning() {
|
||||
return jobRunning;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Always extends SlaveAvailabilityStrategy {
|
||||
|
||||
public Descriptor<SlaveAvailabilityStrategy> getDescriptor() {
|
||||
return DESCRIPTOR;
|
||||
}
|
||||
|
||||
public static final Descriptor<SlaveAvailabilityStrategy> DESCRIPTOR =
|
||||
new DescriptorImpl();
|
||||
|
||||
private static class DescriptorImpl extends Descriptor<SlaveAvailabilityStrategy> {
|
||||
public DescriptorImpl() {
|
||||
super(Always.class);
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return "Keep this slave on-line as much as possible";
|
||||
}
|
||||
|
||||
public SlaveAvailabilityStrategy newInstance(StaplerRequest req, JSONObject formData) throws FormException {
|
||||
return new Always();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
package hudson.model;
|
||||
|
||||
import hudson.model.Slave.ComputerImpl;
|
||||
import static hudson.model.SlaveAvailabilityStrategy.*;
|
||||
import hudson.triggers.SafeTimerTask;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
* Periodically checks the slaves and try to reconnect dead slaves.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class SlaveReconnectionWork extends SafeTimerTask {
|
||||
protected void doRun() {
|
||||
// use a weak hashmap
|
||||
Map<Slave, Long> nextCheck = new WeakHashMap<Slave, Long>();
|
||||
for (Slave s : Hudson.getInstance().getSlaves()) {
|
||||
if (!nextCheck.containsKey(s) || System.currentTimeMillis() > nextCheck.get(s)) {
|
||||
final Queue queue = Hudson.getInstance().getQueue();
|
||||
boolean hasJob = false;
|
||||
for (Executor exec: s.getComputer().getExecutors()) {
|
||||
if (!exec.isIdle()) {
|
||||
hasJob = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO get only the items from the queue that can apply to this slave
|
||||
State state = new State(queue.getItems().length > 0, hasJob);
|
||||
// at the moment I don't trust strategies to wait more than 60 minutes
|
||||
// strategies need to wait at least one minute
|
||||
final long waitInMins = Math.min(1, Math.max(60, s.getAvailabilityStrategy().check(s, state)));
|
||||
nextCheck.put(s, System.currentTimeMillis() + 60 * 1000 * waitInMins);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
package hudson.model;
|
||||
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.model.Slave.ComputerImpl;
|
||||
import hudson.remoting.Channel.Listener;
|
||||
import hudson.util.DescriptorList;
|
||||
import hudson.util.StreamTaskListener;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Extension point to allow control over how Slaves are started.
|
||||
*
|
||||
* @author Stephen Connolly
|
||||
* @since 24-Apr-2008 22:12:35
|
||||
*/
|
||||
public abstract class SlaveStartMethod implements Describable<SlaveStartMethod>, ExtensionPoint {
|
||||
/**
|
||||
* Returns true if this {@link SlaveStartMethod} supports
|
||||
* programatic launch of the slave agent in the target {@link Computer}.
|
||||
*/
|
||||
public boolean isLaunchSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the slave agent for the given {@link Computer}.
|
||||
*
|
||||
* <p>
|
||||
* If the slave agent is launched successfully, {@link ComputerImpl#setChannel(InputStream, OutputStream, OutputStream, Listener)}
|
||||
* should be invoked in the end to notify Hudson of the established connection.
|
||||
* The operation could also fail, in which case there's no need to make any callback notification,
|
||||
* (except to notify the user of the failure through {@link StreamTaskListener}.)
|
||||
*
|
||||
* @param listener
|
||||
* The progress of the launch, as well as any error, should be sent to this listener.
|
||||
*/
|
||||
public abstract void launch(Slave.ComputerImpl computer, StreamTaskListener listener);
|
||||
|
||||
/**
|
||||
* All registered {@link SlaveStartMethod} implementations.
|
||||
*/
|
||||
public static final DescriptorList<SlaveStartMethod> LIST = new DescriptorList<SlaveStartMethod>();
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package hudson.slaves;
|
||||
|
||||
import org.kohsuke.stapler.DataBoundConstructor;
|
||||
import hudson.model.Descriptor;
|
||||
import hudson.model.Slave;
|
||||
import hudson.model.Messages;
|
||||
import hudson.util.StreamTaskListener;
|
||||
import hudson.util.ProcessTreeKiller;
|
||||
import hudson.util.StreamCopyThread;
|
||||
import hudson.Util;
|
||||
import hudson.EnvVars;
|
||||
import hudson.remoting.Channel;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* {@link SlaveStartMethod} through a remote login mechanism like ssh/rsh.
|
||||
*
|
||||
* @author Stephen Connolly
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class CommandStartMethod extends SlaveStartMethod {
|
||||
|
||||
/**
|
||||
* Command line to launch the agent, like
|
||||
* "ssh myslave java -jar /path/to/hudson-remoting.jar"
|
||||
*/
|
||||
private String agentCommand;
|
||||
|
||||
@DataBoundConstructor
|
||||
public CommandStartMethod(String command) {
|
||||
this.agentCommand = command;
|
||||
}
|
||||
|
||||
public String getCommand() {
|
||||
return agentCommand;
|
||||
}
|
||||
|
||||
public Descriptor<SlaveStartMethod> getDescriptor() {
|
||||
return DESCRIPTOR;
|
||||
}
|
||||
|
||||
public static final Descriptor<SlaveStartMethod> DESCRIPTOR = new Descriptor<SlaveStartMethod>(CommandStartMethod.class) {
|
||||
public String getDisplayName() {
|
||||
return "Launch slave via execution of command on the Master";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the formatted current time stamp.
|
||||
*/
|
||||
private static String getTimestamp() {
|
||||
return String.format("[%1$tD %1$tT]", new Date());
|
||||
}
|
||||
|
||||
public void launch(Slave.ComputerImpl computer, final StreamTaskListener listener) {
|
||||
try {
|
||||
listener.getLogger().println(Messages.Slave_Launching(getTimestamp()));
|
||||
listener.getLogger().println("$ " + getCommand());
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder(Util.tokenize(getCommand()));
|
||||
final EnvVars cookie = ProcessTreeKiller.createCookie();
|
||||
pb.environment().putAll(cookie);
|
||||
final Process proc = pb.start();
|
||||
|
||||
// capture error information from stderr. this will terminate itself
|
||||
// when the process is killed.
|
||||
new StreamCopyThread("stderr copier for remote agent on " + computer.getDisplayName(),
|
||||
proc.getErrorStream(), listener.getLogger()).start();
|
||||
|
||||
computer.setChannel(proc.getInputStream(), proc.getOutputStream(), listener.getLogger(), new Channel.Listener() {
|
||||
public void onClosed(Channel channel, IOException cause) {
|
||||
if (cause != null) {
|
||||
cause.printStackTrace(
|
||||
listener.error(Messages.Slave_Terminated(getTimestamp())));
|
||||
}
|
||||
ProcessTreeKiller.get().kill(proc, cookie);
|
||||
}
|
||||
});
|
||||
|
||||
LOGGER.info("slave agent launched for " + computer.getDisplayName());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace(listener.error("aborted"));
|
||||
} catch (IOException e) {
|
||||
Util.displayIOException(e, listener);
|
||||
|
||||
String msg = Util.getWin32ErrorMessage(e);
|
||||
if (msg == null) {
|
||||
msg = "";
|
||||
} else {
|
||||
msg = " : " + msg;
|
||||
}
|
||||
msg = Messages.Slave_UnableToLaunch(computer.getDisplayName(), msg);
|
||||
LOGGER.log(Level.SEVERE, msg, e);
|
||||
e.printStackTrace(listener.error(msg));
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CommandStartMethod.class.getName());
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package hudson.slaves;
|
||||
|
||||
import hudson.model.Slave;
|
||||
import hudson.model.Descriptor;
|
||||
import hudson.util.StreamTaskListener;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
import net.sf.json.JSONObject;
|
||||
|
||||
/**
|
||||
* {@link SlaveStartMethod} via JNLP.
|
||||
*
|
||||
* @author Stephen Connolly
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class JNLPStartMethod extends SlaveStartMethod {
|
||||
|
||||
@Override
|
||||
public boolean isLaunchSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void launch(Slave.ComputerImpl computer, StreamTaskListener listener) {
|
||||
// do nothing as we cannot self start
|
||||
}
|
||||
|
||||
//@DataBoundConstructor
|
||||
public JNLPStartMethod() {
|
||||
}
|
||||
|
||||
public Descriptor<SlaveStartMethod> getDescriptor() {
|
||||
return DESCRIPTOR;
|
||||
}
|
||||
|
||||
public static final Descriptor<SlaveStartMethod> DESCRIPTOR = new Descriptor<SlaveStartMethod>(JNLPStartMethod.class) {
|
||||
public String getDisplayName() {
|
||||
return "Launch slave agents via JNLP";
|
||||
}
|
||||
|
||||
public SlaveStartMethod newInstance(StaplerRequest req, JSONObject formData) throws FormException {
|
||||
return new JNLPStartMethod();
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue