Add support for untrusted CNB builders

A `trustBuilder` configuration option has been added to the Maven and
Gradle CNB integration image building goal and task. A known set of
builders published by Paketo, Heroku, and Google are trusted by default,
all other builders are untrusted by default.

Closes gh-41352
This commit is contained in:
Scott Frederick
2024-07-11 11:43:38 -05:00
parent c848a5e3ed
commit 224b06982e
62 changed files with 1654 additions and 207 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 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.
@@ -27,14 +27,18 @@ import java.time.ZoneId;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.boot.buildpack.platform.docker.type.Binding;
import org.springframework.boot.buildpack.platform.docker.type.ImageName;
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;
@@ -64,7 +68,7 @@ class BuildRequestTests {
writeTestJarFile(jarFile);
BuildRequest request = BuildRequest.forJarFile(jarFile);
assertThat(request.getName()).hasToString("docker.io/library/my-app:0.0.1");
assertThat(request.getBuilder()).hasToString("docker.io/" + BuildRequest.DEFAULT_BUILDER_IMAGE_NAME);
assertThat(request.getBuilder()).hasToString("docker.io/" + BuildRequest.DEFAULT_BUILDER_IMAGE_REF);
assertThat(request.getApplicationContent(Owner.ROOT)).satisfies(this::hasExpectedJarContent);
assertThat(request.getEnv()).isEmpty();
}
@@ -75,7 +79,7 @@ class BuildRequestTests {
writeTestJarFile(jarFile);
BuildRequest request = BuildRequest.forJarFile(ImageReference.of("test-app"), jarFile);
assertThat(request.getName()).hasToString("docker.io/library/test-app:latest");
assertThat(request.getBuilder()).hasToString("docker.io/" + BuildRequest.DEFAULT_BUILDER_IMAGE_NAME);
assertThat(request.getBuilder()).hasToString("docker.io/" + BuildRequest.DEFAULT_BUILDER_IMAGE_REF);
assertThat(request.getApplicationContent(Owner.ROOT)).satisfies(this::hasExpectedJarContent);
assertThat(request.getEnv()).isEmpty();
}
@@ -104,6 +108,7 @@ class BuildRequestTests {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"))
.withBuilder(ImageReference.of("spring/builder"));
assertThat(request.getBuilder()).hasToString("docker.io/spring/builder:latest");
assertThat(request.isTrustBuilder()).isFalse();
}
@Test
@@ -113,6 +118,53 @@ class BuildRequestTests {
.of("spring/builder@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d"));
assertThat(request.getBuilder()).hasToString(
"docker.io/spring/builder@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
assertThat(request.isTrustBuilder()).isFalse();
}
@Test
void withoutBuilderTrustsDefaultBuilder() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
assertThat(request.isTrustBuilder()).isTrue();
}
@Test
void withoutBuilderTrustsDefaultBuilderWithDifferentTag() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"))
.withBuilder(ImageReference.of(ImageName.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME), "other"));
assertThat(request.isTrustBuilder()).isTrue();
}
@Test
void withoutBuilderTrustsDefaultBuilderWithDigest() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"))
.withBuilder(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)
.withDigest("sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d"));
assertThat(request.isTrustBuilder()).isTrue();
}
@ParameterizedTest
@MethodSource("trustedBuilders")
void withKnownTrustedBuilderTrustsBuilder(ImageReference builder) throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar")).withBuilder(builder);
assertThat(request.isTrustBuilder()).isTrue();
}
static Stream<ImageReference> trustedBuilders() {
return BuildRequest.KNOWN_TRUSTED_BUILDERS.stream();
}
@Test
void withoutTrustBuilderAndDefaultBuilderUpdatesTrustsBuilder() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar")).withTrustBuilder(false);
assertThat(request.isTrustBuilder()).isFalse();
}
@Test
void withTrustBuilderAndBuilderUpdatesTrustBuilder() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"))
.withBuilder(ImageReference.of("spring/builder"))
.withTrustBuilder(true);
assertThat(request.isTrustBuilder()).isTrue();
}
@Test

View File

