Add buildpack option for image building
This commit adds configuration to the Maven and Gradle plugins to allow a list of buildpacks to be provided to the image building goal and task. Fixes gh-21722
This commit is contained in:
@@ -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.
|
||||
@@ -22,6 +22,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
@@ -163,6 +164,23 @@ public class BuildRequestTests {
|
||||
.withMessage("Value must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withBuildpacksAddsBuildpacks() throws IOException {
|
||||
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
|
||||
BuildpackReference buildpackReference1 = BuildpackReference.of("example/buildpack1");
|
||||
BuildpackReference buildpackReference2 = BuildpackReference.of("example/buildpack2");
|
||||
BuildRequest withBuildpacks = request.withBuildpacks(buildpackReference1, buildpackReference2);
|
||||
assertThat(request.getBuildpacks()).isEmpty();
|
||||
assertThat(withBuildpacks.getBuildpacks()).containsExactly(buildpackReference1, buildpackReference2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withBuildpacksWhenBuildpacksIsNullThrowsException() throws IOException {
|
||||
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> request.withBuildpacks((List<BuildpackReference>) null))
|
||||
.withMessage("Buildpacks must not be null");
|
||||
}
|
||||
|
||||
private void hasExpectedJarContent(TarArchive archive) {
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Layer;
|
||||
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link BuilderBuildpack}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class BuilderBuildpackTests extends AbstractJsonTests {
|
||||
|
||||
private BuildpackResolverContext resolverContext;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
BuilderMetadata metadata = BuilderMetadata.fromJson(getContentAsString("builder-metadata.json"));
|
||||
this.resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(this.resolverContext.getBuildpackMetadata()).willReturn(metadata.getBuildpacks());
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenFullyQualifiedBuildpackWithVersionResolves() throws Exception {
|
||||
BuildpackReference reference = BuildpackReference.of("urn:cnb:builder:paketo-buildpacks/spring-boot@3.5.0");
|
||||
Buildpack buildpack = BuilderBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack.getCoordinates())
|
||||
.isEqualTo(BuildpackCoordinates.of("paketo-buildpacks/spring-boot", "3.5.0"));
|
||||
assertThatNoLayersAreAdded(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenFullyQualifiedBuildpackWithoutVersionResolves() throws Exception {
|
||||
BuildpackReference reference = BuildpackReference.of("urn:cnb:builder:paketo-buildpacks/spring-boot");
|
||||
Buildpack buildpack = BuilderBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack.getCoordinates())
|
||||
.isEqualTo(BuildpackCoordinates.of("paketo-buildpacks/spring-boot", "3.5.0"));
|
||||
assertThatNoLayersAreAdded(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenUnqualifiedBuildpackWithVersionResolves() throws Exception {
|
||||
BuildpackReference reference = BuildpackReference.of("paketo-buildpacks/spring-boot@3.5.0");
|
||||
Buildpack buildpack = BuilderBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack.getCoordinates())
|
||||
.isEqualTo(BuildpackCoordinates.of("paketo-buildpacks/spring-boot", "3.5.0"));
|
||||
assertThatNoLayersAreAdded(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenUnqualifiedBuildpackWithoutVersionResolves() throws Exception {
|
||||
BuildpackReference reference = BuildpackReference.of("paketo-buildpacks/spring-boot");
|
||||
Buildpack buildpack = BuilderBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack.getCoordinates())
|
||||
.isEqualTo(BuildpackCoordinates.of("paketo-buildpacks/spring-boot", "3.5.0"));
|
||||
assertThatNoLayersAreAdded(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenFullyQualifiedBuildpackWithVersionNotInBuilderThrowsException() {
|
||||
BuildpackReference reference = BuildpackReference.of("urn:cnb:builder:example/buildpack1@1.2.3");
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuilderBuildpack.resolve(this.resolverContext, reference))
|
||||
.withMessageContaining("'urn:cnb:builder:example/buildpack1@1.2.3'")
|
||||
.withMessageContaining("not found in builder");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenFullyQualifiedBuildpackWithoutVersionNotInBuilderThrowsException() {
|
||||
BuildpackReference reference = BuildpackReference.of("urn:cnb:builder:example/buildpack1");
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuilderBuildpack.resolve(this.resolverContext, reference))
|
||||
.withMessageContaining("'urn:cnb:builder:example/buildpack1'")
|
||||
.withMessageContaining("not found in builder");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenUnqualifiedBuildpackNotInBuilderReturnsNull() {
|
||||
BuildpackReference reference = BuildpackReference.of("example/buildpack1@1.2.3");
|
||||
Buildpack buildpack = BuilderBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack).isNull();
|
||||
}
|
||||
|
||||
private void assertThatNoLayersAreAdded(Buildpack buildpack) throws IOException {
|
||||
List<Layer> layers = new ArrayList<>();
|
||||
buildpack.apply(layers::add);
|
||||
assertThat(layers).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
@@ -16,12 +16,8 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.build;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -31,6 +27,7 @@ import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.tuple;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@@ -55,6 +52,14 @@ class BuilderMetadataTests extends AbstractJsonTests {
|
||||
assertThat(metadata.getCreatedBy().getName()).isEqualTo("Pack CLI");
|
||||
assertThat(metadata.getCreatedBy().getVersion())
|
||||
.isEqualTo("v0.9.0 (git sha: d42c384a39f367588f2653f2a99702db910e5ad7)");
|
||||
assertThat(metadata.getBuildpacks()).extracting(BuildpackMetadata::getId, BuildpackMetadata::getVersion)
|
||||
.contains(tuple("paketo-buildpacks/java", "4.10.0"))
|
||||
.contains(tuple("paketo-buildpacks/spring-boot", "3.5.0"))
|
||||
.contains(tuple("paketo-buildpacks/executable-jar", "3.1.3"))
|
||||
.contains(tuple("paketo-buildpacks/graalvm", "4.1.0"))
|
||||
.contains(tuple("paketo-buildpacks/java-native-image", "4.7.0"))
|
||||
.contains(tuple("paketo-buildpacks/spring-boot-native-image", "2.0.1"))
|
||||
.contains(tuple("paketo-buildpacks/bellsoft-liberica", "6.2.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,9 +129,4 @@ class BuilderMetadataTests extends AbstractJsonTests {
|
||||
.isEqualTo(metadata.getStack().getRunImage().getImage());
|
||||
}
|
||||
|
||||
private String getContentAsString(String name) {
|
||||
return new BufferedReader(new InputStreamReader(getContent(name), StandardCharsets.UTF_8)).lines()
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -320,10 +320,8 @@ class BuilderTests {
|
||||
.willAnswer(withPulledImage(builderImage));
|
||||
Builder builder = new Builder(BuildLog.to(out), docker, dockerConfiguration);
|
||||
BuildRequest request = getTestRequest();
|
||||
assertThatIllegalStateException().isThrownBy(() -> builder.build(request))
|
||||
.withMessageContaining(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)
|
||||
.withMessageContaining("example.com/custom/run:latest")
|
||||
.withMessageContaining("must be pulled from the same authenticated registry");
|
||||
assertThatIllegalStateException().isThrownBy(() -> builder.build(request)).withMessage(
|
||||
"Run image 'example.com/custom/run:latest' must be pulled from the 'docker.io' authenticated registry");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -338,10 +336,26 @@ class BuilderTests {
|
||||
.willAnswer(withPulledImage(builderImage));
|
||||
Builder builder = new Builder(BuildLog.to(out), docker, dockerConfiguration);
|
||||
BuildRequest request = getTestRequest().withRunImage(ImageReference.of("example.com/custom/run:latest"));
|
||||
assertThatIllegalStateException().isThrownBy(() -> builder.build(request))
|
||||
.withMessageContaining(BuildRequest.DEFAULT_BUILDER_IMAGE_NAME)
|
||||
.withMessageContaining("example.com/custom/run:latest")
|
||||
.withMessageContaining("must be pulled from the same authenticated registry");
|
||||
assertThatIllegalStateException().isThrownBy(() -> builder.build(request)).withMessage(
|
||||
"Run image 'example.com/custom/run:latest' must be pulled from the 'docker.io' authenticated registry");
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWhenRequestedBuildpackNotInBuilderThrowsException() throws Exception {
|
||||
TestPrintStream out = new TestPrintStream();
|
||||
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()))
|
||||
.willAnswer(withPulledImage(builderImage));
|
||||
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:base-cnb")), any(), isNull()))
|
||||
.willAnswer(withPulledImage(runImage));
|
||||
Builder builder = new Builder(BuildLog.to(out), docker, null);
|
||||
BuildpackReference reference = BuildpackReference.of("urn:cnb:builder:example/buildpack@1.2.3");
|
||||
BuildRequest request = getTestRequest().withBuildpacks(reference);
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> builder.build(request))
|
||||
.withMessageContaining("'urn:cnb:builder:example/buildpack@1.2.3'")
|
||||
.withMessageContaining("not found in builder");
|
||||
}
|
||||
|
||||
private DockerApi mockDockerApi() throws IOException {
|
||||
@@ -349,15 +363,12 @@ class BuilderTests {
|
||||
ContainerReference reference = ContainerReference.of("container-ref");
|
||||
given(containerApi.create(any(), any())).willReturn(reference);
|
||||
given(containerApi.wait(eq(reference))).willReturn(ContainerStatus.of(0, null));
|
||||
|
||||
ImageApi imageApi = mock(ImageApi.class);
|
||||
VolumeApi volumeApi = mock(VolumeApi.class);
|
||||
|
||||
DockerApi docker = mock(DockerApi.class);
|
||||
given(docker.image()).willReturn(imageApi);
|
||||
given(docker.container()).willReturn(containerApi);
|
||||
given(docker.volume()).willReturn(volumeApi);
|
||||
|
||||
return docker;
|
||||
}
|
||||
|
||||
@@ -366,15 +377,12 @@ class BuilderTests {
|
||||
ContainerReference reference = ContainerReference.of("container-ref");
|
||||
given(containerApi.create(any(), any())).willReturn(reference);
|
||||
given(containerApi.wait(eq(reference))).willReturn(ContainerStatus.of(9, null));
|
||||
|
||||
ImageApi imageApi = mock(ImageApi.class);
|
||||
VolumeApi volumeApi = mock(VolumeApi.class);
|
||||
|
||||
DockerApi docker = mock(DockerApi.class);
|
||||
given(docker.image()).willReturn(imageApi);
|
||||
given(docker.container()).willReturn(containerApi);
|
||||
given(docker.volume()).willReturn(volumeApi);
|
||||
|
||||
return docker;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link BuildpackCoordinates}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class BuildpackCoordinatesTests extends AbstractJsonTests {
|
||||
|
||||
private final Path archive = Paths.get("/buildpack/path");
|
||||
|
||||
@Test
|
||||
void fromToml() throws IOException {
|
||||
BuildpackCoordinates coordinates = BuildpackCoordinates
|
||||
.fromToml(createTomlStream("example/buildpack1", "0.0.1", true, false), this.archive);
|
||||
assertThat(coordinates.getId()).isEqualTo("example/buildpack1");
|
||||
assertThat(coordinates.getVersion()).isEqualTo("0.0.1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromTomlWhenMissingDescriptorThrowsException() throws Exception {
|
||||
ByteArrayInputStream coordinates = new ByteArrayInputStream("".getBytes());
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackCoordinates.fromToml(coordinates, this.archive))
|
||||
.withMessageContaining("Buildpack descriptor 'buildpack.toml' is required")
|
||||
.withMessageContaining(this.archive.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromTomlWhenMissingIDThrowsException() throws Exception {
|
||||
InputStream coordinates = createTomlStream(null, null, true, false);
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackCoordinates.fromToml(coordinates, this.archive))
|
||||
.withMessageContaining("Buildpack descriptor must contain ID")
|
||||
.withMessageContaining(this.archive.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromTomlWhenMissingVersionThrowsException() throws Exception {
|
||||
InputStream coordinates = createTomlStream("example/buildpack1", null, true, false);
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackCoordinates.fromToml(coordinates, this.archive))
|
||||
.withMessageContaining("Buildpack descriptor must contain version")
|
||||
.withMessageContaining(this.archive.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromTomlWhenMissingStacksAndOrderThrowsException() throws Exception {
|
||||
InputStream coordinates = createTomlStream("example/buildpack1", "0.0.1", false, false);
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackCoordinates.fromToml(coordinates, this.archive))
|
||||
.withMessageContaining("Buildpack descriptor must contain either 'stacks' or 'order'")
|
||||
.withMessageContaining(this.archive.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromTomlWhenContainsBothStacksAndOrderThrowsException() throws Exception {
|
||||
InputStream coordinates = createTomlStream("example/buildpack1", "0.0.1", true, true);
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackCoordinates.fromToml(coordinates, this.archive))
|
||||
.withMessageContaining("Buildpack descriptor must not contain both 'stacks' and 'order'")
|
||||
.withMessageContaining(this.archive.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromBuildpackMetadataWhenMetadataIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackCoordinates.fromBuildpackMetadata(null))
|
||||
.withMessage("BuildpackMetadata must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromBuildpackMetadataReturnsCoordinates() throws Exception {
|
||||
BuildpackMetadata metadata = BuildpackMetadata.fromJson(getContentAsString("buildpack-metadata.json"));
|
||||
BuildpackCoordinates coordinates = BuildpackCoordinates.fromBuildpackMetadata(metadata);
|
||||
assertThat(coordinates.getId()).isEqualTo("example/hello-universe");
|
||||
assertThat(coordinates.getVersion()).isEqualTo("0.0.1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenIdIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackCoordinates.of(null, null))
|
||||
.withMessage("ID must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofReturnsCoordinates() {
|
||||
BuildpackCoordinates coordinates = BuildpackCoordinates.of("id", "1");
|
||||
assertThat(coordinates).hasToString("id@1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getIdReturnsId() {
|
||||
BuildpackCoordinates coordinates = BuildpackCoordinates.of("id", "1");
|
||||
assertThat(coordinates.getId()).isEqualTo("id");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getVersionReturnsVersion() {
|
||||
BuildpackCoordinates coordinates = BuildpackCoordinates.of("id", "1");
|
||||
assertThat(coordinates.getVersion()).isEqualTo("1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getVersionWhenVersionIsNullReturnsNull() {
|
||||
BuildpackCoordinates coordinates = BuildpackCoordinates.of("id", null);
|
||||
assertThat(coordinates.getVersion()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringReturnsNiceString() {
|
||||
BuildpackCoordinates coordinates = BuildpackCoordinates.of("id", "1");
|
||||
assertThat(coordinates).hasToString("id@1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsAndHashCode() {
|
||||
BuildpackCoordinates c1a = BuildpackCoordinates.of("id", "1");
|
||||
BuildpackCoordinates c1b = BuildpackCoordinates.of("id", "1");
|
||||
BuildpackCoordinates c2 = BuildpackCoordinates.of("id", "2");
|
||||
assertThat(c1a).isEqualTo(c1a).isEqualTo(c1b).isNotEqualTo(c2);
|
||||
assertThat(c1a.hashCode()).isEqualTo(c1b.hashCode());
|
||||
}
|
||||
|
||||
private InputStream createTomlStream(String id, String version, boolean includeStacks, boolean includeOrder) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[buildpack]\n");
|
||||
if (id != null) {
|
||||
builder.append("id = \"").append(id).append("\"\n");
|
||||
}
|
||||
if (version != null) {
|
||||
builder.append("version = \"").append(version).append("\"\n");
|
||||
}
|
||||
builder.append("name = \"Example buildpack\"\n");
|
||||
builder.append("homepage = \"https://github.com/example/example-buildpack\"\n");
|
||||
if (includeStacks) {
|
||||
builder.append("[[stacks]]\n");
|
||||
builder.append("id = \"io.buildpacks.stacks.bionic\"\n");
|
||||
}
|
||||
if (includeOrder) {
|
||||
builder.append("[[order]]\n");
|
||||
builder.append("group = [ { id = \"example/buildpack2\", version=\"0.0.2\" } ]\n");
|
||||
}
|
||||
return new ByteArrayInputStream(builder.toString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageConfig;
|
||||
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link BuildpackMetadata}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class BuildpackMetadataTests extends AbstractJsonTests {
|
||||
|
||||
@Test
|
||||
void fromImageLoadsMetadata() throws IOException {
|
||||
Image image = Image.of(getContent("buildpack-image.json"));
|
||||
BuildpackMetadata metadata = BuildpackMetadata.fromImage(image);
|
||||
assertThat(metadata.getId()).isEqualTo("example/hello-universe");
|
||||
assertThat(metadata.getVersion()).isEqualTo("0.0.1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromImageWhenImageIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackMetadata.fromImage(null))
|
||||
.withMessage("Image must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromImageWhenImageConfigIsNullThrowsException() {
|
||||
Image image = mock(Image.class);
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackMetadata.fromImage(image))
|
||||
.withMessage("ImageConfig must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromImageConfigWhenLabelIsMissingThrowsException() {
|
||||
Image image = mock(Image.class);
|
||||
ImageConfig imageConfig = mock(ImageConfig.class);
|
||||
given(image.getConfig()).willReturn(imageConfig);
|
||||
given(imageConfig.getLabels()).willReturn(Collections.singletonMap("alpha", "a"));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackMetadata.fromImage(image))
|
||||
.withMessage("No 'io.buildpacks.buildpackage.metadata' label found in image config labels 'alpha'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromJsonLoadsMetadata() throws IOException {
|
||||
BuildpackMetadata metadata = BuildpackMetadata.fromJson(getContentAsString("buildpack-metadata.json"));
|
||||
assertThat(metadata.getId()).isEqualTo("example/hello-universe");
|
||||
assertThat(metadata.getVersion()).isEqualTo("0.0.1");
|
||||
assertThat(metadata.getHomepage()).isEqualTo("https://github.com/example/tree/main/buildpacks/hello-universe");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
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 BuildpackReference}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class BuildpackReferenceTests {
|
||||
|
||||
@Test
|
||||
void ofWhenValueIsEmptyThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackReference.of(""))
|
||||
.withMessage("Value must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofCreatesInstance() {
|
||||
BuildpackReference reference = BuildpackReference.of("test");
|
||||
assertThat(reference).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringReturnsValue() {
|
||||
BuildpackReference reference = BuildpackReference.of("test");
|
||||
assertThat(reference).hasToString("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsAndHashCode() {
|
||||
BuildpackReference a = BuildpackReference.of("test1");
|
||||
BuildpackReference b = BuildpackReference.of("test1");
|
||||
BuildpackReference c = BuildpackReference.of("test2");
|
||||
assertThat(a).isEqualTo(a).isEqualTo(b).isNotEqualTo(c);
|
||||
assertThat(a.hashCode()).isEqualTo(b.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPrefixWhenPrefixMatchReturnsTrue() {
|
||||
BuildpackReference reference = BuildpackReference.of("test");
|
||||
assertThat(reference.hasPrefix("te")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPrefixWhenPrifixMismatchReturnsFalse() {
|
||||
BuildpackReference reference = BuildpackReference.of("test");
|
||||
assertThat(reference.hasPrefix("st")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSubReferenceWhenPrefixMatchReturnsSubReference() {
|
||||
BuildpackReference reference = BuildpackReference.of("test");
|
||||
assertThat(reference.getSubReference("te")).isEqualTo("st");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSubReferenceWhenPrefixMismatchReturnsNull() {
|
||||
BuildpackReference reference = BuildpackReference.of("test");
|
||||
assertThat(reference.getSubReference("st")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void asPathWhenFileUrlReturnsPath() {
|
||||
BuildpackReference reference = BuildpackReference.of("file:///test.dat");
|
||||
assertThat(reference.asPath()).isEqualTo(Paths.get("/test.dat"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void asPathWhenPathReturnsPath() {
|
||||
BuildpackReference reference = BuildpackReference.of("/test.dat");
|
||||
assertThat(reference.asPath()).isEqualTo(Paths.get("/test.dat"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link BuildpackResolvers}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class BuildpackResolversTests extends AbstractJsonTests {
|
||||
|
||||
private BuildpackResolverContext resolverContext;
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
BuilderMetadata metadata = BuilderMetadata.fromJson(getContentAsString("builder-metadata.json"));
|
||||
this.resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(this.resolverContext.getBuildpackMetadata()).willReturn(metadata.getBuildpacks());
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveAllWithBuilderBuildpackReferenceReturnsExpectedBuildpack() throws IOException {
|
||||
BuildpackReference reference = BuildpackReference.of("urn:cnb:builder:paketo-buildpacks/spring-boot@3.5.0");
|
||||
Buildpacks buildpacks = BuildpackResolvers.resolveAll(this.resolverContext, Collections.singleton(reference));
|
||||
assertThat(buildpacks.getBuildpacks()).hasSize(1);
|
||||
assertThat(buildpacks.getBuildpacks().get(0)).isInstanceOf(BuilderBuildpack.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveAllWithDirectoryBuildpackReferenceReturnsExpectedBuildpack(@TempDir Path temp) throws IOException {
|
||||
FileCopyUtils.copy(getClass().getResourceAsStream("buildpack.toml"),
|
||||
Files.newOutputStream(temp.resolve("buildpack.toml")));
|
||||
BuildpackReference reference = BuildpackReference.of(temp.toAbsolutePath().toString());
|
||||
Buildpacks buildpacks = BuildpackResolvers.resolveAll(this.resolverContext, Collections.singleton(reference));
|
||||
assertThat(buildpacks.getBuildpacks()).hasSize(1);
|
||||
assertThat(buildpacks.getBuildpacks().get(0)).isInstanceOf(DirectoryBuildpack.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveAllWithTarGzipBuildpackReferenceReturnsExpectedBuildpack(@TempDir File temp) throws Exception {
|
||||
TestTarGzip testTarGzip = new TestTarGzip(temp);
|
||||
Path archive = testTarGzip.createArchive();
|
||||
BuildpackReference reference = BuildpackReference.of(archive.toString());
|
||||
Buildpacks buildpacks = BuildpackResolvers.resolveAll(this.resolverContext, Collections.singleton(reference));
|
||||
assertThat(buildpacks.getBuildpacks()).hasSize(1);
|
||||
assertThat(buildpacks.getBuildpacks().get(0)).isInstanceOf(TarGzipBuildpack.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveAllWithImageBuildpackReferenceReturnsExpectedBuildpack() throws IOException {
|
||||
Image image = Image.of(getContent("buildpack-image.json"));
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(resolverContext.fetchImage(any(), any())).willReturn(image);
|
||||
BuildpackReference reference = BuildpackReference.of("docker://example/buildpack1:latest");
|
||||
Buildpacks buildpacks = BuildpackResolvers.resolveAll(resolverContext, Collections.singleton(reference));
|
||||
assertThat(buildpacks.getBuildpacks()).hasSize(1);
|
||||
assertThat(buildpacks.getBuildpacks().get(0)).isInstanceOf(ImageBuildpack.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveAllWithInvalidLocatorThrowsException() throws IOException {
|
||||
BuildpackReference reference = BuildpackReference.of("unknown-buildpack@0.0.1");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> BuildpackResolvers.resolveAll(this.resolverContext, Collections.singleton(reference)))
|
||||
.withMessageContaining("Invalid buildpack reference")
|
||||
.withMessageContaining("'unknown-buildpack@0.0.1'");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Layer;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link Buildpacks}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class BuildpacksTests {
|
||||
|
||||
@Test
|
||||
void ofWhenBuildpacksIsNullReturnsEmpty() {
|
||||
Buildpacks buildpacks = Buildpacks.of(null);
|
||||
assertThat(buildpacks).isSameAs(Buildpacks.EMPTY);
|
||||
assertThat(buildpacks.getBuildpacks()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofReturnsBuildpacks() {
|
||||
List<Buildpack> buildpackList = new ArrayList<>();
|
||||
buildpackList.add(new TestBuildpack("example/buildpack1", "0.0.1"));
|
||||
buildpackList.add(new TestBuildpack("example/buildpack2", "0.0.2"));
|
||||
Buildpacks buildpacks = Buildpacks.of(buildpackList);
|
||||
assertThat(buildpacks.getBuildpacks()).isEqualTo(buildpackList);
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyWritesLayersAndOrderLayer() throws Exception {
|
||||
List<Buildpack> buildpackList = new ArrayList<>();
|
||||
buildpackList.add(new TestBuildpack("example/buildpack1", "0.0.1"));
|
||||
buildpackList.add(new TestBuildpack("example/buildpack2", "0.0.2"));
|
||||
buildpackList.add(new TestBuildpack("example/buildpack3", null));
|
||||
Buildpacks buildpacks = Buildpacks.of(buildpackList);
|
||||
List<Layer> layers = new ArrayList<>();
|
||||
buildpacks.apply(layers::add);
|
||||
assertThat(layers).hasSize(4);
|
||||
assertThatLayerContentIsCorrect(layers.get(0), "example_buildpack1/0.0.1");
|
||||
assertThatLayerContentIsCorrect(layers.get(1), "example_buildpack2/0.0.2");
|
||||
assertThatLayerContentIsCorrect(layers.get(2), "example_buildpack3/null");
|
||||
assertThatOrderLayerContentIsCorrect(layers.get(3));
|
||||
}
|
||||
|
||||
private void assertThatLayerContentIsCorrect(Layer layer, String path) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
layer.writeTo(out);
|
||||
try (TarArchiveInputStream tar = new TarArchiveInputStream(new ByteArrayInputStream(out.toByteArray()))) {
|
||||
assertThat(tar.getNextEntry().getName()).isEqualTo("/cnb/buildpacks/" + path + "/buildpack.toml");
|
||||
assertThat(tar.getNextEntry()).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertThatOrderLayerContentIsCorrect(Layer layer) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
layer.writeTo(out);
|
||||
try (TarArchiveInputStream tar = new TarArchiveInputStream(new ByteArrayInputStream(out.toByteArray()))) {
|
||||
assertThat(tar.getNextEntry().getName()).isEqualTo("/cnb/order.toml");
|
||||
byte[] content = StreamUtils.copyToByteArray(tar);
|
||||
String toml = new String(content, StandardCharsets.UTF_8);
|
||||
assertThat(toml).isEqualTo(getExpectedToml());
|
||||
}
|
||||
}
|
||||
|
||||
private String getExpectedToml() {
|
||||
StringBuilder toml = new StringBuilder();
|
||||
toml.append("[[order]]\n");
|
||||
toml.append("group = [\n");
|
||||
toml.append(" { id = \"example/buildpack1\", version = \"0.0.1\" }\n");
|
||||
toml.append("]\n\n");
|
||||
toml.append("[[order]]\n");
|
||||
toml.append("group = [\n");
|
||||
toml.append(" { id = \"example/buildpack2\", version = \"0.0.2\" }\n");
|
||||
toml.append("]\n\n");
|
||||
toml.append("[[order]]\n");
|
||||
toml.append("group = [\n");
|
||||
toml.append(" { id = \"example/buildpack3\" }\n");
|
||||
toml.append("]\n\n");
|
||||
return toml.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.PosixFilePermissions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledOnOs;
|
||||
import org.junit.jupiter.api.condition.EnabledOnOs;
|
||||
import org.junit.jupiter.api.condition.OS;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.tuple;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link DirectoryBuildpack}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
@DisabledOnOs(OS.WINDOWS)
|
||||
class DirectoryBuildpackTests {
|
||||
|
||||
@TempDir
|
||||
File temp;
|
||||
|
||||
private File buildpackDir;
|
||||
|
||||
private BuildpackResolverContext resolverContext;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.buildpackDir = new File(this.temp, "buildpack");
|
||||
this.buildpackDir.mkdirs();
|
||||
this.resolverContext = mock(BuildpackResolverContext.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenPath() throws Exception {
|
||||
writeBuildpackDescriptor();
|
||||
writeScripts();
|
||||
BuildpackReference reference = BuildpackReference.of(this.buildpackDir.toString());
|
||||
Buildpack buildpack = DirectoryBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack).isNotNull();
|
||||
assertThat(buildpack.getCoordinates()).hasToString("example/buildpack1@0.0.1");
|
||||
assertHasExpectedLayers(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenFileUrl() throws Exception {
|
||||
writeBuildpackDescriptor();
|
||||
writeScripts();
|
||||
BuildpackReference reference = BuildpackReference.of("file://" + this.buildpackDir.toString());
|
||||
Buildpack buildpack = DirectoryBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack).isNotNull();
|
||||
assertThat(buildpack.getCoordinates()).hasToString("example/buildpack1@0.0.1");
|
||||
assertHasExpectedLayers(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenDirectoryWithoutBuildpackTomlThrowsException() throws Exception {
|
||||
Files.createDirectories(this.buildpackDir.toPath());
|
||||
BuildpackReference reference = BuildpackReference.of(this.buildpackDir.toString());
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> DirectoryBuildpack.resolve(this.resolverContext, reference))
|
||||
.withMessageContaining("Buildpack descriptor 'buildpack.toml' is required")
|
||||
.withMessageContaining(this.buildpackDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenFileReturnsNull() throws Exception {
|
||||
Path file = Files.createFile(Paths.get(this.buildpackDir.toString(), "test"));
|
||||
BuildpackReference reference = BuildpackReference.of(file.toString());
|
||||
Buildpack buildpack = DirectoryBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenDirectoryDoesNotExistReturnsNull() {
|
||||
BuildpackReference reference = BuildpackReference.of("/test/a/missing/buildpack");
|
||||
Buildpack buildpack = DirectoryBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void locateDirectoryAsUrlThatDoesNotExistThrowsException() {
|
||||
BuildpackReference reference = BuildpackReference.of("file:///test/a/missing/buildpack");
|
||||
Buildpack buildpack = DirectoryBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack).isNull();
|
||||
}
|
||||
|
||||
private void assertHasExpectedLayers(Buildpack buildpack) throws IOException {
|
||||
List<ByteArrayOutputStream> layers = new ArrayList<>();
|
||||
buildpack.apply((layer) -> {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
layer.writeTo(out);
|
||||
layers.add(out);
|
||||
});
|
||||
assertThat(layers).hasSize(1);
|
||||
byte[] content = layers.get(0).toByteArray();
|
||||
try (TarArchiveInputStream tar = new TarArchiveInputStream(new ByteArrayInputStream(content))) {
|
||||
List<TarArchiveEntry> entries = new ArrayList<>();
|
||||
TarArchiveEntry entry = tar.getNextTarEntry();
|
||||
while (entry != null) {
|
||||
entries.add(entry);
|
||||
entry = tar.getNextTarEntry();
|
||||
}
|
||||
assertThat(entries).extracting("name", "mode").containsExactlyInAnyOrder(
|
||||
tuple("/cnb/buildpacks/example_buildpack1/0.0.1/buildpack.toml", 0644),
|
||||
tuple("/cnb/buildpacks/example_buildpack1/0.0.1/bin/detect", 0744),
|
||||
tuple("/cnb/buildpacks/example_buildpack1/0.0.1/bin/build", 0744));
|
||||
}
|
||||
}
|
||||
|
||||
private void writeBuildpackDescriptor() throws IOException {
|
||||
File descriptor = new File(this.buildpackDir, "buildpack.toml");
|
||||
try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(descriptor.toPath()))) {
|
||||
writer.println("[buildpack]");
|
||||
writer.println("id = \"example/buildpack1\"");
|
||||
writer.println("version = \"0.0.1\"");
|
||||
writer.println("name = \"Example buildpack\"");
|
||||
writer.println("homepage = \"https://github.com/example/example-buildpack\"");
|
||||
writer.println("[[stacks]]");
|
||||
writer.println("id = \"io.buildpacks.stacks.bionic\"");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeScripts() throws IOException {
|
||||
File binDirectory = new File(this.buildpackDir, "bin");
|
||||
binDirectory.mkdirs();
|
||||
Path detect = Files.createFile(Paths.get(binDirectory.getAbsolutePath(), "detect"),
|
||||
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwxr--r--")));
|
||||
try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(detect))) {
|
||||
writer.println("#!/usr/bin/env bash");
|
||||
writer.println("echo \"---> detect\"");
|
||||
}
|
||||
Path build = Files.createFile(Paths.get(binDirectory.getAbsolutePath(), "build"),
|
||||
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwxr--r--")));
|
||||
try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(build))) {
|
||||
writer.println("#!/usr/bin/env bash");
|
||||
writer.println("echo \"---> build\"");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
@@ -20,12 +20,17 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
@@ -40,6 +45,7 @@ import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageConfig;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -62,7 +68,9 @@ class EphemeralBuilderTests extends AbstractJsonTests {
|
||||
|
||||
private Map<String, String> env;
|
||||
|
||||
private Creator creator = Creator.withVersion("dev");
|
||||
private Buildpacks buildpacks;
|
||||
|
||||
private final Creator creator = Creator.withVersion("dev");
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
@@ -75,15 +83,18 @@ class EphemeralBuilderTests extends AbstractJsonTests {
|
||||
|
||||
@Test
|
||||
void getNameHasRandomName() throws Exception {
|
||||
EphemeralBuilder b1 = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env);
|
||||
EphemeralBuilder b2 = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env);
|
||||
EphemeralBuilder b1 = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env,
|
||||
this.buildpacks);
|
||||
EphemeralBuilder b2 = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env,
|
||||
this.buildpacks);
|
||||
assertThat(b1.getName().toString()).startsWith("pack.local/builder/").endsWith(":latest");
|
||||
assertThat(b1.getName().toString()).isNotEqualTo(b2.getName().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getArchiveHasCreatedByConfig() throws Exception {
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env);
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env,
|
||||
this.buildpacks);
|
||||
ImageConfig config = builder.getArchive().getImageConfig();
|
||||
BuilderMetadata ephemeralMetadata = BuilderMetadata.fromImageConfig(config);
|
||||
assertThat(ephemeralMetadata.getCreatedBy().getName()).isEqualTo("Spring Boot");
|
||||
@@ -92,14 +103,16 @@ class EphemeralBuilderTests extends AbstractJsonTests {
|
||||
|
||||
@Test
|
||||
void getArchiveHasTag() throws Exception {
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env);
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env,
|
||||
this.buildpacks);
|
||||
ImageReference tag = builder.getArchive().getTag();
|
||||
assertThat(tag.toString()).startsWith("pack.local/builder/").endsWith(":latest");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getArchiveHasFixedCreateDate() throws Exception {
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env);
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env,
|
||||
this.buildpacks);
|
||||
Instant createInstant = builder.getArchive().getCreateDate();
|
||||
OffsetDateTime createDateTime = OffsetDateTime.ofInstant(createInstant, ZoneId.of("UTC"));
|
||||
assertThat(createDateTime.getYear()).isEqualTo(1980);
|
||||
@@ -112,12 +125,35 @@ class EphemeralBuilderTests extends AbstractJsonTests {
|
||||
|
||||
@Test
|
||||
void getArchiveContainsEnvLayer() throws Exception {
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env);
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env,
|
||||
this.buildpacks);
|
||||
File directory = unpack(getLayer(builder.getArchive(), 0), "env");
|
||||
assertThat(new File(directory, "platform/env/spring")).usingCharset(StandardCharsets.UTF_8).hasContent("boot");
|
||||
assertThat(new File(directory, "platform/env/empty")).usingCharset(StandardCharsets.UTF_8).hasContent("");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getArchiveContainsBuildpackLayers() throws Exception {
|
||||
List<Buildpack> buildpackList = new ArrayList<>();
|
||||
buildpackList.add(new TestBuildpack("example/buildpack1", "0.0.1"));
|
||||
buildpackList.add(new TestBuildpack("example/buildpack2", "0.0.2"));
|
||||
buildpackList.add(new TestBuildpack("example/buildpack3", "0.0.3"));
|
||||
this.buildpacks = Buildpacks.of(buildpackList);
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, null,
|
||||
this.buildpacks);
|
||||
assertBuildpackLayerContent(builder, 0, "/cnb/buildpacks/example_buildpack1/0.0.1/buildpack.toml");
|
||||
assertBuildpackLayerContent(builder, 1, "/cnb/buildpacks/example_buildpack2/0.0.2/buildpack.toml");
|
||||
assertBuildpackLayerContent(builder, 2, "/cnb/buildpacks/example_buildpack3/0.0.3/buildpack.toml");
|
||||
File orderDirectory = unpack(getLayer(builder.getArchive(), 3), "order");
|
||||
assertThat(new File(orderDirectory, "cnb/order.toml")).usingCharset(StandardCharsets.UTF_8)
|
||||
.hasContent(content("order-versions.toml"));
|
||||
}
|
||||
|
||||
private void assertBuildpackLayerContent(EphemeralBuilder builder, int index, String s) throws Exception {
|
||||
File buildpackDirectory = unpack(getLayer(builder.getArchive(), index), "buildpack");
|
||||
assertThat(new File(buildpackDirectory, s)).usingCharset(StandardCharsets.UTF_8).hasContent("[test]");
|
||||
}
|
||||
|
||||
private TarArchiveInputStream getLayer(ImageArchive archive, int index) throws Exception {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
archive.writeTo(outputStream);
|
||||
@@ -148,4 +184,9 @@ class EphemeralBuilderTests extends AbstractJsonTests {
|
||||
return directory;
|
||||
}
|
||||
|
||||
private String content(String fileName) throws IOException {
|
||||
InputStream in = getClass().getResourceAsStream(fileName);
|
||||
return FileCopyUtils.copyToString(new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ImageBuildpack}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ImageBuildpackTests extends AbstractJsonTests {
|
||||
|
||||
@Test
|
||||
void resolveWhenFullyQualifiedReferenceReturnsBuilder() throws Exception {
|
||||
Image image = Image.of(getContent("buildpack-image.json"));
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(resolverContext.fetchImage(any(), any())).willReturn(image);
|
||||
willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(any(), any());
|
||||
BuildpackReference reference = BuildpackReference.of("docker://example/buildpack1:latest");
|
||||
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
|
||||
assertThat(buildpack.getCoordinates()).hasToString("example/hello-universe@0.0.1");
|
||||
assertHasExpectedLayers(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenUnqualifiedReferenceReturnsBuilder() throws Exception {
|
||||
Image image = Image.of(getContent("buildpack-image.json"));
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(resolverContext.fetchImage(any(), any())).willReturn(image);
|
||||
willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(any(), any());
|
||||
BuildpackReference reference = BuildpackReference.of("example/buildpack1:latest");
|
||||
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
|
||||
assertThat(buildpack.getCoordinates()).hasToString("example/hello-universe@0.0.1");
|
||||
assertHasExpectedLayers(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenWhenImageNotPulledThrowsException() throws Exception {
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(resolverContext.fetchImage(any(), any())).willThrow(IOException.class);
|
||||
BuildpackReference reference = BuildpackReference.of("docker://example/buildpack1:latest");
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> ImageBuildpack.resolve(resolverContext, reference))
|
||||
.withMessageContaining("Error pulling buildpack image")
|
||||
.withMessageContaining("example/buildpack1:latest");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenMissingMetadataLabelThrowsException() throws Exception {
|
||||
Image image = Image.of(getContent("image.json"));
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(resolverContext.fetchImage(any(), any())).willReturn(image);
|
||||
BuildpackReference reference = BuildpackReference.of("docker://example/buildpack1:latest");
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> ImageBuildpack.resolve(resolverContext, reference))
|
||||
.withMessageContaining("No 'io.buildpacks.buildpackage.metadata' label found");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenFullyQualifiedReferenceWithInvalidImageReferenceThrowsException() throws Exception {
|
||||
BuildpackReference reference = BuildpackReference.of("docker://buildpack@0.0.1");
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> ImageBuildpack.resolve(resolverContext, reference))
|
||||
.withMessageContaining("Unable to parse image reference \"buildpack@0.0.1\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenUnqualifiedReferenceWithInvalidImageReferenceReturnsNull() throws Exception {
|
||||
BuildpackReference reference = BuildpackReference.of("buildpack@0.0.1");
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
|
||||
assertThat(buildpack).isNull();
|
||||
}
|
||||
|
||||
private Object withMockLayers(InvocationOnMock invocation) throws Exception {
|
||||
IOBiConsumer<String, TarArchive> consumer = invocation.getArgument(1);
|
||||
TarArchive archive = (out) -> FileCopyUtils.copy(getClass().getResourceAsStream("layer.tar"), out);
|
||||
consumer.accept("test", archive);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void assertHasExpectedLayers(Buildpack buildpack) throws IOException {
|
||||
List<ByteArrayOutputStream> layers = new ArrayList<>();
|
||||
buildpack.apply((layer) -> {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
layer.writeTo(out);
|
||||
layers.add(out);
|
||||
});
|
||||
assertThat(layers).hasSize(1);
|
||||
byte[] content = layers.get(0).toByteArray();
|
||||
List<String> names = new ArrayList<>();
|
||||
try (TarArchiveInputStream tar = new TarArchiveInputStream(new ByteArrayInputStream(content))) {
|
||||
TarArchiveEntry entry = tar.getNextTarEntry();
|
||||
while (entry != null) {
|
||||
names.add(entry.getName());
|
||||
entry = tar.getNextTarEntry();
|
||||
}
|
||||
}
|
||||
assertThat(names).containsExactly("etc/apt/sources.list");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link TarGzipBuildpack}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class TarGzipBuildpackTests {
|
||||
|
||||
private File buildpackDir;
|
||||
|
||||
private TestTarGzip testTarGzip;
|
||||
|
||||
private BuildpackResolverContext resolverContext;
|
||||
|
||||
@BeforeEach
|
||||
void setUp(@TempDir File temp) {
|
||||
this.buildpackDir = new File(temp, "buildpack");
|
||||
this.buildpackDir.mkdirs();
|
||||
this.testTarGzip = new TestTarGzip(this.buildpackDir);
|
||||
this.resolverContext = mock(BuildpackResolverContext.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenFilePathReturnsBuildpack() throws Exception {
|
||||
Path compressedArchive = this.testTarGzip.createArchive();
|
||||
BuildpackReference reference = BuildpackReference.of(compressedArchive.toString());
|
||||
Buildpack buildpack = TarGzipBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack).isNotNull();
|
||||
assertThat(buildpack.getCoordinates()).hasToString("example/buildpack1@0.0.1");
|
||||
this.testTarGzip.assertHasExpectedLayers(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenFileUrlReturnsBuildpack() throws Exception {
|
||||
Path compressedArchive = this.testTarGzip.createArchive();
|
||||
BuildpackReference reference = BuildpackReference.of("file://" + compressedArchive.toString());
|
||||
Buildpack buildpack = TarGzipBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack).isNotNull();
|
||||
assertThat(buildpack.getCoordinates()).hasToString("example/buildpack1@0.0.1");
|
||||
this.testTarGzip.assertHasExpectedLayers(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenArchiveWithoutDescriptorThrowsException() throws Exception {
|
||||
Path compressedArchive = this.testTarGzip.createEmptyArchive();
|
||||
BuildpackReference reference = BuildpackReference.of(compressedArchive.toString());
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> TarGzipBuildpack.resolve(this.resolverContext, reference))
|
||||
.withMessageContaining("Buildpack descriptor 'buildpack.toml' is required")
|
||||
.withMessageContaining(compressedArchive.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenArchiveWithDirectoryReturnsNull() {
|
||||
BuildpackReference reference = BuildpackReference.of(this.buildpackDir.getAbsolutePath());
|
||||
Buildpack buildpack = TarGzipBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenArchiveThatDoesNotExistReturnsNull() {
|
||||
BuildpackReference reference = BuildpackReference.of("/test/i/am/missing/buildpack.tar");
|
||||
Buildpack buildpack = TarGzipBuildpack.resolve(this.resolverContext, reference);
|
||||
assertThat(buildpack).isNull();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Layer;
|
||||
import org.springframework.boot.buildpack.platform.io.Content;
|
||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||
import org.springframework.boot.buildpack.platform.io.Layout;
|
||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||
|
||||
/**
|
||||
* A test {@link Buildpack}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class TestBuildpack implements Buildpack {
|
||||
|
||||
private final BuildpackCoordinates coordinates;
|
||||
|
||||
TestBuildpack(String id, String version) {
|
||||
this.coordinates = BuildpackCoordinates.of(id, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildpackCoordinates getCoordinates() {
|
||||
return this.coordinates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IOConsumer<Layer> layers) throws IOException {
|
||||
layers.accept(Layer.of(this::getContent));
|
||||
}
|
||||
|
||||
private void getContent(Layout layout) throws IOException {
|
||||
String id = this.coordinates.getSanitizedId();
|
||||
String dir = "/cnb/buildpacks/" + id + "/" + this.coordinates.getVersion();
|
||||
layout.file(dir + "/buildpack.toml", Owner.ROOT, Content.of("[test]"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.build;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Utility to create test tgz files.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class TestTarGzip {
|
||||
|
||||
private final File buildpackDir;
|
||||
|
||||
TestTarGzip(File buildpackDir) {
|
||||
this.buildpackDir = buildpackDir;
|
||||
}
|
||||
|
||||
Path createArchive() throws Exception {
|
||||
return createArchive(true);
|
||||
}
|
||||
|
||||
Path createEmptyArchive() throws Exception {
|
||||
return createArchive(false);
|
||||
}
|
||||
|
||||
private Path createArchive(boolean addContent) throws Exception {
|
||||
Path path = Paths.get(this.buildpackDir.getAbsolutePath(), "buildpack.tar");
|
||||
Path archive = Files.createFile(path);
|
||||
if (addContent) {
|
||||
writeBuildpackContentToArchive(archive);
|
||||
}
|
||||
return compressBuildpackArchive(archive);
|
||||
}
|
||||
|
||||
private Path compressBuildpackArchive(Path archive) throws Exception {
|
||||
Path tgzPath = Paths.get(this.buildpackDir.getAbsolutePath(), "buildpack.tgz");
|
||||
FileCopyUtils.copy(Files.newInputStream(archive),
|
||||
new GzipCompressorOutputStream(Files.newOutputStream(tgzPath)));
|
||||
return tgzPath;
|
||||
}
|
||||
|
||||
private void writeBuildpackContentToArchive(Path archive) throws Exception {
|
||||
StringBuilder buildpackToml = new StringBuilder();
|
||||
buildpackToml.append("[buildpack]\n");
|
||||
buildpackToml.append("id = \"example/buildpack1\"\n");
|
||||
buildpackToml.append("version = \"0.0.1\"\n");
|
||||
buildpackToml.append("name = \"Example buildpack\"\n");
|
||||
buildpackToml.append("homepage = \"https://github.com/example/example-buildpack\"\n");
|
||||
buildpackToml.append("[[stacks]]\n");
|
||||
buildpackToml.append("id = \"io.buildpacks.stacks.bionic\"\n");
|
||||
String detectScript = "#!/usr/bin/env bash\n" + "echo \"---> detect\"\n";
|
||||
String buildScript = "#!/usr/bin/env bash\n" + "echo \"---> build\"\n";
|
||||
try (TarArchiveOutputStream tar = new TarArchiveOutputStream(Files.newOutputStream(archive))) {
|
||||
writeEntry(tar, "buildpack.toml", buildpackToml.toString());
|
||||
writeEntry(tar, "bin/detect", detectScript);
|
||||
writeEntry(tar, "bin/build", buildScript);
|
||||
tar.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeEntry(TarArchiveOutputStream tar, String entryName, String content) throws IOException {
|
||||
TarArchiveEntry entry = new TarArchiveEntry(entryName);
|
||||
entry.setSize(content.length());
|
||||
tar.putArchiveEntry(entry);
|
||||
IOUtils.copy(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), tar);
|
||||
tar.closeArchiveEntry();
|
||||
}
|
||||
|
||||
void assertHasExpectedLayers(Buildpack buildpack) throws IOException {
|
||||
List<ByteArrayOutputStream> layers = new ArrayList<>();
|
||||
buildpack.apply((layer) -> {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
layer.writeTo(out);
|
||||
layers.add(out);
|
||||
});
|
||||
assertThat(layers).hasSize(1);
|
||||
byte[] content = layers.get(0).toByteArray();
|
||||
try (TarArchiveInputStream tar = new TarArchiveInputStream(new ByteArrayInputStream(content))) {
|
||||
assertThat(tar.getNextEntry().getName())
|
||||
.isEqualTo("cnb/buildpacks/example_buildpack1/0.0.1/buildpack.toml");
|
||||
assertThat(tar.getNextEntry().getName()).isEqualTo("cnb/buildpacks/example_buildpack1/0.0.1/bin/detect");
|
||||
assertThat(tar.getNextEntry().getName()).isEqualTo("cnb/buildpacks/example_buildpack1/0.0.1/bin/build");
|
||||
assertThat(tar.getNextEntry()).isNull();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -48,6 +51,8 @@ import org.springframework.boot.buildpack.platform.io.Content;
|
||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
@@ -304,6 +309,45 @@ class DockerApiTests {
|
||||
assertThat(image.getLayers()).hasSize(46);
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportLayersWhenReferenceIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.api.exportLayers(null, (name, archive) -> {
|
||||
})).withMessage("Reference must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportLayersWhenExportsIsNullThrowsException() {
|
||||
ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base");
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.api.exportLayers(reference, null))
|
||||
.withMessage("Exports must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportLayersExportsLayerTars() throws Exception {
|
||||
ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base");
|
||||
URI exportUri = new URI(IMAGES_URL + "/gcr.io/paketo-buildpacks/builder:base/get");
|
||||
given(DockerApiTests.this.http.get(exportUri)).willReturn(responseOf("export.tar"));
|
||||
MultiValueMap<String, String> contents = new LinkedMultiValueMap<>();
|
||||
this.api.exportLayers(reference, (name, archive) -> {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
archive.writeTo(out);
|
||||
try (TarArchiveInputStream in = new TarArchiveInputStream(
|
||||
new ByteArrayInputStream(out.toByteArray()))) {
|
||||
TarArchiveEntry entry = in.getNextTarEntry();
|
||||
while (entry != null) {
|
||||
contents.add(name, entry.getName());
|
||||
entry = in.getNextTarEntry();
|
||||
}
|
||||
}
|
||||
});
|
||||
assertThat(contents).hasSize(3).containsKeys(
|
||||
"1bf6c63a1e9ed1dd7cb961273bf60b8e0f440361faf273baf866f408e4910601/layer.tar",
|
||||
"8fdfb915302159a842cbfae6faec5311b00c071ebf14e12da7116ae7532e9319/layer.tar",
|
||||
"93cd584bb189bfca4f51744bd19d836fd36da70710395af5a1523ee88f208c6a/layer.tar");
|
||||
assertThat(contents.get("1bf6c63a1e9ed1dd7cb961273bf60b8e0f440361faf273baf866f408e4910601/layer.tar"))
|
||||
.containsExactly("etc/", "etc/apt/", "etc/apt/sources.list");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
@@ -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.
|
||||
@@ -25,6 +25,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* Tests for {@link ProgressUpdateEvent}.
|
||||
*
|
||||
* @param <E> The event type
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.nio.file.attribute.PosixFilePermissions;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
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 FilePermissions}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class FilePermissionsTests {
|
||||
|
||||
@Test
|
||||
void posixPermissionsToUmask() {
|
||||
Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("rwxrw-r--");
|
||||
assertThat(FilePermissions.posixPermissionsToUmask(permissions)).isEqualTo(0764);
|
||||
}
|
||||
|
||||
@Test
|
||||
void posixPermissionsToUmaskWithEmptyPermissions() {
|
||||
Set<PosixFilePermission> permissions = Collections.emptySet();
|
||||
assertThat(FilePermissions.posixPermissionsToUmask(permissions)).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void posixPermissionsToUmaskWithNullPermissions() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> FilePermissions.posixPermissionsToUmask(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.
|
||||
@@ -31,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* Tests for {@link TarLayoutWriter}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class TarLayoutWriterTests {
|
||||
|
||||
@@ -39,7 +40,7 @@ class TarLayoutWriterTests {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (TarLayoutWriter writer = new TarLayoutWriter(outputStream)) {
|
||||
writer.directory("/foo", Owner.ROOT);
|
||||
writer.file("/foo/bar.txt", Owner.of(1, 1), Content.of("test"));
|
||||
writer.file("/foo/bar.txt", Owner.of(1, 1), 0777, Content.of("test"));
|
||||
}
|
||||
try (TarArchiveInputStream tarInputStream = new TarArchiveInputStream(
|
||||
new ByteArrayInputStream(outputStream.toByteArray()))) {
|
||||
@@ -54,7 +55,7 @@ class TarLayoutWriterTests {
|
||||
assertThat(directoryEntry.getLongGroupId()).isEqualTo(0);
|
||||
assertThat(directoryEntry.getModTime()).isEqualTo(new Date(TarLayoutWriter.NORMALIZED_MOD_TIME));
|
||||
assertThat(fileEntry.getName()).isEqualTo("/foo/bar.txt");
|
||||
assertThat(fileEntry.getMode()).isEqualTo(0644);
|
||||
assertThat(fileEntry.getMode()).isEqualTo(0777);
|
||||
assertThat(fileEntry.getLongUserId()).isEqualTo(1);
|
||||
assertThat(fileEntry.getLongGroupId()).isEqualTo(1);
|
||||
assertThat(fileEntry.getModTime()).isEqualTo(new Date(TarLayoutWriter.NORMALIZED_MOD_TIME));
|
||||
|
||||
@@ -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.
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.json;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@@ -26,6 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* Abstract base class for JSON based tests.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
public abstract class AbstractJsonTests {
|
||||
|
||||
@@ -39,4 +44,9 @@ public abstract class AbstractJsonTests {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected final String getContentAsString(String name) {
|
||||
return new BufferedReader(new InputStreamReader(getContent(name), StandardCharsets.UTF_8)).lines()
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,124 +2,174 @@
|
||||
"description": "Ubuntu bionic base image with buildpacks for Java, NodeJS and Golang",
|
||||
"buildpacks": [
|
||||
{
|
||||
"id": "org.cloudfoundry.googlestackdriver",
|
||||
"version": "v1.1.11"
|
||||
"id": "paketo-buildpacks/dotnet-core",
|
||||
"version": "0.0.9",
|
||||
"homepage": "https://github.com/paketo-buildpacks/dotnet-core"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.springboot",
|
||||
"version": "v1.2.13"
|
||||
"id": "paketo-buildpacks/dotnet-core-runtime",
|
||||
"version": "0.0.201",
|
||||
"homepage": "https://github.com/paketo-buildpacks/dotnet-core-runtime"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.debug",
|
||||
"version": "v1.2.11"
|
||||
"id": "paketo-buildpacks/dotnet-core-sdk",
|
||||
"version": "0.0.196",
|
||||
"homepage": "https://github.com/paketo-buildpacks/dotnet-core-sdk"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.tomcat",
|
||||
"version": "v1.3.18"
|
||||
"id": "paketo-buildpacks/dotnet-execute",
|
||||
"version": "0.0.180",
|
||||
"homepage": "https://github.com/paketo-buildpacks/dotnet-execute"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.go",
|
||||
"version": "v0.0.4"
|
||||
"id": "paketo-buildpacks/dotnet-publish",
|
||||
"version": "0.0.121",
|
||||
"homepage": "https://github.com/paketo-buildpacks/dotnet-publish"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.openjdk",
|
||||
"version": "v1.2.14"
|
||||
"id": "paketo-buildpacks/dotnet-core-aspnet",
|
||||
"version": "0.0.196",
|
||||
"homepage": "https://github.com/paketo-buildpacks/dotnet-core-aspnet"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.buildsystem",
|
||||
"version": "v1.2.15"
|
||||
"id": "paketo-buildpacks/java-native-image",
|
||||
"version": "4.7.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/java-native-image"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.jvmapplication",
|
||||
"version": "v1.1.12"
|
||||
"id": "paketo-buildpacks/spring-boot",
|
||||
"version": "3.5.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/spring-boot"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.springautoreconfiguration",
|
||||
"version": "v1.1.11"
|
||||
"id": "paketo-buildpacks/executable-jar",
|
||||
"version": "3.1.3",
|
||||
"homepage": "https://github.com/paketo-buildpacks/executable-jar"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.archiveexpanding",
|
||||
"version": "v1.0.102"
|
||||
"id": "paketo-buildpacks/graalvm",
|
||||
"version": "4.1.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/graalvm"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.jmx",
|
||||
"version": "v1.1.12"
|
||||
"id": "paketo-buildpacks/gradle",
|
||||
"version": "3.5.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/gradle"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.nodejs",
|
||||
"version": "v2.0.8"
|
||||
"id": "paketo-buildpacks/leiningen",
|
||||
"version": "1.2.1",
|
||||
"homepage": "https://github.com/paketo-buildpacks/leiningen"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.jdbc",
|
||||
"version": "v1.1.14"
|
||||
"id": "paketo-buildpacks/procfile",
|
||||
"version": "3.0.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/procfile"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.procfile",
|
||||
"version": "v1.1.12"
|
||||
"id": "paketo-buildpacks/sbt",
|
||||
"version": "3.6.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/sbt"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.dotnet-core",
|
||||
"version": "v0.0.6"
|
||||
"id": "paketo-buildpacks/spring-boot-native-image",
|
||||
"version": "2.0.1",
|
||||
"homepage": "https://github.com/paketo-buildpacks/spring-boot-native-image"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.azureapplicationinsights",
|
||||
"version": "v1.1.12"
|
||||
"id": "paketo-buildpacks/environment-variables",
|
||||
"version": "2.1.2",
|
||||
"homepage": "https://github.com/paketo-buildpacks/environment-variables"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.distzip",
|
||||
"version": "v1.1.12"
|
||||
"id": "paketo-buildpacks/image-labels",
|
||||
"version": "2.0.7",
|
||||
"homepage": "https://github.com/paketo-buildpacks/image-labels"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.dep",
|
||||
"version": "0.0.101"
|
||||
"id": "paketo-buildpacks/maven",
|
||||
"version": "3.2.1",
|
||||
"homepage": "https://github.com/paketo-buildpacks/maven"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.go-compiler",
|
||||
"version": "0.0.105"
|
||||
"id": "paketo-buildpacks/java",
|
||||
"version": "4.10.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/java"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.go-mod",
|
||||
"version": "0.0.89"
|
||||
"id": "paketo-buildpacks/ca-certificates",
|
||||
"version": "1.0.1",
|
||||
"homepage": "https://github.com/paketo-buildpacks/ca-certificates"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.node-engine",
|
||||
"version": "0.0.163"
|
||||
"id": "paketo-buildpacks/environment-variables",
|
||||
"version": "2.1.2",
|
||||
"homepage": "https://github.com/paketo-buildpacks/environment-variables"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.npm",
|
||||
"version": "0.1.3"
|
||||
"id": "paketo-buildpacks/executable-jar",
|
||||
"version": "3.1.3",
|
||||
"homepage": "https://github.com/paketo-buildpacks/executable-jar"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.yarn-install",
|
||||
"version": "0.1.10"
|
||||
"id": "paketo-buildpacks/procfile",
|
||||
"version": "3.0.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/procfile"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.dotnet-core-aspnet",
|
||||
"version": "0.0.118"
|
||||
"id": "paketo-buildpacks/apache-tomcat",
|
||||
"version": "3.2.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/apache-tomcat"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.dotnet-core-build",
|
||||
"version": "0.0.68"
|
||||
"id": "paketo-buildpacks/gradle",
|
||||
"version": "3.5.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/gradle"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.dotnet-core-conf",
|
||||
"version": "0.0.115"
|
||||
"id": "paketo-buildpacks/maven",
|
||||
"version": "3.2.1",
|
||||
"homepage": "https://github.com/paketo-buildpacks/maven"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.dotnet-core-runtime",
|
||||
"version": "0.0.127"
|
||||
"id": "paketo-buildpacks/sbt",
|
||||
"version": "3.6.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/sbt"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.dotnet-core-sdk",
|
||||
"version": "0.0.122"
|
||||
"id": "paketo-buildpacks/bellsoft-liberica",
|
||||
"version": "6.2.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/bellsoft-liberica"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.icu",
|
||||
"version": "0.0.43"
|
||||
"id": "paketo-buildpacks/image-labels",
|
||||
"version": "2.0.7",
|
||||
"homepage": "https://github.com/paketo-buildpacks/image-labels"
|
||||
},
|
||||
{
|
||||
"id": "org.cloudfoundry.node-engine",
|
||||
"version": "0.0.158"
|
||||
"id": "paketo-buildpacks/debug",
|
||||
"version": "2.1.4",
|
||||
"homepage": "https://github.com/paketo-buildpacks/debug"
|
||||
},
|
||||
{
|
||||
"id": "paketo-buildpacks/dist-zip",
|
||||
"version": "2.2.2",
|
||||
"homepage": "https://github.com/paketo-buildpacks/dist-zip"
|
||||
},
|
||||
{
|
||||
"id": "paketo-buildpacks/spring-boot",
|
||||
"version": "3.5.0",
|
||||
"homepage": "https://github.com/paketo-buildpacks/spring-boot"
|
||||
},
|
||||
{
|
||||
"id": "paketo-buildpacks/jmx",
|
||||
"version": "2.1.4",
|
||||
"homepage": "https://github.com/paketo-buildpacks/jmx"
|
||||
},
|
||||
{
|
||||
"id": "paketo-buildpacks/leiningen",
|
||||
"version": "1.2.1",
|
||||
"homepage": "https://github.com/paketo-buildpacks/leiningen"
|
||||
}
|
||||
],
|
||||
"stack": {
|
||||
@@ -139,4 +189,4 @@
|
||||
"name": "Pack CLI",
|
||||
"version": "v0.9.0 (git sha: d42c384a39f367588f2653f2a99702db910e5ad7)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"Id": "sha256:a266647e285b52403b556adc963f1809556aa999f2f694e8dc54098c570ee55a",
|
||||
"RepoTags": [
|
||||
"example/hello-universe:latest"
|
||||
],
|
||||
"RepoDigests": [],
|
||||
"Parent": "",
|
||||
"Comment": "",
|
||||
"Created": "1980-01-01T00:00:01Z",
|
||||
"Container": "",
|
||||
"ContainerConfig": {
|
||||
"Hostname": "",
|
||||
"Domainname": "",
|
||||
"User": "",
|
||||
"AttachStdin": false,
|
||||
"AttachStdout": false,
|
||||
"AttachStderr": false,
|
||||
"Tty": false,
|
||||
"OpenStdin": false,
|
||||
"StdinOnce": false,
|
||||
"Env": null,
|
||||
"Cmd": null,
|
||||
"Image": "",
|
||||
"Volumes": null,
|
||||
"WorkingDir": "",
|
||||
"Entrypoint": null,
|
||||
"OnBuild": null,
|
||||
"Labels": null
|
||||
},
|
||||
"DockerVersion": "",
|
||||
"Author": "",
|
||||
"Config": {
|
||||
"Hostname": "",
|
||||
"Domainname": "",
|
||||
"User": "",
|
||||
"AttachStdin": false,
|
||||
"AttachStdout": false,
|
||||
"AttachStderr": false,
|
||||
"Tty": false,
|
||||
"OpenStdin": false,
|
||||
"StdinOnce": false,
|
||||
"Env": null,
|
||||
"Cmd": null,
|
||||
"Image": "",
|
||||
"Volumes": null,
|
||||
"WorkingDir": "",
|
||||
"Entrypoint": null,
|
||||
"OnBuild": null,
|
||||
"Labels": {
|
||||
"io.buildpacks.buildpackage.metadata": "{\"id\":\"example/hello-universe\",\"version\":\"0.0.1\",\"homepage\":\"https://github.com/buildpacks/example/tree/main/buildpacks/hello-universe\",\"stacks\":[{\"id\":\"io.buildpacks.example.stacks.alpine\"},{\"id\":\"io.buildpacks.stacks.bionic\"}]}",
|
||||
"io.buildpacks.buildpack.layers": "{\"example/hello-moon\":{\"0.0.3\":{\"api\":\"0.2\",\"stacks\":[{\"id\":\"io.buildpacks.stacks.alpine\"},{\"id\":\"io.buildpacks.stacks.bionic\"}],\"layerDiffID\":\"sha256:4bfdc8714aee68da6662c43bc28d3b41202c88e915641c356523dabe729814c2\",\"homepage\":\"https://github.com/example/tree/main/buildpacks/hello-moon\"}},\"example/hello-universe\":{\"0.0.1\":{\"api\":\"0.2\",\"order\":[{\"group\":[{\"id\":\"example/hello-world\",\"version\":\"0.0.2\"},{\"id\":\"example/hello-moon\",\"version\":\"0.0.2\"}]}],\"layerDiffID\":\"sha256:739b4e8f3caae7237584a1bfe029ebdb05403752b1a60a4f9be991b1d51dbb69\",\"homepage\":\"https://github.com/example/tree/main/buildpacks/hello-universe\"}},\"example/hello-world\":{\"0.0.2\":{\"api\":\"0.2\",\"stacks\":[{\"id\":\"io.buildpacks.stacks.alpine\"},{\"id\":\"io.buildpacks.stacks.bionic\"}],\"layerDiffID\":\"sha256:f752fe099c846e501bdc991d1a22f98c055ddc62f01cfc0495fff2c69f8eb940\",\"homepage\":\"https://github.com/example/tree/main/buildpacks/hello-world\"}}}"
|
||||
}
|
||||
},
|
||||
"Architecture": "amd64",
|
||||
"Os": "linux",
|
||||
"Size": 4654,
|
||||
"VirtualSize": 4654,
|
||||
"GraphDriver": {
|
||||
"Data": {
|
||||
"LowerDir": "/var/lib/docker/overlay2/cbf39b4508463beeb1d0a553c3e2baa84b8cd8dbc95681aaecc243e3ca77bcf4/diff:/var/lib/docker/overlay2/15e3d01b65c962b50a3da1b6663b8196284fb3c7e7f8497f2c1a0a736d0ec237/diff",
|
||||
"MergedDir": "/var/lib/docker/overlay2/1425ea68b0daff01bcc32e55e09eeeada2318d7dd1dc4e184711359da8425bb7/merged",
|
||||
"UpperDir": "/var/lib/docker/overlay2/1425ea68b0daff01bcc32e55e09eeeada2318d7dd1dc4e184711359da8425bb7/diff",
|
||||
"WorkDir": "/var/lib/docker/overlay2/1425ea68b0daff01bcc32e55e09eeeada2318d7dd1dc4e184711359da8425bb7/work"
|
||||
},
|
||||
"Name": "overlay2"
|
||||
},
|
||||
"RootFS": {
|
||||
"Type": "layers",
|
||||
"Layers": [
|
||||
"sha256:4bfdc8714aee68da6662c43bc28d3b41202c88e915641c356523dabe729814c2",
|
||||
"sha256:f752fe099c846e501bdc991d1a22f98c055ddc62f01cfc0495fff2c69f8eb940",
|
||||
"sha256:739b4e8f3caae7237584a1bfe029ebdb05403752b1a60a4f9be991b1d51dbb69"
|
||||
]
|
||||
},
|
||||
"Metadata": {
|
||||
"LastTagTime": "2021-01-27T22:56:06.4599859Z"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "example/hello-universe",
|
||||
"version": "0.0.1",
|
||||
"homepage": "https://github.com/example/tree/main/buildpacks/hello-universe",
|
||||
"stacks": [
|
||||
{
|
||||
"id": "io.buildpacks.stacks.alpine"
|
||||
},
|
||||
{
|
||||
"id": "io.buildpacks.stacks.bionic"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
[buildpack]
|
||||
id = "test";
|
||||
version = "1.0.0"
|
||||
name = "Example buildpack"
|
||||
homepage = "https://github.com/example/example-buildpack"
|
||||
|
||||
[[stacks]]
|
||||
id = "io.buildpacks.stacks.bionic"
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1,15 @@
|
||||
[[order]]
|
||||
group = [
|
||||
{ id = "example/buildpack1", version = "0.0.1" }
|
||||
]
|
||||
|
||||
[[order]]
|
||||
group = [
|
||||
{ id = "example/buildpack2", version = "0.0.2" }
|
||||
]
|
||||
|
||||
[[order]]
|
||||
group = [
|
||||
{ id = "example/buildpack3", version = "0.0.3" }
|
||||
]
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[[order]]
|
||||
group = [
|
||||
{ id = "example/buildpack1" }
|
||||
]
|
||||
|
||||
[[order]]
|
||||
group = [
|
||||
{ id = "example/buildpack2" }
|
||||
]
|
||||
|
||||
[[order]]
|
||||
group = [
|
||||
{ id = "example/buildpack3" }
|
||||
]
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user