Add OFFLINE_NODE_WHEN_COMPLETE option

If a build job is requested with the "OFFLINE_NODE_WHEN_COMPLETE"
parameter set to a true value, then mark the node as temporarily
offline when the build is complete (regardless of the outcome).

This facilitates single-use slaves (or slaves that need cleanup
after their jobs).  "Temporarily offline" was chosen as the
most lightweight method of preventing new builds to facilitate
either performing an external cleanup action (which would then
online the existing node), or external deletion of the node.

To accomplish this, the NodeAvailabilityMonitor unlock call is
moved from the StartJobWorker gearman function out into the gearman
worker so that the lock is held during the entire run of the job
and further past the point where the StartJobWorker will set
the node offline.

Also, supply the name of the gearman worker (which includes the
node name) with the build data to the client.  This way the client
will know which worker performed the job, and whose node may need
to be manipulated if the offline flag is set.

Change-Id: I5cda75eb44b26ec58e5f03d0aa980af09ee023f6
This commit is contained in:
James E. Blair 2013-08-06 11:09:14 -07:00
parent 84a20df823
commit e45ffe249d
2 changed files with 26 additions and 6 deletions

View File

@ -599,13 +599,11 @@ public class MyGearmanWorkerImpl implements GearmanSessionEventHandler {
LOG.warn("---- Worker " + this + " receieved IOException while" +
" running function",io);
session.closeSession();
// The reconnect will unlock the monitor if needed.
} catch (Exception e) {
LOG.warn("---- Worker " + this + " exception while executing function " + fun.getName(), e);
// Unlock the monitor for this worker in case we didn't
// make it as far as the schedule job unlock.
availability.unlock(this);
}
// Unlock the monitor for this worker
availability.unlock(this);
}
private GearmanPacketType getGrabJobPacketType() {

View File

@ -25,12 +25,14 @@ import hudson.model.Result;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Cause;
import hudson.model.Computer;
import hudson.model.Hudson;
import hudson.model.Node;
import hudson.model.Project;
import hudson.model.Queue;
import hudson.model.StringParameterValue;
import hudson.model.queue.QueueTaskFuture;
import hudson.slaves.OfflineCause;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@ -91,6 +93,7 @@ public class StartJobWorker extends AbstractGearmanFunction {
data.put("name", project.getName());
data.put("number", build.getNumber());
data.put("manager", masterName);
data.put("worker", this.worker.getWorkerID());
String rootUrl = Hudson.getInstance().getRootUrl();
if (rootUrl != null) {
@ -132,6 +135,7 @@ public class StartJobWorker extends AbstractGearmanFunction {
// create new parameter objects to pass to jenkins build
List<ParameterValue> buildParams = new ArrayList<ParameterValue>();
String decodedData = null;
boolean offlineWhenComplete = false;
if (this.data != null) {
// decode the data from the client
decodedData = new String((byte[]) this.data, "UTF-8");
@ -144,6 +148,13 @@ public class StartJobWorker extends AbstractGearmanFunction {
for (Map.Entry<String, String> entry : inParams.entrySet()) {
buildParams.add(new StringParameterValue(entry.getKey(), entry.getValue()));
}
String offline = inParams.get("OFFLINE_NODE_WHEN_COMPLETE");
if (offline != null) {
if (offline.equals("1") || offline.equals("true") ||
offline.equals("True") || offline.equals("TRUE")) {
offlineWhenComplete = true;
}
}
}
/*
@ -191,8 +202,6 @@ public class StartJobWorker extends AbstractGearmanFunction {
Queue.Executable exec = future.getStartCondition().get();
AbstractBuild<?, ?> currBuild = (AbstractBuild<?, ?>) exec;
availability.unlock(worker);
long now = new Date().getTime();
int duration = (int) (now - currBuild.getStartTimeInMillis());
int estimatedDuration = (int) currBuild.getEstimatedDuration();
@ -226,6 +235,19 @@ public class StartJobWorker extends AbstractGearmanFunction {
sess.driveSessionIO();
}
if (offlineWhenComplete) {
Computer computer = node.toComputer();
if (computer == null) {
logger.error("---- Worker " + this.worker + " has no " +
"computer while trying to take node offline.");
} else {
logger.info("---- Worker " + this.worker + " setting " +
"node offline.");
computer.setTemporarilyOffline(true,
new OfflineCause.ByCLI("Offline due to Gearman request"));
}
}
// check Jenkins build results
Result result = currBuild.getResult();
if (result == Result.SUCCESS) {