Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added workflow/pipeline support for pooled ports. #8

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.424</version>
<version>1.609.1</version>
</parent>

<artifactId>port-allocator</artifactId>
Expand Down Expand Up @@ -31,6 +31,10 @@
</developer>
</developers>

<properties>
<workflow.version>1.8</workflow.version>
</properties>

<scm>
<connection>scm:git:git://github.com/jenkinsci/port-allocator-plugin.git</connection>
<developerConnection>scm:git:ssh://[email protected]/jenkinsci/port-allocator-plugin.git</developerConnection>
Expand Down Expand Up @@ -59,6 +63,57 @@
</pluginRepositories>

<dependencies>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-job</artifactId>
<version>${workflow.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-basic-steps</artifactId>
<version>${workflow.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-cps</artifactId>
<version>${workflow.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-durable-task-step</artifactId>
<version>${workflow.version}</version>
<scope>test</scope>
</dependency>
<dependency> <!-- StepConfigTester -->
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-step-api</artifactId>
<version>${workflow.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency> <!-- JenkinsRuleExt -->
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-aggregator</artifactId>
<version>${workflow.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.modules</groupId>
<artifactId>sshd</artifactId>
<version>1.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.0-rc1</version>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
Expand All @@ -67,4 +122,4 @@
<scope>test</scope>
</dependency>
</dependencies>
</project>
</project>
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -27,7 +28,7 @@

/**
* GlassFish JMX port so that runaway GF instance can be terminated.
*
*
* @author Kohsuke Kawaguchi
*/
public class GlassFishJmxPortType extends PortType {
Expand All @@ -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<Void,IOException>, 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 {
Expand All @@ -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) {
Expand All @@ -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));
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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. */
Expand All @@ -28,7 +32,7 @@ public final class PortAllocationManager {
/**
* Ports currently in use, to the build that uses it.
*/
private final Map<Integer,AbstractBuild> ports = new HashMap<Integer,AbstractBuild>();
private final Map<Integer, String> ports = new HashMap<Integer, String>();

private static final Map<Computer, WeakReference<PortAllocationManager>> INSTANCES =
new WeakHashMap<Computer, WeakReference<PortAllocationManager>>();
Expand All @@ -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,
Expand All @@ -56,16 +60,15 @@ 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;
}

/**
* 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
Expand All @@ -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];
Expand All @@ -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) {
Expand All @@ -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();

/*
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -254,5 +256,9 @@ public Integer call() throws IOException {
}

private static final long serialVersionUID = 1L;

@Override
public void checkRoles(RoleChecker roleChecker) throws SecurityException {
}
}
}
Loading