Add support for caching to bind mounts when building images
When building an image using the Maven `spring-boot:build-image` goal or the Gradle `bootBuildImage` task, the build and launch caches can be configured to use a bind mount as an alternative to using a named volume. Closes gh-28387
This commit is contained in:
@@ -420,6 +420,13 @@ The cache volumes can be configured to use alternative names to give more contro
|
||||
include::../maven/packaging-oci-image/caches-pom.xml[tags=caches]
|
||||
----
|
||||
|
||||
The caches can be configured to use bind mounts instead of named volumes, as shown in the following example:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,attributes",tabsize=4]
|
||||
----
|
||||
include::../maven/packaging-oci-image/bind-caches-pom.xml[tags=caches]
|
||||
----
|
||||
|
||||
[[build-image.examples.docker]]
|
||||
=== Docker Configuration
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- tag::caches[] -->
|
||||
<project>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<image>
|
||||
<buildCache>
|
||||
<bind>
|
||||
<source>/tmp/cache-${project.artifactId}.build</source>
|
||||
</bind>
|
||||
</buildCache>
|
||||
<launchCache>
|
||||
<bind>
|
||||
<source>/tmp/cache-${project.artifactId}.launch</source>
|
||||
</bind>
|
||||
</launchCache>
|
||||
</image>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
<!-- end::caches[] -->
|
||||
@@ -37,6 +37,7 @@ import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
import org.springframework.boot.testsupport.junit.DisabledOnOs;
|
||||
import org.springframework.boot.testsupport.testcontainers.DisabledIfDockerUnavailable;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -385,19 +386,41 @@ class BuildImageTests extends AbstractArchiveIntegrationTests {
|
||||
@TestTemplate
|
||||
void whenBuildImageIsInvokedWithVolumeCaches(MavenBuild mavenBuild) {
|
||||
String testBuildId = randomString();
|
||||
mavenBuild.project("build-image-caches")
|
||||
mavenBuild.project("build-image-volume-caches")
|
||||
.goals("package")
|
||||
.systemProperty("spring-boot.build-image.pullPolicy", "IF_NOT_PRESENT")
|
||||
.systemProperty("test-build-id", testBuildId)
|
||||
.execute((project) -> {
|
||||
assertThat(buildLog(project)).contains("Building image")
|
||||
.contains("docker.io/library/build-image-caches:0.0.1.BUILD-SNAPSHOT")
|
||||
.contains("docker.io/library/build-image-volume-caches:0.0.1.BUILD-SNAPSHOT")
|
||||
.contains("Successfully built image");
|
||||
removeImage("build-image-caches", "0.0.1.BUILD-SNAPSHOT");
|
||||
removeImage("build-image-volume-caches", "0.0.1.BUILD-SNAPSHOT");
|
||||
deleteVolumes("cache-" + testBuildId + ".build", "cache-" + testBuildId + ".launch");
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenBuildImageIsInvokedWithBindCaches(MavenBuild mavenBuild) {
|
||||
String testBuildId = randomString();
|
||||
mavenBuild.project("build-image-bind-caches")
|
||||
.goals("package")
|
||||
.systemProperty("spring-boot.build-image.pullPolicy", "IF_NOT_PRESENT")
|
||||
.systemProperty("test-build-id", testBuildId)
|
||||
.execute((project) -> {
|
||||
assertThat(buildLog(project)).contains("Building image")
|
||||
.contains("docker.io/library/build-image-bind-caches:0.0.1.BUILD-SNAPSHOT")
|
||||
.contains("Successfully built image");
|
||||
removeImage("build-image-bind-caches", "0.0.1.BUILD-SNAPSHOT");
|
||||
String tempDir = System.getProperty("java.io.tmpdir");
|
||||
Path buildCachePath = Paths.get(tempDir, "junit-image-cache-" + testBuildId + "-build");
|
||||
Path launchCachePath = Paths.get(tempDir, "junit-image-cache-" + testBuildId + "-launch");
|
||||
assertThat(buildCachePath).exists().isDirectory();
|
||||
assertThat(launchCachePath).exists().isDirectory();
|
||||
FileSystemUtils.deleteRecursively(buildCachePath);
|
||||
FileSystemUtils.deleteRecursively(launchCachePath);
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenBuildImageIsInvokedWithCreatedDate(MavenBuild mavenBuild) {
|
||||
String testBuildId = randomString();
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>build-image-bind-caches</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>@java.version@</maven.compiler.source>
|
||||
<maven.compiler.target>@java.version@</maven.compiler.target>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>@project.groupId@</groupId>
|
||||
<artifactId>@project.artifactId@</artifactId>
|
||||
<version>@project.version@</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>build-image-no-fork</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<image>
|
||||
<builder>projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.2</builder>
|
||||
<buildCache>
|
||||
<bind>
|
||||
<source>${java.io.tmpdir}/junit-image-cache-${test-build-id}-build</source>
|
||||
</bind>
|
||||
</buildCache>
|
||||
<launchCache>
|
||||
<bind>
|
||||
<source>${java.io.tmpdir}/junit-image-cache-${test-build-id}-launch</source>
|
||||
</bind>
|
||||
</launchCache>
|
||||
</image>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -3,7 +3,7 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>build-image-caches</artifactId>
|
||||
<artifactId>build-image-volume-caches</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.test;
|
||||
|
||||
public class SampleApplication {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("Launched");
|
||||
synchronized(args) {
|
||||
args.wait(); // Prevent exit
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,8 +32,8 @@ public class CacheInfo {
|
||||
public CacheInfo() {
|
||||
}
|
||||
|
||||
CacheInfo(VolumeCacheInfo volumeCacheInfo) {
|
||||
this.cache = Cache.volume(volumeCacheInfo.getName());
|
||||
private CacheInfo(Cache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public void setVolume(VolumeCacheInfo info) {
|
||||
@@ -41,10 +41,23 @@ public class CacheInfo {
|
||||
this.cache = Cache.volume(info.getName());
|
||||
}
|
||||
|
||||
public void setBind(BindCacheInfo info) {
|
||||
Assert.state(this.cache == null, "Each image building cache can be configured only once");
|
||||
this.cache = Cache.bind(info.getSource());
|
||||
}
|
||||
|
||||
Cache asCache() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
static CacheInfo fromVolume(VolumeCacheInfo cacheInfo) {
|
||||
return new CacheInfo(Cache.volume(cacheInfo.getName()));
|
||||
}
|
||||
|
||||
static CacheInfo fromBind(BindCacheInfo cacheInfo) {
|
||||
return new CacheInfo(Cache.bind(cacheInfo.getSource()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates configuration of an image building cache stored in a volume.
|
||||
*/
|
||||
@@ -69,4 +82,28 @@ public class CacheInfo {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates configuration of an image building cache stored in a bind mount.
|
||||
*/
|
||||
public static class BindCacheInfo {
|
||||
|
||||
private String source;
|
||||
|
||||
public BindCacheInfo() {
|
||||
}
|
||||
|
||||
BindCacheInfo(String name) {
|
||||
this.source = name;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
import org.springframework.boot.maven.CacheInfo.BindCacheInfo;
|
||||
import org.springframework.boot.maven.CacheInfo.VolumeCacheInfo;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -170,21 +171,37 @@ class ImageTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBuildRequestWhenHasBuildVolumeCacheUsesCache() {
|
||||
void getBuildRequestWhenHasBuildCacheVolumeUsesCache() {
|
||||
Image image = new Image();
|
||||
image.buildCache = new CacheInfo(new VolumeCacheInfo("build-cache-vol"));
|
||||
image.buildCache = CacheInfo.fromVolume(new VolumeCacheInfo("build-cache-vol"));
|
||||
BuildRequest request = image.getBuildRequest(createArtifact(), mockApplicationContent());
|
||||
assertThat(request.getBuildCache()).isEqualTo(Cache.volume("build-cache-vol"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBuildRequestWhenHasLaunchVolumeCacheUsesCache() {
|
||||
void getBuildRequestWhenHasLaunchCacheVolumeUsesCache() {
|
||||
Image image = new Image();
|
||||
image.launchCache = new CacheInfo(new VolumeCacheInfo("launch-cache-vol"));
|
||||
image.launchCache = CacheInfo.fromVolume(new VolumeCacheInfo("launch-cache-vol"));
|
||||
BuildRequest request = image.getBuildRequest(createArtifact(), mockApplicationContent());
|
||||
assertThat(request.getLaunchCache()).isEqualTo(Cache.volume("launch-cache-vol"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBuildRequestWhenHasBuildCacheBindUsesCache() {
|
||||
Image image = new Image();
|
||||
image.buildCache = CacheInfo.fromBind(new BindCacheInfo("build-cache-dir"));
|
||||
BuildRequest request = image.getBuildRequest(createArtifact(), mockApplicationContent());
|
||||
assertThat(request.getBuildCache()).isEqualTo(Cache.bind("build-cache-dir"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBuildRequestWhenHasLaunchCacheBindUsesCache() {
|
||||
Image image = new Image();
|
||||
image.launchCache = CacheInfo.fromBind(new BindCacheInfo("launch-cache-dir"));
|
||||
BuildRequest request = image.getBuildRequest(createArtifact(), mockApplicationContent());
|
||||
assertThat(request.getLaunchCache()).isEqualTo(Cache.bind("launch-cache-dir"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBuildRequestWhenHasCreatedDateUsesCreatedDate() {
|
||||
Image image = new Image();
|
||||
|
||||
Reference in New Issue
Block a user