Spring Boot 3 Buildpacks with Testcontainers Cloud
Published: 2022-11-25 12:21:53

---
title: Spring Boot 3 Buildpacks with Testcontainers Cloud
published: true
date: 2022-11-22 05:55:59 UTC
tags: 
canonical_url: https://dashaun.com/posts/spring-boot-3-buildpacks-with-testcontainers-cloud/
cover_image: https://dashaun.com/posts/spring-boot-3-buildpacks-with-testcontainers-cloud/images/spring-boot-3-tcc_1280x640.png
series: spring boot 3 native
---

## Background 

I have a modest laptop. It checks the boxes, for just about everything I need to do for work. Just about. AOT processing, with GraalVM, is not a great experience on this machine.

## The origin story 

Back in June 2022, I was presenting at SpringOne Tour NYC. As part of the demo, I built a `native` image using Spring Boot 2.7 with the `spring-native` experimental dependency. The session went well, except for the building the native image part of it. During my 50-minute session, my laptop was busy building the native image for over 8-minutes, using the buildpack. It was a humbling experience. After the session, I hung out with Sergei Egorov, and we worked on something very special. With help from [one of his previous blog posts](https://bsideup.github.io/posts/spring_boot_in_container/), we created a proof of concept. We used [Testcontainers Cloud](https://www.testcontainers.cloud/) to build the native OCI image, in the cloud, in a little over 3-minutes. Pairing with Sergei is amazing, if you get the chance, I highly recommend it.

[![Origin Story](https://dashaun.com/posts/spring-boot-3-buildpacks-with-testcontainers-cloud/images/origin-tweet.png)](https://twitter.com/dashaun/status/1541812027286495235)

## It sat on the back-burner and fire is hot 

I had this code sitting in my repository, waiting to be used. When it was time for SpringOne Tour Tel Aviv, Nov 2022, I was ready to show it off. Unfortunately for me, my session was shortened, so I pulled it out of the presentation. I shouldn’t have pulled it out, I made the exact same mistake that I made in NYC. When I built the native image using buildpacks, it took way too long on my laptop.

## Momentum 

My adventures with buildpacks have been top of mind for a couple of weeks now. Oleg Šelajev and Cora Iberklied also presented about testcontainers in Tel Aviv. The stage was set for me to take this use case further.

## The Goal 

### Build Spring Boot 3, native OCI images, with buildpacks, during demos, faster, with Testcontainers Cloud 

## Prerequisites 

You need to have an account at [https://testcontainers.cloud](https://testcontainers.cloud).

## Login to your account 

I’m logging in with `v1.3.11` of the cloud desktop app for Mac, this is still in private beta at the time of this writing.

```
docker context list

```

> You should see at least one context named ’tcc'

```
docker context use tcc

```

## Create an application example 

Create a Spring Boot 3 application with `web`, `actuator`, and `testcontainers` for a simple test.

```
curl https://start.spring.io/starter.tgz -d dependencies=web,actuator,testcontainers -d javaVersion=17 -d bootVersion=3.0.0-RC2 -d type=maven-project | tar -xzf -

```

> Spring Boot 3 goes GA later this week, but I can’t wait that long

In the pom.xml add this dependency:

```
<dependency>
    <groupId>org.apache.maven.shared</groupId>
    <artifactId>maven-invoker</artifactId>
    <version>3.2.0</version>
    <scope>test</scope>
</dependency>

```

Add this property:

```
<testcontainers.version>1.17.6</testcontainers.version>

```

Add this dependency management section:

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>testcontainers-bom</artifactId>
            <version>${testcontainers.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

```

Those pieces will allow you to write a test, that creates an OCI image, using buildpacks, with Testcontainers Cloud.

## Write the test class 

Here is the test class that I’ve been reusing since June.

```
package com.example.demo;

import org.apache.maven.shared.invoker.DefaultInvocationRequest;
import org.apache.maven.shared.invoker.DefaultInvoker;
import org.apache.maven.shared.invoker.InvocationResult;
import org.apache.maven.shared.invoker.MavenInvocationException;
import org.junit.jupiter.api.Test;
import org.springframework.util.DigestUtils;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.LazyFuture;

import java.io.File;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Future;

public class TccTest {
    private static final Future<String> IMAGE_FUTURE = new LazyFuture<>() {
        @Override
        protected String resolve() {
            // Find project's root dir
            File cwd;
            for (
                    cwd = new File(".");
                    !new File(cwd, "mvnw").isFile();
                    cwd = cwd.getParentFile()
            );

            // Make it unique per folder (for caching)
            var imageName = String.format(
                    "local/app-%s:%s",
                    DigestUtils.md5DigestAsHex(cwd.getAbsolutePath().getBytes()),
                    System.currentTimeMillis()
            );

            var properties = new Properties();
            properties.put("spring-boot.build-image.imageName", imageName);
            properties.put("skipTests", "true");

            var request = new DefaultInvocationRequest()
                    .addShellEnvironment("DOCKER_HOST", DockerClientFactory.instance().getTransportConfig().getDockerHost().toString())
                    .setPomFile(new File(cwd, "pom.xml"))
                    .setGoals(List.of("spring-boot:build-image"))
                    .setMavenExecutable(new File(cwd, "mvnw"))
                    .setProfiles(List.of("native"))
                    .setProperties(properties);

            InvocationResult invocationResult = null;
            try {
                invocationResult = new DefaultInvoker().execute(request);
            } catch (MavenInvocationException e) {
                throw new RuntimeException(e);
            }

            if (invocationResult.getExitCode() != 0) {
                throw new RuntimeException(invocationResult.getExecutionException());
            }

            return imageName;
        }
    };

    static final GenericContainer<?> APP = new GenericContainer<>(IMAGE_FUTURE)
            .withExposedPorts(8080);

    @Test
    void letsGo() throws Exception {
        APP.start();
    }
}

```

In [my example repository](https://github.com/dashaun/spring-boot-3-buildpacks-with-testcontainers-cloud), I actually create test classes for a regular OCI image and a `native` OCI image.

## Verify 

```
./mvnw clean test

```

Boom! You get an OCI image, or two if you use [my example repository](https://github.com/dashaun/spring-boot-3-buildpacks-with-testcontainers-cloud).

```
docker images | grep "local"

```

Enjoy!

## Summary 

This is slick. It brings me joy. When I am presenting this topic, I no longer need to have `Docker Desktop` running on my laptop. Therefore, my laptop battery will last longer. My builds will be consistently faster, using Testcontainers Cloud, and my demos will be smoother.

I am completely rethinking a few of my own use cases. I will definitely create more Testcontainers Cloud content in the future.

Please let me know what you think!

## Links 

- [Testcontainers Cloud](https://testcontainers.cloud)
- [Example Repository](https://github.com/dashaun/spring-boot-3-buildpacks-with-testcontainers-cloud)
- [Sergei Egorov Blog](https://bsideup.github.io/posts/spring_boot_in_container/)

![Pairing with Sergei](https://dashaun.com/posts/spring-boot-3-buildpacks-with-testcontainers-cloud/images/sergei-pairing.png)