From ac1d57e4bf892a71f314b3b796bc36685d8521ef Mon Sep 17 00:00:00 2001 From: zaro Date: Sun, 24 Feb 2013 14:24:49 -0800 Subject: [PATCH] fix plugin extension point, enhanced jelly script, added test connection button .classpath Added eclipse classpath to web resources pom.xml Update jenkins dependency to ver 1.502 src/main/java/hudson/plugins/gearman/Constants.java Added default setting for launchWorker configuration. src/main/java/hudson/plugins/gearman/GearmanPluginConfig.java This plugin was extending the wrong jenkins extension point. It's now extending GlobalConfiguration instead of Builder which I believe is more correct. I had to rename GearmanPlugin to GearmanPluginConfig because for some reason extending GlobalConfiguration from a GearmanPlugin class doesn't work. Also had to rename the associated jelly script from global.jelly to config.jelly Moved and renamed help files to let jenkins framework load help files named help-$field.html src/main/java/hudson/plugins/gearman/ManagementWorkerThread.java changed debugging from System.out to logger.info src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/config.jelly Added basic verification for host config (make it required). Changed port config type from textbox to number and added basic verification (make it a required number) Added a 'Test Connection' button to test the Gearman server connection. Remove references to help files to let the jenkins framework load help files named help-$field.html src/main/resources/index.jelly Update description Change-Id: If0a521c8611d6a93f0434000452dc8c284f5fd74 --- .classpath | 2 +- pom.xml | 2 +- .../hudson/plugins/gearman/Constants.java | 1 + .../hudson/plugins/gearman/GearmanPlugin.java | 277 ------------------ .../plugins/gearman/GearmanPluginConfig.java | 272 +++++++++++++++++ .../gearman/ManagementWorkerThread.java | 4 - .../gearman/GearmanPlugin/config.jelly | 2 - .../gearman/GearmanPlugin/global.jelly | 21 -- .../gearman/GearmanPluginConfig/config.jelly | 17 ++ .../GearmanPluginConfig/help-host.html} | 0 .../help-launchWorker.html | 0 .../GearmanPluginConfig/help-port.html | 5 + src/main/resources/index.jelly | 2 +- src/main/webapp/help-globalConfig.html | 5 - src/main/webapp/help-serverPort.html | 5 - 15 files changed, 298 insertions(+), 317 deletions(-) delete mode 100644 src/main/java/hudson/plugins/gearman/GearmanPlugin.java create mode 100644 src/main/java/hudson/plugins/gearman/GearmanPluginConfig.java delete mode 100644 src/main/resources/hudson/plugins/gearman/GearmanPlugin/config.jelly delete mode 100644 src/main/resources/hudson/plugins/gearman/GearmanPlugin/global.jelly create mode 100644 src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/config.jelly rename src/main/{webapp/help-serverHost.html => resources/hudson/plugins/gearman/GearmanPluginConfig/help-host.html} (100%) rename src/main/{webapp => resources/hudson/plugins/gearman/GearmanPluginConfig}/help-launchWorker.html (100%) create mode 100644 src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/help-port.html delete mode 100644 src/main/webapp/help-globalConfig.html delete mode 100644 src/main/webapp/help-serverPort.html diff --git a/.classpath b/.classpath index 24e7d74..38cdfe2 100644 --- a/.classpath +++ b/.classpath @@ -1,7 +1,7 @@ - + diff --git a/pom.xml b/pom.xml index e4cdca8..c7a6d9b 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.jenkins-ci.plugins plugin - 1.460 + 1.502 gearman-plugin diff --git a/src/main/java/hudson/plugins/gearman/Constants.java b/src/main/java/hudson/plugins/gearman/Constants.java index 8ada2ef..c9c6129 100644 --- a/src/main/java/hudson/plugins/gearman/Constants.java +++ b/src/main/java/hudson/plugins/gearman/Constants.java @@ -24,6 +24,7 @@ package hudson.plugins.gearman; public interface Constants { /* Defines. */ + public static final boolean GEARMAN_DEFAULT_LAUNCH_WORKER = false; public static final String GEARMAN_DEFAULT_TCP_HOST = "127.0.0.1"; public static final int GEARMAN_DEFAULT_TCP_PORT = 4730; diff --git a/src/main/java/hudson/plugins/gearman/GearmanPlugin.java b/src/main/java/hudson/plugins/gearman/GearmanPlugin.java deleted file mode 100644 index 8d77a0a..0000000 --- a/src/main/java/hudson/plugins/gearman/GearmanPlugin.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * - * Copyright 2013 Hewlett-Packard Development Company, L.P. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package hudson.plugins.gearman; - -import hudson.Extension; -import hudson.Launcher; -import hudson.model.BuildListener; -import hudson.model.AbstractBuild; -import hudson.model.Computer; -import hudson.model.Node; -import hudson.tasks.BuildStepDescriptor; -import hudson.tasks.Builder; - -import java.util.List; -import java.util.Stack; - -import jenkins.model.Jenkins; -import net.sf.json.JSONObject; - -import org.apache.commons.lang.StringUtils; -import org.gearman.common.GearmanNIOJobServerConnection; -import org.gearman.worker.GearmanWorkerImpl; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.StaplerRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * GearmanPlugin {@link Builder}. - * - *

- * This sets up the gearman plugin as another plugin in Jenkins - * It will allow us to start and stop the gearman workers. - *

- * - * @author Khai Do - */ -public class GearmanPlugin extends Builder { - - private static final Logger logger = LoggerFactory - .getLogger(Constants.PLUGIN_LOGGER_NAME); - private final String name; - - @DataBoundConstructor - public GearmanPlugin(String name) { - logger.info("--- GearmanPlugin Constructor ---" + name); - this.name = name; - } - - public String getName() { - return name; - } - - @Override - public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) { - - return true; - } - - @Override - public DescriptorImpl getDescriptor() { - - return (DescriptorImpl)super.getDescriptor(); - } - - @Extension - public static final class DescriptorImpl extends - BuildStepDescriptor { - - private static final Logger logger = LoggerFactory - .getLogger(Constants.PLUGIN_LOGGER_NAME); - private boolean launchWorker; // launchWorker state (from UI checkbox) - private String host; // gearman server host - private int port; // gearman server port - private final Jenkins jenkins; - - // handles to gearman workers - public static Stack gewtHandles; - public static Stack gmwtHandles; - - public DescriptorImpl() { - logger.info("--- DescriptorImpl Constructor ---"); - - jenkins = Jenkins.getInstance(); - gewtHandles = new Stack(); - gmwtHandles = new Stack(); - - logger.info("--- DescriptorImpl Constructor ---" + host); - logger.info("--- DescriptorImpl Constructor ---" + port); - - load(); - - /* - * Not sure when to register gearman functions yet so for now always - * initialize the launch worker flag to disabled state at jenkins - * startup so we are always at a known state - */ - this.launchWorker = false; - save(); - } - - @Override - public String getDisplayName() { - return "Gearman Plugin"; - } - - @Override - public boolean isApplicable(Class type) { - return true; - } - - @Override - public boolean configure(StaplerRequest staplerRequest, JSONObject json) - throws FormException { - launchWorker = json.getBoolean("launchWorker"); - logger.info("--- LaunchWorker = "+ launchWorker); - - // set the gearman server host from value in jenkins config page - try { - host = json.getString("host"); - } catch (Exception e) { - throw new RuntimeException("Error getting the gearman host name"); - } - - // set the gearman server port from value in jenkins config page - try { - port = Integer.parseInt(json.getString("port")); - } catch (Exception e) { - throw new RuntimeException("Invalid gearman port value"); - } - - /* - * Purpose here is to create a 1:1 mapping of 'gearman - * worker':'jenkins executor' then use the gearman worker to execute - * builds on that jenkins nodes - */ - if (launchWorker && gmwtHandles.isEmpty() && gewtHandles.isEmpty()) { - - // user input verification - if (StringUtils.isEmpty(host) || StringUtils.isBlank(host)) - throw new RuntimeException("Invalid gearman host name"); - - // i believe gearman already checks for port range, just want to do - // basic verification here - if (port <= 0) - throw new RuntimeException("Invalid gearman port value"); - - logger.info("--- Hostname = "+ this.getHost()); - logger.info("--- Port = "+ this.getPort()); - - // check for a valid connection to gearman server - logger.info("--- Check connection to Gearman Server "+host+":"+port); - boolean validConn = new GearmanWorkerImpl().addServer( - new GearmanNIOJobServerConnection(host, port)); - if (!validConn) { - logger.info("--- Could not get connection to Gearman Server "+host+":"+port); - this.launchWorker = false; // will not spawn any workers, disable flag because - throw new RuntimeException("Could not get connection to Gearman Server " + - host+":"+port); - } - - /* - * Spawn management executor. This worker does not need any - * executors. It only needs to work with gearman. - */ - AbstractWorkerThread gwt = null; - gwt = new ManagementWorkerThread(host, port, host); - gwt.registerJobs(); - gwt.start(); - gmwtHandles.push(gwt); - - /* - * Spawn executors for the jenkins master - * Need to treat the master differently than slaves because - * the master is not the same as a slave - */ - // make sure master is enabled (or has executors) - Node masterNode = null; - try { - masterNode = Computer.currentComputer().getNode(); - } catch (NullPointerException npe) { - logger.info("--- Master is offline"); - } catch (Exception e) { - logger.info("--- Can't get Master"); - e.printStackTrace(); - } - - if (masterNode != null) { - Computer computer = masterNode.toComputer(); - int executors = computer.getExecutors().size(); - for (int i=0; i nodes = jenkins.getNodes(); - if (!nodes.isEmpty()) { - for (Node node : nodes) { - Computer computer = node.toComputer(); - if (computer.isOnline()) { - // create a gearman executor for every jenkins executor - int slaveExecutors = computer.getExecutors().size(); - for (int i=0; i gewtHandles; + public static Stack gmwtHandles; + + /** + * Constructor. + */ + public GearmanPluginConfig() { + logger.info("--- GearmanPluginConfig Constructor ---"); + + gewtHandles = new Stack(); + gmwtHandles = new Stack(); + + load(); + + /* + * Not sure when to register gearman functions yet so for now always + * initialize the launch worker flag to disabled state at jenkins + * startup so we are always at a known state + */ + this.launchWorker = Constants.GEARMAN_DEFAULT_LAUNCH_WORKER; + save(); + } + + /* + * This method checks whether a connection can be made to a host:port + * + * @param host + * the host name + * + * @param port + * the host port + * + * @param timeout + * the timeout (milliseconds) to try the connection + * + * @return + * true if a socket connection can be established otherwise false + */ + public boolean connectionIsAvailable(String host, int port, int timeout) { + + InetSocketAddress endPoint = new InetSocketAddress(host, port); + Socket socket = new Socket(); + + if (endPoint.isUnresolved()) { + System.out.println("Failure " + endPoint); + } else { + try { + socket.connect(endPoint, timeout); + logger.info("Connection Success: "+endPoint); + return true; + } catch (Exception e) { + logger.info("Connection Failure: "+endPoint+" message: " + +e.getClass().getSimpleName()+" - " + +e.getMessage()); + } finally { + if (socket != null) { + try { + socket.close(); + } catch (Exception e) { + logger.info(e.getMessage()); + } + } + } + } + return false; + } + + /* + * This method runs when user clicks Test Connection button. + * + * @return + * message indicating whether connection test passed or failed + */ + public FormValidation doTestConnection( + @QueryParameter("host") final String host, + @QueryParameter("port") final int port) throws IOException, + ServletException { + + if (connectionIsAvailable(host, port, 5000)) { + return FormValidation.ok("Success"); + } else { + return FormValidation.error("Failed: Unable to Connect"); + } + } + + @Override + public boolean configure(StaplerRequest req, JSONObject json) + throws Descriptor.FormException { + + // set the gearman config from user entered values in jenkins config page + launchWorker = json.getBoolean("launchWorker"); + host = json.getString("host"); + port = json.getInt("port"); + + /* + * Purpose here is to create a 1:1 mapping of 'gearman worker':'jenkins + * executor' then use the gearman worker to execute builds on that + * jenkins nodes + */ + if (launchWorker && gmwtHandles.isEmpty() && gewtHandles.isEmpty()) { + + // check for a valid connection to gearman server + logger.info("--- Check connection to Gearman Server " + getHost() + ":" + + getPort()); + if (!connectionIsAvailable(host, port, 5000)) { + this.launchWorker = false; + throw new RuntimeException( + "Could not get connection to Gearman Server " + getHost() + + ":" + getPort()); + } + + /* + * Spawn management executor worker. This worker does not need any + * executors. It only needs to work with gearman. + */ + AbstractWorkerThread gwt = null; + gwt = new ManagementWorkerThread(host, port, host); + gwt.registerJobs(); + gwt.start(); + gmwtHandles.push(gwt); + + /* + * Spawn executors for the jenkins master Need to treat the master + * differently than slaves because the master is not the same as a + * slave + */ + // first make sure master is enabled (or has executors) + Node masterNode = null; + try { + masterNode = Computer.currentComputer().getNode(); + } catch (NullPointerException npe) { + logger.info("--- Master is offline"); + } catch (Exception e) { + logger.info("--- Can't get Master"); + e.printStackTrace(); + } + + if (masterNode != null) { + Computer computer = masterNode.toComputer(); + int executors = computer.getExecutors().size(); + for (int i = 0; i < executors; i++) { + // create a gearman worker for every executor on the master + gwt = new ExecutorWorkerThread(host, port, "master-exec" + + Integer.toString(i), masterNode); + gwt.registerJobs(); + gwt.start(); + gewtHandles.push(gwt); + } + } + + /* + * Spawn executors for the jenkins slaves + */ + List nodes = Jenkins.getInstance().getNodes(); + if (!nodes.isEmpty()) { + for (Node node : nodes) { + Computer computer = node.toComputer(); + if (computer.isOnline()) { + // create a gearman worker for every executor on the slave + int slaveExecutors = computer.getExecutors().size(); + for (int i = 0; i < slaveExecutors; i++) { + gwt = new ExecutorWorkerThread(host, port, + node.getNodeName() + "-exec" + + Integer.toString(i), node); + gwt.registerJobs(); + gwt.start(); + gewtHandles.push(gwt); + } + } + } + } + } + + // stop gearman workers + if (!launchWorker) { + while (!gewtHandles.isEmpty()) { // stop executors + gewtHandles.pop().stop(); + } + while (!gmwtHandles.isEmpty()) { // stop management + gmwtHandles.pop().stop(); + } + } + + int runningExecutors = gmwtHandles.size() + gewtHandles.size(); + logger.info("--- Num of executors running = " + runningExecutors); + + req.bindJSON(this, json); + save(); + return true; + } + + /** + * This method returns true if the global configuration says we should + * launch worker. + */ + public boolean launchWorker() { + return launchWorker; + } + + /** + * This method returns the value from the server host text box + */ + public String getHost() { + return host; + } + + /** + * This method returns the value from the server port text box + */ + public int getPort() { + return port; + } + +} diff --git a/src/main/java/hudson/plugins/gearman/ManagementWorkerThread.java b/src/main/java/hudson/plugins/gearman/ManagementWorkerThread.java index ef37fd4..09d49e7 100644 --- a/src/main/java/hudson/plugins/gearman/ManagementWorkerThread.java +++ b/src/main/java/hudson/plugins/gearman/ManagementWorkerThread.java @@ -34,10 +34,6 @@ public class ManagementWorkerThread extends AbstractWorkerThread{ private static final Logger logger = LoggerFactory .getLogger(AbstractWorkerThread.class); - public ManagementWorkerThread(String host, int port){ - super(host, port); - } - public ManagementWorkerThread(String host, int port, String name){ super(host, port, name); } diff --git a/src/main/resources/hudson/plugins/gearman/GearmanPlugin/config.jelly b/src/main/resources/hudson/plugins/gearman/GearmanPlugin/config.jelly deleted file mode 100644 index f984738..0000000 --- a/src/main/resources/hudson/plugins/gearman/GearmanPlugin/config.jelly +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/gearman/GearmanPlugin/global.jelly b/src/main/resources/hudson/plugins/gearman/GearmanPlugin/global.jelly deleted file mode 100644 index c75df87..0000000 --- a/src/main/resources/hudson/plugins/gearman/GearmanPlugin/global.jelly +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/config.jelly b/src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/config.jelly new file mode 100644 index 0000000..0ac4b1d --- /dev/null +++ b/src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/config.jelly @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/help-serverHost.html b/src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/help-host.html similarity index 100% rename from src/main/webapp/help-serverHost.html rename to src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/help-host.html diff --git a/src/main/webapp/help-launchWorker.html b/src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/help-launchWorker.html similarity index 100% rename from src/main/webapp/help-launchWorker.html rename to src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/help-launchWorker.html diff --git a/src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/help-port.html b/src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/help-port.html new file mode 100644 index 0000000..06fd5c3 --- /dev/null +++ b/src/main/resources/hudson/plugins/gearman/GearmanPluginConfig/help-port.html @@ -0,0 +1,5 @@ +

+

+ Set the Gearman server port. By default the Gearman server's port typically set to 4730 +

+
\ No newline at end of file diff --git a/src/main/resources/index.jelly b/src/main/resources/index.jelly index 9d2baff..7413f09 100644 --- a/src/main/resources/index.jelly +++ b/src/main/resources/index.jelly @@ -1,4 +1,4 @@
This plugin integrates Gearman with Jenkins. Its purpose is to use Gearman workers - to execute Jenkins builds. + to manage Jenkins builds.
\ No newline at end of file diff --git a/src/main/webapp/help-globalConfig.html b/src/main/webapp/help-globalConfig.html deleted file mode 100644 index 2e4f376..0000000 --- a/src/main/webapp/help-globalConfig.html +++ /dev/null @@ -1,5 +0,0 @@ -
-

- This plugin integrates Gearman with Jenkins. -

-
\ No newline at end of file diff --git a/src/main/webapp/help-serverPort.html b/src/main/webapp/help-serverPort.html deleted file mode 100644 index 4e218fa..0000000 --- a/src/main/webapp/help-serverPort.html +++ /dev/null @@ -1,5 +0,0 @@ -
-

- Set the Gearman server port. The Gearman server's port is typically set to 4730 by default. -

-
\ No newline at end of file