package org.arquillian.cube.docker.impl.client.containerobject.dsl;
import org.arquillian.cube.containerobject.ConnectionMode;
import org.arquillian.cube.docker.impl.client.config.Await;
import org.arquillian.cube.docker.impl.client.config.BuildImage;
import org.arquillian.cube.docker.impl.client.config.CubeContainer;
import org.arquillian.cube.docker.impl.client.config.ExposedPort;
import org.arquillian.cube.docker.impl.client.config.Image;
import org.arquillian.cube.docker.impl.client.config.Link;
import org.arquillian.cube.docker.impl.client.config.PortBinding;
import org.arquillian.cube.docker.impl.client.config.StarOperator;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.exporter.ExplodedExporter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Builder object to create containers object.
*/
public class ContainerBuilder {
private static final String TEMPORARY_FOLDER_PREFIX = "arquilliancube_";
private static final String TEMPORARY_FOLDER_SUFFIX = ".build";
private static final Logger logger = Logger.getLogger(ContainerBuilder.class.getName());
private String id;
private CubeContainer cubeContainer = new CubeContainer();
private ContainerBuilder(String id) {
this.id = id;
}
public static ContainerBuilder newContainer(String id) {
return new ContainerBuilder(id);
}
public static ContainerBuilder newContainerFromImage(String image) {
ContainerBuilder containerBuilder = new ContainerBuilder(convertImageToId(image));
containerBuilder.fromImage(image);
return containerBuilder;
}
private static String convertImageToId(String imageId) {
return imageId
.replace('/', '_')
.replace(':', '_')
.replace('.', '_');
}
public ContainerOptionsBuilder fromImage(String image) {
cubeContainer.setImage(Image.valueOf(image));
return new ContainerOptionsBuilder();
}
public ContainerOptionsBuilder fromBuildDirectory(String directory) {
BuildImage buildImage = new BuildImage(directory, null, true, true);
cubeContainer.setBuildImage(buildImage);
return new ContainerOptionsBuilder();
}
public ContainerOptionsBuilder fromBuildDirectory(Archive<?> buildDirectory) {
File output = createTemporalDirectoryForCopyingDockerfile(id);
logger.finer(String.format("Created %s directory for storing contents of %s cube.", output, id));
buildDirectory.as(ExplodedExporter.class).exportExplodedInto(output);
return fromBuildDirectory(output.getAbsolutePath());
}
private File createTemporalDirectoryForCopyingDockerfile(String containerName) {
File dir;
try {
dir = File.createTempFile(TEMPORARY_FOLDER_PREFIX+containerName, TEMPORARY_FOLDER_SUFFIX);
dir.delete();
if (!dir.mkdirs()) {
throw new IllegalArgumentException("Temp Dir for storing Dockerfile contents could not be created.");
}
dir.deleteOnExit();
return dir;
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
public class ContainerOptionsBuilder {
private ContainerBuilder containerBuilder;
private ConnectionMode connectionMode = ConnectionMode.START_AND_STOP_AROUND_CLASS;
public ContainerOptionsBuilder withConnectionMode(ConnectionMode connectionMode) {
this.connectionMode = connectionMode;
return this;
}
public ContainerOptionsBuilder withExposedPorts(Integer... ports) {
List<ExposedPort> exposedPortList = Arrays.stream(ports)
.map(ExposedPort::valueOf)
.collect(Collectors.toList());
setExposedPorts(exposedPortList);
return this;
}
public ContainerOptionsBuilder withExposedPorts(String... ports) {
List<ExposedPort> exposedPortList = Arrays.stream(ports)
.map(ExposedPort::valueOf)
.collect(Collectors.toList());
setExposedPorts(exposedPortList);
return this;
}
public ContainerOptionsBuilder withPortBinding(Integer... ports) {
List<PortBinding> portBindingList = Arrays.stream(ports)
.map(port -> Integer.toString(port))
.map(PortBinding::valueOf)
.collect(Collectors.toList());
setPortBinding(portBindingList);
return this;
}
public ContainerOptionsBuilder withPortBinding(String... ports) {
List<PortBinding> portBindingList = Arrays.stream(ports)
.map(PortBinding::valueOf)
.collect(Collectors.toList());
setPortBinding(portBindingList);
return this;
}
public ContainerOptionsBuilder withEnvironment(String key, Object value, Object...keyValues) {
if (keyValues.length % 2 != 0) {
throw new IllegalArgumentException("Key Values should be a pair of key, value");
}
List<String> environment = new ArrayList<>();
environment.add(toEnv(key, value));
for (int i = 0; i < keyValues.length; i += 2) {
environment.add(toEnv(keyValues[i], keyValues[i + 1]));
}
setEnvironment(environment);
return this;
}
public ContainerOptionsBuilder withCommand(String command) {
cubeContainer.setCmd(Arrays.asList(command.split(" ")));
return this;
}
public ContainerOptionsBuilder withCommand(String... command) {
cubeContainer.setCmd(Arrays.asList(command));
return this;
}
public ContainerOptionsBuilder withVolume(String hostPath, String containerPath) {
return withVolume(hostPath, containerPath, BindMode.READ_WRITE);
}
public ContainerOptionsBuilder withVolume(String hostPath, String containerPath, BindMode bindMode) {
setVolume(hostPath + ":" + containerPath + ":" + bindMode.accessMode.name());
return this;
}
public ContainerOptionsBuilder withNetworkMode(String networkMode) {
cubeContainer.setNetworkMode(networkMode);
return this;
}
public ContainerOptionsBuilder withNetworkMode(Network networkMode) {
return this.withNetworkMode(networkMode.getId());
}
public ContainerOptionsBuilder withNetworks(String... networks) {
cubeContainer.setNetworks(Arrays.asList(networks));
return this;
}
public ContainerOptionsBuilder withNetworks(Network... networks) {
return withNetworks(Arrays.stream(networks)
.map(Network::getId)
.toArray( String[]::new)
);
}
public ContainerOptionsBuilder withPriviledgedMode(boolean mode) {
cubeContainer.setPrivileged(mode);
return this;
}
public ContainerOptionsBuilder withLink(String link) {
setLink(link);
return this;
}
public ContainerOptionsBuilder withLink(String service, String alias) {
return withLink(service + ":" + alias);
}
public ContainerOptionsBuilder withLink(Container container) {
return withLink(container.getContainerName(), container.getContainerName());
}
public ContainerOptionsBuilder withAwaitStrategy(Await awaitStrategy) {
cubeContainer.setAwait(awaitStrategy);
return this;
}
public Container build() {
if (id.endsWith("*")) {
final UUID uuid = UUID.randomUUID();
StarOperator.adaptPortBindingToParallelRun(cubeContainer);
StarOperator.adaptLinksToParallelRun(uuid, cubeContainer);
String templateName = id.substring(0, id.lastIndexOf('*'));
id = StarOperator.generateNewName(templateName, uuid);
}
return new Container(id, cubeContainer, connectionMode);
}
private String toEnv(Object key, Object value) {
return key.toString() + "=" + value.toString();
}
private void setExposedPorts(List<ExposedPort> exposedPorts) {
if (cubeContainer.getExposedPorts() == null) {
cubeContainer.setExposedPorts(exposedPorts);
} else {
cubeContainer.getExposedPorts().addAll(exposedPorts);
}
}
private void setPortBinding(List<PortBinding> portBindings) {
if (cubeContainer.getPortBindings() == null) {
cubeContainer.setPortBindings(portBindings);
} else {
cubeContainer.getPortBindings().addAll(portBindings);
}
}
private void setEnvironment(List<String> environments) {
if (cubeContainer.getEnv() == null) {
cubeContainer.setEnv(environments);
} else {
cubeContainer.getEnv().addAll(environments);
}
}
private void setVolume(String volume) {
if (cubeContainer.getBinds() == null) {
List<String> binds = new ArrayList<>();
binds.add(volume);
cubeContainer.setBinds(binds);
} else {
cubeContainer.getBinds().add(volume);
}
}
private void setLink(String link) {
if (cubeContainer.getLinks() == null) {
List<Link> links = new ArrayList<>();
links.add(Link.valueOf(link));
cubeContainer.setLinks(links);
} else {
cubeContainer.getLinks().add(Link.valueOf(link));
}
}
}
}