Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Fix for JENKINS-24071
Refactor remaining commands to use Channel so they can work on Jenkins
slaves.
  • Loading branch information
dodie committed Jan 13, 2018
1 parent eca479c commit 11a0346
Show file tree
Hide file tree
Showing 38 changed files with 1,657 additions and 589 deletions.
Expand Up @@ -4,14 +4,17 @@
import hudson.Extension;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.Descriptor;
import jenkins.model.Jenkins;

import org.jenkinsci.plugins.dockerbuildstep.DockerBuilder;
import org.jenkinsci.plugins.dockerbuildstep.DockerBuilder.Config;
import org.jenkinsci.plugins.dockerbuildstep.cmd.remote.CommitRemoteCallable;
import org.jenkinsci.plugins.dockerbuildstep.log.ConsoleLogger;
import org.jenkinsci.plugins.dockerbuildstep.util.Resolver;
import org.kohsuke.stapler.DataBoundConstructor;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.exception.DockerException;
import com.github.dockerjava.api.command.CommitCmd;

/**
* This command commits changes done in specified container and create new image from it.
Expand Down Expand Up @@ -65,11 +68,18 @@ public void execute(Launcher launcher, @SuppressWarnings("rawtypes") AbstractBui
String tagRes = Resolver.buildVar(build, tag);
String runCmdRes = Resolver.buildVar(build, runCmd);

DockerClient client = getClient(build, null);
CommitCmd commitCmd =
client.commitCmd(containerIdRes).withRepository(repoRes).withTag(tagRes).withCmd(runCmdRes);
String imageId = commitCmd.exec();

String imageId;
try {
Config cfgData = getConfig(build);
Descriptor<?> descriptor = Jenkins.getInstance().getDescriptor(DockerBuilder.class);

imageId = launcher.getChannel().call(new CommitRemoteCallable(cfgData, descriptor, containerIdRes, repoRes, tagRes, runCmdRes));
} catch (Exception e) {
console.logError("Failed to commit image: " + e.getMessage());
e.printStackTrace();
throw new IllegalArgumentException(e);
}

console.logInfo("Container " + containerIdRes + " commited as image " + imageId);
}

Expand All @@ -80,5 +90,5 @@ public String getDisplayName() {
return "Commit container changes";
}
}

}
@@ -1,16 +1,19 @@
package org.jenkinsci.plugins.dockerbuildstep.cmd;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.exception.DockerException;
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.InspectContainerResponse;
import com.github.dockerjava.api.model.*;
import hudson.Extension;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.Descriptor;
import hudson.util.FormValidation;
import jenkins.model.Jenkins;

import org.jenkinsci.plugins.dockerbuildstep.DockerBuilder;
import org.jenkinsci.plugins.dockerbuildstep.DockerBuilder.Config;
import org.jenkinsci.plugins.dockerbuildstep.action.EnvInvisibleAction;
import org.jenkinsci.plugins.dockerbuildstep.cmd.remote.CreateContainerRemoteCallable;
import org.jenkinsci.plugins.dockerbuildstep.log.ConsoleLogger;
import org.jenkinsci.plugins.dockerbuildstep.util.*;
import org.kohsuke.stapler.DataBoundConstructor;
Expand Down Expand Up @@ -144,87 +147,90 @@ public void execute(Launcher launcher, @SuppressWarnings("rawtypes") AbstractBui
throw new IllegalArgumentException("At least one parameter is required");
}

String imageRes = Resolver.buildVar(build, image);
String commandRes = Resolver.buildVar(build, command);
String hostNameRes = Resolver.buildVar(build, hostName);
String containerNameRes = Resolver.buildVar(build, containerName);
String envVarsRes = Resolver.buildVar(build, envVars);
Links linksRes = LinkUtils.parseLinks(Resolver.buildVar(build, links));
String exposedPortsRes = Resolver.buildVar(build, exposedPorts);
String cpuSharesRes = Resolver.buildVar(build, cpuShares);
String memoryLimitRes = Resolver.buildVar(build, memoryLimit);

DockerClient client = getClient(build, null);
CreateContainerCmd cfgCmd = client.createContainerCmd(imageRes);
if (!commandRes.isEmpty()) {
cfgCmd.withCmd(commandRes.split(" "));
}
cfgCmd.withHostName(hostNameRes);
cfgCmd.withName(containerNameRes);
HostConfig hc = new HostConfig();
cfgCmd.withLinks(linksRes.getLinks());
if (!envVarsRes.isEmpty()) {
String[] encVarResSlitted = envVarsRes.split("\\r?\\n");
cfgCmd.withEnv(encVarResSlitted);
}
// Parse and log parameters

final String imageRes = Resolver.buildVar(build, image);
final String commandRawRes = Resolver.buildVar(build, command);
final String[] commandRes = commandRawRes.isEmpty() ? null : commandRawRes.split(" ");
final String hostNameRes = Resolver.buildVar(build, hostName);
final String containerNameRes = Resolver.buildVar(build, containerName);
final String envVarsRawRes = Resolver.buildVar(build, envVars);
final String[] envVarsRes = envVarsRawRes.isEmpty() ? null : envVarsRawRes.split("\\r?\\n");
final Links linksRes = LinkUtils.parseLinks(Resolver.buildVar(build, links));
final String exposedPortsRes = Resolver.buildVar(build, exposedPorts);
final ExposedPort[] ports;
if (exposedPortsRes != null && !exposedPortsRes.isEmpty()) {
String[] exposedPortsSplitted = exposedPortsRes.split(",");
ExposedPort[] ports = new ExposedPort[exposedPortsSplitted.length];
ports = new ExposedPort[exposedPortsSplitted.length];
for (int i = 0; i < ports.length; i++) {
ports[i] = ExposedPort.parse(exposedPortsSplitted[i]);
}
cfgCmd.withExposedPorts(ports);
}
if (cpuSharesRes != null && !cpuSharesRes.isEmpty()) {
cfgCmd.withCpuShares(Integer.parseInt(cpuSharesRes));
} else {
ports = null;
}
if (memoryLimitRes != null && !memoryLimitRes.isEmpty()) {
long ml = CommandUtils.sizeInBytes(memoryLimitRes);
final String cpuSharesRawRes = Resolver.buildVar(build, cpuShares);
final Integer cpuSharesRes = cpuSharesRawRes == null || cpuSharesRawRes.isEmpty() ? null : Integer.parseInt(cpuSharesRawRes);
final String memoryLimitRawRes = Resolver.buildVar(build, memoryLimit);
final Long memoryLimitRes;
if (memoryLimitRawRes != null && !memoryLimitRawRes.isEmpty()) {
long ml = CommandUtils.sizeInBytes(memoryLimitRawRes);
if (ml > -1) {
cfgCmd.withMemory(ml);
memoryLimitRes = ml;
} else {
console.logWarn("Unable to parse memory limit '" + memoryLimitRes + "', memory limit not enforced!");
memoryLimitRes = null;
console.logWarn("Unable to parse memory limit '" + memoryLimitRawRes + "', memory limit not enforced!");
}
} else {
memoryLimitRes = null;
}

final String[] dnsRes;
if (dns != null && !dns.isEmpty()) {
console.logInfo("set dns: " + dns);
String[] dnsArray = dns.split(",");
if (dnsArray == null || dnsArray.length == 0) {
cfgCmd.withDns(dns);
} else {
cfgCmd.withDns(dnsArray);
}
dnsRes = dns.split(",");
} else {
dnsRes = null;
}

final String[] extraHostsRes;
if (extraHosts != null && !extraHosts.isEmpty()) {
console.logInfo("set extraHosts: " + extraHosts);
String[] extraHostsArray = extraHosts.split(",");
if (extraHostsArray == null || extraHostsArray.length == 0) {
cfgCmd.withExtraHosts(extraHosts);
} else {
cfgCmd.withExtraHosts(extraHostsArray);
}
extraHostsRes = extraHosts.split(",");
} else {
extraHostsRes = null;
}


final PortBinding[] portBindingsRes;
if (portBindings != null && !portBindings.isEmpty()) {
console.logInfo("set portBindings: " + portBindings);
PortBinding[] portBindingsRes = PortBindingParser.parse(Resolver.buildVar(build, portBindings));
cfgCmd.withPortBindings(portBindingsRes);
portBindingsRes = PortBindingParser.parse(Resolver.buildVar(build, portBindings));
} else {
portBindingsRes = null;
}


final Bind[] bindMountsRes;
if (bindMounts != null && !bindMounts.isEmpty()) {
console.logInfo("set portBindings: " + bindMounts);
Bind[] bindsRes = BindParser.parse(Resolver.buildVar(build, bindMounts));
cfgCmd.withBinds(bindsRes);
bindMountsRes = BindParser.parse(Resolver.buildVar(build, bindMounts));
} else {
bindMountsRes = null;
}
if (alwaysRestart) {
cfgCmd.withRestartPolicy(RestartPolicy.alwaysRestart());

// Call Docker

try {
Config cfgData = getConfig(build);
Descriptor<?> descriptor = Jenkins.getInstance().getDescriptor(DockerBuilder.class);

InspectContainerResponse inspectResp = launcher.getChannel().call(new CreateContainerRemoteCallable(cfgData, descriptor, imageRes, commandRes, hostNameRes, containerNameRes, linksRes, envVarsRes, ports, cpuSharesRes, memoryLimitRes, dnsRes, extraHostsRes, portBindingsRes, bindMountsRes, alwaysRestart, publishAllPorts, privileged));
console.logInfo("created container id " + inspectResp.getId() + " (from image " + imageRes + ")");
EnvInvisibleAction envAction = new EnvInvisibleAction(inspectResp);
build.addAction(envAction);
} catch (Exception e) {
console.logError("failed to stop all containers");
e.printStackTrace();
throw new IllegalArgumentException(e);
}
CreateContainerResponse resp = cfgCmd.withPublishAllPorts(publishAllPorts).withPrivileged(privileged).exec();
console.logInfo("created container id " + resp.getId() + " (from image " + imageRes + ")");

InspectContainerResponse inspectResp = client.inspectContainerCmd(resp.getId()).exec();
EnvInvisibleAction envAction = new EnvInvisibleAction(inspectResp);
build.addAction(envAction);
}

@Extension
Expand Down
@@ -1,26 +1,19 @@
package org.jenkinsci.plugins.dockerbuildstep.cmd;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.BuildImageCmd;
import com.github.dockerjava.api.exception.DockerException;
import com.github.dockerjava.api.model.BuildResponseItem;
import com.github.dockerjava.core.command.BuildImageResultCallback;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.Descriptor;
import hudson.remoting.Callable;
import jenkins.model.Jenkins;

import org.jenkinsci.plugins.dockerbuildstep.DockerBuilder;
import org.jenkinsci.plugins.dockerbuildstep.DockerBuilder.Config;
import org.jenkinsci.plugins.dockerbuildstep.cmd.remote.CreateImageRemoteCallable;
import org.jenkinsci.plugins.dockerbuildstep.log.ConsoleLogger;
import org.jenkinsci.plugins.dockerbuildstep.util.Resolver;
import org.kohsuke.stapler.DataBoundConstructor;

import java.io.File;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -111,93 +104,18 @@ public void execute(Launcher launcher, @SuppressWarnings("rawtypes") AbstractBui
String imageId = null;
try {
Config cfgData = getConfig(build);
imageId = launcher.getChannel().call(new RemoteCallable(expandedDockerFolder, expandedImageTag, dockerFileRes, cfgData, buildArgsMap, noCache, rm, Jenkins.getInstance().getDescriptor(DockerBuilder.class)));
Descriptor<?> descriptor = Jenkins.getInstance().getDescriptor(DockerBuilder.class);

imageId = launcher.getChannel().call(new CreateImageRemoteCallable(cfgData, descriptor, expandedDockerFolder, expandedImageTag, dockerFileRes, buildArgsMap, noCache, rm));
} catch (Exception e) {
console.logError("Failed to create docker image: " + e.getMessage());
console.logError("Failed to create docker image: " + e.getMessage());
e.printStackTrace();
throw new IllegalArgumentException(e);
}

console.logInfo("Build image id:" + imageId);
}

public static class RemoteCallable implements Callable<String, Exception>, Serializable {

private static final long serialVersionUID = -6593420984897195978L;

String expandedDockerFolder;
String expandedImageTag;
String dockerFileRes;
Config cfgData;
Map<String, String> buildArgsMap;
boolean noCache;
boolean rm;

Descriptor<?> descriptor;

public RemoteCallable(String expandedDockerFolder, String expandedImageTag, String dockerFileRes, Config cfgData, Map<String, String> buildArgsMap, boolean noCache, boolean rm, Descriptor descriptor) {
this.expandedDockerFolder = expandedDockerFolder;
this.expandedImageTag = expandedImageTag;
this.dockerFileRes = dockerFileRes;
this.cfgData = cfgData;
this.buildArgsMap = buildArgsMap;
this.noCache = noCache;
this.rm = rm;
this.descriptor = descriptor;
}

public String call() throws Exception {
FilePath folder = new FilePath(new File(expandedDockerFolder));

if (!exist(folder))
throw new IllegalArgumentException(
"configured dockerFolder '" + expandedDockerFolder + "' does not exist.");

if (!exist(folder.child(dockerFileRes))) {
throw new IllegalArgumentException(
String.format("Configured Docker file '%s' does not exist.", dockerFileRes));
}

File docker = new File(expandedDockerFolder, dockerFileRes);

BuildImageResultCallback callback = new BuildImageResultCallback() {
@Override
public void onNext(BuildResponseItem item) {
super.onNext(item);
}

@Override
public void onError(Throwable throwable) {
super.onError(throwable);
}
};

DockerClient client = getClient(descriptor, cfgData.dockerUrlRes, cfgData.dockerVersionRes, cfgData.dockerCertPathRes, null);
BuildImageCmd buildImageCmd = client
.buildImageCmd(docker)
.withTag(expandedImageTag)
.withNoCache(noCache)
.withRemove(rm);
if (!buildArgsMap.isEmpty()) {
for (final Map.Entry<String, String> entry : buildArgsMap.entrySet()) {
buildImageCmd = buildImageCmd.withBuildArg(entry.getKey(), entry.getValue());
}
}

BuildImageResultCallback result = buildImageCmd.exec(callback);

return result.awaitImageId();
}

private boolean exist(FilePath filePath) throws DockerException {
try {
return filePath.exists();
} catch (Exception e) {
throw new DockerException("Could not check file", 0, e);
}
}
}

@Extension
public static class CreateImageCommandDescriptor extends DockerCommandDescriptor {
@Override
Expand Down
Expand Up @@ -95,7 +95,7 @@ protected static DockerClient getClient(AbstractBuild<?,?> build, AuthConfig aut
.getDockerClient(build, authConfig);
}

protected static DockerClient getClient(Descriptor<?> descriptor, String dockerUrlRes, String dockerVersionRes, String dockerCertPathRes, AuthConfig authConfig) {
public static DockerClient getClient(Descriptor<?> descriptor, String dockerUrlRes, String dockerVersionRes, String dockerCertPathRes, AuthConfig authConfig) {
return ((DockerBuilder.DescriptorImpl) descriptor)
.getDockerClient(dockerUrlRes, dockerVersionRes, dockerCertPathRes, authConfig);
}
Expand Down

0 comments on commit 11a0346

Please sign in to comment.