Customizing Dockerunit
Every time you start using a new tool, especially if you are enjoying it, it’s just a matter of time before you hit the wall of a missing feature. For this reason, Dockerunit has been built with extensibility in mind!
Dockerunit allows you to customize container creation in two ways:
- By using the
@ContainerBuilder
pass through annotation. - By defining your own annotations.
Using @ContainerBuilder
Behind the scenes, Dockerunit creates Docker containers using the CreateContainerCmd
interface from docker-java. You can get access to an instance of CreateContainerCmd
, before the command is sent to Docker, by using the @ContainerBuilder
annotation. Let’s see how to.
Imagine you want to set a specific DNS server for your service. Here is how your descriptor would look like if you want to achieve that using @ContainerBuilder
:
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.github.dockerunit.core.annotation.*;
import com.github.dockerunit.discovery.consul.annotation.WebHealthCheck;
@Svc(name="my-svc", image="my-image:latest")
@WebHealthCheck(port=8080)
@PublishPort(container=8080, host=8080)
public class MyDescriptor {
@ContainerBuilder
public CreateContainerCmd build(CreateContainerCmd cmd) {
return cmd.withHostConfig(cmd.getHostConfig().withDns("8.8.8.8"));
}
}
For this to work, your method must both accept a parameter and return a value of type CreateContainerCmd
. Dockerunit makes sure that your method is called after all the other annotations have been processed, so that:
- You are sure that your changes are not overwritten.
- You can still overwrite any of the settings that are coming from other annotations, if needed.
Once you are happy with the effect of your @ContainerBuilder
, you can easily make its usage declarative by evolving it into a custom annotations.
Defining custom annotations
Plugging a new annotation into Dockerunit requires two simple steps:
- Defining an extension annotation.
- Building an extension interpreter.
Let’s create a @Dns
annotation that sets a specific DNS server for our container:
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import com.example.annotations.DnsExtensionInterpreter;
@Retention(RUNTIME)
@Target(TYPE)
@ExtensionMarker(DnsExtensionInterpreter.class)
public @interface Dns {
String value();
}
The key element is the @ExtensionMarker
annotation which wires this annotation to its interpreter. Let’s see how the interpreter looks like:
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.github.dockerunit.core.annotation.ExtensionInterpreter;
import com.github.dockerunit.core.internal.ServiceDescriptor;
import com.example.annotations.Dns;
public class DnsExtensionInterpreter implements ExtensionInterpreter<Dns> {
@Override
public CreateContainerCmd build(ServiceDescriptor sd, CreateContainerCmd cmd, Dns dns) {
return cmd.withHostConfig(cmd.getHostConfig().withDns(dns.value()));
}
Finally, we can use the newly created annotation to replace the @ContainerBuilder
method.
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.github.dockerunit.core.annotation.*;
import com.github.dockerunit.discovery.consul.annotation.WebHealthCheck;
import com.example.annotations.Dns;
@Svc(name="my-svc", image="my-image:latest")
@WebHealthCheck(port=8080)
@PublishPort(container=8080, host=8080)
@Dns("8.8.8.8")
public class MyDescriptor {
}
Simple enough? Why not playing with a real example!