From b0b2cd79b4ae9e4987fda219e42c90d1ab749284 Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Tue, 7 Dec 2021 14:10:10 -0600 Subject: [PATCH] Use latest buildpack image tag when no tag is provided Fixes gh-28921 --- .../platform/build/ImageBuildpack.java | 7 +-- .../platform/build/ImageBuildpackTests.java | 45 ++++++++++++++++--- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java index 7c26f9da5121..6b383bbfcfd1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java @@ -54,14 +54,15 @@ final class ImageBuildpack implements Buildpack { private final ExportedLayers exportedLayers; private ImageBuildpack(BuildpackResolverContext context, ImageReference imageReference) { + ImageReference reference = imageReference.inTaggedOrDigestForm(); try { - Image image = context.fetchImage(imageReference, ImageType.BUILDPACK); + Image image = context.fetchImage(reference, ImageType.BUILDPACK); BuildpackMetadata buildpackMetadata = BuildpackMetadata.fromImage(image); this.coordinates = BuildpackCoordinates.fromBuildpackMetadata(buildpackMetadata); - this.exportedLayers = new ExportedLayers(context, imageReference); + this.exportedLayers = new ExportedLayers(context, reference); } catch (IOException | DockerEngineException ex) { - throw new IllegalArgumentException("Error pulling buildpack image '" + imageReference + "'", ex); + throw new IllegalArgumentException("Error pulling buildpack image '" + reference + "'", ex); } } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/ImageBuildpackTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/ImageBuildpackTests.java index bbe213a247a1..a9195daa78f1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/ImageBuildpackTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/ImageBuildpackTests.java @@ -31,6 +31,7 @@ import org.mockito.invocation.InvocationOnMock; import org.springframework.boot.buildpack.platform.docker.type.Image; +import org.springframework.boot.buildpack.platform.docker.type.ImageReference; import org.springframework.boot.buildpack.platform.io.IOBiConsumer; import org.springframework.boot.buildpack.platform.io.TarArchive; import org.springframework.boot.buildpack.platform.json.AbstractJsonTests; @@ -40,6 +41,7 @@ import static org.assertj.core.api.Assertions.fail; import static org.assertj.core.api.Assertions.tuple; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willAnswer; import static org.mockito.Mockito.mock; @@ -64,10 +66,11 @@ void setUp() { @Test void resolveWhenFullyQualifiedReferenceReturnsBuilder() 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.fetchImage(any(), any())).willReturn(image); - willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(any(), any()); - BuildpackReference reference = BuildpackReference.of("docker://example/buildpack1:latest"); + 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); @@ -76,10 +79,38 @@ void resolveWhenFullyQualifiedReferenceReturnsBuilder() throws Exception { @Test void resolveWhenUnqualifiedReferenceReturnsBuilder() 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.fetchImage(any(), any())).willReturn(image); - willAnswer(this::withMockLayers).given(resolverContext).exportImageLayers(any(), any()); - BuildpackReference reference = BuildpackReference.of("example/buildpack1:latest"); + 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); + } + + @Test + void resolveReferenceWithoutTagUsesLatestTag() throws Exception { + Image image = Image.of(getContent("buildpack-image.json")); + ImageReference imageReference = ImageReference.of("example/buildpack1:latest"); + BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class); + 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); + } + + @Test + void resolveReferenceWithDigestUsesDigest() throws Exception { + Image image = Image.of(getContent("buildpack-image.json")); + String digest = "sha256:4acb6bfd6c4f0cabaf7f3690e444afe51f1c7de54d51da7e63fac709c56f1c30"; + ImageReference imageReference = ImageReference.of("example/buildpack1@" + digest); + BuildpackResolverContext resolverContext = mock(BuildpackResolverContext.class); + 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); @@ -89,7 +120,7 @@ void resolveWhenUnqualifiedReferenceReturnsBuilder() throws Exception { 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"); + BuildpackReference reference = BuildpackReference.of("docker://example/buildpack1"); assertThatIllegalArgumentException().isThrownBy(() -> ImageBuildpack.resolve(resolverContext, reference)) .withMessageContaining("Error pulling buildpack image") .withMessageContaining("example/buildpack1:latest");