@@ -86,7 +86,7 @@ class BuilderTests {
DockerApi docker = mockDockerApi();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image.json");
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(), isNull()))
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(), isNull()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb")), any(), isNull()))
.willAnswer(withPulledImage(runImage));
@@ -97,7 +97,7 @@ class BuilderTests {
assertThat(out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
ArgumentCaptor<ImageArchive> archive = ArgumentCaptor.forClass(ImageArchive.class);
then(docker.image()).should()
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(), isNull());
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(), isNull());
then(docker.image()).should()
.pull(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb")), any(), isNull());
then(docker.image()).should().load(archive.capture(), any());
@@ -115,7 +115,7 @@ class BuilderTests {
.withBuilderRegistryTokenAuthentication("builder token")
.withPublishRegistryTokenAuthentication("publish token");
given(docker.image()
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(),
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(),
eq(dockerConfiguration.getBuilderRegistryAuthentication().getAuthHeader())))
.willAnswer(withPulledImage(builderImage));
given(docker.image()
@@ -129,7 +129,7 @@ class BuilderTests {
assertThat(out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
ArgumentCaptor<ImageArchive> archive = ArgumentCaptor.forClass(ImageArchive.class);
then(docker.image()).should()
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(),
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(),
eq(dockerConfiguration.getBuilderRegistryAuthentication().getAuthHeader()));
then(docker.image()).should()
.pull(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb")), any(),
@@ -168,7 +168,7 @@ class BuilderTests {
DockerApi docker = mockDockerApi();
Image builderImage = loadImage("image-with-run-image-digest.json");
Image runImage = loadImage("run-image.json");
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(), isNull()))
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(), isNull()))
.willAnswer(withPulledImage(builderImage));
given(docker.image()
.pull(eq(ImageReference
@@ -211,7 +211,7 @@ class BuilderTests {
DockerApi docker = mockDockerApi();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image.json");
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(), isNull()))
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(), isNull()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("example.com/custom/run:latest")), any(), isNull()))
.willAnswer(withPulledImage(runImage));
@@ -231,11 +231,11 @@ class BuilderTests {
DockerApi docker = mockDockerApi();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image.json");
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(), isNull()))
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(), isNull()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb")), any(), isNull()))
.willAnswer(withPulledImage(runImage));
given(docker.image().inspect(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME))))
given(docker.image().inspect(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF))))
.willReturn(builderImage);
given(docker.image().inspect(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb"))))
.willReturn(runImage);
@@ -257,11 +257,11 @@ class BuilderTests {
DockerApi docker = mockDockerApi();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image.json");
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(), isNull()))
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(), isNull()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb")), any(), isNull()))
.willAnswer(withPulledImage(runImage));
given(docker.image().inspect(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME))))
given(docker.image().inspect(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF))))
.willReturn(builderImage);
given(docker.image().inspect(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb"))))
.willReturn(runImage);
@@ -283,11 +283,11 @@ class BuilderTests {
DockerApi docker = mockDockerApi();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image.json");
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(), isNull()))
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(), isNull()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb")), any(), isNull()))
.willAnswer(withPulledImage(runImage));
given(docker.image().inspect(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME))))
given(docker.image().inspect(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF))))
.willThrow(
new DockerEngineException("docker://localhost/", new URI("example"), 404, "NOT FOUND", null, null))
.willReturn(builderImage);
@@ -313,7 +313,7 @@ class BuilderTests {
DockerApi docker = mockDockerApi();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image.json");
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(), isNull()))
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(), isNull()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb")), any(), isNull()))
.willAnswer(withPulledImage(runImage));
@@ -339,7 +339,7 @@ class BuilderTests {
.withBuilderRegistryTokenAuthentication("builder token")
.withPublishRegistryTokenAuthentication("publish token");
given(docker.image()
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(),
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(),
eq(dockerConfiguration.getBuilderRegistryAuthentication().getAuthHeader())))
.willAnswer(withPulledImage(builderImage));
given(docker.image()
@@ -354,7 +354,7 @@ class BuilderTests {
assertThat(out.toString()).contains("Successfully created image tag 'docker.io/library/my-application:1.2.3'");
then(docker.image()).should()
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(),
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(),
eq(dockerConfiguration.getBuilderRegistryAuthentication().getAuthHeader()));
then(docker.image()).should()
.pull(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb")), any(),
@@ -378,7 +378,7 @@ class BuilderTests {
DockerApi docker = mockDockerApi();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image-with-bad-stack.json");
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(), isNull()))
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(), isNull()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb")), any(), isNull()))
.willAnswer(withPulledImage(runImage));
@@ -395,7 +395,7 @@ class BuilderTests {
DockerApi docker = mockDockerApiLifecycleError();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image.json");
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(), isNull()))
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(), isNull()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb")), any(), isNull()))
.willAnswer(withPulledImage(runImage));
@@ -413,7 +413,7 @@ class BuilderTests {
DockerConfiguration dockerConfiguration = new DockerConfiguration()
.withBuilderRegistryTokenAuthentication("builder token");
given(docker.image()
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(),
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(),
eq(dockerConfiguration.getBuilderRegistryAuthentication().getAuthHeader())))
.willAnswer(withPulledImage(builderImage));
Builder builder = new Builder(BuildLog.to(out), docker, dockerConfiguration);
@@ -431,7 +431,7 @@ class BuilderTests {
DockerConfiguration dockerConfiguration = new DockerConfiguration()
.withBuilderRegistryTokenAuthentication("builder token");
given(docker.image()
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(),
.pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(),
eq(dockerConfiguration.getBuilderRegistryAuthentication().getAuthHeader())))
.willAnswer(withPulledImage(builderImage));
Builder builder = new Builder(BuildLog.to(out), docker, dockerConfiguration);
@@ -447,7 +447,7 @@ class BuilderTests {
DockerApi docker = mockDockerApiLifecycleError();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image.json");
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)), any(), isNull()))
given(docker.image().pull(eq(ImageReference.of(BuildRequest.DEFAULT_BUILDER_IMAGE_REF)), any(), isNull()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb")), any(), isNull()))
.willAnswer(withPulledImage(runImage));
@@ -490,7 +490,7 @@ class BuilderTests {
private BuildRequest getTestRequest() {
TarArchive content = mock(TarArchive.class);
ImageReference name = ImageReference.of("my-application");
return BuildRequest.of(name, (owner) -> content);
return BuildRequest.of(name, (owner) -> content).withTrustBuilder(true);
}
private Image loadImage(String name) throws IOException {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 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.
@@ -34,6 +34,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.mockito.stubbing.Answer;
import org.skyscreamer.jsonassert.JSONAssert;
@@ -53,6 +54,7 @@ import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
import org.springframework.boot.buildpack.platform.io.IOConsumer;
import org.springframework.boot.buildpack.platform.io.TarArchive;
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
import org.springframework.boot.testsupport.junit.BooleanValueSource;
import org.springframework.util.FileCopyUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -87,13 +89,23 @@ class LifecycleTests {
this.docker = mockDockerApi();
}
@Test
void executeExecutesPhases() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeExecutesPhases(boolean trustBuilder) 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));
createLifecycle().execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator.json"));
createLifecycle(trustBuilder).execute();
if (trustBuilder) {
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator.json"));
}
else {
assertPhaseWasRun("analyzer", withExpectedConfig("lifecycle-analyzer.json"));
assertPhaseWasRun("detector", withExpectedConfig("lifecycle-detector.json"));
assertPhaseWasRun("restorer", withExpectedConfig("lifecycle-restorer.json"));
assertPhaseWasRun("builder", withExpectedConfig("lifecycle-builder.json"));
assertPhaseWasRun("exporter", withExpectedConfig("lifecycle-exporter.json"));
}
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@@ -102,7 +114,7 @@ class LifecycleTests {
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"),
BuildRequest request = getTestRequest(true).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"));
@@ -114,48 +126,62 @@ class LifecycleTests {
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));
createLifecycle("builder-metadata-platform-api-0.3.json").execute();
createLifecycle(true, "builder-metadata-platform-api-0.3.json").execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-platform-api-0.3.json"));
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@Test
void executeOnlyUploadsContentOnce() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeOnlyUploadsContentOnce(boolean trustBuilder) 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));
createLifecycle().execute();
createLifecycle(trustBuilder).execute();
assertThat(this.content).hasSize(1);
}
@Test
void executeWhenAlreadyRunThrowsException() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeWhenAlreadyRunThrowsException(boolean trustBuilder) 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));
Lifecycle lifecycle = createLifecycle();
Lifecycle lifecycle = createLifecycle(trustBuilder);
lifecycle.execute();
assertThatIllegalStateException().isThrownBy(lifecycle::execute)
.withMessage("Lifecycle has already been executed");
}
@Test
void executeWhenBuilderReturnsErrorThrowsException() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeWhenBuilderReturnsErrorThrowsException(boolean trustBuilder) 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(9, null));
assertThatExceptionOfType(BuilderException.class).isThrownBy(() -> createLifecycle().execute())
.withMessage("Builder lifecycle 'creator' failed with status code 9");
assertThatExceptionOfType(BuilderException.class).isThrownBy(() -> createLifecycle(trustBuilder).execute())
.withMessage(
"Builder lifecycle '" + ((trustBuilder) ? "creator" : "analyzer") + "' failed with status code 9");
}
@Test
void executeWhenCleanCacheClearsCache() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeWhenCleanCacheClearsCache(boolean trustBuilder) 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().withCleanCache(true);
BuildRequest request = getTestRequest(trustBuilder).withCleanCache(true);
createLifecycle(request).execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-clean-cache.json"));
if (trustBuilder) {
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-clean-cache.json"));
}
else {
assertPhaseWasRun("analyzer", withExpectedConfig("lifecycle-analyzer.json"));
assertPhaseWasRun("detector", withExpectedConfig("lifecycle-detector.json"));
assertPhaseWasRun("builder", withExpectedConfig("lifecycle-builder.json"));
assertPhaseWasRun("exporter", withExpectedConfig("lifecycle-exporter.json"));
assertThat(this.out.toString()).contains("Skipping restorer because 'cleanCache' is enabled");
}
VolumeName name = VolumeName.of("pack-cache-b35197ac41ea.build");
then(this.docker.volume()).should().delete(name, true);
}
@@ -166,7 +192,7 @@ class LifecycleTests {
given(this.docker.container().create(any(), any())).willAnswer(answerWithGeneratedContainerId());
given(this.docker.container().wait(any())).willReturn(ContainerStatus.of(0, null));
assertThatIllegalStateException()
.isThrownBy(() -> createLifecycle("builder-metadata-unsupported-api.json").execute())
.isThrownBy(() -> createLifecycle(true, "builder-metadata-unsupported-api.json").execute())
.withMessageContaining("Detected platform API versions '0.2' are not included in supported versions");
}
@@ -176,22 +202,32 @@ class LifecycleTests {
given(this.docker.container().create(any(), any())).willAnswer(answerWithGeneratedContainerId());
given(this.docker.container().wait(any())).willReturn(ContainerStatus.of(0, null));
assertThatIllegalStateException()
.isThrownBy(() -> createLifecycle("builder-metadata-unsupported-apis.json").execute())
.isThrownBy(() -> createLifecycle(true, "builder-metadata-unsupported-apis.json").execute())
.withMessageContaining("Detected platform API versions '0.1,0.2' are not included in supported versions");
}
@Test
void executeWhenMultiplePlatformApisSupportedExecutesPhase() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeWhenMultiplePlatformApisSupportedExecutesPhase(boolean trustBuilder) 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));
createLifecycle("builder-metadata-supported-apis.json").execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator.json"));
createLifecycle(trustBuilder, "builder-metadata-supported-apis.json").execute();
if (trustBuilder) {
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator.json"));
}
else {
assertPhaseWasRun("analyzer", withExpectedConfig("lifecycle-analyzer.json"));
assertPhaseWasRun("detector", withExpectedConfig("lifecycle-detector.json"));
assertPhaseWasRun("restorer", withExpectedConfig("lifecycle-restorer.json"));
assertPhaseWasRun("builder", withExpectedConfig("lifecycle-builder.json"));
assertPhaseWasRun("exporter", withExpectedConfig("lifecycle-exporter.json"));
}
}
@Test
void closeClearsVolumes() throws Exception {
createLifecycle().close();
createLifecycle(true).close();
then(this.docker.volume()).should().delete(VolumeName.of("pack-layers-aaaaaaaaaa"), true);
then(this.docker.volume()).should().delete(VolumeName.of("pack-app-aaaaaaaaaa"), true);
}
@@ -201,92 +237,163 @@ class LifecycleTests {
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().withNetwork("test");
BuildRequest request = getTestRequest(true).withNetwork("test");
createLifecycle(request).execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-network.json"));
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@Test
void executeWithCacheVolumeNamesExecutesPhases() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeWithCacheVolumeNamesExecutesPhases(boolean trustBuilder) 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().withBuildWorkspace(Cache.volume("work-volume"))
BuildRequest request = getTestRequest(trustBuilder).withBuildWorkspace(Cache.volume("work-volume"))
.withBuildCache(Cache.volume("build-volume"))
.withLaunchCache(Cache.volume("launch-volume"));
createLifecycle(request).execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-cache-volumes.json"));
if (trustBuilder) {
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-cache-volumes.json"));
}
else {
assertPhaseWasRun("analyzer", withExpectedConfig("lifecycle-analyzer-cache-volumes.json"));
assertPhaseWasRun("detector", withExpectedConfig("lifecycle-detector-cache-volumes.json"));
assertPhaseWasRun("restorer", withExpectedConfig("lifecycle-restorer-cache-volumes.json"));
assertPhaseWasRun("builder", withExpectedConfig("lifecycle-builder-cache-volumes.json"));
assertPhaseWasRun("exporter", withExpectedConfig("lifecycle-exporter-cache-volumes.json"));
}
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@Test
void executeWithCacheBindMountsExecutesPhases() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeWithCacheBindMountsExecutesPhases(boolean trustBuilder) 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().withBuildWorkspace(Cache.bind("/tmp/work"))
BuildRequest request = getTestRequest(trustBuilder).withBuildWorkspace(Cache.bind("/tmp/work"))
.withBuildCache(Cache.bind("/tmp/build-cache"))
.withLaunchCache(Cache.bind("/tmp/launch-cache"));
createLifecycle(request).execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-cache-bind-mounts.json"));
if (trustBuilder) {
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-cache-bind-mounts.json"));
}
else {
assertPhaseWasRun("analyzer", withExpectedConfig("lifecycle-analyzer-cache-bind-mounts.json"));
assertPhaseWasRun("detector", withExpectedConfig("lifecycle-detector-cache-bind-mounts.json"));
assertPhaseWasRun("restorer", withExpectedConfig("lifecycle-restorer-cache-bind-mounts.json"));
assertPhaseWasRun("builder", withExpectedConfig("lifecycle-builder-cache-bind-mounts.json"));
assertPhaseWasRun("exporter", withExpectedConfig("lifecycle-exporter-cache-bind-mounts.json"));
}
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@Test
void executeWithCreatedDateExecutesPhases() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeWithCreatedDateExecutesPhases(boolean trustBuilder) 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().withCreatedDate("2020-07-01T12:34:56Z");
BuildRequest request = getTestRequest(trustBuilder).withCreatedDate("2020-07-01T12:34:56Z");
createLifecycle(request).execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-created-date.json"));
if (trustBuilder) {
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-created-date.json"));
}
else {
assertPhaseWasRun("analyzer", withExpectedConfig("lifecycle-analyzer.json"));
assertPhaseWasRun("detector", withExpectedConfig("lifecycle-detector.json"));
assertPhaseWasRun("restorer", withExpectedConfig("lifecycle-restorer.json"));
assertPhaseWasRun("builder", withExpectedConfig("lifecycle-builder.json"));
assertPhaseWasRun("exporter", withExpectedConfig("lifecycle-exporter-created-date.json"));
}
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@Test
void executeWithApplicationDirectoryExecutesPhases() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeWithApplicationDirectoryExecutesPhases(boolean trustBuilder) 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().withApplicationDirectory("/application");
BuildRequest request = getTestRequest(trustBuilder).withApplicationDirectory("/application");
createLifecycle(request).execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-app-dir.json"));
if (trustBuilder) {
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-app-dir.json"));
}
else {
assertPhaseWasRun("analyzer", withExpectedConfig("lifecycle-analyzer.json"));
assertPhaseWasRun("detector", withExpectedConfig("lifecycle-detector-app-dir.json"));
assertPhaseWasRun("restorer", withExpectedConfig("lifecycle-restorer.json"));
assertPhaseWasRun("builder", withExpectedConfig("lifecycle-builder-app-dir.json"));
assertPhaseWasRun("exporter", withExpectedConfig("lifecycle-exporter-app-dir.json"));
}
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@Test
void executeWithSecurityOptionsExecutesPhases() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeWithSecurityOptionsExecutesPhases(boolean trustBuilder) 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().withSecurityOptions(List.of("label=user:USER", "label=role:ROLE"));
BuildRequest request = getTestRequest(trustBuilder)
.withSecurityOptions(List.of("label=user:USER", "label=role:ROLE"));
createLifecycle(request).execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-security-opts.json", true));
if (trustBuilder) {
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-security-opts.json", true));
}
else {
assertPhaseWasRun("analyzer", withExpectedConfig("lifecycle-analyzer-security-opts.json"));
assertPhaseWasRun("detector", withExpectedConfig("lifecycle-detector.json"));
assertPhaseWasRun("restorer", withExpectedConfig("lifecycle-restorer-security-opts.json"));
assertPhaseWasRun("builder", withExpectedConfig("lifecycle-builder.json"));
assertPhaseWasRun("exporter", withExpectedConfig("lifecycle-exporter-security-opts.json"));
}
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@Test
void executeWithDockerHostAndRemoteAddressExecutesPhases() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeWithDockerHostAndRemoteAddressExecutesPhases(boolean trustBuilder) 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();
BuildRequest request = getTestRequest(trustBuilder);
createLifecycle(request, ResolvedDockerHost.from(DockerHostConfiguration.forAddress("tcp://192.168.1.2:2376")))
.execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-inherit-remote.json"));
if (trustBuilder) {
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-inherit-remote.json"));
}
else {
assertPhaseWasRun("analyzer", withExpectedConfig("lifecycle-analyzer-inherit-remote.json"));
assertPhaseWasRun("detector", withExpectedConfig("lifecycle-detector.json"));
assertPhaseWasRun("restorer", withExpectedConfig("lifecycle-restorer-inherit-remote.json"));
assertPhaseWasRun("builder", withExpectedConfig("lifecycle-builder.json"));
assertPhaseWasRun("exporter", withExpectedConfig("lifecycle-exporter-inherit-remote.json"));
}
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@Test
void executeWithDockerHostAndLocalAddressExecutesPhases() throws Exception {
@ParameterizedTest
@BooleanValueSource
void executeWithDockerHostAndLocalAddressExecutesPhases(boolean trustBuilder) 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();
BuildRequest request = getTestRequest(trustBuilder);
createLifecycle(request, ResolvedDockerHost.from(DockerHostConfiguration.forAddress("/var/alt.sock")))
.execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-inherit-local.json"));
if (trustBuilder) {
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-inherit-local.json"));
}
else {
assertPhaseWasRun("analyzer", withExpectedConfig("lifecycle-analyzer-inherit-local.json"));
assertPhaseWasRun("detector", withExpectedConfig("lifecycle-detector.json"));
assertPhaseWasRun("restorer", withExpectedConfig("lifecycle-restorer-inherit-local.json"));
assertPhaseWasRun("builder", withExpectedConfig("lifecycle-builder.json"));
assertPhaseWasRun("exporter", withExpectedConfig("lifecycle-exporter-inherit-local.json"));
}
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@@ -301,14 +408,16 @@ class LifecycleTests {
return docker;
}
private BuildRequest getTestRequest() {
private BuildRequest getTestRequest(boolean trustBuilder) {
TarArchive content = mock(TarArchive.class);
ImageReference name = ImageReference.of("my-application");
return BuildRequest.of(name, (owner) -> content).withRunImage(ImageReference.of("cloudfoundry/run"));
return BuildRequest.of(name, (owner) -> content)
.withRunImage(ImageReference.of("cloudfoundry/run"))
.withTrustBuilder(trustBuilder);
}
private Lifecycle createLifecycle() throws IOException {
return createLifecycle(getTestRequest());
private Lifecycle createLifecycle(boolean trustBuilder) throws IOException {
return createLifecycle(getTestRequest(trustBuilder));
}
private Lifecycle createLifecycle(BuildRequest request) throws IOException {
@@ -316,9 +425,9 @@ class LifecycleTests {
return createLifecycle(request, builder);
}
private Lifecycle createLifecycle(String builderMetadata) throws IOException {
private Lifecycle createLifecycle(boolean trustBuilder, String builderMetadata) throws IOException {
EphemeralBuilder builder = mockEphemeralBuilder(builderMetadata);
return createLifecycle(getTestRequest(), builder);
return createLifecycle(getTestRequest(trustBuilder), builder);
}
private Lifecycle createLifecycle(BuildRequest request, ResolvedDockerHost dockerHost) throws IOException {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-2024 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.
@@ -66,7 +66,7 @@ class PhaseTests {
Update update = mock(Update.class);
phase.apply(update);
then(update).should().withUser("root");
then(update).should().withCommand("/cnb/lifecycle/test", NO_ARGS);
then(update).should().withCommand("/cnb/lifecycle/test", "-daemon");
then(update).should().withLabel("author", "spring-boot");
then(update).shouldHaveNoMoreInteractions();
}
@@ -74,7 +74,6 @@ class PhaseTests {
@Test
void applyWhenWithLogLevelArgAndVerboseLoggingUpdatesConfigurationWithLogLevel() {
Phase phase = new Phase("test", true);
phase.withLogLevelArg();
Update update = mock(Update.class);
phase.apply(update);
then(update).should().withCommand("/cnb/lifecycle/test", "-log-level", "debug");
@@ -85,7 +84,6 @@ class PhaseTests {
@Test
void applyWhenWithLogLevelArgAndNonVerboseLoggingDoesNotUpdateLogLevel() {
Phase phase = new Phase("test", false);
phase.withLogLevelArg();
Update update = mock(Update.class);
phase.apply(update);
then(update).should().withCommand("/cnb/lifecycle/test");
@@ -133,7 +131,7 @@ class PhaseTests {
@Test
void applyWhenWithNetworkModeUpdatesConfigurationWithNetworkMode() {
Phase phase = new Phase("test", true);
Phase phase = new Phase("test", false);
phase.withNetworkMode("test");
Update update = mock(Update.class);
phase.apply(update);
@@ -145,7 +143,7 @@ class PhaseTests {
@Test
void applyWhenWithSecurityOptionsUpdatesConfigurationWithSecurityOptions() {
Phase phase = new Phase("test", true);
Phase phase = new Phase("test", false);
phase.withSecurityOption("option1=value1");
phase.withSecurityOption("option2=value2");
Update update = mock(Update.class);

View File

@@ -0,0 +1,31 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/analyzer",
"-daemon",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"-run-image",
"docker.io/cloudfoundry/run:latest",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"/tmp/launch-cache:/launch-cache",
"/tmp/work-layers:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,31 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/analyzer",
"-daemon",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"-run-image",
"docker.io/cloudfoundry/run:latest",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"launch-volume:/launch-cache",
"work-volume-layers:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,31 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/analyzer",
"-daemon",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"-run-image",
"docker.io/cloudfoundry/run:latest",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/alt.sock:/var/run/docker.sock",
"pack-cache-b35197ac41ea.launch:/launch-cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,31 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/analyzer",
"-daemon",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"-run-image",
"docker.io/cloudfoundry/run:latest",
"docker.io/library/my-application:latest"
],
"Env": [
"DOCKER_HOST=tcp://192.168.1.2:2376",
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"pack-cache-b35197ac41ea.launch:/launch-cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,32 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/analyzer",
"-daemon",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"-run-image",
"docker.io/cloudfoundry/run:latest",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-cache-b35197ac41ea.launch:/launch-cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=user:USER",
"label=role:ROLE"
]
}
}

View File

@@ -0,0 +1,31 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/analyzer",
"-daemon",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"-run-image",
"docker.io/cloudfoundry/run:latest",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-cache-b35197ac41ea.launch:/launch-cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,24 @@
{
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/builder",
"-app",
"/application",
"-layers",
"/layers",
"-platform",
"/platform"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"pack-app-aaaaaaaaaa:/application",
"pack-layers-aaaaaaaaaa:/layers"
]
}
}

View File

@@ -0,0 +1,24 @@
{
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/builder",
"-app",
"/workspace",
"-layers",
"/layers",
"-platform",
"/platform"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/tmp/work-app:/workspace",
"/tmp/work-layers:/layers"
]
}
}

View File

@@ -0,0 +1,24 @@
{
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/builder",
"-app",
"/workspace",
"-layers",
"/layers",
"-platform",
"/platform"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"work-volume-app:/workspace",
"work-volume-layers:/layers"
]
}
}

View File

@@ -0,0 +1,24 @@
{
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/builder",
"-app",
"/workspace",
"-layers",
"/layers",
"-platform",
"/platform"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"pack-app-aaaaaaaaaa:/workspace",
"pack-layers-aaaaaaaaaa:/layers"
]
}
}

View File

@@ -26,11 +26,11 @@
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-layers-aaaaaaaaaa:/layers",
"pack-app-aaaaaaaaaa:/application",
"pack-layers-aaaaaaaaaa:/layers",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache"
"pack-cache-b35197ac41ea.launch:/launch-cache",
"/var/run/docker.sock:/var/run/docker.sock"
],
"SecurityOpt" : [
"label=disable"

View File

@@ -26,11 +26,11 @@
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-layers-aaaaaaaaaa:/layers",
"pack-app-aaaaaaaaaa:/workspace",
"pack-layers-aaaaaaaaaa:/layers",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache",
"/var/run/docker.sock:/var/run/docker.sock",
"/host/src/path:/container/dest/path:ro",
"volume-name:/container/volume/path:rw"
],

View File

@@ -26,11 +26,11 @@
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"/tmp/work-layers:/layers",
"/tmp/work-app:/workspace",
"/tmp/work-layers:/layers",
"/tmp/build-cache:/cache",
"/tmp/launch-cache:/launch-cache"
"/tmp/launch-cache:/launch-cache",
"/var/run/docker.sock:/var/run/docker.sock"
],
"SecurityOpt" : [
"label=disable"

View File

@@ -26,11 +26,11 @@
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"work-volume-layers:/layers",
"work-volume-app:/workspace",
"work-volume-layers:/layers",
"build-volume:/cache",
"launch-volume:/launch-cache"
"launch-volume:/launch-cache",
"/var/run/docker.sock:/var/run/docker.sock"
],
"SecurityOpt" : [
"label=disable"

View File

@@ -27,11 +27,11 @@
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-layers-aaaaaaaaaa:/layers",
"pack-app-aaaaaaaaaa:/workspace",
"pack-layers-aaaaaaaaaa:/layers",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache"
"pack-cache-b35197ac41ea.launch:/launch-cache",
"/var/run/docker.sock:/var/run/docker.sock"
],
"SecurityOpt" : [
"label=disable"

View File

@@ -27,11 +27,11 @@
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-layers-aaaaaaaaaa:/layers",
"pack-app-aaaaaaaaaa:/workspace",
"pack-layers-aaaaaaaaaa:/layers",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache"
"pack-cache-b35197ac41ea.launch:/launch-cache",
"/var/run/docker.sock:/var/run/docker.sock"
],
"SecurityOpt" : [
"label=disable"

View File

@@ -26,11 +26,11 @@
},
"HostConfig": {
"Binds": [
"/var/alt.sock:/var/run/docker.sock",
"pack-layers-aaaaaaaaaa:/layers",
"pack-app-aaaaaaaaaa:/workspace",
"pack-layers-aaaaaaaaaa:/layers",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache"
"pack-cache-b35197ac41ea.launch:/launch-cache",
"/var/alt.sock:/var/run/docker.sock"
],
"SecurityOpt" : [
"label=disable"

View File

@@ -27,8 +27,8 @@
},
"HostConfig": {
"Binds": [
"pack-layers-aaaaaaaaaa:/layers",
"pack-app-aaaaaaaaaa:/workspace",
"pack-layers-aaaaaaaaaa:/layers",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache"
],

View File

@@ -27,11 +27,11 @@
"HostConfig": {
"NetworkMode": "test",
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-layers-aaaaaaaaaa:/layers",
"pack-app-aaaaaaaaaa:/workspace",
"pack-layers-aaaaaaaaaa:/layers",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache"
"pack-cache-b35197ac41ea.launch:/launch-cache",
"/var/run/docker.sock:/var/run/docker.sock"
],
"SecurityOpt" : [
"label=disable"

View File

@@ -7,7 +7,13 @@
"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" ],
"Binds" : [
"pack-app-aaaaaaaaaa:/workspace",
"pack-layers-aaaaaaaaaa:/layers",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache",
"/var/run/docker.sock:/var/run/docker.sock"
],
"SecurityOpt" : [
"label=disable"
]

View File

@@ -26,11 +26,11 @@
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-layers-aaaaaaaaaa:/layers",
"pack-app-aaaaaaaaaa:/workspace",
"pack-layers-aaaaaaaaaa:/layers",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache"
"pack-cache-b35197ac41ea.launch:/launch-cache",
"/var/run/docker.sock:/var/run/docker.sock"
],
"SecurityOpt" : [
"label=user:USER",

View File

@@ -26,11 +26,11 @@
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-layers-aaaaaaaaaa:/layers",
"pack-app-aaaaaaaaaa:/workspace",
"pack-layers-aaaaaaaaaa:/layers",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache"
"pack-cache-b35197ac41ea.launch:/launch-cache",
"/var/run/docker.sock:/var/run/docker.sock"
],
"SecurityOpt" : [
"label=disable"

View File

@@ -0,0 +1,24 @@
{
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/detector",
"-app",
"/application",
"-layers",
"/layers",
"-platform",
"/platform"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"pack-app-aaaaaaaaaa:/application",
"pack-layers-aaaaaaaaaa:/layers"
]
}
}

View File

@@ -0,0 +1,24 @@
{
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/detector",
"-app",
"/workspace",
"-layers",
"/layers",
"-platform",
"/platform"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/tmp/work-app:/workspace",
"/tmp/work-layers:/layers"
]
}
}

