Using Testcontainers in a Kubernetes Jenkins pod

This post will show you how to run testcontainers in Jenkins on Kubernetes.

Setting up a Kubernetes cluster and Jenkins is outside the scope of this article.

Testcontainers

Testcontainers is a Java library that makes it easy to create integration tests with external dependencies. Instead of creating and managing new instances of databases, queue systems etc. which your application depends on, you can let testcontainers do the heavy lifting for you. It will create and manage throw-away Docker containers, and you can set up your tests to use those instead. Very highly recommended!

Jenkins on Kubernetes

For a new application I have been working on, I have set up a simple Kubernetes cluster, and installed Jenkins on it. The application source repo is in Gitlab, and I have set up a Webhook to start the Jenkins build when ever I push changes to the code.

As a part of the project I have also created a Jenskinsfile which Jenkins can use to create a new Kubernetes pod for each build.

A rough template looks like:

def label = "worker-${UUID.randomUUID().toString()}"

podTemplate(label: label, containers: [
            containerTemplate(name: 'jdk',
                    image: 'adoptopenjdk/openjdk11:jdk-11.0.6_10-ubuntu-slim',
                    command: 'cat',
                    ttyEnabled: true),
        ],
        volumes: [
                hostPathVolume(mountPath: '/home/gradle/.gradle',
                        hostPath: '/tmp/jenkins/.gradle'),
        ]) {

    node(label) {
            stage('Check') {
                container('jdk') {
                    sh """./gradlew --no-daemon check"""
                }
            }
    }
}

This creates a pod based on the template, using the openjdk11 image for the Docker container in the pod. The project supplies a Gradle wrapper script, so that will be installed on-the-fly. Not optimal, but also not relevant just now.

Running the tests

The project also uses testcontainers to handle the database which the application needs. On my local development environment, this just works. The Gradle check task will run the project tests, and the integration tests that access the database pass correctly.

When I pushed this to Gitlab, and the Jenkins build ran, the tests were failing. This happens because I am using testcontainers inside a Docker container, as described here.

Once I knew what the issue was, the fix was relatively simple: Add the Docker socket as a volume mount:

def label = "worker-${UUID.randomUUID().toString()}"

podTemplate(label: label, containers: [
            containerTemplate(name: 'jdk',
                    image: 'adoptopenjdk/openjdk11:jdk-11.0.6_10-ubuntu-slim',
                    command: 'cat',
                    ttyEnabled: true),
        ],
        volumes: [
                hostPathVolume(mountPath: '/home/gradle/.gradle',
                        hostPath: '/tmp/jenkins/.gradle'),
                hostPathVolume(mountPath: '/var/run/docker.sock',
                        hostPath: '/var/run/docker.sock')
        ]) {

    node(label) {
            stage('Check') {
                container('jdk') {
                    sh """./gradlew --no-daemon check"""
                }
            }
    }
}

With that change the tests were running correctly in the Jenkins build as well.