Polish "Add option to customize cache volume names when building an image"
See gh-28292
This commit is contained in:
@@ -187,10 +187,18 @@ The value supplied will be passed unvalidated to Docker when creating the builde
|
||||
| One or more additional tags to apply to the generated image.
|
||||
|
|
||||
|
||||
| `cacheVolumeNames`
|
||||
| `caches`
|
||||
| Cache volume names that should be used by the builder instead of generating random names.
|
||||
|
|
||||
|
||||
| `buildCache`
|
||||
| A cache containing layers created by buildpacks and used by the image building process.
|
||||
| A named volume in the Docker daemon, with a name derived from the image name.
|
||||
|
||||
| `launchCache`
|
||||
| A cache containing layers created by buildpacks and used by the image launching process.
|
||||
| A named volume in the Docker daemon, with a name derived from the image name.
|
||||
|
||||
|===
|
||||
|
||||
NOTE: The plugin detects the target Java compatibility of the project using the compiler's plugin configuration or the `maven.compiler.target` property.
|
||||
@@ -344,10 +352,23 @@ The `publish` option can be specified on the command line as well, as shown in t
|
||||
$ mvn spring-boot:build-image -Dspring-boot.build-image.imageName=docker.example.com/library/my-app:v1 -Dspring-boot.build-image.publish=true
|
||||
----
|
||||
|
||||
[[build-image.examples.caches]]
|
||||
=== Builder Cache Configuration
|
||||
|
||||
The CNB builder caches layers that are used when building and launching an image.
|
||||
By default, these caches are stored as named volumes in the Docker daemon with names that are derived from the full name of the target image.
|
||||
If the image name changes frequently, for example when the project version is used as a tag in the image name, then the caches can be invalidated frequently.
|
||||
|
||||
The cache volumes can be configured to use alternative names to give more control over cache lifecycle as shown in the following example:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,attributes",tabsize=4]
|
||||
----
|
||||
include::../maven/packaging-oci-image/caches-pom.xml[tags=caches]
|
||||
----
|
||||
|
||||
[[build-image.examples.docker]]
|
||||
=== Docker Configuration
|
||||
|
||||
If you need the plugin to communicate with the Docker daemon using a remote connection instead of the default local connection, the connection details can be provided using `docker` parameters as shown in the following example:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,attributes",tabsize=4]
|
||||
|
||||
@@ -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>
|
||||
<volume>
|
||||
<name>cache-${project.artifactId}.build</name>
|
||||
</volume>
|
||||
</buildCache>
|
||||
<launchCache>
|
||||
<volume>
|
||||
<name>cache-${project.artifactId}.launch</name>
|
||||
</volume>
|
||||
</launchCache>
|
||||
</image>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
<!-- end::caches[] -->
|
||||
@@ -27,8 +27,10 @@ import org.junit.jupiter.api.TestTemplate;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageName;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
import org.springframework.boot.testsupport.testcontainers.DisabledIfDockerUnavailable;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -306,6 +308,18 @@ class BuildImageTests extends AbstractArchiveIntegrationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenBuildImageIsInvokedWithVolumeCaches(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("build-image-caches").goals("package")
|
||||
.systemProperty("spring-boot.build-image.pullPolicy", "IF_NOT_PRESENT").execute((project) -> {
|
||||
assertThat(buildLog(project)).contains("Building image")
|
||||
.contains("docker.io/library/build-image-caches:0.0.1.BUILD-SNAPSHOT")
|
||||
.contains("Successfully built image");
|
||||
removeImage("build-image-caches", "0.0.1.BUILD-SNAPSHOT");
|
||||
deleteVolumes("build-cache-volume", "launch-cache-volume");
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void failsWhenBuildImageIsInvokedOnMultiModuleProjectWithBuildImageGoal(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("build-image-multi-module").goals("spring-boot:build-image")
|
||||
@@ -343,6 +357,13 @@ class BuildImageTests extends AbstractArchiveIntegrationTests {
|
||||
.contains("is required for building an image"));
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void failsWhenCachesAreConfiguredTwice(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("build-image-caches-multiple").goals("package")
|
||||
.executeAndFail((project) -> assertThat(buildLog(project))
|
||||
.contains("Each image building cache can be configured only once"));
|
||||
}
|
||||
|
||||
private void writeLongNameResource(File project) {
|
||||
StringBuilder name = new StringBuilder();
|
||||
new Random().ints('a', 'z' + 1).limit(128).forEach((i) -> name.append((char) i));
|
||||
@@ -366,4 +387,11 @@ class BuildImageTests extends AbstractArchiveIntegrationTests {
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteVolumes(String... names) throws IOException {
|
||||
VolumeApi volumeApi = new DockerApi().volume();
|
||||
for (String name : names) {
|
||||
volumeApi.delete(VolumeName.of(name), false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?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-caches-multiple</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</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<image>
|
||||
<builder>projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1</builder>
|
||||
<buildCache>
|
||||
<volume>
|
||||
<name>build-cache-volume1</name>
|
||||
</volume>
|
||||
<volume>
|
||||
<name>build-cache-volume2</name>
|
||||
</volume>
|
||||
</buildCache>
|
||||
</image>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2012-2021 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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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-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</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<image>
|
||||
<builder>projects.registry.vmware.com/springboot/spring-boot-cnb-builder:0.0.1</builder>
|
||||
<buildCache>
|
||||
<volume>
|
||||
<name>build-cache-volume</name>
|
||||
</volume>
|
||||
</buildCache>
|
||||
<launchCache>
|
||||
<volume>
|
||||
<name>launch-cache-volume</name>
|
||||
</volume>
|
||||
</launchCache>
|
||||
</image>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2012-2021 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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2012-2021 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.springframework.boot.maven;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.build.Cache;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Encapsulates configuration of an image building cache.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public class CacheInfo {
|
||||
|
||||
private Cache cache;
|
||||
|
||||
public CacheInfo() {
|
||||
}
|
||||
|
||||
CacheInfo(VolumeCacheInfo volumeCacheInfo) {
|
||||
this.cache = Cache.volume(volumeCacheInfo.getName());
|
||||
}
|
||||
|
||||
public void setVolume(VolumeCacheInfo info) {
|
||||
Assert.state(this.cache == null, "Each image building cache can be configured only once");
|
||||
this.cache = Cache.volume(info.getName());
|
||||
}
|
||||
|
||||
Cache asCache() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates configuration of an image building cache stored in a volume.
|
||||
*/
|
||||
public static class VolumeCacheInfo {
|
||||
|
||||
private String name;
|
||||
|
||||
public VolumeCacheInfo() {
|
||||
}
|
||||
|
||||
VolumeCacheInfo(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -70,7 +70,9 @@ public class Image {
|
||||
|
||||
List<String> tags;
|
||||
|
||||
Map<String, String> cacheVolumeNames;
|
||||
CacheInfo buildCache;
|
||||
|
||||
CacheInfo launchCache;
|
||||
|
||||
/**
|
||||
* The name of the created image.
|
||||
@@ -215,8 +217,11 @@ public class Image {
|
||||
if (!CollectionUtils.isEmpty(this.tags)) {
|
||||
request = request.withTags(this.tags.stream().map(ImageReference::of).collect(Collectors.toList()));
|
||||
}
|
||||
if (this.cacheVolumeNames != null && !this.cacheVolumeNames.isEmpty()) {
|
||||
request = request.withCacheVolumeNames(this.cacheVolumeNames);
|
||||
if (this.buildCache != null) {
|
||||
request = request.withBuildCache(this.buildCache.asCache());
|
||||
}
|
||||
if (this.launchCache != null) {
|
||||
request = request.withLaunchCache(this.launchCache.asCache());
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -28,11 +28,13 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
||||
import org.springframework.boot.buildpack.platform.build.BuildpackReference;
|
||||
import org.springframework.boot.buildpack.platform.build.Cache;
|
||||
import org.springframework.boot.buildpack.platform.build.PullPolicy;
|
||||
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.VolumeCacheInfo;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
@@ -167,6 +169,22 @@ class ImageTests {
|
||||
ImageReference.of("example.com/my-app:0.0.1-SNAPSHOT"), ImageReference.of("example.com/my-app:latest"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBuildRequestWhenHasBuildVolumeCacheUsesCache() {
|
||||
Image image = new Image();
|
||||
image.buildCache = new CacheInfo(new VolumeCacheInfo("build-cache-vol"));
|
||||
BuildRequest request = image.getBuildRequest(createArtifact(), mockApplicationContent());
|
||||
assertThat(request.getBuildCache()).isEqualTo(Cache.volume("build-cache-vol"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBuildRequestWhenHasLaunchVolumeCacheUsesCache() {
|
||||
Image image = new Image();
|
||||
image.launchCache = new CacheInfo(new VolumeCacheInfo("launch-cache-vol"));
|
||||
BuildRequest request = image.getBuildRequest(createArtifact(), mockApplicationContent());
|
||||
assertThat(request.getLaunchCache()).isEqualTo(Cache.volume("launch-cache-vol"));
|
||||
}
|
||||
|
||||
private Artifact createArtifact() {
|
||||
return new DefaultArtifact("com.example", "my-app", VersionRange.createFromVersion("0.0.1-SNAPSHOT"), "compile",
|
||||
"jar", null, new DefaultArtifactHandler());
|
||||
|
||||
Reference in New Issue
Block a user