commit
e49597c1ae
|
|
@ -107,7 +107,8 @@ public class Builder {
|
|||
Image runImage = imageFetcher.fetchImage(ImageType.RUNNER, request.getRunImage());
|
||||
assertStackIdsMatch(runImage, builderImage);
|
||||
BuildOwner buildOwner = BuildOwner.fromEnv(builderImage.getConfig().getEnv());
|
||||
Buildpacks buildpacks = getBuildpacks(request, imageFetcher, builderMetadata);
|
||||
BuildpackLayersMetadata buildpackLayersMetadata = BuildpackLayersMetadata.fromImage(builderImage);
|
||||
Buildpacks buildpacks = getBuildpacks(request, imageFetcher, builderMetadata, buildpackLayersMetadata);
|
||||
EphemeralBuilder ephemeralBuilder = new EphemeralBuilder(buildOwner, builderImage, request.getName(),
|
||||
builderMetadata, request.getCreator(), request.getEnv(), buildpacks);
|
||||
this.docker.image().load(ephemeralBuilder.getArchive(), UpdateListener.none());
|
||||
|
|
@ -143,8 +144,10 @@ public class Builder {
|
|||
+ "' does not match builder stack '" + builderImageStackId + "'");
|
||||
}
|
||||
|
||||
private Buildpacks getBuildpacks(BuildRequest request, ImageFetcher imageFetcher, BuilderMetadata builderMetadata) {
|
||||
BuildpackResolverContext resolverContext = new BuilderResolverContext(imageFetcher, builderMetadata);
|
||||
private Buildpacks getBuildpacks(BuildRequest request, ImageFetcher imageFetcher, BuilderMetadata builderMetadata,
|
||||
BuildpackLayersMetadata buildpackLayersMetadata) {
|
||||
BuildpackResolverContext resolverContext = new BuilderResolverContext(imageFetcher, builderMetadata,
|
||||
buildpackLayersMetadata);
|
||||
return BuildpackResolvers.resolveAll(resolverContext, request.getBuildpacks());
|
||||
}
|
||||
|
||||
|
|
@ -245,9 +248,13 @@ public class Builder {
|
|||
|
||||
private final BuilderMetadata builderMetadata;
|
||||
|
||||
BuilderResolverContext(ImageFetcher imageFetcher, BuilderMetadata builderMetadata) {
|
||||
private final BuildpackLayersMetadata buildpackLayersMetadata;
|
||||
|
||||
BuilderResolverContext(ImageFetcher imageFetcher, BuilderMetadata builderMetadata,
|
||||
BuildpackLayersMetadata buildpackLayersMetadata) {
|
||||
this.imageFetcher = imageFetcher;
|
||||
this.builderMetadata = builderMetadata;
|
||||
this.buildpackLayersMetadata = buildpackLayersMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -255,6 +262,11 @@ public class Builder {
|
|||
return this.builderMetadata.getBuildpacks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildpackLayersMetadata getBuildpackLayersMetadata() {
|
||||
return this.buildpackLayersMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image fetchImage(ImageReference reference, ImageType imageType) throws IOException {
|
||||
return this.imageFetcher.fetchImage(imageType, reference);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright 2012-2022 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.lang.invoke.MethodHandles;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
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.MappedObject;
|
||||
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Buildpack layers metadata information.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
final class BuildpackLayersMetadata extends MappedObject {
|
||||
|
||||
private static final String LABEL_NAME = "io.buildpacks.buildpack.layers";
|
||||
|
||||
private final Buildpacks buildpacks;
|
||||
|
||||
private BuildpackLayersMetadata(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
this.buildpacks = Buildpacks.fromJson(getNode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the metadata details of a buildpack with the given ID and version.
|
||||
* @param id the buildpack ID
|
||||
* @param version the buildpack version
|
||||
* @return the buildpack details or {@code null} if a buildpack with the given ID and
|
||||
* version does not exist in the metadata
|
||||
*/
|
||||
BuildpackLayerDetails getBuildpack(String id, String version) {
|
||||
return this.buildpacks.getBuildpack(id, version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link BuildpackLayersMetadata} from an image.
|
||||
* @param image the source image
|
||||
* @return the buildpack layers metadata
|
||||
* @throws IOException on IO error
|
||||
*/
|
||||
static BuildpackLayersMetadata fromImage(Image image) throws IOException {
|
||||
Assert.notNull(image, "Image must not be null");
|
||||
return fromImageConfig(image.getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link BuildpackLayersMetadata} from image config.
|
||||
* @param imageConfig the source image config
|
||||
* @return the buildpack layers metadata
|
||||
* @throws IOException on IO error
|
||||
*/
|
||||
static BuildpackLayersMetadata fromImageConfig(ImageConfig imageConfig) throws IOException {
|
||||
Assert.notNull(imageConfig, "ImageConfig must not be null");
|
||||
String json = imageConfig.getLabels().get(LABEL_NAME);
|
||||
Assert.notNull(json, () -> "No '" + LABEL_NAME + "' label found in image config labels '"
|
||||
+ StringUtils.collectionToCommaDelimitedString(imageConfig.getLabels().keySet()) + "'");
|
||||
return fromJson(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link BuildpackLayersMetadata} from JSON.
|
||||
* @param json the source JSON
|
||||
* @return the buildpack layers metadata
|
||||
* @throws IOException on IO error
|
||||
*/
|
||||
static BuildpackLayersMetadata fromJson(String json) throws IOException {
|
||||
return fromJson(SharedObjectMapper.get().readTree(json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link BuildpackLayersMetadata} from JSON.
|
||||
* @param node the source JSON
|
||||
* @return the buildpack layers metadata
|
||||
*/
|
||||
static BuildpackLayersMetadata fromJson(JsonNode node) {
|
||||
return new BuildpackLayersMetadata(node);
|
||||
}
|
||||
|
||||
private static class Buildpacks {
|
||||
|
||||
private final Map<String, BuildpackVersions> buildpacks = new HashMap<>();
|
||||
|
||||
private BuildpackLayerDetails getBuildpack(String id, String version) {
|
||||
if (this.buildpacks.containsKey(id)) {
|
||||
return this.buildpacks.get(id).getBuildpack(version);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addBuildpackVersions(String id, BuildpackVersions versions) {
|
||||
this.buildpacks.put(id, versions);
|
||||
}
|
||||
|
||||
private static Buildpacks fromJson(JsonNode node) {
|
||||
Buildpacks buildpacks = new Buildpacks();
|
||||
node.fields().forEachRemaining((field) -> buildpacks.addBuildpackVersions(field.getKey(),
|
||||
BuildpackVersions.fromJson(field.getValue())));
|
||||
return buildpacks;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class BuildpackVersions {
|
||||
|
||||
private final Map<String, BuildpackLayerDetails> versions = new HashMap<>();
|
||||
|
||||
private BuildpackLayerDetails getBuildpack(String version) {
|
||||
return this.versions.get(version);
|
||||
}
|
||||
|
||||
private void addBuildpackVersion(String version, BuildpackLayerDetails details) {
|
||||
this.versions.put(version, details);
|
||||
}
|
||||
|
||||
private static BuildpackVersions fromJson(JsonNode node) {
|
||||
BuildpackVersions versions = new BuildpackVersions();
|
||||
node.fields().forEachRemaining((field) -> versions.addBuildpackVersion(field.getKey(),
|
||||
BuildpackLayerDetails.fromJson(field.getValue())));
|
||||
return versions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class BuildpackLayerDetails extends MappedObject {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String homepage;
|
||||
|
||||
private final String layerDiffId;
|
||||
|
||||
private BuildpackLayerDetails(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
this.name = valueAt("/name", String.class);
|
||||
this.homepage = valueAt("/homepage", String.class);
|
||||
this.layerDiffId = valueAt("/layerDiffID", String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buildpack name.
|
||||
* @return the name
|
||||
*/
|
||||
String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buildpack homepage address.
|
||||
* @return the homepage address
|
||||
*/
|
||||
String getHomepage() {
|
||||
return this.homepage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buildpack layer {@code diffID}.
|
||||
* @return the layer {@code diffID}
|
||||
*/
|
||||
String getLayerDiffId() {
|
||||
return this.layerDiffId;
|
||||
}
|
||||
|
||||
private static BuildpackLayerDetails fromJson(JsonNode node) {
|
||||
return new BuildpackLayerDetails(node);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -34,6 +34,8 @@ interface BuildpackResolverContext {
|
|||
|
||||
List<BuildpackMetadata> getBuildpackMetadata();
|
||||
|
||||
BuildpackLayersMetadata getBuildpackLayersMetadata();
|
||||
|
||||
/**
|
||||
* Retrieve an image.
|
||||
* @param reference the image reference
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
|
|
@ -28,10 +28,12 @@ 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.springframework.boot.buildpack.platform.build.BuildpackLayersMetadata.BuildpackLayerDetails;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Layer;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.LayerId;
|
||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
|
@ -59,13 +61,28 @@ final class ImageBuildpack implements Buildpack {
|
|||
Image image = context.fetchImage(reference, ImageType.BUILDPACK);
|
||||
BuildpackMetadata buildpackMetadata = BuildpackMetadata.fromImage(image);
|
||||
this.coordinates = BuildpackCoordinates.fromBuildpackMetadata(buildpackMetadata);
|
||||
this.exportedLayers = new ExportedLayers(context, reference);
|
||||
if (!buildpackExistsInBuilder(context, image.getLayers())) {
|
||||
this.exportedLayers = new ExportedLayers(context, reference);
|
||||
}
|
||||
else {
|
||||
this.exportedLayers = null;
|
||||
}
|
||||
}
|
||||
catch (IOException | DockerEngineException ex) {
|
||||
throw new IllegalArgumentException("Error pulling buildpack image '" + reference + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean buildpackExistsInBuilder(BuildpackResolverContext context, List<LayerId> imageLayers) {
|
||||
BuildpackLayerDetails buildpackLayerDetails = context.getBuildpackLayersMetadata()
|
||||
.getBuildpack(this.coordinates.getId(), this.coordinates.getVersion());
|
||||
if (buildpackLayerDetails != null) {
|
||||
String layerDiffId = buildpackLayerDetails.getLayerDiffId();
|
||||
return imageLayers.stream().map(LayerId::toString).anyMatch((layerId) -> layerId.equals(layerDiffId));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildpackCoordinates getCoordinates() {
|
||||
return this.coordinates;
|
||||
|
|
@ -73,7 +90,9 @@ final class ImageBuildpack implements Buildpack {
|
|||
|
||||
@Override
|
||||
public void apply(IOConsumer<Layer> layers) throws IOException {
|
||||
this.exportedLayers.apply(layers);
|
||||
if (this.exportedLayers != null) {
|
||||
this.exportedLayers.apply(layers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2012-2022 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 BuildpackLayersMetadata}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class BuildpackLayersMetadataTests extends AbstractJsonTests {
|
||||
|
||||
@Test
|
||||
void fromImageLoadsMetadata() throws IOException {
|
||||
Image image = Image.of(getContent("buildpack-image.json"));
|
||||
BuildpackLayersMetadata metadata = BuildpackLayersMetadata.fromImage(image);
|
||||
assertThat(metadata.getBuildpack("example/hello-moon", "0.0.3")).extracting("homepage", "layerDiffId")
|
||||
.containsExactly("https://github.com/example/tree/main/buildpacks/hello-moon",
|
||||
"sha256:4bfdc8714aee68da6662c43bc28d3b41202c88e915641c356523dabe729814c2");
|
||||
assertThat(metadata.getBuildpack("example/hello-world", "0.0.2")).extracting("homepage", "layerDiffId")
|
||||
.containsExactly("https://github.com/example/tree/main/buildpacks/hello-world",
|
||||
"sha256:f752fe099c846e501bdc991d1a22f98c055ddc62f01cfc0495fff2c69f8eb940");
|
||||
assertThat(metadata.getBuildpack("example/hello-world", "version-does-not-exist")).isNull();
|
||||
assertThat(metadata.getBuildpack("id-does-not-exist", "9.9.9")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromImageWhenImageIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackLayersMetadata.fromImage(null))
|
||||
.withMessage("Image must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromImageWhenImageConfigIsNullThrowsException() {
|
||||
Image image = mock(Image.class);
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> BuildpackLayersMetadata.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(() -> BuildpackLayersMetadata.fromImage(image))
|
||||
.withMessage("No 'io.buildpacks.buildpack.layers' label found in image config labels 'alpha'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromJsonLoadsMetadata() throws IOException {
|
||||
BuildpackLayersMetadata metadata = BuildpackLayersMetadata
|
||||
.fromJson(getContentAsString("buildpack-layers-metadata.json"));
|
||||
assertThat(metadata.getBuildpack("example/hello-moon", "0.0.3")).extracting("name", "homepage", "layerDiffId")
|
||||
.containsExactly("Example hello-moon buildpack",
|
||||
"https://github.com/example/tree/main/buildpacks/hello-moon",
|
||||
"sha256:4bfdc8714aee68da6662c43bc28d3b41202c88e915641c356523dabe729814c2");
|
||||
assertThat(metadata.getBuildpack("example/hello-world", "0.0.1")).extracting("name", "homepage", "layerDiffId")
|
||||
.containsExactly("Example hello-world buildpack",
|
||||
"https://github.com/example/tree/main/buildpacks/hello-world",
|
||||
"sha256:1c90e0b80d92555a0523c9ee6500845328fc39ba9dca9d30a877ff759ffbff28");
|
||||
assertThat(metadata.getBuildpack("example/hello-world", "0.0.2")).extracting("name", "homepage", "layerDiffId")
|
||||
.containsExactly("Example hello-world buildpack",
|
||||
"https://github.com/example/tree/main/buildpacks/hello-world",
|
||||
"sha256:f752fe099c846e501bdc991d1a22f98c055ddc62f01cfc0495fff2c69f8eb940");
|
||||
assertThat(metadata.getBuildpack("example/hello-world", "version-does-not-exist")).isNull();
|
||||
assertThat(metadata.getBuildpack("id-does-not-exist", "9.9.9")).isNull();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
|
|
@ -84,6 +84,7 @@ class BuildpackResolversTests extends AbstractJsonTests {
|
|||
void resolveAllWithImageBuildpackReferenceReturnsExpectedBuildpack() throws IOException {
|
||||
Image image = Image.of(getContent("buildpack-image.json"));
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(resolverContext.getBuildpackLayersMetadata()).willReturn(BuildpackLayersMetadata.fromJson("{}"));
|
||||
given(resolverContext.fetchImage(any(), any())).willReturn(image);
|
||||
BuildpackReference reference = BuildpackReference.of("docker://example/buildpack1:latest");
|
||||
Buildpacks buildpacks = BuildpackResolvers.resolveAll(resolverContext, Collections.singleton(reference));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
|
|
@ -64,29 +64,31 @@ class ImageBuildpackTests extends AbstractJsonTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenFullyQualifiedReferenceReturnsBuilder() throws Exception {
|
||||
void resolveWhenFullyQualifiedReferenceReturnsBuildpack() throws Exception {
|
||||
Image image = Image.of(getContent("buildpack-image.json"));
|
||||
ImageReference imageReference = ImageReference.of("example/buildpack1:1.0.0");
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(resolverContext.getBuildpackLayersMetadata()).willReturn(BuildpackLayersMetadata.fromJson("{}"));
|
||||
given(resolverContext.fetchImage(eq(imageReference), eq(ImageType.BUILDPACK))).willReturn(image);
|
||||
willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(eq(imageReference), any());
|
||||
BuildpackReference reference = BuildpackReference.of("docker://example/buildpack1:1.0.0");
|
||||
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
|
||||
assertThat(buildpack.getCoordinates()).hasToString("example/hello-universe@0.0.1");
|
||||
assertHasExpectedLayers(buildpack);
|
||||
assertAppliesExpectedLayers(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenUnqualifiedReferenceReturnsBuilder() throws Exception {
|
||||
void resolveWhenUnqualifiedReferenceReturnsBuildpack() throws Exception {
|
||||
Image image = Image.of(getContent("buildpack-image.json"));
|
||||
ImageReference imageReference = ImageReference.of("example/buildpack1:1.0.0");
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(resolverContext.getBuildpackLayersMetadata()).willReturn(BuildpackLayersMetadata.fromJson("{}"));
|
||||
given(resolverContext.fetchImage(eq(imageReference), eq(ImageType.BUILDPACK))).willReturn(image);
|
||||
willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(eq(imageReference), any());
|
||||
BuildpackReference reference = BuildpackReference.of("example/buildpack1:1.0.0");
|
||||
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
|
||||
assertThat(buildpack.getCoordinates()).hasToString("example/hello-universe@0.0.1");
|
||||
assertHasExpectedLayers(buildpack);
|
||||
assertAppliesExpectedLayers(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -94,12 +96,13 @@ class ImageBuildpackTests extends AbstractJsonTests {
|
|||
Image image = Image.of(getContent("buildpack-image.json"));
|
||||
ImageReference imageReference = ImageReference.of("example/buildpack1:latest");
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(resolverContext.getBuildpackLayersMetadata()).willReturn(BuildpackLayersMetadata.fromJson("{}"));
|
||||
given(resolverContext.fetchImage(eq(imageReference), eq(ImageType.BUILDPACK))).willReturn(image);
|
||||
willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(eq(imageReference), any());
|
||||
BuildpackReference reference = BuildpackReference.of("example/buildpack1");
|
||||
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
|
||||
assertThat(buildpack.getCoordinates()).hasToString("example/hello-universe@0.0.1");
|
||||
assertHasExpectedLayers(buildpack);
|
||||
assertAppliesExpectedLayers(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -108,12 +111,28 @@ class ImageBuildpackTests extends AbstractJsonTests {
|
|||
String digest = "sha256:4acb6bfd6c4f0cabaf7f3690e444afe51f1c7de54d51da7e63fac709c56f1c30";
|
||||
ImageReference imageReference = ImageReference.of("example/buildpack1@" + digest);
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(resolverContext.getBuildpackLayersMetadata()).willReturn(BuildpackLayersMetadata.fromJson("{}"));
|
||||
given(resolverContext.fetchImage(eq(imageReference), eq(ImageType.BUILDPACK))).willReturn(image);
|
||||
willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(eq(imageReference), any());
|
||||
BuildpackReference reference = BuildpackReference.of("example/buildpack1@" + digest);
|
||||
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
|
||||
assertThat(buildpack.getCoordinates()).hasToString("example/hello-universe@0.0.1");
|
||||
assertHasExpectedLayers(buildpack);
|
||||
assertAppliesExpectedLayers(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenBuildpackExistsInBuilderSkipsLayers() throws Exception {
|
||||
Image image = Image.of(getContent("buildpack-image.json"));
|
||||
ImageReference imageReference = ImageReference.of("example/buildpack1:1.0.0");
|
||||
BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class);
|
||||
given(resolverContext.getBuildpackLayersMetadata())
|
||||
.willReturn(BuildpackLayersMetadata.fromJson(getContentAsString("buildpack-layers-metadata.json")));
|
||||
given(resolverContext.fetchImage(eq(imageReference), eq(ImageType.BUILDPACK))).willReturn(image);
|
||||
willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(eq(imageReference), any());
|
||||
BuildpackReference reference = BuildpackReference.of("docker://example/buildpack1:1.0.0");
|
||||
Buildpack buildpack = ImageBuildpack.resolve(resolverContext, reference);
|
||||
assertThat(buildpack.getCoordinates()).hasToString("example/hello-universe@0.0.1");
|
||||
assertAppliesNoLayers(buildpack);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -181,7 +200,7 @@ class ImageBuildpackTests extends AbstractJsonTests {
|
|||
tarOut.closeArchiveEntry();
|
||||
}
|
||||
|
||||
private void assertHasExpectedLayers(Buildpack buildpack) throws IOException {
|
||||
private void assertAppliesExpectedLayers(Buildpack buildpack) throws IOException {
|
||||
List<ByteArrayOutputStream> layers = new ArrayList<>();
|
||||
buildpack.apply((layer) -> {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
|
@ -208,4 +227,14 @@ class ImageBuildpackTests extends AbstractJsonTests {
|
|||
TarArchiveEntry.DEFAULT_FILE_MODE));
|
||||
}
|
||||
|
||||
private void assertAppliesNoLayers(Buildpack buildpack) throws IOException {
|
||||
List<ByteArrayOutputStream> layers = new ArrayList<>();
|
||||
buildpack.apply((layer) -> {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
layer.writeTo(out);
|
||||
layers.add(out);
|
||||
});
|
||||
assertThat(layers).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"example/hello-moon": {
|
||||
"0.0.3": {
|
||||
"api": "0.2",
|
||||
"stacks": [
|
||||
{
|
||||
"id": "io.buildpacks.stacks.alpine"
|
||||
},
|
||||
{
|
||||
"id": "io.buildpacks.stacks.bionic"
|
||||
}
|
||||
],
|
||||
"name": "Example hello-moon buildpack",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": "Example hello-universe buildpack",
|
||||
"layerDiffID": "sha256:739b4e8f3caae7237584a1bfe029ebdb05403752b1a60a4f9be991b1d51dbb69",
|
||||
"homepage": "https://github.com/example/tree/main/buildpacks/hello-universe"
|
||||
}
|
||||
},
|
||||
"example/hello-world": {
|
||||
"0.0.1": {
|
||||
"api": "0.2",
|
||||
"stacks": [
|
||||
{
|
||||
"id": "io.buildpacks.stacks.alpine"
|
||||
},
|
||||
{
|
||||
"id": "io.buildpacks.stacks.bionic"
|
||||
}
|
||||
],
|
||||
"name": "Example hello-world buildpack",
|
||||
"layerDiffID": "sha256:1c90e0b80d92555a0523c9ee6500845328fc39ba9dca9d30a877ff759ffbff28",
|
||||
"homepage": "https://github.com/example/tree/main/buildpacks/hello-world"
|
||||
},
|
||||
"0.0.2": {
|
||||
"api": "0.2",
|
||||
"stacks": [
|
||||
{
|
||||
"id": "io.buildpacks.stacks.alpine"
|
||||
},
|
||||
{
|
||||
"id": "io.buildpacks.stacks.bionic"
|
||||
}
|
||||
],
|
||||
"name": "Example hello-world buildpack",
|
||||
"layerDiffID": "sha256:f752fe099c846e501bdc991d1a22f98c055ddc62f01cfc0495fff2c69f8eb940",
|
||||
"homepage": "https://github.com/example/tree/main/buildpacks/hello-world"
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue