Update zuul-gearman protocol.
* Use name+number as the build identifier for all meta-jobs. (Zuul has name + number as build metadata, so avoid adding new/duplicate features). * Refer to the name of the manager worker as 'manager' instead of 'master' to avoid jenkins specifics. * Just call the url "url" instead of "full_url". * Change SetDescriptionWorker to use name+number as the build id. Also, expect 'html_description' instead of 'description'. * Don't catch as many exceptions, instead, let them propogate so that they get turned into WORK_EXCEPTION packets with information (instead of WORK_FAIL packets which have no info). * Change StopJobWorker to use the same name+number interface as setDescriptionWorker (for consistency, expandability, plus it makes the code simpler). Change-Id: I8e078540c252bf9c1f14b79f8182517cbaa13555
This commit is contained in:
parent
78abc3d903
commit
7a8d940c4b
|
@ -18,8 +18,12 @@
|
||||||
|
|
||||||
package hudson.plugins.gearman;
|
package hudson.plugins.gearman;
|
||||||
|
|
||||||
|
import hudson.model.AbstractProject;
|
||||||
import hudson.model.Computer;
|
import hudson.model.Computer;
|
||||||
import hudson.model.Node;
|
import hudson.model.Node;
|
||||||
|
import hudson.model.Run;
|
||||||
|
|
||||||
|
import jenkins.model.Jenkins;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -55,4 +59,25 @@ public class GearmanPluginUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to finds the build with the unique build id.
|
||||||
|
*
|
||||||
|
* @param jobName
|
||||||
|
* The jenkins job or project name
|
||||||
|
* @param buildNumber
|
||||||
|
* The jenkins build number
|
||||||
|
* @return
|
||||||
|
* the build Run if found, otherwise return null
|
||||||
|
*/
|
||||||
|
public static Run<?,?> findBuild(String jobName, int buildNumber) {
|
||||||
|
|
||||||
|
AbstractProject<?,?> project = Jenkins.getInstance().getItemByFullName(jobName, AbstractProject.class);
|
||||||
|
if (project != null){
|
||||||
|
Run<?,?> run = project.getBuildByNumber(buildNumber);
|
||||||
|
if (run != null) {
|
||||||
|
return run;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,15 +19,12 @@
|
||||||
|
|
||||||
package hudson.plugins.gearman;
|
package hudson.plugins.gearman;
|
||||||
|
|
||||||
import hudson.model.AbstractProject;
|
|
||||||
import hudson.model.Run;
|
import hudson.model.Run;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import jenkins.model.Jenkins;
|
|
||||||
|
|
||||||
import org.gearman.client.GearmanJobResult;
|
import org.gearman.client.GearmanJobResult;
|
||||||
import org.gearman.client.GearmanJobResultImpl;
|
import org.gearman.client.GearmanJobResultImpl;
|
||||||
import org.gearman.worker.AbstractGearmanFunction;
|
import org.gearman.worker.AbstractGearmanFunction;
|
||||||
|
@ -59,82 +56,49 @@ public class SetDescriptionWorker extends AbstractGearmanFunction {
|
||||||
|
|
||||||
// check job results
|
// check job results
|
||||||
boolean jobResult = false;
|
boolean jobResult = false;
|
||||||
String jobExceptionMsg = "";
|
|
||||||
String jobWarningMsg = "";
|
|
||||||
String jobResultMsg = "";
|
String jobResultMsg = "";
|
||||||
|
|
||||||
|
String decodedData;
|
||||||
|
// decode json
|
||||||
try {
|
try {
|
||||||
// decode json
|
decodedData = new String((byte[]) this.data, "UTF-8");
|
||||||
String decodedData = new String((byte[]) this.data, "UTF-8");
|
|
||||||
// convert parameters passed in from client to hash map
|
|
||||||
Gson gson = new Gson();
|
|
||||||
Map<String, String> data = gson.fromJson(decodedData,
|
|
||||||
new TypeToken<Map<String, String>>() {
|
|
||||||
}.getType());
|
|
||||||
|
|
||||||
// get build description
|
|
||||||
String buildDescription = data.get("description");
|
|
||||||
// get build id
|
|
||||||
String buildId = data.get("build_id");
|
|
||||||
String[] idToken = buildId.split(":");
|
|
||||||
if (idToken.length != 2 || buildDescription == null || buildId == null) {
|
|
||||||
jobExceptionMsg = "Invalid Unique Id";
|
|
||||||
throw new IllegalArgumentException(jobExceptionMsg);
|
|
||||||
} else {
|
|
||||||
String jobName = idToken[0];
|
|
||||||
String jobId = idToken[1];
|
|
||||||
if (!jobName.isEmpty() && !jobId.isEmpty()) {
|
|
||||||
// find build then update its description
|
|
||||||
Run<?,?> build = findBuild(jobName, jobId);
|
|
||||||
if (build != null) {
|
|
||||||
build.setDescription(buildDescription);
|
|
||||||
jobResultMsg = "Description for Jenkins build " +buildId+" was pdated to " + buildDescription;
|
|
||||||
jobResult = true;
|
|
||||||
} else {
|
|
||||||
jobExceptionMsg = "Cannot find build with id " + buildId;
|
|
||||||
throw new IllegalArgumentException(jobExceptionMsg);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
jobExceptionMsg = "Build id is invalid or not specified";
|
|
||||||
throw new IllegalArgumentException(jobExceptionMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
jobExceptionMsg = "Error decoding parameters";
|
throw new IllegalArgumentException("Unsupported encoding exception in argument");
|
||||||
} catch (NullPointerException e) {
|
}
|
||||||
jobExceptionMsg = "Error decoding parameters";
|
|
||||||
} catch (IOException e) {
|
// convert parameters passed in from client to hash map
|
||||||
jobExceptionMsg = "Error setting build description";
|
Gson gson = new Gson();
|
||||||
} catch (IllegalArgumentException e) {
|
Map<String, String> data = gson.fromJson(decodedData,
|
||||||
jobExceptionMsg = e.getMessage();
|
new TypeToken<Map<String, String>>() {
|
||||||
|
}.getType());
|
||||||
|
|
||||||
|
// get build description
|
||||||
|
String buildDescription = data.get("html_description");
|
||||||
|
// get build id
|
||||||
|
String jobName = data.get("name");
|
||||||
|
String buildNumber = data.get("number");
|
||||||
|
if (!jobName.isEmpty() && !buildNumber.isEmpty()) {
|
||||||
|
// find build then update its description
|
||||||
|
Run<?,?> build = GearmanPluginUtil.findBuild(jobName, Integer.parseInt(buildNumber));
|
||||||
|
if (build != null) {
|
||||||
|
try {
|
||||||
|
build.setDescription(buildDescription);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalArgumentException("Unable to set description for " +
|
||||||
|
jobName + ": " + buildNumber);
|
||||||
|
}
|
||||||
|
jobResultMsg = "Description for Jenkins build " +buildNumber+" was updated to " + buildDescription;
|
||||||
|
jobResult = true;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Cannot find build number " +
|
||||||
|
buildNumber);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Build id is invalid or not specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
GearmanJobResult gjr = new GearmanJobResultImpl(this.jobHandle, jobResult,
|
GearmanJobResult gjr = new GearmanJobResultImpl(this.jobHandle, jobResult,
|
||||||
jobResultMsg.getBytes(), jobWarningMsg.getBytes(),
|
jobResultMsg.getBytes(), null, null, 0, 0);
|
||||||
jobExceptionMsg.getBytes(), 0, 0);
|
|
||||||
return gjr;
|
return gjr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to finds the build with the unique build id.
|
|
||||||
*
|
|
||||||
* @param jobName
|
|
||||||
* The jenkins job or project name
|
|
||||||
* @param uuid
|
|
||||||
* The jenkins job id
|
|
||||||
* @return
|
|
||||||
* the build Run if found, otherwise return null
|
|
||||||
*/
|
|
||||||
private Run<?,?> findBuild(String jobName, String jobId) {
|
|
||||||
|
|
||||||
AbstractProject<?,?> project = Jenkins.getInstance().getItemByFullName(jobName, AbstractProject.class);
|
|
||||||
if (project != null){
|
|
||||||
Run<?,?> run = project.getBuild(jobId);
|
|
||||||
if (run != null) {
|
|
||||||
return run;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,14 +87,11 @@ public class StartJobWorker extends AbstractGearmanFunction {
|
||||||
|
|
||||||
data.put("name", project.getName());
|
data.put("name", project.getName());
|
||||||
data.put("number", build.getNumber());
|
data.put("number", build.getNumber());
|
||||||
data.put("id", build.getId());
|
data.put("manager", masterName);
|
||||||
data.put("build_id", project.getName()+":"+build.getId());
|
|
||||||
data.put("url", build.getUrl());
|
|
||||||
data.put("master", masterName);
|
|
||||||
|
|
||||||
String rootUrl = Hudson.getInstance().getRootUrl();
|
String rootUrl = Hudson.getInstance().getRootUrl();
|
||||||
if (rootUrl != null) {
|
if (rootUrl != null) {
|
||||||
data.put("full_url", rootUrl + build.getUrl());
|
data.put("url", rootUrl + build.getUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
Result result = build.getResult();
|
Result result = build.getResult();
|
||||||
|
|
|
@ -19,16 +19,11 @@
|
||||||
|
|
||||||
package hudson.plugins.gearman;
|
package hudson.plugins.gearman;
|
||||||
|
|
||||||
import hudson.model.AbstractBuild;
|
import hudson.model.Run;
|
||||||
import hudson.model.Computer;
|
|
||||||
import hudson.model.Executor;
|
import hudson.model.Executor;
|
||||||
import hudson.model.Node;
|
|
||||||
import hudson.model.Queue;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.List;
|
import java.util.Map;
|
||||||
|
|
||||||
import jenkins.model.Jenkins;
|
|
||||||
|
|
||||||
import org.gearman.client.GearmanJobResult;
|
import org.gearman.client.GearmanJobResult;
|
||||||
import org.gearman.client.GearmanJobResultImpl;
|
import org.gearman.client.GearmanJobResultImpl;
|
||||||
|
@ -36,6 +31,9 @@ import org.gearman.worker.AbstractGearmanFunction;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a gearman function that will cancel/abort jenkins builds
|
* This is a gearman function that will cancel/abort jenkins builds
|
||||||
*
|
*
|
||||||
|
@ -53,137 +51,54 @@ public class StopJobWorker extends AbstractGearmanFunction {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public GearmanJobResult executeFunction() {
|
public GearmanJobResult executeFunction() {
|
||||||
String cancelID = null;
|
|
||||||
if (this.data != null) {
|
|
||||||
// decode the data from the client
|
|
||||||
try {
|
|
||||||
cancelID = new String((byte[]) this.data, "UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check build and pass results back to client
|
// check job results
|
||||||
boolean jobResult = true;
|
boolean jobResult = false;
|
||||||
String jobFailureMsg = "";
|
|
||||||
String jobWarningMsg = "";
|
|
||||||
String jobResultMsg = "";
|
String jobResultMsg = "";
|
||||||
|
|
||||||
if (cancelID == null || cancelID.isEmpty()) {
|
String decodedData;
|
||||||
logger.info("---- Client passed in an invalid UUID");
|
// decode json
|
||||||
jobFailureMsg = "I need the job Id please";
|
try {
|
||||||
jobResult = false;
|
decodedData = new String((byte[]) this.data, "UTF-8");
|
||||||
} else {
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new IllegalArgumentException("Unsupported encoding exception in argument");
|
||||||
|
}
|
||||||
|
// convert parameters passed in from client to hash map
|
||||||
|
Gson gson = new Gson();
|
||||||
|
Map<String, String> data = gson.fromJson(decodedData,
|
||||||
|
new TypeToken<Map<String, String>>() {
|
||||||
|
}.getType());
|
||||||
|
|
||||||
// Abort running jenkins build that contain matching uuid
|
// get build id
|
||||||
jobResult = abortBuild(cancelID);
|
String jobName = data.get("name");
|
||||||
|
String buildNumber = data.get("number");
|
||||||
|
if (jobName.isEmpty() || buildNumber.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Build id is invalid or not specified");
|
||||||
|
}
|
||||||
|
|
||||||
if (jobResult){
|
// Abort running jenkins build that contain matching uuid
|
||||||
jobResultMsg = "Canceled jenkins build " + cancelID;
|
Run<?,?> build = GearmanPluginUtil.findBuild(jobName, Integer.parseInt(buildNumber));
|
||||||
|
if (build != null) {
|
||||||
|
if (build.isBuilding()) {
|
||||||
|
Executor executor = build.getExecutor();
|
||||||
|
// abort the running jenkins build
|
||||||
|
if (!executor.isInterrupted()) {
|
||||||
|
executor.interrupt();
|
||||||
|
logger.info("---- Aborting build : " +
|
||||||
|
jobName + ": " + buildNumber);
|
||||||
|
jobResult = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
jobFailureMsg = "Could not cancel build " + cancelID;
|
logger.info("---- Request to abourt non-building build : " +
|
||||||
jobResult = false;
|
jobName + ": " + buildNumber);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Cannot find build " +
|
||||||
|
jobName + ": " + buildNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
GearmanJobResult gjr = new GearmanJobResultImpl(this.jobHandle, jobResult,
|
GearmanJobResult gjr = new GearmanJobResultImpl(this.jobHandle, jobResult,
|
||||||
jobResultMsg.getBytes(), jobWarningMsg.getBytes(),
|
jobResultMsg.getBytes(), null, null, 0, 0);
|
||||||
jobFailureMsg.getBytes(), 0, 0);
|
|
||||||
return gjr;
|
return gjr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to abort a currently running Jenkins build
|
|
||||||
* Running Jenkins builds are builds that actively being
|
|
||||||
* executed by Jenkins
|
|
||||||
*
|
|
||||||
* @param uuid
|
|
||||||
* The build UUID
|
|
||||||
* @return
|
|
||||||
* true if build was aborted, otherwise false
|
|
||||||
*/
|
|
||||||
private boolean abortBuild (String uuid) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iterate over the executors on master and slave nodes to find the
|
|
||||||
* build on the executor with the matching uuid
|
|
||||||
*/
|
|
||||||
// look at executors on master
|
|
||||||
Node masterNode = Computer.currentComputer().getNode();
|
|
||||||
Computer masterComp = masterNode.toComputer();
|
|
||||||
if (!masterComp.isIdle()) { // ignore idle master
|
|
||||||
List<Executor> masterExecutors = masterComp.getExecutors();
|
|
||||||
for (Executor executor: masterExecutors) {
|
|
||||||
|
|
||||||
if (executor.isIdle()) { // ignore idle executors
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup the running build with matching uuid
|
|
||||||
Queue.Executable executable = executor.getCurrentExecutable();
|
|
||||||
AbstractBuild<?, ?> currBuild = (AbstractBuild) executable;
|
|
||||||
int buildNum = currBuild.getNumber();
|
|
||||||
String buildId = currBuild.getId();
|
|
||||||
String runNodeName = currBuild.getBuiltOn().getNodeName();
|
|
||||||
NodeParametersAction param = currBuild.getAction(NodeParametersAction.class);
|
|
||||||
String buildParams = param.getParameters().toString();
|
|
||||||
|
|
||||||
if (param.getUuid().equals(uuid)) {
|
|
||||||
|
|
||||||
logger.info("---- Aborting build : "+buildNum+": "+buildId+" on " + runNodeName
|
|
||||||
+" with UUID " + uuid + " and build params " + buildParams);
|
|
||||||
|
|
||||||
// abort the running jenkins build
|
|
||||||
if (!executor.isInterrupted()) {
|
|
||||||
executor.interrupt();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// look at executors on slave nodes
|
|
||||||
List<Node> nodes = Jenkins.getInstance().getNodes();
|
|
||||||
if (nodes.isEmpty()) { //NOOP
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Node node: nodes){
|
|
||||||
|
|
||||||
Computer slave = node.toComputer();
|
|
||||||
if (slave.isIdle()) { // ignore all idle slaves
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Executor> executors = slave.getExecutors();
|
|
||||||
for (Executor executor: executors) {
|
|
||||||
|
|
||||||
if (executor.isIdle()) { // ignore idle executors
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup the running build with matching uuid
|
|
||||||
Queue.Executable executable = executor.getCurrentExecutable();
|
|
||||||
AbstractBuild<?, ?> currBuild = (AbstractBuild) executable;
|
|
||||||
int buildNum = currBuild.getNumber();
|
|
||||||
String buildId = currBuild.getId();
|
|
||||||
String runNodeName = currBuild.getBuiltOn().getNodeName();
|
|
||||||
NodeParametersAction param = currBuild.getAction(NodeParametersAction.class);
|
|
||||||
String buildParams = param.getParameters().toString();
|
|
||||||
|
|
||||||
if (param.getUuid().equals(uuid)) {
|
|
||||||
|
|
||||||
logger.info("---- Aborting build : "+buildNum+": "+buildId+" on " + runNodeName
|
|
||||||
+" with UUID " + uuid + " and build params " + buildParams);
|
|
||||||
|
|
||||||
// abort the running jenkins build
|
|
||||||
if (!executor.isInterrupted()) {
|
|
||||||
executor.interrupt();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue