From 7a8d940c4b590d8d95ed48413a164f4ac1c5a230 Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Tue, 4 Jun 2013 15:52:01 -0700 Subject: [PATCH] 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 --- .../plugins/gearman/GearmanPluginUtil.java | 25 +++ .../plugins/gearman/SetDescriptionWorker.java | 108 ++++------- .../plugins/gearman/StartJobWorker.java | 7 +- .../hudson/plugins/gearman/StopJobWorker.java | 171 +++++------------- 4 files changed, 106 insertions(+), 205 deletions(-) diff --git a/src/main/java/hudson/plugins/gearman/GearmanPluginUtil.java b/src/main/java/hudson/plugins/gearman/GearmanPluginUtil.java index 8e08c41..a48aba9 100644 --- a/src/main/java/hudson/plugins/gearman/GearmanPluginUtil.java +++ b/src/main/java/hudson/plugins/gearman/GearmanPluginUtil.java @@ -18,8 +18,12 @@ package hudson.plugins.gearman; +import hudson.model.AbstractProject; import hudson.model.Computer; import hudson.model.Node; +import hudson.model.Run; + +import jenkins.model.Jenkins; import org.slf4j.Logger; 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; + } } diff --git a/src/main/java/hudson/plugins/gearman/SetDescriptionWorker.java b/src/main/java/hudson/plugins/gearman/SetDescriptionWorker.java index d0c778a..7bb245d 100644 --- a/src/main/java/hudson/plugins/gearman/SetDescriptionWorker.java +++ b/src/main/java/hudson/plugins/gearman/SetDescriptionWorker.java @@ -19,15 +19,12 @@ package hudson.plugins.gearman; -import hudson.model.AbstractProject; import hudson.model.Run; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Map; -import jenkins.model.Jenkins; - import org.gearman.client.GearmanJobResult; import org.gearman.client.GearmanJobResultImpl; import org.gearman.worker.AbstractGearmanFunction; @@ -59,82 +56,49 @@ public class SetDescriptionWorker extends AbstractGearmanFunction { // check job results boolean jobResult = false; - String jobExceptionMsg = ""; - String jobWarningMsg = ""; String jobResultMsg = ""; + String decodedData; + // decode json try { - // decode json - String decodedData = new String((byte[]) this.data, "UTF-8"); - // convert parameters passed in from client to hash map - Gson gson = new Gson(); - Map data = gson.fromJson(decodedData, - new TypeToken>() { - }.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); - } - } + decodedData = new String((byte[]) this.data, "UTF-8"); } catch (UnsupportedEncodingException e) { - jobExceptionMsg = "Error decoding parameters"; - } catch (NullPointerException e) { - jobExceptionMsg = "Error decoding parameters"; - } catch (IOException e) { - jobExceptionMsg = "Error setting build description"; - } catch (IllegalArgumentException e) { - jobExceptionMsg = e.getMessage(); + throw new IllegalArgumentException("Unsupported encoding exception in argument"); + } + + // convert parameters passed in from client to hash map + Gson gson = new Gson(); + Map data = gson.fromJson(decodedData, + new TypeToken>() { + }.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, - jobResultMsg.getBytes(), jobWarningMsg.getBytes(), - jobExceptionMsg.getBytes(), 0, 0); + jobResultMsg.getBytes(), null, null, 0, 0); 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; - } } diff --git a/src/main/java/hudson/plugins/gearman/StartJobWorker.java b/src/main/java/hudson/plugins/gearman/StartJobWorker.java index 1fc59b7..0f0a9c6 100644 --- a/src/main/java/hudson/plugins/gearman/StartJobWorker.java +++ b/src/main/java/hudson/plugins/gearman/StartJobWorker.java @@ -87,14 +87,11 @@ public class StartJobWorker extends AbstractGearmanFunction { data.put("name", project.getName()); data.put("number", build.getNumber()); - data.put("id", build.getId()); - data.put("build_id", project.getName()+":"+build.getId()); - data.put("url", build.getUrl()); - data.put("master", masterName); + data.put("manager", masterName); String rootUrl = Hudson.getInstance().getRootUrl(); if (rootUrl != null) { - data.put("full_url", rootUrl + build.getUrl()); + data.put("url", rootUrl + build.getUrl()); } Result result = build.getResult(); diff --git a/src/main/java/hudson/plugins/gearman/StopJobWorker.java b/src/main/java/hudson/plugins/gearman/StopJobWorker.java index 06c22dd..b28dbaf 100644 --- a/src/main/java/hudson/plugins/gearman/StopJobWorker.java +++ b/src/main/java/hudson/plugins/gearman/StopJobWorker.java @@ -19,16 +19,11 @@ package hudson.plugins.gearman; -import hudson.model.AbstractBuild; -import hudson.model.Computer; +import hudson.model.Run; import hudson.model.Executor; -import hudson.model.Node; -import hudson.model.Queue; import java.io.UnsupportedEncodingException; -import java.util.List; - -import jenkins.model.Jenkins; +import java.util.Map; import org.gearman.client.GearmanJobResult; import org.gearman.client.GearmanJobResultImpl; @@ -36,6 +31,9 @@ import org.gearman.worker.AbstractGearmanFunction; import org.slf4j.Logger; 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 * @@ -53,137 +51,54 @@ public class StopJobWorker extends AbstractGearmanFunction { */ @Override 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 - boolean jobResult = true; - String jobFailureMsg = ""; - String jobWarningMsg = ""; + // check job results + boolean jobResult = false; String jobResultMsg = ""; - if (cancelID == null || cancelID.isEmpty()) { - logger.info("---- Client passed in an invalid UUID"); - jobFailureMsg = "I need the job Id please"; - jobResult = false; - } else { + String decodedData; + // decode json + try { + decodedData = new String((byte[]) this.data, "UTF-8"); + } 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 data = gson.fromJson(decodedData, + new TypeToken>() { + }.getType()); - // Abort running jenkins build that contain matching uuid - jobResult = abortBuild(cancelID); + // get build id + 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){ - jobResultMsg = "Canceled jenkins build " + cancelID; + // Abort running jenkins build that contain matching uuid + 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 { - jobFailureMsg = "Could not cancel build " + cancelID; - jobResult = false; + logger.info("---- Request to abourt non-building build : " + + jobName + ": " + buildNumber); } + } else { + throw new IllegalArgumentException("Cannot find build " + + jobName + ": " + buildNumber); } GearmanJobResult gjr = new GearmanJobResultImpl(this.jobHandle, jobResult, - jobResultMsg.getBytes(), jobWarningMsg.getBytes(), - jobFailureMsg.getBytes(), 0, 0); + jobResultMsg.getBytes(), null, null, 0, 0); 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 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 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 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; - } }