Skip to content

Commit

Permalink
upgrade to latest Jenkins and dependencies
Browse files Browse the repository at this point in the history
workaround for resurrecting JENKINS-39669
  • Loading branch information
ndeloof committed Nov 17, 2017
1 parent b84d0a7 commit 9bf481a
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 116 deletions.
45 changes: 37 additions & 8 deletions docker-plugin/pom.xml
Expand Up @@ -16,7 +16,7 @@
<name>Docker plugin</name>
<description>Provide Cloud Provisioning and other Docker features</description>
<url>http://wiki.jenkins-ci.org/display/JENKINS/Docker+Plugin</url>

<developers>
<developer>
<id>ndeloof</id>
Expand All @@ -26,8 +26,8 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.level>7</java.level>
<jenkins.version>2.19.3</jenkins.version>
<java.level>8</java.level>
<jenkins.version>2.73.3</jenkins.version>
</properties>

<dependencies>
Expand All @@ -46,13 +46,13 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>docker-commons</artifactId>
<version>1.8</version>
<version>1.9</version>
</dependency>

<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>ssh-slaves</artifactId>
<version>1.6</version>
<version>1.22</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand All @@ -62,19 +62,48 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>durable-task</artifactId>
<version>1.3</version>
<version>1.16</version>
</dependency>

<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-api</artifactId>
<version>2.20</version>
<version>2.23.1</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-support</artifactId>
<version>2.14</version>
<version>2.16</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-durable-task-step</artifactId>
<version>2.17</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-job</artifactId>
<version>2.15</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-cps</artifactId>
<version>2.41</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>script-security</artifactId>
<version>1.35</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-step-api</artifactId>
<version>2.13</version>
<optional>true</optional>
</dependency>

Expand Down
Expand Up @@ -33,8 +33,6 @@
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;

import static hudson.plugins.sshslaves.SSHLauncher.lookupSystemCredentials;

/**
* Utilities to fetch things out of jenkins environment.
*/
Expand Down
@@ -1,36 +1,21 @@
package io.jenkins.docker.pipeline;

import com.google.common.collect.ImmutableSet;
import com.nirima.jenkins.plugins.docker.DockerCloud;
import com.nirima.jenkins.plugins.docker.DockerSlave;
import com.nirima.jenkins.plugins.docker.DockerTemplate;
import com.nirima.jenkins.plugins.docker.DockerTemplateBase;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.Computer;
import hudson.model.Node;
import hudson.model.TaskListener;
import hudson.slaves.Cloud;
import hudson.util.ListBoxModel;
import io.jenkins.docker.client.DockerAPI;
import io.jenkins.docker.connector.DockerComputerAttachConnector;
import io.jenkins.docker.connector.DockerComputerConnector;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.docker.commons.credentials.DockerServerEndpoint;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.steps.BodyExecutionCallback;
import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.jenkinsci.plugins.workflow.support.actions.WorkspaceActionImpl;
import org.kohsuke.stapler.DataBoundConstructor;

import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;

/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
Expand All @@ -39,24 +24,28 @@ public class DockerNodeStep extends Step {

private String dockerHost;

private String credentialsId;

private String image;

private String remoteFs;

private DockerComputerConnector connector;

@DataBoundConstructor
public DockerNodeStep(String dockerHost, String image, String remoteFs) {
public DockerNodeStep(String dockerHost, String credentialsId, String image, String remoteFs) {
this.dockerHost = dockerHost;
this.credentialsId = credentialsId;
this.image = image;
this.remoteFs = remoteFs;
this.connector = new DockerComputerAttachConnector();
}

public String getDockerHost() {
return dockerHost;
}

public String getCredentialsId() {
return credentialsId;
}

public String getImage() {
return image;
}
Expand All @@ -65,88 +54,10 @@ public String getRemoteFs() {
return remoteFs;
}

public DockerComputerConnector getConnector() {
return connector;
}

@Override
public StepExecution start(StepContext context) throws Exception {
return new Execution(context, dockerHost, image, remoteFs, connector);
}


private static class Execution extends StepExecution {

private final String dockerHost;
private final String image;
private final String remoteFs;
private final DockerComputerConnector connector;

public Execution(StepContext context, String dockerHost, String image, String remoteFs, DockerComputerConnector connector) {
super(context);
this.dockerHost = dockerHost;
this.image = image;
this.remoteFs = remoteFs;
this.connector = connector;
}

@Override
public boolean start() throws Exception {
TaskListener listener = getContext().get(TaskListener.class);
final String uuid = UUID.randomUUID().toString();

final DockerTemplate t = new DockerTemplate(
new DockerTemplateBase(image),
uuid, remoteFs, null, "1", Collections.EMPTY_LIST);
t.setConnector(connector);
t.setMode(Node.Mode.EXCLUSIVE); // Doing this we enforce no other task will use this agent

// TODO launch asynchronously, not in CPS VM thread
listener.getLogger().println("Launching new docker node based on "+image);

final DockerAPI api = new DockerAPI(new DockerServerEndpoint(dockerHost, null));
final DockerSlave slave = t.provisionFromTemplate(listener, api);

Jenkins.getInstance().addNode(slave);

// FIXME we need to wait for node to be online ...
listener.getLogger().println("Waiting for node to be online ...");
while (slave.toComputer().isOffline()) {
Thread.sleep(1000);
}
listener.getLogger().println("Node is connected.");

final FilePath ws = slave.createPath(remoteFs+"/workspace");
FlowNode flowNode = getContext().get(FlowNode.class);
flowNode.addAction(new WorkspaceActionImpl(ws, flowNode));
getContext().newBodyInvoker().withCallback(new Callback(slave)).withContexts(slave.toComputer(), ws).start();

return false;
}

@Override
public void stop(@Nonnull Throwable cause) throws Exception {

}
}


private static class Callback extends BodyExecutionCallback.TailCall {

private final String nodeName;

public Callback(DockerSlave node) {
this.nodeName = node.getNodeName();
}

@Override
protected void finished(StepContext context) throws Exception {
final DockerSlave node = (DockerSlave) Jenkins.getInstance().getNode(nodeName);
if (node != null) {
node.terminate();
Jenkins.getInstance().removeNode(node);
}
}
return new DockerNodeStepExecution(context, dockerHost, credentialsId, image, remoteFs);
}

@Extension(optional = true)
Expand Down Expand Up @@ -177,14 +88,6 @@ public Set<? extends Class<?>> getRequiredContext() {
// TODO can/should we provide Executor? We cannot access Executor.start(WorkUnit) from outside the package. cf. isAcceptingTasks, withContexts
return ImmutableSet.of(Computer.class, FilePath.class, /* DefaultStepContext infers from Computer: */ Node.class, Launcher.class);
}

public ListBoxModel doFillCloudNameItems() {
ListBoxModel model = new ListBoxModel();
for (Cloud cloud : DockerCloud.instances()) {
model.add(cloud.name);
}
return model;
}
}

}
@@ -0,0 +1,100 @@
package io.jenkins.docker.pipeline;