View File

@@ -0,0 +1,24 @@
{
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/detector",
"-app",
"/workspace",
"-layers",
"/layers",
"-platform",
"/platform"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"work-volume-app:/workspace",
"work-volume-layers:/layers"
]
}
}

View File

@@ -0,0 +1,24 @@
{
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/detector",
"-app",
"/workspace",
"-layers",
"/layers",
"-platform",
"/platform"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"pack-app-aaaaaaaaaa:/workspace",
"pack-layers-aaaaaaaaaa:/layers"
]
}
}

View File

@@ -0,0 +1,35 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/exporter",
"-daemon",
"-app",
"/application",
"-cache-dir",
"/cache",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-app-aaaaaaaaaa:/application",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,35 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/exporter",
"-daemon",
"-app",
"/workspace",
"-cache-dir",
"/cache",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"/tmp/work-app:/workspace",
"/tmp/build-cache:/cache",
"/tmp/launch-cache:/launch-cache",
"/tmp/work-layers:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,35 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/exporter",
"-daemon",
"-app",
"/workspace",
"-cache-dir",
"/cache",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"work-volume-app:/workspace",
"build-volume:/cache",
"launch-volume:/launch-cache",
"work-volume-layers:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,36 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/exporter",
"-daemon",
"-app",
"/workspace",
"-cache-dir",
"/cache",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8",
"SOURCE_DATE_EPOCH=1593606896"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-app-aaaaaaaaaa:/workspace",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,35 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/exporter",
"-daemon",
"-app",
"/workspace",
"-cache-dir",
"/cache",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/alt.sock:/var/run/docker.sock",
"pack-app-aaaaaaaaaa:/workspace",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,35 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/exporter",
"-daemon",
"-app",
"/workspace",
"-cache-dir",
"/cache",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"docker.io/library/my-application:latest"
],
"Env": [
"DOCKER_HOST=tcp://192.168.1.2:2376",
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"pack-app-aaaaaaaaaa:/workspace",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,36 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/exporter",
"-daemon",
"-app",
"/workspace",
"-cache-dir",
"/cache",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-app-aaaaaaaaaa:/workspace",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=user:USER",
"label=role:ROLE"
]
}
}

