Skip to content

Commit

Permalink
Continue refactoring of docker compose support
Browse files Browse the repository at this point in the history
  • Loading branch information
rnorth committed Jun 5, 2016
1 parent 5916cc9 commit 9b760a1
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.exception.DockerException;
import com.github.dockerjava.api.model.Container;
import com.google.common.base.Joiner;
import com.google.common.util.concurrent.Uninterruptibles;
import org.junit.runner.Description;
import org.rnorth.ducttape.ratelimits.RateLimiter;
Expand Down Expand Up @@ -46,7 +47,7 @@ public class DockerComposeContainer<SELF extends DockerComposeContainer<SELF>> e

private static final RateLimiter AMBASSADOR_CREATION_RATE_LIMITER = RateLimiterBuilder
.newBuilder()
.withRate(1, TimeUnit.SECONDS)
.withRate(6, TimeUnit.MINUTES)
.withConstantThroughput()
.build();

Expand Down Expand Up @@ -81,38 +82,12 @@ protected void starting(Description description) {

}

private GenericContainer createComposeInstance() {
return new GenericContainer("dduportal/docker-compose:1.6.0")
.withEnv("COMPOSE_PROJECT_NAME", identifier)
// Map the docker compose file into the container
.withEnv("COMPOSE_FILE", "/compose/" + composeFile.getAbsoluteFile().getName())
.withFileSystemBind(composeFile.getAbsoluteFile().getParentFile().getAbsolutePath(), "/compose", 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)
// we map the socket file outside of /var/run, as just /docker.sock
.withFileSystemBind("/var/run/docker.sock", "/docker.sock", READ_WRITE)
.withEnv("DOCKER_HOST", "unix:///docker.sock")
.withStartupCheckStrategy(new OneShotStartupCheckStrategy());
}

private void createServices() {
// Start the docker-compose container, which starts up the services
GenericContainer composeInstance = createComposeInstance().withCommand("up -d");
runCompose(composeInstance);
}

private void runCompose(GenericContainer composeInstance) {
composeInstance.start();
composeInstance.followOutput(new Slf4jLogConsumer(logger()), OutputFrame.OutputType.STDERR);

// wait for the compose container to stop, which should only happen after it has spawned all the service containers
logger().info("Docker compose container is running - service creation will start now");
while (composeInstance.isRunning()) {
logger().trace("Compose container is still running");
Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
}
logger().info("Docker compose has finished running");
new DockerCompose(composeFile, identifier)
.withCommand("up -d")
.start();
}

private void applyScaling() {
Expand All @@ -123,8 +98,9 @@ private void applyScaling() {
sb.append(" ").append(scale.getKey()).append("=").append(scale.getValue());
}

GenericContainer composeInstance = createComposeInstance().withCommand(sb.toString());
runCompose(composeInstance);
new DockerCompose(composeFile, identifier)
.withCommand(sb.toString())
.start();
}
}

