
com.rationaleemotions.server.DockerHelper Maven / Gradle / Ivy
The newest version!
package com.rationaleemotions.server;
import com.google.common.base.Preconditions;
import com.rationaleemotions.config.ConfigReader;
import com.rationaleemotions.config.MappingInfo;
import com.rationaleemotions.server.docker.DeviceInfo;
import com.spotify.docker.client.DefaultDockerClient;
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.LoggingBuildHandler;
import com.spotify.docker.client.ProgressHandler;
import com.spotify.docker.client.exceptions.DockerException;
import com.spotify.docker.client.messages.*;
import java.util.stream.Collectors;
import org.openqa.selenium.net.PortProber;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.rationaleemotions.config.ConfigReader.getInstance;
/**
* A Helper class that facilitates interaction with a Docker Daemon.
*/
class DockerHelper {
private interface Marker {
}
private static final Logger LOG = Logger.getLogger(Marker.class.getEnclosingClass().getName());
public static final String UNIX_SCHEME = "unix";
private DockerHelper() {
DockerClient client = getClient();
Runtime.getRuntime().addShutdownHook(new Thread(new DockerCleanup(client)));
}
/**
* @param id - The ID of the container that is to be cleaned up.
* @throws DockerException - In case of any issues.
* @throws InterruptedException - In case of any issues.
*/
static void killAndRemoveContainer(String id) throws DockerException, InterruptedException {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Killing the container : [" + id + "].");
}
getClient().killContainer(id);
getClient().removeContainer(id);
}
/**
* @param image - The name of the image for which a docker container is to be spun off. For e.g., you could
* specify the image name as selenium/standalone-chrome:3.0.1
to download the
* standalone-chrome
image with its tag as 3.0.1
* @param isPrivileged - true
if the container is to be run in privileged mode.
* @param devices - A List of {@link DeviceInfo} objects
* @return - A {@link ContainerInfo} object that represents the newly spun off container.
* @throws DockerException - In case of any issues.
* @throws InterruptedException - In case of any issues.
*/
static ContainerInfo startContainerFor(String image, boolean isPrivileged, List devices)
throws DockerException, InterruptedException {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Commencing starting of container for the image [" + image + "].");
}
Preconditions.checkState("ok".equalsIgnoreCase(getClient().ping()),
"Ensuring that the Docker Daemon is up and running.");
DockerHelper.predownloadImagesIfRequired();
final Map> portBindings = new HashMap<>();
List randomPort = new ArrayList<>();
int port = PortProber.findFreePort();
String localHost = ConfigReader.getInstance().getLocalhost();
PortBinding binding = PortBinding.create(localHost, Integer.toString(port));
randomPort.add(binding);
portBindings.put(ConfigReader.getInstance().getDockerImagePort(), randomPort);
List deviceList = new LinkedList<>();
for (DeviceInfo each : devices) {
deviceList.add(new Device() {
@Override
public String pathOnHost() {
return each.getPathOnHost();
}
@Override
public String pathInContainer() {
return each.getPathOnContainer();
}
@Override
public String cgroupPermissions() {
return each.getGroupPermissions();
}
});
}
HostConfig.Builder hostConfigBuilder = HostConfig.builder()
.portBindings(portBindings)
.privileged(isPrivileged);
String volume = ConfigReader.getInstance().getVolume();
if (volume != null && !volume.isEmpty()) {
hostConfigBuilder = hostConfigBuilder.binds(ConfigReader.getInstance().getVolume());
}
if (!deviceList.isEmpty()) {
hostConfigBuilder = hostConfigBuilder.devices(deviceList);
}
final HostConfig hostConfig = hostConfigBuilder.build();
// add environmental variables
List envVariables = ConfigReader.getInstance().getEnvironment()
.entrySet().stream()
.map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.toList());
final ContainerConfig containerConfig = ContainerConfig.builder()
.hostConfig(hostConfig)
.image(image).exposedPorts(ConfigReader.getInstance().getDockerImagePort()).env(envVariables)
.build();
final ContainerCreation creation = getClient().createContainer(containerConfig);
final String id = creation.id();
// Inspect container
final com.spotify.docker.client.messages.ContainerInfo containerInfo = getClient().inspectContainer(id);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine(String.format("Container Information %s", containerInfo));
String msg = "Checking to see if the container with id [" + id + "] and name [" +
containerInfo.name() + "]...";
LOG.fine(msg);
}
if (!containerInfo.state().running()) {
// Start container
getClient().startContainer(id);
if (LOG.isLoggable(Level.FINE)) {
LOG.info(containerInfo.name() + " is now running.");
}
} else {
if (LOG.isLoggable(Level.FINE)) {
LOG.info(containerInfo.name() + " was already running.");
}
}
ContainerInfo info = new ContainerInfo(id, port);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("******" + info + "******");
}
return info;
}
private static void predownloadImagesIfRequired() throws DockerException, InterruptedException {
DockerClient client = getClient();
LOG.warning("Commencing download of images.");
Collection images = getInstance().getMapping().values();
ProgressHandler handler = new LoggingBuildHandler();
for (MappingInfo image : images) {
List foundImages = client.listImages(DockerClient.ListImagesParam.byName(image.getTarget()));
if (! foundImages.isEmpty()) {
LOG.warning(String.format("Skipping download for Image [%s] because it's already available.",
image.getTarget()));
continue;
}
client.pull(image.getTarget(), handler);
}
}
private static DockerClient getClient() {
return DefaultDockerClient.builder().uri(getInstance().getDockerRestApiUri()).build();
}
/**
* A Simple POJO that represents the newly spun off container, encapsulating the container Id and the port on which
* the container is running.
*/
public static class ContainerInfo {
private int port;
private String containerId;
ContainerInfo(String containerId, int port) {
this.port = port;
this.containerId = containerId;
}
public int getPort() {
return port;
}
public String getContainerId() {
return containerId;
}
@Override
public String toString() {
return String.format("%s running on %d", containerId, port);
}
}
private static class DockerCleanup implements Runnable {
private DockerClient client;
DockerCleanup(DockerClient client) {
this.client = client;
}
@Override
public void run() {
if (client != null) {
client.close();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy