Skip to content

Commit

Permalink
JENKINS-11255 Pooled port allocation support (+ implement ResourceAct…
Browse files Browse the repository at this point in the history
…ivity to queue builds instead of blocking after start).
  • Loading branch information
pepov committed Feb 3, 2013
1 parent 93925c5 commit 283b53e
Show file tree
Hide file tree
Showing 14 changed files with 354 additions and 32 deletions.
9 changes: 6 additions & 3 deletions pom.xml
Expand Up @@ -25,7 +25,12 @@
<id>oldelvet</id>
<name>Richard Mortimer</name>
</developer>
<developer>
<id>pepov</id>
<name>Peter Wilcsinszky</name>
</developer>
</developers>

<scm>
<connection>scm:git:git://github.com/jenkinsci/port-allocator-plugin.git</connection>
<developerConnection>scm:git:git@github.com:jenkinsci/port-allocator-plugin.git</developerConnection>
Expand All @@ -45,6 +50,4 @@
<url>http://repo.jenkins-ci.org/public/</url>
</pluginRepository>
</pluginRepositories>
</project>


</project>
Expand Up @@ -12,6 +12,7 @@ public class PluginImpl extends Plugin {
@Override
public void start() throws Exception {
PortTypeDescriptor.LIST.add(DefaultPortType.DescriptorImpl.INSTANCE);
PortTypeDescriptor.LIST.add(PooledPortType.DescriptorImpl.INSTANCE);
PortTypeDescriptor.LIST.add(GlassFishJmxPortType.DescriptorImpl.INSTANCE);
PortTypeDescriptor.LIST.add(TomcatShutdownPortType.DescriptorImpl.INSTANCE);
}
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/org/jvnet/hudson/plugins/port_allocator/Pool.java
@@ -0,0 +1,25 @@
package org.jvnet.hudson.plugins.port_allocator;

/**
* Represents a port pool.
*
* @author pepov
*/
public class Pool {

public String name;
public String ports;

public int[] getPortsAsInt() {

String[] portsItemsAsString = ports.split(",");

int[] portsItems = new int[portsItemsAsString.length];

for (int i = 0; i < portsItemsAsString.length; i++) {
portsItems[i] = Integer.parseInt(portsItemsAsString[i]);
}

return portsItems;
}
}
@@ -0,0 +1,7 @@
package org.jvnet.hudson.plugins.port_allocator;

/**
* @author pepov
*/
public class PoolNotDefinedException extends Throwable {
}
@@ -0,0 +1,32 @@
package org.jvnet.hudson.plugins.port_allocator;

import java.io.IOException;

/**
* Represents a port, that has been allocated from a pool.
*
* Holds a PortAllocationManager to trigger cleanup.
*
* @author pepov
*/
public class PooledPort extends Port {

private int selectedPort;
private PortAllocationManager manager;

public PooledPort(PooledPortType portType, int selectedPort, PortAllocationManager manager) {
super(portType);
this.selectedPort = selectedPort;
this.manager = manager;
}

@Override
public int get() {
return selectedPort;
}

@Override
public void cleanUp() throws IOException, InterruptedException {
manager.free(selectedPort);
}
}
@@ -0,0 +1,102 @@
package org.jvnet.hudson.plugins.port_allocator;

import hudson.Extension;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.util.ListBoxModel;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;

import java.io.IOException;

/**
* Port type for representing a pool of ports used concurrently by parallel jobs
*
* @author pepov
*/
public class PooledPortType extends PortType {

private int[] pool;

@DataBoundConstructor
public PooledPortType(String name) {
super(name);
}

/**
* Try to allocate one free port from the given pool.
* 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 {

try {
while (true) {

Pool pool = PortAllocator.DESCRIPTOR.getPoolByName(name);

synchronized (pool) {
for (int port : pool.getPortsAsInt()) {
if (manager.isFree(port)) {
manager.allocate(build, port);
return new PooledPort(this, port, manager);
}
}
pool.wait(500);
}
}
} catch (PoolNotDefinedException e) {
throw new RuntimeException("Undefined pool: " + name);
}
}

@Override
public DescriptorImpl getDescriptor() {
return DescriptorImpl.INSTANCE;
}

@Extension
public static final class DescriptorImpl extends PortTypeDescriptor {

public DescriptorImpl() {
super(PooledPortType.class);
}

public PooledPortType newInstance(StaplerRequest req, JSONObject formData) throws FormException {

if ("".equals(formData.getString("name"))) {
throw new FormException(
"Unable to setup port allocator for an undefined pool!" +
" Please configure pools in the global configuration settings!",
"name"
);
}

return new PooledPortType(
formData.getString("name")
);
}

public String getDisplayName() {
return "Pooled TCP port";
}

public ListBoxModel doFillNameItems() {
ListBoxModel model = new ListBoxModel();
for (Pool p : PortAllocator.DESCRIPTOR.getPools()) {
model.add(p.name, p.name);
}
return model;
}

public static final DescriptorImpl INSTANCE = new DescriptorImpl();
}
}
Expand Up @@ -100,6 +100,14 @@ the port actually becomes available (not only in our book-keeping but also at OS
return port;
}

public synchronized boolean isFree(int port) {
AbstractBuild owner = ports.get(port);
if (owner == null) {
return true;
}
return false;
}

public static PortAllocationManager getManager(Computer node) {
PortAllocationManager pam;
WeakReference<PortAllocationManager> ref = INSTANCES.get(node);
Expand Down

0 comments on commit 283b53e

Please sign in to comment.