A simple Docker orchestrator written in Groovy.
MotherDocker was created as a tool for easing integration testing with Docker on the JVM. It is a partial rewrite in Groovy of Docker Compose (formerly Fig). Although this library doesn't have (and was not intended to) all the system management options of Docker Compose, it can fully understand its YAML configuration files.
MotherDocker is based on the excellent Spotify's Docker Client.
// Initializes the Docker client
new DefaultDockerClient("unix:///var/run/docker.sock").withCloseable { client ->
// Creates the project
def project = MotherDocker.buildProjectFromFile(filename, client)
// Starts the project
project.start()
// Retrieves the services info
def info = project.getSericesInfo()
// Application logic...
// Stop the project
project.stop()
}
If you're using Spock you could use the provided @WithDockerConfig
extension (as an example see this) or, if you're using Junit, you could use a simple Rule, for example:
public class MotherDockingRule extends ExternalResource {
private MotherDockingProject project;
public MotherDockingRule(String filename) {
DockerClient client = new DefaultDockerClient("unix:///var/run/docker.sock");
this.project = (MotherDockingProject) MotherDocker.buildProjectFromFile(filename, client);
}
public Map<String, ContainerInfo> getServicesInfo() {
return project.getServicesInfo();
}
@Override
public Statement apply(Statement base, Description description) {
return super.apply(base, description);
}
@Override
protected void before() throws Throwable {
super.before();
project.start();
}
@Override
protected void after() {
super.after();
project.stop();
}
}
When using Spring's testing annotation like @RunWith(SpringJUnit4ClassRunner.class)
and @SpringApplicationConfiguration
you will want use @ClassRule
instead of @Rule
to have to containers ready before the Spring's context start.
If you're using Maven:
<dependency>
<groupId>com.github.fbertola</groupId>
<artifactId>mother-docker</artifactId>
<version>1.0.0-Beta3</version>
</dependency>
If you're using Gradle:
compile 'com.github.fbertola:mother-docker:1.0.0-Beta3'
MotherDocker adds some new useful extensions to the original YAML syntax:
It is often advisable not to statically bind specific ports in your containers, either because this will prevents from running the same container twice (e.g. when container creation is a part of multiple integration tests) or because some service might already using it. In these cases, it is useful to let Docker assign an ephemeral port to all the exposed ones; this is possible with the publish_all
option.
For further documentation see this link.
When building a complex multi-container project, it is often useful to have the possibility to wait for an image to reach a certain state before proceeding. MotherDocker provides three different wait strategies:
time
: sleeps for the specified amount of time (milliseconds)log_message
: wait until the specified string shows up in the logsexec
: wait until the specified command terminates successfully
To avoid an infinite wait (e.g. when an error occurred inside a container) all the above commands are terminated after an amount of time configurable via the system property motherdocker.wait.max
Additionally, the log_message
strategy accepts the system property motherdocker.exec.retry.pause
which specifies how often it checks for the exec
status.
- This is intended to be a replacement of Docker Compose?
No, MotherDocker it is not intended to be a replacement for Docker Compose. It lacks all the images management options (e.g. the up
or restart
commands) but nevertheless it can fully understand its configuration files.
- It is possible to run multiple instances of MotherDocker on the same CI?
Definitely yes, but it depends on how the containers are configured.
One of the major problems is port collision, that is, two or more containers try to listen on the same ports. The safest way is to use links or to use the publish_all
option, as explained earlier.
Additionally, be careful when using external volumes.
- Why the name?
For the lulz 😛