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.InspectContainerResponse;
import hudson.Extension;
import hudson.model.AbstractBuild;
import hudson.util.FormValidation;
import org.jenkinsci.plugins.dockerbuildstep.action.DockerContainerConsoleAction;
import org.jenkinsci.plugins.dockerbuildstep.action.EnvInvisibleAction;
import org.jenkinsci.plugins.dockerbuildstep.log.ConsoleLogger;
import org.jenkinsci.plugins.dockerbuildstep.util.BindParser;
import org.jenkinsci.plugins.dockerbuildstep.util.PortBindingParser;
import org.jenkinsci.plugins.dockerbuildstep.util.PortUtils;
import org.jenkinsci.plugins.dockerbuildstep.util.Resolver;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* This command starts one or more Docker containers. It also exports some build environment variables like IP or
* started containers.
*
* @author vjuranek
* @see http://docs.docker.com/reference/api/docker_remote_api_v1.13/#start-a-container
*/
public class StartCommand extends DockerCommand {
private final String containerIds;
private final String waitPorts;
private final String containerIdsLogging;
@DataBoundConstructor
public StartCommand(String containerIds, String waitPorts, String containerIdsLogging) {
this.containerIds = containerIds;
this.waitPorts = waitPorts;
this.containerIdsLogging = containerIdsLogging;
}
public String getContainerIds() {
return containerIds;
}
public String getWaitPorts() {
return waitPorts;
}
public String getContainerIdsLogging() {
return containerIdsLogging;
}
@Override
public void execute(@SuppressWarnings("rawtypes") AbstractBuild build, ConsoleLogger console)
throws DockerException {
if (containerIds == null || containerIds.isEmpty()) {
throw new IllegalArgumentException("At least one parameter is required");
}
List<String> ids = Arrays.asList(Resolver.buildVar(build, containerIds).split(","));
List<String> logIds = Arrays.asList(Resolver.buildVar(build, containerIdsLogging).split(","));
DockerClient client = getClient(build, null);
// TODO check, if container exists and is stopped (probably catch exception)
for (String id : ids) {
id = id.trim();
DockerContainerConsoleAction outAction = null;
if (logIds.contains(id)) {
outAction = attachContainerOutput(build, id);
}
client.startContainerCmd(id).exec();
console.logInfo("started container id " + id);
InspectContainerResponse inspectResp = client.inspectContainerCmd(id).exec();
if (outAction != null) {
outAction.setContainerName(inspectResp.getName());
}
EnvInvisibleAction envAction = new EnvInvisibleAction(inspectResp);
build.addAction(envAction);
}
// wait for ports
if (waitPorts != null && !waitPorts.isEmpty()) {
String waitPortsResolved = Resolver.buildVar(build, waitPorts);
waitForPorts(waitPortsResolved, client, console);
}
}
private void waitForPorts(String waitForPorts, DockerClient client, ConsoleLogger console) throws DockerException {
Map<String, List<Integer>> containers = PortUtils.parsePorts(waitForPorts);
for (String cId : containers.keySet()) {
InspectContainerResponse inspectResp = client.inspectContainerCmd(cId).exec();
String ip = inspectResp.getNetworkSettings().getIpAddress();
List<Integer> ports = containers.get(cId);
for (Integer port : ports) {
console.logInfo("Waiting for port " + port + " on " + ip + " (container ID " + cId + ")");
boolean portReady = PortUtils.waitForPort(ip, port);
if (portReady) {
console.logInfo(ip + ":" + port + " ready");
} else {
// TODO fail the build, but make timeout configurable first
console.logWarn(ip + ":" + port + " still not available (container ID " + cId
+ "), but build continues ...");
}
}
}
}
@Extension
public static class StartCommandDescriptor extends DockerCommandDescriptor {
@Override
public String getDisplayName() {
return "Start container(s)";
}
}
}