Skip to content

Fix #31 Write all exposed ports to a docker mappings file #110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pom.xml here: [pom.xml](https://github.com/wouterd/docker-maven-plugin/blob/mast
at the end of the build even if the stop-containers goal is not executed
(useful for preventing Ctrl+C causing dangling containers) -->
<forceCleanup>false</forceCleanup>
<dockerMappingsFile/>
<containers>
<container>
<id>mongo</id>
Expand Down Expand Up @@ -163,6 +164,13 @@ By default, all exposed ports are published on the host. The following two prope
- docker.containers.[id].ports.[portname].host (f.ex 'docker.containers.id.app.ports.80/tcp.host')
- docker.containers.[id].ports.[portname].port (f.ex 'docker.containers.id.app.ports.80/tcp.port')

All properties are also added to the file `${project.build.directory}/docker-plugin/docker-mappings.properties` so that you can easily load this properties file into your integration tests.
You can change the name or location of this file by modifying `<dockerMappingsFile>` in the configuration block of the start-containers execution block.

<dockerMappingsFile>${project.build.directory}/docker-plugin/docker-mappings.properties</dockerMappingsFile>

The location specified above is also exposed as a maven variable called `${docker.mappings.file}`

You can pass those project properties over to your integration test and use them to connect to your application.

The plugin will connect to a docker instance over HTTP, linux socket support will be added after 1.0. It will look up
Expand Down
58 changes: 49 additions & 9 deletions src/main/java/net/wouterdanes/docker/maven/StartContainerMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,15 @@
import org.apache.maven.project.MavenProject;

import javax.inject.Inject;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import java.util.regex.Pattern;

import static java.nio.file.StandardOpenOption.APPEND;