View File

@@ -0,0 +1,35 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/exporter",
"-daemon",
"-app",
"/workspace",
"-cache-dir",
"/cache",
"-launch-cache",
"/launch-cache",
"-layers",
"/layers",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-app-aaaaaaaaaa:/workspace",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,28 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/restorer",
"-daemon",
"-cache-dir",
"/cache",
"-layers",
"/layers"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"/tmp/build-cache:/cache",
"/tmp/work-layers:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,28 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/restorer",
"-daemon",
"-cache-dir",
"/cache",
"-layers",
"/layers"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"build-volume:/cache",
"work-volume-layers:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,28 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/restorer",
"-daemon",
"-cache-dir",
"/cache",
"-layers",
"/layers"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/alt.sock:/var/run/docker.sock",
"pack-cache-b35197ac41ea.build:/cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,28 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/restorer",
"-daemon",
"-cache-dir",
"/cache",
"-layers",
"/layers"
],
"Env": [
"DOCKER_HOST=tcp://192.168.1.2:2376",
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"pack-cache-b35197ac41ea.build:/cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}

View File

@@ -0,0 +1,29 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/restorer",
"-daemon",
"-cache-dir",
"/cache",
"-layers",
"/layers"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-cache-b35197ac41ea.build:/cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=user:USER",
"label=role:ROLE"
]
}
}

View File

@@ -0,0 +1,28 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/restorer",
"-daemon",
"-cache-dir",
"/cache",
"-layers",
"/layers"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-cache-b35197ac41ea.build:/cache",
"pack-layers-aaaaaaaaaa:/layers"
],
"SecurityOpt" : [
"label=disable"
]
}
}