From e4d57bf75d6adf1b8fd5f9d7f1f10759d2776f51 Mon Sep 17 00:00:00 2001 From: m00re Date: Fri, 22 Jul 2016 08:56:02 +0200 Subject: [PATCH 1/8] Added workflow/pipeline support for pooled ports. --- pom.xml | 62 +++++++++++++++- .../port_allocator/DefaultPortType.java | 13 ++-- .../port_allocator/GlassFishJmxPortType.java | 35 +++++---- .../port_allocator/PooledPortType.java | 16 ++--- .../hudson/plugins/port_allocator/Port.java | 3 +- .../port_allocator/PortAllocationManager.java | 40 ++++++----- .../plugins/port_allocator/PortAllocator.java | 72 +++++++++++-------- .../plugins/port_allocator/PortType.java | 9 +-- .../TomcatShutdownPortType.java | 31 ++++---- 9 files changed, 185 insertions(+), 96 deletions(-) diff --git a/pom.xml b/pom.xml index b754cb3..64cf28f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.jenkins-ci.plugins plugin - 1.424 + 1.609.1 port-allocator @@ -29,8 +29,15 @@ pepov Peter Wilcsinszky + + Jens Mittag + + + 1.8 + + scm:git:git://github.com/jenkinsci/port-allocator-plugin.git scm:git:ssh://git@github.com/jenkinsci/port-allocator-plugin.git @@ -59,6 +66,57 @@ + + org.jenkins-ci.plugins.workflow + workflow-job + ${workflow.version} + test + + + org.jenkins-ci.plugins.workflow + workflow-basic-steps + ${workflow.version} + test + + + org.jenkins-ci.plugins.workflow + workflow-cps + ${workflow.version} + test + + + org.jenkins-ci.plugins.workflow + workflow-durable-task-step + ${workflow.version} + test + + + org.jenkins-ci.plugins.workflow + workflow-step-api + ${workflow.version} + tests + test + + + org.jenkins-ci.plugins.workflow + workflow-aggregator + ${workflow.version} + tests + test + + + org.jenkins-ci.modules + sshd + 1.6 + test + + + org.mockito + mockito-all + 1.9.0-rc1 + jar + test + org.mockito mockito-all @@ -67,4 +125,4 @@ test - + diff --git a/src/main/java/org/jvnet/hudson/plugins/port_allocator/DefaultPortType.java b/src/main/java/org/jvnet/hudson/plugins/port_allocator/DefaultPortType.java index d4b3aef..603d911 100644 --- a/src/main/java/org/jvnet/hudson/plugins/port_allocator/DefaultPortType.java +++ b/src/main/java/org/jvnet/hudson/plugins/port_allocator/DefaultPortType.java @@ -1,8 +1,9 @@ package org.jvnet.hudson.plugins.port_allocator; -import hudson.model.AbstractBuild; import hudson.model.BuildListener; import hudson.Launcher; +import hudson.model.Run; +import hudson.model.TaskListener; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; @@ -22,13 +23,15 @@ public DefaultPortType(String name) { } @Override - public Port allocate(AbstractBuild build, final PortAllocationManager manager, int prefPort, Launcher launcher, BuildListener buildListener) throws IOException, InterruptedException { + public Port allocate(Run run, final PortAllocationManager manager, int prefPort, Launcher launcher, TaskListener taskListener) + throws IOException, InterruptedException + { final int n; if(isFixedPort()) - n = manager.allocate(build, getFixedPort()); + n = manager.allocate(run, getFixedPort()); else - n = manager.allocateRandom(build, prefPort); - + n = manager.allocateRandom(run, prefPort); + return new Port(this) { public int get() { return n; diff --git a/src/main/java/org/jvnet/hudson/plugins/port_allocator/GlassFishJmxPortType.java b/src/main/java/org/jvnet/hudson/plugins/port_allocator/GlassFishJmxPortType.java index 6154e13..3f0ba75 100644 --- a/src/main/java/org/jvnet/hudson/plugins/port_allocator/GlassFishJmxPortType.java +++ b/src/main/java/org/jvnet/hudson/plugins/port_allocator/GlassFishJmxPortType.java @@ -1,10 +1,11 @@ package org.jvnet.hudson.plugins.port_allocator; import hudson.Launcher; -import hudson.model.AbstractBuild; -import hudson.model.BuildListener; +import hudson.model.Run; +import hudson.model.TaskListener; import hudson.remoting.Callable; import net.sf.json.JSONObject; +import org.jenkinsci.remoting.RoleChecker; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; @@ -27,7 +28,7 @@ /** * GlassFish JMX port so that runaway GF instance can be terminated. - * + * * @author Kohsuke Kawaguchi */ public class GlassFishJmxPortType extends PortType { @@ -48,21 +49,23 @@ public GlassFishJmxPortType(String name, String userName, String password) { } @Override - public Port allocate(AbstractBuild build, final PortAllocationManager manager, int prefPort, final Launcher launcher, final BuildListener buildListener) throws IOException, InterruptedException { + public Port allocate(Run run, final PortAllocationManager manager, int prefPort, final Launcher launcher, final TaskListener taskListener) + throws IOException, InterruptedException + { final int n; if(isFixedPort()) - n = manager.allocate(build, getFixedPort()); + n = manager.allocate(run, getFixedPort()); else - n = manager.allocateRandom(build, prefPort); + n = manager.allocateRandom(run, prefPort); /** * Cleans up GlassFish instance. */ final class GlassFishCleanUpTask implements Callable, Serializable { - private final BuildListener buildListener; + private final TaskListener taskListener; - public GlassFishCleanUpTask(BuildListener buildListener) { - this.buildListener = buildListener; + public GlassFishCleanUpTask(TaskListener taskListener) { + this.taskListener = taskListener; } public Void call() throws IOException { @@ -87,23 +90,27 @@ public Void call() throws IOException { } catch (UnmarshalException e) { if(e.getCause() instanceof SocketException || e.getCause() instanceof IOException) { // to be expected, as the above would shut down the server. - buildListener.getLogger().println("GlassFish was shut down"); + taskListener.getLogger().println("GlassFish was shut down"); } else { throw e; } } catch (MalformedObjectNameException e) { throw new AssertionError(e); // impossible } catch (InstanceNotFoundException e) { - e.printStackTrace(buildListener.error("Unable to find J2EEServer mbean")); + e.printStackTrace(taskListener.error("Unable to find J2EEServer mbean")); } catch (ReflectionException e) { - e.printStackTrace(buildListener.error("Unable to access J2EEServer mbean")); + e.printStackTrace(taskListener.error("Unable to access J2EEServer mbean")); } catch (MBeanException e) { - e.printStackTrace(buildListener.error("Unable to call J2EEServer mbean")); + e.printStackTrace(taskListener.error("Unable to call J2EEServer mbean")); } return null; } private static final long serialVersionUID = 1L; + + @Override + public void checkRoles(RoleChecker roleChecker) throws SecurityException { + } } return new Port(this) { @@ -113,7 +120,7 @@ public int get() { public void cleanUp() throws IOException, InterruptedException { manager.free(n); - launcher.getChannel().call(new GlassFishCleanUpTask(buildListener)); + launcher.getChannel().call(new GlassFishCleanUpTask(taskListener)); } }; } diff --git a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PooledPortType.java b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PooledPortType.java index 3ed9f01..4e64f5c 100644 --- a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PooledPortType.java +++ b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PooledPortType.java @@ -2,8 +2,9 @@ import hudson.Extension; import hudson.Launcher; -import hudson.model.AbstractBuild; import hudson.model.BuildListener; +import hudson.model.Run; +import hudson.model.TaskListener; import hudson.util.ListBoxModel; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; @@ -30,23 +31,14 @@ public PooledPortType(String name) { * Wait for a short period if no free port is available, then try again. */ @Override - public Port allocate( - AbstractBuild build, - final PortAllocationManager manager, - int prefPort, - Launcher launcher, - BuildListener buildListener - ) throws IOException, InterruptedException { - + public Port allocate(Run run, PortAllocationManager manager, int prefPort, Launcher launcher, TaskListener taskListener) throws IOException, InterruptedException { try { while (true) { - Pool pool = PortAllocator.DESCRIPTOR.getPoolByName(name); - synchronized (pool) { for (int port : pool.getPortsAsInt()) { if (manager.isFree(port)) { - manager.allocate(build, port); + manager.allocate(run, port); return new PooledPort(this, port, manager); } } diff --git a/src/main/java/org/jvnet/hudson/plugins/port_allocator/Port.java b/src/main/java/org/jvnet/hudson/plugins/port_allocator/Port.java index c18b336..d4968e9 100644 --- a/src/main/java/org/jvnet/hudson/plugins/port_allocator/Port.java +++ b/src/main/java/org/jvnet/hudson/plugins/port_allocator/Port.java @@ -1,13 +1,14 @@ package org.jvnet.hudson.plugins.port_allocator; import java.io.IOException; +import java.io.Serializable; /** * Represents an assigned TCP port and encapsulates how it should be cleaned up. * * @author Kohsuke Kawaguchi */ -public abstract class Port { +public abstract class Port implements Serializable { /** * {@link PortType} that created this port. */ diff --git a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocationManager.java b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocationManager.java index c884fb0..d18d138 100644 --- a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocationManager.java +++ b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocationManager.java @@ -1,10 +1,13 @@ package org.jvnet.hudson.plugins.port_allocator; -import hudson.model.AbstractBuild; +import hudson.Extension; import hudson.model.Computer; +import hudson.model.Run; import hudson.remoting.Callable; +import org.jenkinsci.remoting.RoleChecker; import java.io.IOException; +import java.io.Serializable; import java.lang.ref.WeakReference; import java.net.ServerSocket; import java.util.HashMap; @@ -19,7 +22,8 @@ * @author Rama Pulavarthi * @author Kohsuke Kawaguchi */ -public final class PortAllocationManager { + +public final class PortAllocationManager implements Serializable { private final Computer node; /** Maximum number of tries to allocate a specific port range. */ @@ -28,7 +32,7 @@ public final class PortAllocationManager { /** * Ports currently in use, to the build that uses it. */ - private final Map ports = new HashMap(); + private final Map ports = new HashMap(); private static final Map> INSTANCES = new WeakHashMap>(); @@ -47,7 +51,7 @@ private PortAllocationManager(Computer node) { * Preffered port. This method trys to assign this port, and upon failing, fall back to * assigning a random port. */ - public synchronized int allocateRandom(AbstractBuild owner, int prefPort) throws InterruptedException, IOException { + public synchronized int allocateRandom(Run run, int prefPort) throws InterruptedException, IOException { int i; try { // try to allocate preferential port, @@ -56,7 +60,7 @@ public synchronized int allocateRandom(AbstractBuild owner, int prefPort) throws // if not available, assign a random port i = allocatePort(0); } - ports.put(i,owner); + ports.put(i,run.getId()); return i; } @@ -64,8 +68,7 @@ public synchronized int allocateRandom(AbstractBuild owner, int prefPort) throws * Allocate a continuous range of ports within specified limits. * The caller is responsible for freeing the individual ports within * the allocated range. - * @param portAllocator - * @param build the current build + * @param run the current build * @param start the first in the range of allowable ports * @param end the last entry in the range of allowable ports * @param count the number of ports to allocate @@ -75,7 +78,7 @@ public synchronized int allocateRandom(AbstractBuild owner, int prefPort) throws * @throws IOException if the allocation failed */ public int[] allocatePortRange( - final AbstractBuild owner, + final Run run, int start, int end, int count, boolean isConsecutive) throws InterruptedException, IOException { int[] allocated = new int[count]; @@ -100,7 +103,7 @@ public int[] allocatePortRange( final int i; synchronized (this) { i = allocatePort(requestedPort); - ports.put(i, owner); + ports.put(i, run.getId()); } allocated[offset] = i; } catch (PortUnavailableException ex) { @@ -126,8 +129,8 @@ public int[] allocatePortRange( * * This method blocks until the port becomes available. */ - public synchronized int allocate(AbstractBuild owner, int port) throws InterruptedException, IOException { - while(ports.get(port)!=null) + public synchronized int allocate(Run run, int port) throws InterruptedException, IOException { + while (ports.get(port) != null) wait(); /* @@ -160,13 +163,12 @@ the port actually becomes available (not only in our book-keeping but also at OS // wait(10000); // } // } - ports.put(port,owner); + ports.put(port,run.getId()); return port; } public synchronized boolean isFree(int port) { - AbstractBuild owner = ports.get(port); - if (owner == null) { + if (ports.get(port) == null) { return true; } return false; @@ -211,9 +213,9 @@ public synchronized void free(int n) { * If the specified port is not available */ private int allocatePort(final int port) throws InterruptedException, IOException { - AbstractBuild owner = ports.get(port); - if(owner!=null) - throw new PortUnavailableException("Owned by "+owner); + String id = ports.get(port); + if (id != null) + throw new PortUnavailableException("Owned by run " + id); return node.getChannel().call(new AllocateTask(port)); } @@ -254,5 +256,9 @@ public Integer call() throws IOException { } private static final long serialVersionUID = 1L; + + @Override + public void checkRoles(RoleChecker roleChecker) throws SecurityException { + } } } diff --git a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java index bc12a32..bfbc043 100644 --- a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java +++ b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java @@ -1,13 +1,17 @@ package org.jvnet.hudson.plugins.port_allocator; +import hudson.EnvVars; import hudson.Extension; +import hudson.FilePath; import hudson.Launcher; import hudson.model.*; import hudson.tasks.BuildWrapper; +import jenkins.tasks.SimpleBuildWrapper; import hudson.util.FormValidation; import net.sf.json.JSONObject; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; @@ -22,11 +26,11 @@ * *