/**
* This class is responsible for starting docking containers in the pre-integration phase of the maven build. The goal
* is called "start-containers"
Expand All @@ -62,11 +68,15 @@ public StartContainerMojo(List<ContainerStartConfiguration> containers) {
@Parameter(defaultValue = "${mojoExecution}", readonly = true)
private MojoExecution mojoExecution;

@Parameter(defaultValue = "${project.build.directory}/docker-plugin/docker-mappings.properties")
private File dockerMappingsFile;

@Override
public void doExecute() throws MojoExecutionException, MojoFailureException {
if (hasDuplicateIds() || hasInvalidLinks()) {
return;
}
generateMappingsFile();
DockerProvider provider = getDockerProvider();
for (ContainerStartConfiguration configuration : containers) {
for (ContainerLink link : configuration.getLinks()) {
Expand All @@ -84,6 +94,7 @@ public void doExecute() throws MojoExecutionException, MojoFailureException {
String containerId = container.getId();
List<ExposedPort> exposedPorts = provider.getExposedPorts(containerId);
exposePortsToProject(configuration, exposedPorts);
writeListOfPortsToFile(configuration, exposedPorts);
getLog().info(String.format("Started container with id '%s'", containerId));
registerStartedContainer(configuration.getId(), container);
} catch (DockerException e) {
Expand All @@ -98,15 +109,14 @@ public void doExecute() throws MojoExecutionException, MojoFailureException {
}
}

/** Avoid dangling containers if the build is interrupted (e.g. via Ctrl+C) before the StopContainer mojo runs. */
private void addShutdownHookToCleanUpContainers()
{
/**
* Avoid dangling containers if the build is interrupted (e.g. via Ctrl+C) before the StopContainer mojo runs.
*/
private void addShutdownHookToCleanUpContainers() {
getLog().info("Started containers will be forcibly cleaned up when the build finishes");
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable()
{
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run()
{
public void run() {
cleanUpStartedContainers();
}
}));
Expand Down Expand Up @@ -193,13 +203,39 @@ private boolean hasDuplicateIds() {

private void exposePortsToProject(ContainerStartConfiguration configuration, List<ExposedPort> exposedPorts) {
exposedPorts.parallelStream().forEach(port -> {
String prefix = String.format("docker.containers.%s.ports.%s.",
configuration.getId(), port.getContainerPort());
String prefix = String.format("docker.containers.%s.ports.%s.", configuration.getId(), port.getContainerPort());
addPropertyToProject(prefix + "host", port.getHost());
addPropertyToProject(prefix + "port", String.valueOf(port.getExternalPort()));
});
}

private void generateMappingsFile() throws MojoExecutionException {
try {
if (dockerMappingsFile.getParentFile() != null) {
Files.createDirectories(dockerMappingsFile.getParentFile().toPath());
}
Files.createFile(dockerMappingsFile.toPath());
addPropertyToProject("docker.mappings.file", dockerMappingsFile.getAbsolutePath());
} catch (IOException e) {
throw new MojoExecutionException("Error generating docker mapping file: ", e);
}
}

private void writeListOfPortsToFile(ContainerStartConfiguration configuration, List<ExposedPort> exposedPorts) throws MojoExecutionException {
try (BufferedWriter writer = Files.newBufferedWriter(dockerMappingsFile.toPath(), APPEND)) {
getLog().info(String.format("Writing properties for container '%s' to '%s'", configuration.getId(), dockerMappingsFile.getName()));
for (ExposedPort port : exposedPorts) {
String prefix = String.format("docker.containers.%s.ports.%s.", configuration.getId(), port.getContainerPort());
writer.write(prefix + "host=" + port.getHost());
writer.newLine();
writer.write(prefix + "port=" + String.valueOf(port.getExternalPort()));
writer.newLine();
}
} catch (IOException e) {
throw new MojoExecutionException("Error writing to docker mapping file: ", e);
}
}

private void replaceImageWithBuiltImageIdIfInternalId(ContainerStartConfiguration configuration) {
Optional<BuiltImageInfo> builtImage = getBuiltImageForStartId(configuration.getImage());
if (builtImage.isPresent()) {
Expand Down Expand Up @@ -227,6 +263,10 @@ public void setMojoExecution(final MojoExecution mojoExecution) {
this.mojoExecution = mojoExecution;
}

public void setDockerMappingsFile(File dockerMappingsFile) {
this.dockerMappingsFile = dockerMappingsFile;
}

private void addPropertyToProject(String key, String value) {
getLog().info(String.format("Setting property '%s' to '%s'", key, value));
project.getProperties().setProperty(key, value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,20 @@
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Matchers;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Stream;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
Expand All @@ -45,6 +51,7 @@
public class StartContainerMojoTest {

private static final String FAKE_PROVIDER_KEY = UUID.randomUUID().toString();
private static final String DOCKER_MAPPINGS_FILE = System.getProperty("java.io.tmpdir") + File.separator + "docker-maven-plugin" + File.separator + "docker-mappings.properties";

private final MavenProject mavenProject = mock(MavenProject.class);
private final MojoExecution mojoExecution = new MojoExecution(null, "start-containers", "some-id");
Expand All @@ -69,6 +76,7 @@ public void setUp() throws Exception {
@After
public void tearDown() throws Exception {
DockerProviderSupplier.removeProvider(FAKE_PROVIDER_KEY);
Files.deleteIfExists(Paths.get(DOCKER_MAPPINGS_FILE));
}

@Test
Expand Down Expand Up @@ -285,8 +293,8 @@ public void testThatContainerWaitsForLinkedContainerToStart() throws Exception {
final ContainerStartConfiguration parent = new ContainerStartConfiguration()
.withId("parent")
.withLink(new ContainerLink()
.toContainer("linked")
.withAlias("database")
.toContainer("linked")
.withAlias("database")
);

when(FakeDockerProvider.instance.getLogs("linked")).thenReturn("", "", "there");
Expand All @@ -311,15 +319,15 @@ public Object answer(final InvocationOnMock invocation) throws Throwable {

@Test
public void testThatMojoStartsAContainerOnTheProviderWithEnvironmentVariables() throws Exception {
Map<String, String> env = new HashMap<>();
env.put("TEST_KEY", "test value");
Map<String, String> env = new HashMap<>();
env.put("TEST_KEY", "test value");

ContainerStartConfiguration startConfiguration = new ContainerStartConfiguration()
.withEnv(env);
.withEnv(env);
StartContainerMojo mojo = createMojo(startConfiguration);

mojo.execute();

ArgumentCaptor<ContainerStartConfiguration> captor = ArgumentCaptor.forClass(ContainerStartConfiguration.class);
verify(FakeDockerProvider.instance).startContainer(captor.capture());

Expand Down Expand Up @@ -361,6 +369,22 @@ public void testThatConfiguredMacAddressGetsPassedToDocker() throws Exception {
assertEquals("12:34:56:78:9a:bc", passedValue.getMacAddress());
}

@Test
public void testThatDockerMappingsFileIsCreated() throws Exception {
ContainerStartConfiguration startConfiguration = new ContainerStartConfiguration();
StartContainerMojo mojo = createMojo(startConfiguration);

File dockerMappingsFile = new File(DOCKER_MAPPINGS_FILE);

assertEquals(dockerMappingsFile.exists(), false);

mojo.execute();

verify(FakeDockerProvider.instance).startContainer(startConfiguration);

assertEquals(dockerMappingsFile.exists(), true);
}

private StartContainerMojo createMojo(final ContainerStartConfiguration startConfiguration) {
return createMojo(startConfiguration, FAKE_PROVIDER_KEY);
}
Expand All @@ -375,6 +399,7 @@ private StartContainerMojo createMojo(List<ContainerStartConfiguration> startCon
mojo.setProviderName(provider);
mojo.setPluginContext(new HashMap());
mojo.setMojoExecution(mojoExecution);
mojo.setDockerMappingsFile(new File(DOCKER_MAPPINGS_FILE));

return mojo;
}
Expand Down