testcontainerspodmantestcontainers-junit5

Testcontainers with Podman in Java tests


Is it possible to use Testcontainers with Podman in Java tests?

As of March 2022, the Testcontainers library doesn't detect an installed Podman as a valid Docker environment.

Can Podman be a Docker replacement on both MacOS with Apple silicon (local development environment) and Linux x86_64 (CI/CD environment)?


Solution

  • It is possible to use Podman with Testcontainers in Java projects, that use Gradle on Linux and MacOS (both x86_64 and Apple silicon).

    Prerequisites

    Enable the Podman service

    Testcontainers library communicates with Podman using socket file.

    Linux

    Start Podman service for a regular user (rootless) and make it listen to a socket:

    systemctl --user enable --now podman.socket
    

    Check the Podman service status:

    systemctl --user status podman.socket
    

    Check the socket file exists:

    ls -la /run/user/$UID/podman/podman.sock
    

    MacOS

    Podman socket file /run/user/1000/podman/podman.sock can be found inside the Podman-managed Linux VM. A local socket on MacOS can be forwarded to a remote socket on Podman-managed VM using SSH tunneling.

    The port of the Podman-managed VM can be found with the command podman system connection list --format=json.

    Install jq to parse JSON:

    brew install jq
    

    Create a shell alias to forward the local socket /tmp/podman.sock to the remote socket /run/user/1000/podman/podman.sock:

    echo "alias podman-sock=\"rm -f /tmp/podman.sock && ssh -i ~/.ssh/podman-machine-default -p \$(podman system connection list --format=json | jq '.[0].URI' | sed -E 's|.+://.+@.+:([[:digit:]]+)/.+|\1|') -L'/tmp/podman.sock:/run/user/1000/podman/podman.sock' -N core@localhost\"" >> ~/.zprofile
    source ~/.zprofile
    

    Open an SSH tunnel:

    podman-sock
    

    Make sure the SSH tunnel is open before executing tests using Testcontainers.

    Configure Gradle build script

    build.gradle

    test {
        OperatingSystem os = DefaultNativePlatform.currentOperatingSystem;
        if (os.isLinux()) {
            def uid = ["id", "-u"].execute().text.trim()
            environment "DOCKER_HOST", "unix:///run/user/$uid/podman/podman.sock"
        } else if (os.isMacOsX()) {
            environment "DOCKER_HOST", "unix:///tmp/podman.sock"
        }
        environment "TESTCONTAINERS_RYUK_DISABLED", "true"
    }
    

    Set DOCKER_HOST environment variable to Podman socket file depending on the operating system.

    Disable Ryuk with the environment variable TESTCONTAINERS_RYUK_DISABLED.

    Moby Ryuk helps you to remove containers/networks/volumes/images by given filter after specified delay.

    Ryuk is a technology for Docker and doesn't support Podman. See testcontainers/moby-ryuk#23

    Testcontainers library uses Ruyk to remove containers. Instead of relying on Ryuk to implicitly remove containers, we will explicitly remove containers with a JVM shutdown hook:

    Runtime.getRuntime().addShutdownHook(new Thread(container::stop));
    

    Pass the environment variables

    As an alternative to configuring Testcontainers in a Gradle build script, you can pass the environment variables to Gradle.

    Linux

    DOCKER_HOST="unix:///run/user/$UID/podman/podman.sock" \
    TESTCONTAINERS_RYUK_DISABLED="true" \
    ./gradlew clean build -i
    

    MacOS

    DOCKER_HOST="unix:///tmp/podman.sock" \
    TESTCONTAINERS_RYUK_DISABLED="true" \
    ./gradlew clean build -i
    

    Full example

    See the full example https://github.com/evgeniy-khist/podman-testcontainers