import com.nirima.jenkins.plugins.docker.DockerSlave;
import com.nirima.jenkins.plugins.docker.DockerTemplate;
import com.nirima.jenkins.plugins.docker.DockerTemplateBase;
import hudson.FilePath;
import hudson.model.Node;
import hudson.model.TaskListener;
import io.jenkins.docker.client.DockerAPI;
import io.jenkins.docker.connector.DockerComputerAttachConnector;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.docker.commons.credentials.DockerServerEndpoint;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.steps.BodyExecutionCallback;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.jenkinsci.plugins.workflow.support.actions.WorkspaceActionImpl;

import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.UUID;

/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
class DockerNodeStepExecution extends StepExecution {

private final String dockerHost;
private final String credentialsId;
private final String image;
private final String remoteFs;

public DockerNodeStepExecution(StepContext context, String dockerHost, String credentialsId, String image, String remoteFs) {
super(context);
this.dockerHost = dockerHost;
this.credentialsId = credentialsId;
this.image = image;
this.remoteFs = remoteFs;
}

@Override
public boolean start() throws Exception {
TaskListener listener = getContext().get(TaskListener.class);
final String uuid = UUID.randomUUID().toString();

final DockerTemplate t = new DockerTemplate(
new DockerTemplateBase(image),
uuid, remoteFs, null, "1", Collections.EMPTY_LIST);

t.setConnector(new DockerComputerAttachConnector());
t.setMode(Node.Mode.EXCLUSIVE); // Doing this we enforce no other task will use this agent

// TODO launch asynchronously, not in CPS VM thread
listener.getLogger().println("Launching new docker node based on " + image);

final DockerAPI api = new DockerAPI(new DockerServerEndpoint(dockerHost, credentialsId));
final DockerSlave slave = t.provisionFromTemplate(listener, api);

Jenkins.getInstance().addNode(slave);

// FIXME we need to wait for node to be online ...
listener.getLogger().println("Waiting for node to be online ...");
while (slave.toComputer().isOffline()) {
Thread.sleep(1000);
}
listener.getLogger().println("Node " + slave.getNodeName() + " is online.");

final FilePath ws = slave.createPath(remoteFs + "/workspace");
FlowNode flowNode = getContext().get(FlowNode.class);
flowNode.addAction(new WorkspaceActionImpl(ws, flowNode));
getContext().newBodyInvoker().withCallback(new Callback(slave)).withContexts(slave.toComputer(), ws).start();

return false;
}

@Override
public void stop(@Nonnull Throwable cause) throws Exception {

}

private static class Callback extends BodyExecutionCallback.TailCall {

private final String nodeName;

public Callback(DockerSlave node) {
this.nodeName = node.getNodeName();
}

@Override
protected void finished(StepContext context) throws Exception {
final DockerSlave node = (DockerSlave) Jenkins.getInstance().getNode(nodeName);
if (node != null) {
TaskListener listener = context.get(TaskListener.class);
listener.getLogger().println("Waiting for node to be online ...");
node.terminate();
Jenkins.getInstance().removeNode(node);
}
}
}
}
Expand Up @@ -5,6 +5,10 @@
<f:textbox/>
</f:entry>

<f:entry field="credentialsId" title="Docker API credentials">
<c:select/>
</f:entry>

<f:entry title="Docker image" field="image">
<f:textbox/>
</f:entry>
Expand Down

0 comments on commit 9bf481a

Please sign in to comment.