Add bindings option for image building
This commit adds configuration to the Maven and Gradle plugins to allow a list of volume mount bindings to be provided to the image building goal and task. This enables service bindings to be mounted in the builder image that are recognized by buildpacks to support custom certificates, build tool configuration, APM integration, and other buildpack features. Fixes gh-23518
This commit is contained in:
@@ -31,6 +31,7 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
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;
|
||||
@@ -181,6 +182,23 @@ public class BuildRequestTests {
|
||||
.withMessage("Buildpacks must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withBindingsAddsBindings() throws IOException {
|
||||
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
|
||||
BuildRequest withBindings = request.withBindings(Binding.of("/host/path:/container/path:ro"),
|
||||
Binding.of("volume-name:/container/path:rw"));
|
||||
assertThat(request.getBindings()).isEmpty();
|
||||
assertThat(withBindings.getBindings()).containsExactly(Binding.of("/host/path:/container/path:ro"),
|
||||
Binding.of("volume-name:/container/path:rw"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void withBindingsWhenBindingsIsNullThrowsException() throws IOException {
|
||||
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> request.withBindings((List<Binding>) null))
|
||||
.withMessage("Bindings must not be null");
|
||||
}
|
||||
|
||||
private void hasExpectedJarContent(TarArchive archive) {
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* 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.
|
||||
@@ -36,6 +36,7 @@ import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.ImageApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
||||
@@ -88,6 +89,18 @@ class LifecycleTests {
|
||||
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeWithBindingsExecutesPhases() throws Exception {
|
||||
given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId());
|
||||
given(this.docker.container().create(any(), any())).willAnswer(answerWithGeneratedContainerId());
|
||||
given(this.docker.container().wait(any())).willReturn(ContainerStatus.of(0, null));
|
||||
BuildRequest request = getTestRequest().withBindings(Binding.of("/host/src/path:/container/dest/path:ro"),
|
||||
Binding.of("volume-name:/container/volume/path:rw"));
|
||||
createLifecycle(request).execute();
|
||||
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-bindings.json"));
|
||||
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeExecutesPhasesWithPlatformApi03() throws Exception {
|
||||
given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.boot.buildpack.platform.build;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig.Update;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
|
||||
@@ -65,7 +66,7 @@ class PhaseTests {
|
||||
Update update = mock(Update.class);
|
||||
phase.apply(update);
|
||||
verify(update).withUser("root");
|
||||
verify(update).withBind("/var/run/docker.sock", "/var/run/docker.sock");
|
||||
verify(update).withBinding(Binding.from("/var/run/docker.sock", "/var/run/docker.sock"));
|
||||
verify(update).withCommand("/cnb/lifecycle/test", NO_ARGS);
|
||||
verify(update).withLabel("author", "spring-boot");
|
||||
verifyNoMoreInteractions(update);
|
||||
@@ -108,12 +109,12 @@ class PhaseTests {
|
||||
void applyWhenWithBindsUpdatesConfigurationWithBinds() {
|
||||
Phase phase = new Phase("test", false);
|
||||
VolumeName volumeName = VolumeName.of("test");
|
||||
phase.withBinds(volumeName, "/test");
|
||||
phase.withBinding(Binding.from(volumeName, "/test"));
|
||||
Update update = mock(Update.class);
|
||||
phase.apply(update);
|
||||
verify(update).withCommand("/cnb/lifecycle/test");
|
||||
verify(update).withLabel("author", "spring-boot");
|
||||
verify(update).withBind(volumeName, "/test");
|
||||
verify(update).withBinding(Binding.from(volumeName, "/test"));
|
||||
verifyNoMoreInteractions(update);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.buildpack.platform.docker.type;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link Binding}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class BindingTests {
|
||||
|
||||
@Test
|
||||
void ofReturnsValue() {
|
||||
Binding binding = Binding.of("host-src:container-dest:ro");
|
||||
assertThat(binding.getValue()).isEqualTo("host-src:container-dest:ro");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWithNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> Binding.of(null))
|
||||
.withMessageContaining("Value must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromReturnsValue() {
|
||||
Binding binding = Binding.from("host-src", "container-dest");
|
||||
assertThat(binding.getValue()).isEqualTo("host-src:container-dest");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromWithNullSourceThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> Binding.from((String) null, "container-dest"))
|
||||
.withMessageContaining("Source must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromWithNullDestinationThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> Binding.from("host-src", null))
|
||||
.withMessageContaining("Destination must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromVolumeNameSourceReturnsValue() {
|
||||
Binding binding = Binding.from(VolumeName.of("host-src"), "container-dest");
|
||||
assertThat(binding.getValue()).isEqualTo("host-src:container-dest");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromVolumeNameSourceWithNullSourceThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> Binding.from((VolumeName) null, "container-dest"))
|
||||
.withMessageContaining("SourceVolume must not be null");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* 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.
|
||||
@@ -56,7 +56,7 @@ class ContainerConfigTests extends AbstractJsonTests {
|
||||
update.withCommand("ls", "-l");
|
||||
update.withArgs("-h");
|
||||
update.withLabel("spring", "boot");
|
||||
update.withBind("bind-source", "bind-dest");
|
||||
update.withBinding(Binding.from("bind-source", "bind-dest"));
|
||||
update.withEnv("name1", "value1");
|
||||
update.withEnv("name2", "value2");
|
||||
});
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"User" : "root",
|
||||
"Image" : "pack.local/ephemeral-builder",
|
||||
"Cmd" : [ "/cnb/lifecycle/creator", "-app", "/workspace", "-platform", "/platform", "-run-image", "docker.io/cloudfoundry/run:latest", "-layers", "/layers", "-cache-dir", "/cache", "-launch-cache", "/launch-cache", "-daemon", "-process-type=web", "docker.io/library/my-application:latest" ],
|
||||
"Env" : [ "CNB_PLATFORM_API=0.4" ],
|
||||
"Labels" : {
|
||||
"author" : "spring-boot"
|
||||
},
|
||||
"HostConfig" : {
|
||||
"Binds" : [ "/var/run/docker.sock:/var/run/docker.sock", "pack-layers-aaaaaaaaaa:/layers", "pack-app-aaaaaaaaaa:/workspace", "pack-cache-b35197ac41ea.build:/cache", "pack-cache-b35197ac41ea.launch:/launch-cache", "/host/src/path:/container/dest/path:ro", "volume-name:/container/volume/path:rw" ]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user