Expand Down Expand Up @@ -185,8 +161,15 @@ private Logger logger() {

@Override
protected void finished(Description description) {
// this, the compose container, should not be running, but just in case something has gone wrong
createComposeInstance().stop();


// Kill the services using docker-compose
new DockerCompose(composeFile, identifier)
.withCommand("kill")
.start();
new DockerCompose(composeFile, identifier)
.withCommand("rm -f -v")
.start();

// shut down all the ambassador containers
ambassadorContainers.forEach((String address, AmbassadorContainer container) -> container.stop());
Expand All @@ -208,8 +191,7 @@ public SELF withExposedService(String serviceName, int servicePort) {
* This avoids the need for the docker compose file to explicitly expose ports on all the
* services.
*/
AmbassadorContainer ambassadorContainer = new AmbassadorContainer<>(new FutureContainer(this.identifier + "_" + serviceName), serviceName, servicePort)
.withNetwork(identifier + "_default");
AmbassadorContainer ambassadorContainer = new AmbassadorContainer<>(new FutureContainer(this.identifier + "_" + serviceName), serviceName, servicePort);

// Ambassador containers will all be started together after docker compose has started
ambassadorContainers.put(serviceName + ":" + servicePort, ambassadorContainer);
Expand Down Expand Up @@ -245,8 +227,42 @@ public Integer getServicePort(String serviceName, Integer servicePort) {
return ambassadorContainers.get(serviceName + ":" + servicePort).getMappedPort(servicePort);
}

public DockerComposeContainer withScaledService(String serviceBaseName, int numInstances) {
public SELF withScaledService(String serviceBaseName, int numInstances) {
scalingPreferences.put(serviceBaseName, numInstances);
return this;

return (SELF) this;
}
}

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

super("dduportal/docker-compose:1.6.0");
addEnv("COMPOSE_PROJECT_NAME", identifier);
// Map the docker compose file into the container
addEnv("COMPOSE_FILE", "/compose/" + composeFile.getAbsoluteFile().getName());
addFileSystemBind(composeFile.getAbsoluteFile().getParentFile().getAbsolutePath(), "/compose", 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)
// we map the socket file outside of /var/run, as just /docker.sock
addFileSystemBind("/var/run/docker.sock", "/docker.sock", READ_WRITE);
addEnv("DOCKER_HOST", "unix:///docker.sock");
setStartupCheckStrategy(new OneShotStartupCheckStrategy());
}

@Override
public void start() {
super.start();

this.followOutput(new Slf4jLogConsumer(logger()), OutputFrame.OutputType.STDERR);

// wait for the compose container to stop, which should only happen after it has spawned all the service containers
logger().info("Docker compose container is running for command: {}", Joiner.on(" ").join(this.getCommandParts()));
while (this.isRunning()) {
logger().trace("Compose container is still running");
Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
}
logger().info("Docker compose has finished running");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,6 @@ public class GenericContainer<SELF extends GenericContainer<SELF>>

private StartupCheckStrategy startupCheckStrategy = new IsRunningStartupCheckStrategy();

@Nullable
private String networkMode = null;

/*
* Unique instance of DockerClient for use by this container object.
*/
Expand Down Expand Up @@ -332,15 +329,21 @@ private void applyConfiguration(CreateContainerCmd createCommand) {
.toArray(new Link[linkedContainers.size()]);
createCommand.withLinks(linksArray);

for (LinkableContainer linkableContainer : linkedContainers.values()) {
Set<String> linkedContainerNetworks = dockerClient.listContainersCmd().exec().stream()
.filter(container -> container.getNames()[0].equals("/" + linkableContainer.getContainerName()))
.flatMap(container -> container.getNetworkSettings().getNetworks().keySet().stream())
.distinct()
.collect(Collectors.toSet());

// TODO attach this container to the relevant networks
}

createCommand.withPublishAllPorts(true);

String[] extraHostsArray = extraHosts.stream()
.toArray(String[]::new);
createCommand.withExtraHosts(extraHostsArray);

if (networkMode != null) {
createCommand.withNetworkMode(networkMode);
}
}

/**
Expand Down Expand Up @@ -732,11 +735,6 @@ public ExecResult execInContainer(Charset outputCharset, String... command)
return result;
}

public SELF withNetwork(String network) {
this.networkMode = network;
return self();
}

/**
* Convenience class with access to non-public members of GenericContainer.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ public void stopAndRemoveContainer(String containerId, String imageName) {
private void stopContainer(String containerId, String imageName) {
try {
LOGGER.trace("Stopping container: {}", containerId);
dockerClient.killContainerCmd(containerId).exec();
dockerClient.killContainerCmd(containerId)
.exec();
LOGGER.trace("Stopped container: {}", imageName);
} catch (DockerException e) {
LOGGER.trace("Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped: {}", containerId, e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public void simpleTest() {
jedis.incr("test");
jedis.incr("test");

assertEquals("A redis instance defined in compose can be used in isolation", 3, jedis.get("test"));
assertEquals("A redis instance defined in compose can be used in isolation", "3", jedis.get("test"));
}

@Test
Expand All @@ -35,7 +35,7 @@ public void secondTest() {
jedis.incr("test");
jedis.incr("test");

assertEquals("Tests use fresh container instances", 3, jedis.get("test"));
assertEquals("Tests use fresh container instances", "3", jedis.get("test"));
// if these end up using the same container one of the test methods will fail.
// However, @Rule creates a separate DockerComposeContainer instance per test, so this just shouldn't happen
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public void simpleTest() {
for (int i = 0; i < 3; i++) {
clients[i].incr("somekey");

assertEquals("Each redis instance is separate", 1, clients[i].get("somekey"));
assertEquals("Each redis instance is separate", "1", clients[i].get("somekey"));
}
}
}
2 changes: 2 additions & 0 deletions core/src/test/resources/logback-test.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@
<logger name="com.github.dockerjava" level="WARN"/>
<logger name="org.zeroturnaround.exec" level="WARN"/>
<logger name="com.zaxxer.hikari" level="INFO"/>
<logger name="org.rnorth.tcpunixsocketproxy" level="WARN" />
<logger name="io.netty" level="WARN" />
</configuration>

0 comments on commit 9b760a1

Please sign in to comment.