/hudson-core/src/main/java/hudson/model/Slave.java
http://github.com/hudson/hudson · Java · 422 lines · 238 code · 68 blank · 116 comment · 29 complexity · c5feb1256b1052d236e754d8b4721549 MD5 · raw file
- /*
- * The MIT License
- *
- * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Erik Ramfelt, Martin Eigenbrodt, Stephen Connolly, Tom Huybrechts
- *
- * 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.model;
- import hudson.FilePath;
- import hudson.Launcher;
- import hudson.Util;
- import hudson.Launcher.RemoteLauncher;
- import hudson.diagnosis.OldDataMonitor;
- import hudson.model.Descriptor.FormException;
- import hudson.remoting.Callable;
- import hudson.remoting.VirtualChannel;
- import hudson.slaves.CommandLauncher;
- import hudson.slaves.ComputerLauncher;
- import hudson.slaves.DumbSlave;
- import hudson.slaves.JNLPLauncher;
- import hudson.slaves.NodeDescriptor;
- import hudson.slaves.NodeProperty;
- import hudson.slaves.NodePropertyDescriptor;
- import hudson.slaves.RetentionStrategy;
- import hudson.slaves.SlaveComputer;
- import hudson.util.ClockDifference;
- import hudson.util.DescribableList;
- import hudson.util.FormValidation;
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.Serializable;
- import java.net.MalformedURLException;
- import java.net.URL;
- import java.net.URLConnection;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Set;
- import javax.servlet.ServletException;
- import org.apache.commons.io.IOUtils;
- import org.kohsuke.stapler.DataBoundConstructor;
- import org.kohsuke.stapler.HttpResponse;
- import org.kohsuke.stapler.QueryParameter;
- import org.kohsuke.stapler.StaplerRequest;
- import org.kohsuke.stapler.StaplerResponse;
- /**
- * Information about a Hudson slave node.
- *
- * <p>
- * Ideally this would have been in the <tt>hudson.slaves</tt> package,
- * but for compatibility reasons, it can't.
- *
- * <p>
- * TODO: move out more stuff to {@link DumbSlave}.
- *
- * @author Kohsuke Kawaguchi
- */
- public abstract class Slave extends Node implements Serializable {
- /**
- * Name of this slave node.
- */
- protected String name;
- /**
- * Description of this node.
- */
- private final String description;
- /**
- * Path to the root of the workspace
- * from the view point of this node, such as "/hudson"
- */
- protected final String remoteFS;
- /**
- * Number of executors of this node.
- */
- private int numExecutors = 2;
- /**
- * Job allocation strategy.
- */
- private Mode mode;
- /**
- * Slave availablility strategy.
- */
- private RetentionStrategy retentionStrategy;
- /**
- * The starter that will startup this slave.
- */
- private ComputerLauncher launcher;
- /**
- * Whitespace-separated labels.
- */
- private String label="";
-
- private /*almost final*/ DescribableList<NodeProperty<?>,NodePropertyDescriptor> nodeProperties = new DescribableList<NodeProperty<?>,NodePropertyDescriptor>(Hudson.getInstance());
- /**
- * Lazily computed set of labels from {@link #label}.
- */
- private transient volatile Set<Label> labels;
- @DataBoundConstructor
- public Slave(String name, String nodeDescription, String remoteFS, String numExecutors,
- Mode mode, String labelString, ComputerLauncher launcher, RetentionStrategy retentionStrategy, List<? extends NodeProperty<?>> nodeProperties) throws FormException, IOException {
- this(name,nodeDescription,remoteFS,Util.tryParseNumber(numExecutors, 1).intValue(),mode,labelString,launcher,retentionStrategy, nodeProperties);
- }
- /**
- * @deprecated since 2009-02-20.
- */
- @Deprecated
- public Slave(String name, String nodeDescription, String remoteFS, int numExecutors,
- Mode mode, String labelString, ComputerLauncher launcher, RetentionStrategy retentionStrategy) throws FormException, IOException {
- this(name, nodeDescription, remoteFS, numExecutors, mode, labelString, launcher, retentionStrategy, new ArrayList());
- }
-
- public Slave(String name, String nodeDescription, String remoteFS, int numExecutors,
- Mode mode, String labelString, ComputerLauncher launcher, RetentionStrategy retentionStrategy, List<? extends NodeProperty<?>> nodeProperties) throws FormException, IOException {
- this.name = name;
- this.description = nodeDescription;
- this.numExecutors = numExecutors;
- this.mode = mode;
- this.remoteFS = Util.fixNull(remoteFS).trim();
- this.label = Util.fixNull(labelString).trim();
- this.launcher = launcher;
- this.retentionStrategy = retentionStrategy;
- getAssignedLabels(); // compute labels now
-
- this.nodeProperties.replaceBy(nodeProperties);
- if (name.equals(""))
- throw new FormException(Messages.Slave_InvalidConfig_NoName(), null);
- // if (remoteFS.equals(""))
- // throw new FormException(Messages.Slave_InvalidConfig_NoRemoteDir(name), null);
- if (this.numExecutors<=0)
- throw new FormException(Messages.Slave_InvalidConfig_Executors(name), null);
- }
- public ComputerLauncher getLauncher() {
- return launcher == null ? new JNLPLauncher() : launcher;
- }
- public void setLauncher(ComputerLauncher launcher) {
- this.launcher = launcher;
- }
- public String getRemoteFS() {
- return remoteFS;
- }
- public String getNodeName() {
- return name;
- }
- public void setNodeName(String name) {
- this.name = name;
- }
- public String getNodeDescription() {
- return description;
- }
- public int getNumExecutors() {
- return numExecutors;
- }
- public Mode getMode() {
- return mode;
- }
- public void setMode(Mode mode) {
- this.mode = mode;
- }
- public DescribableList<NodeProperty<?>, NodePropertyDescriptor> getNodeProperties() {
- return nodeProperties;
- }
-
- public RetentionStrategy getRetentionStrategy() {
- return retentionStrategy == null ? RetentionStrategy.Always.INSTANCE : retentionStrategy;
- }
- public void setRetentionStrategy(RetentionStrategy availabilityStrategy) {
- this.retentionStrategy = availabilityStrategy;
- }
- public String getLabelString() {
- return Util.fixNull(label).trim();
- }
- public ClockDifference getClockDifference() throws IOException, InterruptedException {
- VirtualChannel channel = getChannel();
- if(channel==null)
- throw new IOException(getNodeName()+" is offline");
- long startTime = System.currentTimeMillis();
- long slaveTime = channel.call(new GetSystemTime());
- long endTime = System.currentTimeMillis();
- return new ClockDifference((startTime+endTime)/2 - slaveTime);
- }
- public Computer createComputer() {
- return new SlaveComputer(this);
- }
- public FilePath getWorkspaceFor(TopLevelItem item) {
- FilePath r = getWorkspaceRoot();
- if(r==null) return null; // offline
- return r.child(item.getName());
- }
- public FilePath getRootPath() {
- return createPath(remoteFS);
- }
- /**
- * Root directory on this slave where all the job workspaces are laid out.
- * @return
- * null if not connected.
- */
- public FilePath getWorkspaceRoot() {
- FilePath r = getRootPath();
- if(r==null) return null;
- return r.child(WORKSPACE_ROOT);
- }
- /**
- * Web-bound object used to serve jar files for JNLP.
- */
- public static final class JnlpJar implements HttpResponse {
- private final String fileName;
- public JnlpJar(String fileName) {
- this.fileName = fileName;
- }
- public void doIndex( StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
- URLConnection con = connect();
- // since we end up redirecting users to jnlpJars/foo.jar/, set the content disposition
- // so that browsers can download them in the right file name.
- // see http://support.microsoft.com/kb/260519 and http://www.boutell.com/newfaq/creating/forcedownload.html
- rsp.setHeader("Content-Disposition", "attachment; filename=" + fileName);
- InputStream in = con.getInputStream();
- rsp.serveFile(req, in, con.getLastModified(), con.getContentLength(), "*.jar" );
- in.close();
- }
- public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
- doIndex(req,rsp);
- }
- private URLConnection connect() throws IOException {
- URL res = getURL();
- return res.openConnection();
- }
- public URL getURL() throws MalformedURLException {
- URL res = Hudson.getInstance().servletContext.getResource("/WEB-INF/" + fileName);
- if(res==null) {
- // during the development this path doesn't have the files.
- res = new URL(new File(".").getAbsoluteFile().toURI().toURL(),"target/generated-resources/WEB-INF/"+fileName);
- }
- return res;
- }
- public byte[] readFully() throws IOException {
- InputStream in = connect().getInputStream();
- try {
- return IOUtils.toByteArray(in);
- } finally {
- in.close();
- }
- }
- }
- public Launcher createLauncher(TaskListener listener) {
- SlaveComputer c = getComputer();
- return new RemoteLauncher(listener, c.getChannel(), c.isUnix()).decorateFor(this);
- }
- /**
- * Gets the corresponding computer object.
- */
- public SlaveComputer getComputer() {
- return (SlaveComputer)toComputer();
- }
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- final Slave that = (Slave) o;
- return name.equals(that.name);
- }
- @Override
- public int hashCode() {
- return name.hashCode();
- }
- /**
- * Invoked by XStream when this object is read into memory.
- */
- private Object readResolve() {
- // convert the old format to the new one
- if(command!=null && agentCommand==null) {
- if(command.length()>0) command += ' ';
- agentCommand = command+"java -jar ~/bin/slave.jar";
- }
- if (command!=null || localFS!=null)
- OldDataMonitor.report(Hudson.getInstance(), "1.69");
- if (launcher == null) {
- launcher = (agentCommand == null || agentCommand.trim().length() == 0)
- ? new JNLPLauncher()
- : new CommandLauncher(agentCommand);
- }
- if(nodeProperties==null)
- nodeProperties = new DescribableList<NodeProperty<?>,NodePropertyDescriptor>(Hudson.getInstance());
- return this;
- }
- public SlaveDescriptor getDescriptor() {
- Descriptor d = Hudson.getInstance().getDescriptorOrDie(getClass());
- if (d instanceof SlaveDescriptor)
- return (SlaveDescriptor) d;
- throw new IllegalStateException(d.getClass()+" needs to extend from SlaveDescriptor");
- }
- public static abstract class SlaveDescriptor extends NodeDescriptor {
- public FormValidation doCheckNumExecutors(@QueryParameter String value) {
- return FormValidation.validatePositiveInteger(value);
- }
- /**
- * Performs syntactical check on the remote FS for slaves.
- */
- public FormValidation doCheckRemoteFS(@QueryParameter String value) throws IOException, ServletException {
- if(Util.fixEmptyAndTrim(value)==null)
- return FormValidation.error(Messages.Slave_Remote_Director_Mandatory());
- if(value.startsWith("\\\\") || value.startsWith("/net/"))
- return FormValidation.warning(Messages.Slave_Network_Mounted_File_System_Warning());
- return FormValidation.ok();
- }
- }
- //
- // backward compatibility
- //
- /**
- * In Hudson < 1.69 this was used to store the local file path
- * to the remote workspace. No longer in use.
- *
- * @deprecated
- * ... but still in use during the transition.
- */
- private File localFS;
- /**
- * In Hudson < 1.69 this was used to store the command
- * to connect to the remote machine, like "ssh myslave".
- *
- * @deprecated
- */
- private transient String command;
- /**
- * Command line to launch the agent, like
- * "ssh myslave java -jar /path/to/hudson-remoting.jar"
- */
- private transient String agentCommand;
- /**
- * Obtains the system clock.
- */
- private static final class GetSystemTime implements Callable<Long,RuntimeException> {
- public Long call() {
- return System.currentTimeMillis();
- }
- private static final long serialVersionUID = 1L;
- }
- /**
- * Determines the workspace root file name for those who really really need the shortest possible path name.
- */
- private static final String WORKSPACE_ROOT = System.getProperty(Slave.class.getName()+".workspaceRoot","workspace");
- }