* This just mediates between different Jobs running on the same Computer - * by assigning free ports and its the jobs responsibility to open and close the ports. + * by assigning free ports and its the jobs responsibility to open and close the ports. * * @author Rama Pulavarthi */ -public class PortAllocator extends BuildWrapper +public class PortAllocator extends SimpleBuildWrapper { private static final Log log = LogFactory.getLog(PortAllocator.class); @@ -36,14 +40,21 @@ private PortAllocator(PortType[] ports){ this.ports = ports; } + @DataBoundConstructor + public PortAllocator(String pool) { + List ports = new ArrayList(); + ports.add(new PooledPortType(pool)); + this.ports = ports.toArray(new PortType[ports.size()]); + } + @Override - public Environment setUp(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException { - PrintStream logger = listener.getLogger(); + public void setUp(Context context, Run run, FilePath workspace, Launcher launcher, TaskListener taskListener, EnvVars envVars) throws IOException, InterruptedException { + PrintStream logger = taskListener.getLogger(); - final Computer cur = Executor.currentExecutor().getOwner(); + Computer cur = workspace.toComputer(); Map prefPortMap = new HashMap(); - if (build.getPreviousBuild() != null) { - AllocatedPortAction prevAlloc = build.getPreviousBuild().getAction(AllocatedPortAction.class); + if (run.getPreviousBuild() != null) { + AllocatedPortAction prevAlloc = run.getPreviousBuild().getAction(AllocatedPortAction.class); if (prevAlloc != null) { // try to assign ports assigned in previous build prefPortMap = prevAlloc.getPreviousAllocatedPorts(); @@ -56,31 +67,19 @@ public Environment setUp(AbstractBuild build, Launcher launcher, BuildListener l for (PortType pt : ports) { logger.println("Allocating TCP port "+pt.name); int prefPort = prefPortMap.get(pt.name)== null?0:prefPortMap.get(pt.name); - Port p = pt.allocate(build, pam, prefPort, launcher, listener); + Port p = pt.allocate(run, pam, prefPort, launcher, taskListener); allocated.add(p); - portMap.put(pt.name,p.get()); + portMap.put(pt.name, p.get()); logger.println(" -> Assigned "+p.get()); } // TODO: only log messages when we are blocking. logger.println("TCP port allocation complete"); - build.addAction(new AllocatedPortAction(portMap)); + run.addAction(new AllocatedPortAction(portMap)); - return new Environment() { - - @Override - public void buildEnvVars(Map env) { - for (Port p : allocated) - env.put(p.type.name, String.valueOf(p.get())); - } - - @Override - public boolean tearDown(AbstractBuild build, BuildListener listener) throws IOException, InterruptedException { - for (Port p : allocated) - p.cleanUp(); - return true; - } - }; + context.setDisposer(new CleanupDisposer(allocated)); + for (Port p : allocated) + context.env(p.type.name, String.valueOf(p.get())); } public String getDisplayName() { @@ -97,7 +96,7 @@ public Descriptor getDescriptor() { public static final class DescriptorImpl extends Descriptor { - private Pool[] pools = new Pool[] {}; + private List pools = new ArrayList(); public DescriptorImpl() { super(PortAllocator.class); @@ -114,7 +113,7 @@ public String getHelpFile() { } public List getPortTypes() { - return PortTypeDescriptor.LIST; + return PortTypeDescriptor.LIST; } @Override @@ -139,8 +138,8 @@ public boolean configure(StaplerRequest req, JSONObject formData) throws FormExc for (Pool p : pools) { p.name = checkPoolName(p.name); checkPortNumbers(p.ports); + this.pools.add(p); } - this.pools = pools; save(); return super.configure(req,formData); } @@ -184,7 +183,7 @@ private void checkPortNumbers(String ports) throws FormException { } } - public Pool[] getPools() { + public List getPools() { return pools; } @@ -201,4 +200,19 @@ public int getPoolSize(String poolName) throws PoolNotDefinedException { return getPoolByName(poolName).getPortsAsInt().length; } } + + private static class CleanupDisposer extends Disposer { + + List allocated; + + public CleanupDisposer(List allocated) { + this.allocated = allocated; + } + + @Override + public void tearDown(Run build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException { + for (Port p : allocated) + p.cleanUp(); + } + } } diff --git a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortType.java b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortType.java index ffab86a..3ed40ea 100644 --- a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortType.java +++ b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortType.java @@ -2,9 +2,10 @@ import hudson.ExtensionPoint; import hudson.Launcher; -import hudson.model.AbstractBuild; import hudson.model.Describable; import hudson.model.BuildListener; +import hudson.model.Run; +import hudson.model.TaskListener; import java.io.IOException; import java.io.Serializable; @@ -53,15 +54,15 @@ public final boolean isFixedPort() { /** * Allocates a new port for a given build. - * + * @param run + * The current build * @param manager * This can be used to assign a new TCP port number. * @param prefPort * The port number allocated to this type the last time. * @param launcher - * @param buildListener */ - public abstract Port allocate(AbstractBuild build, PortAllocationManager manager, int prefPort, Launcher launcher, BuildListener buildListener) throws IOException, InterruptedException; + public abstract Port allocate(Run run, PortAllocationManager manager, int prefPort, Launcher launcher, TaskListener taskListener) throws IOException, InterruptedException; public abstract PortTypeDescriptor getDescriptor(); diff --git a/src/main/java/org/jvnet/hudson/plugins/port_allocator/TomcatShutdownPortType.java b/src/main/java/org/jvnet/hudson/plugins/port_allocator/TomcatShutdownPortType.java index 89f471c..c3246b9 100644 --- a/src/main/java/org/jvnet/hudson/plugins/port_allocator/TomcatShutdownPortType.java +++ b/src/main/java/org/jvnet/hudson/plugins/port_allocator/TomcatShutdownPortType.java @@ -1,10 +1,11 @@ package org.jvnet.hudson.plugins.port_allocator; import hudson.Launcher; -import hudson.model.AbstractBuild; -import hudson.model.BuildListener; +import hudson.model.Run; +import hudson.model.TaskListener; import hudson.remoting.Callable; import net.sf.json.JSONObject; +import org.jenkinsci.remoting.RoleChecker; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; @@ -14,7 +15,7 @@ /** * Tomcat shutdown port. - * + * * @author Kohsuke Kawaguchi */ public class TomcatShutdownPortType extends PortType { @@ -30,18 +31,20 @@ public TomcatShutdownPortType(String name, String password) { } @Override - public Port allocate(AbstractBuild build, final PortAllocationManager manager, int prefPort, final Launcher launcher, final BuildListener buildListener) throws IOException, InterruptedException { + public Port allocate(Run run, final PortAllocationManager manager, int prefPort, final Launcher launcher, final TaskListener taskListener) + throws IOException, InterruptedException + { final int n; if(isFixedPort()) - n = manager.allocate(build, getFixedPort()); + n = manager.allocate(run, getFixedPort()); else - n = manager.allocateRandom(build, prefPort); + n = manager.allocateRandom(run, prefPort); final class TomcatCleanUpTask implements Callable, Serializable { - private final BuildListener buildListener; + private final TaskListener taskListener; - public TomcatCleanUpTask(BuildListener buildListener) { - this.buildListener = buildListener; + public TomcatCleanUpTask(TaskListener taskListener) { + this.taskListener = taskListener; } public Void call() throws IOException { @@ -56,14 +59,18 @@ public Void call() throws IOException { try { s.getOutputStream().write(password.getBytes()); s.close(); - buildListener.getLogger().println("Shutdown left-over Tomcat"); + taskListener.getLogger().println("Shutdown left-over Tomcat"); } catch (IOException x) { - x.printStackTrace(buildListener.error("Failed to write to Tomcat shutdown port")); + x.printStackTrace(taskListener.error("Failed to write to Tomcat shutdown port")); } return null; } private static final long serialVersionUID = 1L; + + @Override + public void checkRoles(RoleChecker roleChecker) throws SecurityException { + } } return new Port(this) { @@ -73,7 +80,7 @@ public int get() { public void cleanUp() throws IOException, InterruptedException { manager.free(n); - launcher.getChannel().call(new TomcatCleanUpTask(buildListener)); + launcher.getChannel().call(new TomcatCleanUpTask(taskListener)); } }; } From 61b7bdb7d402e6cf3080ad274f808eed6423d829 Mon Sep 17 00:00:00 2001 From: m00re Date: Fri, 5 Aug 2016 12:00:27 +0200 Subject: [PATCH 2/8] Testcase for workflow usage added and removal of 'Jens Mittag' from list of developers. --- pom.xml | 3 - .../PortAllocationWorkflowTest.java | 68 +++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/jvnet/hudson/plugins/port_allocator/PortAllocationWorkflowTest.java diff --git a/pom.xml b/pom.xml index 64cf28f..9c2dbde 100644 --- a/pom.xml +++ b/pom.xml @@ -29,9 +29,6 @@ pepov Peter Wilcsinszky - - Jens Mittag - diff --git a/src/test/java/org/jvnet/hudson/plugins/port_allocator/PortAllocationWorkflowTest.java b/src/test/java/org/jvnet/hudson/plugins/port_allocator/PortAllocationWorkflowTest.java new file mode 100644 index 0000000..f5ac22e --- /dev/null +++ b/src/test/java/org/jvnet/hudson/plugins/port_allocator/PortAllocationWorkflowTest.java @@ -0,0 +1,68 @@ +package org.jvnet.hudson.plugins.port_allocator; + +import hudson.model.Label; +import hudson.model.Node; +import hudson.model.Result; +import hudson.model.queue.QueueTaskFuture; +import hudson.slaves.DumbSlave; +import hudson.slaves.NodeProperty; +import hudson.slaves.RetentionStrategy; +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.jenkinsci.plugins.workflow.job.WorkflowRun; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runners.MethodSorters; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.RestartableJenkinsRule; + +import java.util.Collections; +import java.util.List; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class PortAllocationWorkflowTest { + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + + @Test + public void wrap_01_WithNonExistingPool() throws Exception { + j.jenkins.addNode(new DumbSlave("slave", "dummy", + tmp.newFolder("remoteFS").getPath(), "1", Node.Mode.NORMAL, "", + j.createComputerLauncher(null), RetentionStrategy.NOOP, Collections.>emptyList())); + WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition( + "node('slave') {\n" + + " wrap([$class: 'PortAllocator', pool: 'WEBLOGIC']) {\n" + + " }\n" + + "}" + )); + j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + } + + @Test + public void wrap_02_WithExistingPool() throws Exception { + j.jenkins.addNode(new DumbSlave("slave", "dummy", + tmp.newFolder("remoteFS").getPath(), "1", Node.Mode.NORMAL, "", + j.createComputerLauncher(null), RetentionStrategy.NOOP, Collections.>emptyList())); + PortAllocator.DescriptorImpl desc = j.jenkins.getDescriptorByType(PortAllocator.DescriptorImpl.class); + Pool weblogic = new Pool(); + weblogic.name = "WEBLOGIC"; + weblogic.ports = "7001,8001"; + desc.getPools().add(weblogic); + WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition( + "node('slave') {\n" + + " wrap([$class: 'PortAllocator', pool: 'WEBLOGIC']) {\n" + + " }\n" + + "}" + )); + j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + } +} From b007264f16be67020cfda72c057627e707fafa72 Mon Sep 17 00:00:00 2001 From: Roman Schaller Date: Mon, 3 Oct 2016 18:02:44 +0200 Subject: [PATCH 3/8] clear list before saving it. Otherwise we always double the configuration --- .../org/jvnet/hudson/plugins/port_allocator/PortAllocator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java index bfbc043..47426cc 100644 --- a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java +++ b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java @@ -135,6 +135,7 @@ public BuildWrapper newInstance(StaplerRequest req, JSONObject formData) throws @Override public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { Pool[] pools = req.bindParametersToList(Pool.class, "pool.").toArray(new Pool[] {}); + this.pools.clear(); for (Pool p : pools) { p.name = checkPoolName(p.name); checkPortNumbers(p.ports); From fc80faba68f28a6e92344ee05d304a2918761bc1 Mon Sep 17 00:00:00 2001 From: Roman Schaller Date: Tue, 4 Oct 2016 09:59:51 +0200 Subject: [PATCH 4/8] Add some tests to PortAllocator.DescriptorImpl --- .../PortAllocatorDescriptorTest.java | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/test/java/org/jvnet/hudson/plugins/port_allocator/PortAllocatorDescriptorTest.java diff --git a/src/test/java/org/jvnet/hudson/plugins/port_allocator/PortAllocatorDescriptorTest.java b/src/test/java/org/jvnet/hudson/plugins/port_allocator/PortAllocatorDescriptorTest.java new file mode 100644 index 0000000..6e7d830 --- /dev/null +++ b/src/test/java/org/jvnet/hudson/plugins/port_allocator/PortAllocatorDescriptorTest.java @@ -0,0 +1,110 @@ +package org.jvnet.hudson.plugins.port_allocator; + +import net.sf.json.JSONObject; +import org.junit.Assert; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runners.MethodSorters; +import org.jvnet.hudson.test.JenkinsRule; +import org.kohsuke.stapler.StaplerRequest; +import org.mockito.Mockito; + +import java.util.Collections; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class PortAllocatorDescriptorTest { + + @Rule + public JenkinsRule j = new JenkinsRule(); + + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + + private PortAllocator.DescriptorImpl descriptor; + + @Before + public void setUp() { + descriptor = new PortAllocator.DescriptorImpl(); + } + + @Test + public void test_01_AddAPool() throws Exception { + Pool pool = new Pool(); + pool.name = "mypool"; + pool.ports = "7001,7002"; + + StaplerRequest requestMock = Mockito.mock(StaplerRequest.class); + Mockito.when(requestMock.bindParametersToList(Pool.class, "pool.")).thenReturn(Collections.singletonList(pool)); + JSONObject jsonObject = new JSONObject(); + descriptor.configure(requestMock, jsonObject); + Assert.assertEquals(1, descriptor.getPools().size()); + } + + @Test + public void test_02_AddAPoolTwice() throws Exception { + Pool pool = new Pool(); + pool.name = "otherPool"; + pool.ports = "7003,7004"; + + StaplerRequest requestMock = Mockito.mock(StaplerRequest.class); + Mockito.when(requestMock.bindParametersToList(Pool.class, "pool.")).thenReturn(Collections.singletonList(pool)); + JSONObject jsonObject = new JSONObject(); + descriptor.configure(requestMock, jsonObject); + Assert.assertEquals(1, descriptor.getPools().size()); + + // configure the same pool again. It should yield to the same result + descriptor.configure(requestMock, jsonObject); + Assert.assertEquals(1, descriptor.getPools().size()); + } + + @Test + public void test_03_RemoveAPool() throws Exception { + Pool pool = new Pool(); + pool.name = "wrongPool"; + pool.ports = "7033,7044"; + + StaplerRequest requestMock = Mockito.mock(StaplerRequest.class); + + Mockito.when(requestMock.bindParametersToList(Pool.class, "pool.")).thenReturn(Collections.singletonList(pool)); + JSONObject jsonObject = new JSONObject(); + // add the pool + descriptor.configure(requestMock, jsonObject); + Assert.assertEquals(1, descriptor.getPools().size()); + + // remove the pool + Mockito.reset(requestMock); + Mockito.when(requestMock.bindParametersToList(Pool.class, "pool.")).thenReturn(Collections.emptyList()); + descriptor.configure(requestMock, jsonObject); + Assert.assertEquals(0, descriptor.getPools().size()); + } + + @Test + public void test_04_ChangeAPool() throws Exception { + Pool pool = new Pool(); + pool.name = "wrongPool"; + pool.ports = "7033,7044"; + + StaplerRequest requestMock = Mockito.mock(StaplerRequest.class); + + Mockito.when(requestMock.bindParametersToList(Pool.class, "pool.")).thenReturn(Collections.singletonList(pool)); + JSONObject jsonObject = new JSONObject(); + // add the pool + descriptor.configure(requestMock, jsonObject); + Assert.assertEquals(1, descriptor.getPools().size()); + Assert.assertEquals("WRONGPOOL", descriptor.getPools().get(0).name); + Assert.assertEquals("7033,7044", descriptor.getPools().get(0).ports); + + // change the pool + pool.name = "correctPool"; + pool.ports = "8001,8002,8003,8004"; + Mockito.reset(requestMock); + Mockito.when(requestMock.bindParametersToList(Pool.class, "pool.")).thenReturn(Collections.singletonList(pool)); + descriptor.configure(requestMock, jsonObject); + Assert.assertEquals(1, descriptor.getPools().size()); + Assert.assertEquals("CORRECTPOOL", descriptor.getPools().get(0).name); + Assert.assertEquals("8001,8002,8003,8004", descriptor.getPools().get(0).ports); + } +} From f9c0a82c9e47c10ab3fa66972cd7bc0417668f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Pedersen?= Date: Tue, 25 Oct 2016 14:49:59 +0200 Subject: [PATCH 5/8] Implement multiple pools and ports for workflow Change away from DataBoundConstructor (the empty default is still needed) to DataBoundSetters to allow both multiple pools and plain ports in a single wrap. The legacy single pool variant is still supported. --- .../plugins/port_allocator/PortAllocator.java | 69 +++++++++++++++---- .../PortAllocationWorkflowTest.java | 43 +++++++++++- 2 files changed, 97 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java index 47426cc..6a74378 100644 --- a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java +++ b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java @@ -12,9 +12,12 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; +import com.google.common.collect.Lists; + import java.io.IOException; import java.io.PrintStream; import java.util.*; @@ -34,18 +37,60 @@ public class PortAllocator extends SimpleBuildWrapper { private static final Log log = LogFactory.getLog(PortAllocator.class); - public final PortType[] ports; - - private PortAllocator(PortType[] ports){ - this.ports = ports; - } - - @DataBoundConstructor - public PortAllocator(String pool) { - List ports = new ArrayList(); - ports.add(new PooledPortType(pool)); - this.ports = ports.toArray(new PortType[ports.size()]); - } + public final List ports = Lists.newArrayList(); + + private String pool; + private final List pools = Lists.newArrayList(); + private final List plainports = Lists.newArrayList(); + + private PortAllocator(PortType[] ports) { + this.ports.addAll(Arrays.asList(ports)); + } + + @DataBoundConstructor + public PortAllocator() { + + } + + @DataBoundSetter + public void setPool(String pool) { + if (pool != null) { + this.ports.add(new PooledPortType(pool)); + this.pool = pool; + } + } + + public String getPool() { + return this.pool; + } + + @DataBoundSetter + public void setPools(String[] pools) { + if (pools != null) { + for (String pool : pools) { + this.ports.add(new PooledPortType(pool)); + this.pools.add(pool); + } + } + } + + public String[] getPools() { + return this.pools.toArray(new String[this.pools.size()]); + } + + @DataBoundSetter + public void setPlainports(String[] plainports) { + if (plainports != null) { + for (String port : plainports) { + this.ports.add(new DefaultPortType(port)); + this.plainports.add(port); + } + } + } + + public String[] getPlainports() { + return this.plainports.toArray(new String[this.plainports.size()]); + } @Override public void setUp(Context context, Run run, FilePath workspace, Launcher launcher, TaskListener taskListener, EnvVars envVars) throws IOException, InterruptedException { diff --git a/src/test/java/org/jvnet/hudson/plugins/port_allocator/PortAllocationWorkflowTest.java b/src/test/java/org/jvnet/hudson/plugins/port_allocator/PortAllocationWorkflowTest.java index f5ac22e..b082b05 100644 --- a/src/test/java/org/jvnet/hudson/plugins/port_allocator/PortAllocationWorkflowTest.java +++ b/src/test/java/org/jvnet/hudson/plugins/port_allocator/PortAllocationWorkflowTest.java @@ -39,15 +39,35 @@ public void wrap_01_WithNonExistingPool() throws Exception { WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p"); p.setDefinition(new CpsFlowDefinition( "node('slave') {\n" - + " wrap([$class: 'PortAllocator', pool: 'WEBLOGIC']) {\n" + + " wrap([$class: 'PortAllocator', pools: ['WEBLOGIC']]) {\n" + " }\n" + "}" )); j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); } - + @Test public void wrap_02_WithExistingPool() throws Exception { + j.jenkins.addNode(new DumbSlave("slave", "dummy", + tmp.newFolder("remoteFS").getPath(), "1", Node.Mode.NORMAL, "", + j.createComputerLauncher(null), RetentionStrategy.NOOP, Collections.>emptyList())); + PortAllocator.DescriptorImpl desc = j.jenkins.getDescriptorByType(PortAllocator.DescriptorImpl.class); + Pool weblogic = new Pool(); + weblogic.name = "WEBLOGIC"; + weblogic.ports = "7001,8001"; + desc.getPools().add(weblogic); + WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition( + "node('slave') {\n" + + " wrap([$class: 'PortAllocator', pools: ['WEBLOGIC']]) {\n" + + " }\n" + + "}" + )); + j.assertBuildStatusSuccess(p.scheduleBuild2(0).get()); + } + + @Test + public void wrap_03_WithExistingLegacyPool() throws Exception { j.jenkins.addNode(new DumbSlave("slave", "dummy", tmp.newFolder("remoteFS").getPath(), "1", Node.Mode.NORMAL, "", j.createComputerLauncher(null), RetentionStrategy.NOOP, Collections.>emptyList())); @@ -63,6 +83,23 @@ public void wrap_02_WithExistingPool() throws Exception { + " }\n" + "}" )); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); + j.assertBuildStatusSuccess(p.scheduleBuild2(0).get()); + } + + @Test + public void wrap_04_WithPlainPort() throws Exception { + j.jenkins.addNode(new DumbSlave("slave", "dummy", + tmp.newFolder("remoteFS").getPath(), "1", Node.Mode.NORMAL, "", + j.createComputerLauncher(null), RetentionStrategy.NOOP, Collections.>emptyList())); + PortAllocator.DescriptorImpl desc = j.jenkins.getDescriptorByType(PortAllocator.DescriptorImpl.class); + WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition( + "node('slave') {\n" + + " wrap([$class: 'PortAllocator', plainports: ['PLAINPORT']]) {\n" + + " }\n" + + "}" + )); + j.assertBuildStatusSuccess(p.scheduleBuild2(0).get()); } + } From 692b72f38fdbbd8b846ba87565e6cfb1f45acc57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Pedersen?= Date: Tue, 25 Oct 2016 14:49:59 +0200 Subject: [PATCH 6/8] Symbol supoort --- pom.xml | 11 ++--------- .../plugins/port_allocator/PortAllocator.java | 11 +++++++++-- src/main/resources/index.jelly | 1 + .../port_allocator/DefaultPortType/config.jelly | 3 ++- .../GlassFishJmxPortType/config.jelly | 3 ++- .../port_allocator/PooledPortType/config.jelly | 3 ++- .../port_allocator/PortAllocator/config.jelly | 3 ++- .../port_allocator/PortAllocator/global.jelly | 3 ++- .../TomcatShutdownPortType/config.jelly | 3 ++- .../PortAllocationWorkflowTest.java | 16 ++++++++++++++++ 10 files changed, 40 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 9c2dbde..4ae39d2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,9 +3,8 @@ org.jenkins-ci.plugins plugin - 1.609.1 + 2.4 - port-allocator hpi 1.9-SNAPSHOT @@ -32,6 +31,7 @@ + 2.7.1 1.8 @@ -114,12 +114,5 @@ jar test - - org.mockito - mockito-all - 1.9.0-rc1 - jar - test - diff --git a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java index 6a74378..b6022d8 100644 --- a/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java +++ b/src/main/java/org/jvnet/hudson/plugins/port_allocator/PortAllocator.java @@ -11,6 +11,7 @@ import net.sf.json.JSONObject; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; @@ -42,14 +43,18 @@ public class PortAllocator extends SimpleBuildWrapper private String pool; private final List pools = Lists.newArrayList(); private final List plainports = Lists.newArrayList(); + private final List genericPorts = Lists.newArrayList(); private PortAllocator(PortType[] ports) { - this.ports.addAll(Arrays.asList(ports)); + if (ports != null) { + this.ports.addAll(Arrays.asList(ports)); + } + } @DataBoundConstructor public PortAllocator() { - + // empty } @DataBoundSetter @@ -92,6 +97,7 @@ public String[] getPlainports() { return this.plainports.toArray(new String[this.plainports.size()]); } + @Override public void setUp(Context context, Run run, FilePath workspace, Launcher launcher, TaskListener taskListener, EnvVars envVars) throws IOException, InterruptedException { PrintStream logger = taskListener.getLogger(); @@ -139,6 +145,7 @@ public Descriptor getDescriptor() { @Extension public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); + @Symbol("portallocator") public static final class DescriptorImpl extends Descriptor { private List pools = new ArrayList(); diff --git a/src/main/resources/index.jelly b/src/main/resources/index.jelly index b8c39be..921df5a 100644 --- a/src/main/resources/index.jelly +++ b/src/main/resources/index.jelly @@ -1,3 +1,4 @@ +