From 9c546d6d7cd8dadf49f039c32f7e1a9e18ded7a3 Mon Sep 17 00:00:00 2001 From: kohsuke Date: Sun, 18 May 2008 21:45:31 +0000 Subject: [PATCH] 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 --- core/src/main/java/hudson/model/Slave.java | 122 +----------------- .../model/SlaveAvailabilityStrategy.java | 79 ------------ .../hudson/model/SlaveReconnectionWork.java | 38 ------ .../java/hudson/model/SlaveStartMethod.java | 45 ------- .../hudson/slaves/CommandStartMethod.java | 103 +++++++++++++++ .../java/hudson/slaves/JNLPStartMethod.java | 43 ++++++ 6 files changed, 150 insertions(+), 280 deletions(-) delete mode 100644 core/src/main/java/hudson/model/SlaveAvailabilityStrategy.java delete mode 100644 core/src/main/java/hudson/model/SlaveReconnectionWork.java delete mode 100644 core/src/main/java/hudson/model/SlaveStartMethod.java create mode 100644 core/src/main/java/hudson/slaves/CommandStartMethod.java create mode 100644 core/src/main/java/hudson/slaves/JNLPStartMethod.java diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java index c8366ec3f6..3ef861876f 100644 --- a/core/src/main/java/hudson/model/Slave.java +++ b/core/src/main/java/hudson/model/Slave.java @@ -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 getDescriptor() { - return DESCRIPTOR; - } - - public static final Descriptor DESCRIPTOR = new Descriptor(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 getDescriptor() { - return DESCRIPTOR; - } - - public static final Descriptor DESCRIPTOR = new Descriptor(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); } diff --git a/core/src/main/java/hudson/model/SlaveAvailabilityStrategy.java b/core/src/main/java/hudson/model/SlaveAvailabilityStrategy.java deleted file mode 100644 index cd016e4e8c..0000000000 --- a/core/src/main/java/hudson/model/SlaveAvailabilityStrategy.java +++ /dev/null @@ -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, 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 LIST = new DescriptorList( - 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 getDescriptor() { - return DESCRIPTOR; - } - - public static final Descriptor DESCRIPTOR = - new DescriptorImpl(); - - private static class DescriptorImpl extends Descriptor { - 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(); - } - - - } - } -} diff --git a/core/src/main/java/hudson/model/SlaveReconnectionWork.java b/core/src/main/java/hudson/model/SlaveReconnectionWork.java deleted file mode 100644 index b696144f3c..0000000000 --- a/core/src/main/java/hudson/model/SlaveReconnectionWork.java +++ /dev/null @@ -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 nextCheck = new WeakHashMap(); - 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); - } - } - } -} diff --git a/core/src/main/java/hudson/model/SlaveStartMethod.java b/core/src/main/java/hudson/model/SlaveStartMethod.java deleted file mode 100644 index 028ca5b2e1..0000000000 --- a/core/src/main/java/hudson/model/SlaveStartMethod.java +++ /dev/null @@ -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, 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}. - * - *

- * 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 LIST = new DescriptorList(); -} diff --git a/core/src/main/java/hudson/slaves/CommandStartMethod.java b/core/src/main/java/hudson/slaves/CommandStartMethod.java new file mode 100644 index 0000000000..7d7d5d6e25 --- /dev/null +++ b/core/src/main/java/hudson/slaves/CommandStartMethod.java @@ -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 getDescriptor() { + return DESCRIPTOR; + } + + public static final Descriptor DESCRIPTOR = new Descriptor(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()); +} diff --git a/core/src/main/java/hudson/slaves/JNLPStartMethod.java b/core/src/main/java/hudson/slaves/JNLPStartMethod.java new file mode 100644 index 0000000000..87b6a32a13 --- /dev/null +++ b/core/src/main/java/hudson/slaves/JNLPStartMethod.java @@ -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 getDescriptor() { + return DESCRIPTOR; + } + + public static final Descriptor DESCRIPTOR = new Descriptor(JNLPStartMethod.class) { + public String getDisplayName() { + return "Launch slave agents via JNLP"; + } + + public SlaveStartMethod newInstance(StaplerRequest req, JSONObject formData) throws FormException { + return new JNLPStartMethod(); + } + }; +} -- GitLab