Start Postgres container and connect with JDBC

Quick example of how to set up a Docker container with Postgresql, using the Spotify Docker Java client.

This supposes a running Docker installation on your local computer, for example using boot2docker.

import com.google.common.base.Strings;  
import com.google.common.net.HostAndPort;  
import com.spotify.docker.client.DefaultDockerClient;  
import com.spotify.docker.client.DockerCertificateException;  
import com.spotify.docker.client.DockerClient;  
import com.spotify.docker.client.DockerException;  
import com.spotify.docker.client.messages.ContainerConfig;  
import com.spotify.docker.client.messages.ContainerCreation;  
import com.spotify.docker.client.messages.ContainerInfo;  
import com.spotify.docker.client.messages.HostConfig;  
import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;  
import java.sql.DriverManager;  
import java.sql.SQLException;

@Slf4j
public abstract class PostgresContainerExample {

    public static Connection setUpDbContainer() throws SQLException {
        try {
            // This will only work with the DOCKER_HOST environment variable set
            final DockerClient docker = DefaultDockerClient.fromEnv().build();
            final ContainerConfig config = ContainerConfig.builder()
                    .image("postgres:9.3")
                    .build();
            final ContainerCreation creation = docker.createContainer(config);
            final String id = creation.id();

            final HostConfig hostConfig = HostConfig.builder()
                    .publishAllPorts(true)
                    .build();

            // Container is now created, let's start it up
            docker.startContainer(id, hostConfig);

            // startContainer swallows errors, so check if the container is in the running state
            final ContainerInfo info = docker.inspectContainer(id);
            if (!info.state().running()) {
                throw new IllegalStateException("Could not start Postgres container");
            }

            // We need to build a connection string to connect to Postgres
            // Fiddly way to find the host, relies on the environment variable.
            final String endpoint = System.getenv("DOCKER_HOST");
            final HostAndPort hostAndPort = HostAndPort.fromString(endpoint.replaceAll(".*://", ""));
            final String hostText = hostAndPort.getHostText();
            final String address = Strings.isNullOrEmpty(hostText) ? "127.0.0.1" : hostText;

            // Find the random port in the network settings
            final int port = Integer.valueOf(info.networkSettings().ports().get("5432/tcp").get(0).hostPort());

            final String connectionString = String.format("jdbc:postgresql://%s:%d/postgres?user=postgres", address, port);

            // It takes a while for the Postgres application to start up inside the container. We're going to give it 5 seconds.
            Connection conn = null;
            int tries = 1;
            while (conn == null && tries <= 10) {
                try {
                    conn = DriverManager.getConnection(connectionString);
                } catch (SQLException ignored) {
                    tries++;
                    log.debug("Retrying ({}/10)...", tries);

                    Thread.sleep(500);
                }
            }
            return conn;
        } catch (DockerCertificateException | InterruptedException | DockerException e) {
            e.printStackTrace();
        }
        throw new SQLException("Could not connect to Postgres container");
    }
}
comments powered by Disqus