Skip to content

Commit

Permalink
Merge pull request #227 from sourcecase/feature-docker-compose-extend…
Browse files Browse the repository at this point in the history
…s-support

Add support for https://docs.docker.com/compose/extends/ Next Try
  • Loading branch information
rnorth committed Sep 25, 2016
2 parents dd6209c + 5c91a71 commit 8c4cb1d
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@
import org.testcontainers.utility.ResourceReaper;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.testcontainers.containers.BindMode.READ_ONLY;
import static org.testcontainers.containers.BindMode.READ_WRITE;

Expand All @@ -42,7 +41,7 @@ public class DockerComposeContainer<SELF extends DockerComposeContainer<SELF>> e
*/
private final String identifier;
private final Map<String, AmbassadorContainer> ambassadorContainers = new HashMap<>();
private final File composeFile;
private final List<File> composeFiles;
private Set<String> spawnedContainerIds;
private Map<String, Integer> scalingPreferences = new HashMap<>();
private DockerClient dockerClient;
Expand All @@ -61,13 +60,26 @@ public class DockerComposeContainer<SELF extends DockerComposeContainer<SELF>> e
.withConstantThroughput()
.build();

public DockerComposeContainer(File composeFile) {
this(composeFile, Base58.randomString(6).toLowerCase());
@Deprecated
public DockerComposeContainer(File composeFile, String identifier) {
this(identifier, composeFile);
}

@SuppressWarnings("WeakerAccess")
public DockerComposeContainer(File composeFile, String identifier) {
this.composeFile = composeFile;
public DockerComposeContainer(File... composeFiles) {
this(Arrays.asList(composeFiles));
}

public DockerComposeContainer(List<File> composeFiles) {
this(Base58.randomString(6).toLowerCase(), composeFiles);
}

public DockerComposeContainer(String identifier, File... composeFiles) {
this(identifier, Arrays.asList(composeFiles));
}

public DockerComposeContainer(String identifier, List<File> composeFiles) {

this.composeFiles = composeFiles;

// Use a unique identifier so that containers created for this compose environment can be identified
this.identifier = identifier;
Expand Down Expand Up @@ -103,7 +115,7 @@ private void createServices() {
}

private DockerCompose getDockerCompose(String cmd) {
return new DockerCompose(composeFile, identifier)
return new DockerCompose(composeFiles, identifier)
.withCommand(cmd)
.withEnv(env);
}
Expand Down Expand Up @@ -288,20 +300,31 @@ private SELF self() {
}

class DockerCompose extends GenericContainer<DockerCompose> {
public DockerCompose(File composeFile, String identifier) {
public DockerCompose(List<File> composeFiles, String identifier) {

super("docker/compose:1.8.0");
checkNotNull(composeFiles);
checkArgument(!composeFiles.isEmpty(), "No docker compose file have been provided");

addEnv("COMPOSE_PROJECT_NAME", identifier);
// Map the docker compose file into the container
String pwd = composeFile.getAbsoluteFile().getParentFile().getAbsolutePath();
String containerPwd = pwd;

// Map the docker compose file into the container
File dockerComposeBaseFile = composeFiles.get(0);
final String pwd = dockerComposeBaseFile.getAbsoluteFile().getParentFile().getAbsolutePath();
final String containerPwd;
if (SystemUtils.IS_OS_WINDOWS) {
containerPwd = PathUtils.createMinGWPath(containerPwd).substring(1);
containerPwd = PathUtils.createMinGWPath(pwd).substring(1);
} else {
containerPwd = pwd;
}

addEnv("COMPOSE_FILE", containerPwd + "/" + composeFile.getAbsoluteFile().getName());
List<String> absoluteDockerComposeFiles = composeFiles.stream().map(
file -> containerPwd + "/" + file.getAbsoluteFile().getName()).collect(Collectors.toList());
String composeFileEnvVariableValue = Joiner.on(File.pathSeparator).join(absoluteDockerComposeFiles);
logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue);
addEnv("COMPOSE_FILE", composeFileEnvVariableValue);
addFileSystemBind(pwd, containerPwd, READ_ONLY);

// Ensure that compose can access docker. Since the container is assumed to be running on the same machine
// as the docker daemon, just mapping the docker control socket is OK.
// As there seems to be a problem with mapping to the /var/run directory in certain environments (e.g. CircleCI)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.testcontainers.junit;

import com.google.common.util.concurrent.Uninterruptibles;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.rnorth.ducttape.unreliables.Unreliables;
import org.testcontainers.containers.DockerComposeContainer;
import org.testcontainers.utility.TestEnvironment;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.concurrent.TimeUnit;

import static org.rnorth.visibleassertions.VisibleAssertions.info;
import static org.rnorth.visibleassertions.VisibleAssertions.pass;

/**
* Created by rnorth on 11/06/2016.
*/
public class DockerComposeDoNotOverrideTest {

static final String DOCKER_COMPOSE_OVERRIDE_TEST_BASE_YML = "src/test/resources/docker-compose-base.yml";
static final String DOCKER_COMPOSE_OVERRIDE_TEST_BASE_ENV = "bar=base";

static final String DOCKER_COMPOSE_OVERRIDE_TEST_OVERRIDE_YML = "src/test/resources/docker-compose-non-default-override.yml";
static final String DOCKER_COMPOSE_OVERRIDE_TEST_OVERRIDE_ENV = "bar=overwritten";

@Rule
public DockerComposeContainer compose =
new DockerComposeContainer(new File(DOCKER_COMPOSE_OVERRIDE_TEST_BASE_YML))
.withExposedService("alpine_1", 3000);

@BeforeClass
public static void checkVersion() {
Assume.assumeTrue(TestEnvironment.dockerApiAtLeast("1.22"));
}


@Test(timeout = 30_000)
public void testEnvVar() throws IOException {
BufferedReader br = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> {
Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);

Socket socket = new Socket(compose.getServiceHost("alpine_1", 3000), compose.getServicePort("alpine_1", 3000));
return new BufferedReader(new InputStreamReader(socket.getInputStream()));
});

Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> {
while (br.ready()) {
String line = br.readLine();
if (line.contains(DOCKER_COMPOSE_OVERRIDE_TEST_BASE_ENV)) {
pass("Mapped environment variable was found");
return true;
}
}
info("Mapped environment variable was not found yet - process probably not ready");
Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
return false;
});

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.testcontainers.junit;

import com.google.common.util.concurrent.Uninterruptibles;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.rnorth.ducttape.unreliables.Unreliables;
import org.testcontainers.containers.DockerComposeContainer;
import org.testcontainers.utility.TestEnvironment;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.concurrent.TimeUnit;

import static org.rnorth.visibleassertions.VisibleAssertions.info;
import static org.rnorth.visibleassertions.VisibleAssertions.pass;
import static org.testcontainers.junit.DockerComposeDoNotOverrideTest.*;

/**
* Created by rnorth on 11/06/2016.
*/
public class DockerComposeOverrideTest {

@Rule
public DockerComposeContainer compose =
new DockerComposeContainer(
new File(DOCKER_COMPOSE_OVERRIDE_TEST_BASE_YML),
new File(DOCKER_COMPOSE_OVERRIDE_TEST_OVERRIDE_YML))
.withExposedService("alpine_1", 3000);

@BeforeClass
public static void checkVersion() {
Assume.assumeTrue(TestEnvironment.dockerApiAtLeast("1.22"));
}


@Test(timeout = 30_000)
public void testEnvVar() throws IOException {
BufferedReader br = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> {
Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);

Socket socket = new Socket(compose.getServiceHost("alpine_1", 3000), compose.getServicePort("alpine_1", 3000));
return new BufferedReader(new InputStreamReader(socket.getInputStream()));
});

Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> {
while (br.ready()) {
String line = br.readLine();
if (line.contains(DOCKER_COMPOSE_OVERRIDE_TEST_OVERRIDE_ENV)) {
pass("Mapped environment variable was found");
return true;
}
}
info("Mapped environment variable was not found yet - process probably not ready");
Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
return false;
});

}
}
6 changes: 6 additions & 0 deletions core/src/test/resources/docker-compose-base.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: '2'
services:
alpine:
build: compose-dockerfile
environment:
bar: base
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
version: '2'
services:
alpine:
environment:
bar: overwritten

0 comments on commit 8c4cb1d

Please sign in to comment.