提交 9c546d6d 编写于 作者: K kohsuke

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
上级 375cca85
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);
}
......
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();
}
}
}
}
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);
}
}
}
}
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>();
}
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());
}
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();
}
};
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册