From e294d26458a198f29f8562771c60081b49ddfceb Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Wed, 12 Feb 2020 13:55:54 -0600 Subject: [PATCH] Set ephemeral builder container creation to a fixed date This commit fixes the `Created` date and time of the ephemeral builder container image at the Windows epoch plus one second (1980-01-01T00:00:01Z). This date matches the created date of the builder image and influences the created date of the resulting image. Using a fixed date for images ensures that the digest is consistent for all images with the same version. Fixes gh-20126 --- .../platform/build/EphemeralBuilder.java | 18 ------------------ .../platform/docker/type/ImageArchive.java | 7 ++++++- .../platform/build/EphemeralBuilderTests.java | 19 ++++++++++++------- .../docker/type/ImageArchiveTests.java | 9 ++------- .../docker/type/image-archive-config.json | 2 +- .../docker/type/image-archive-manifest.json | 2 +- 6 files changed, 22 insertions(+), 35 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java index 5b1b43659e..39f9ddcb27 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java @@ -17,8 +17,6 @@ package org.springframework.boot.buildpack.platform.build; import java.io.IOException; -import java.time.Clock; -import java.time.Instant; import java.util.Map; import org.springframework.boot.buildpack.platform.docker.type.Image; @@ -55,21 +53,6 @@ class EphemeralBuilder { */ EphemeralBuilder(BuildOwner buildOwner, Image builderImage, BuilderMetadata builderMetadata, Creator creator, Map env) throws IOException { - this(Clock.systemUTC(), buildOwner, builderImage, builderMetadata, creator, env); - } - - /** - * Create a new {@link EphemeralBuilder} instance with a specific clock. - * @param clock the clock used for the current time - * @param buildOwner the build owner - * @param builderImage the image - * @param builderMetadata the builder metadata - * @param creator the builder creator - * @param env the builder env - * @throws IOException on IO error - */ - EphemeralBuilder(Clock clock, BuildOwner buildOwner, Image builderImage, BuilderMetadata builderMetadata, - Creator creator, Map env) throws IOException { ImageReference name = ImageReference.random("pack.local/builder/").inTaggedForm(); this.buildOwner = buildOwner; this.creator = creator; @@ -77,7 +60,6 @@ class EphemeralBuilder { this.archive = ImageArchive.from(builderImage, (update) -> { update.withUpdatedConfig(this.builderMetadata::attachTo); update.withTag(name); - update.withCreateDate(Instant.now(clock)); if (env != null && !env.isEmpty()) { update.withNewLayer(getEnvLayer(env)); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchive.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchive.java index e9baf846bd..01e2225604 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchive.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchive.java @@ -21,6 +21,7 @@ import java.io.OutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Instant; +import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -46,6 +47,7 @@ import org.springframework.util.Assert; * An image archive that can be loaded into Docker. * * @author Phillip Webb + * @author Scott Frederick * @since 2.3.0 * @see #from(Image, IOConsumer) * @see Docker Image @@ -53,6 +55,9 @@ import org.springframework.util.Assert; */ public class ImageArchive implements TarArchive { + private static final Instant WINDOWS_EPOCH_PLUS_SECOND = OffsetDateTime.of(1980, 1, 1, 0, 0, 1, 0, ZoneOffset.UTC) + .toInstant(); + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ISO_ZONED_DATE_TIME .withZone(ZoneOffset.UTC); @@ -248,7 +253,7 @@ public class ImageArchive implements TarArchive { private ImageArchive applyTo(IOConsumer update) throws IOException { update.accept(this); - Instant createDate = (this.createDate != null) ? this.createDate : Instant.now(); + Instant createDate = (this.createDate != null) ? this.createDate : WINDOWS_EPOCH_PLUS_SECOND; return new ImageArchive(SharedObjectMapper.get(), this.config, createDate, this.tag, this.image.getOs(), this.image.getLayers(), Collections.unmodifiableList(this.newLayers)); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java index 04f3304503..0649ba2ca2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java @@ -22,9 +22,9 @@ import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; -import java.time.Clock; import java.time.Instant; -import java.time.ZoneOffset; +import java.time.OffsetDateTime; +import java.time.ZoneId; import java.util.Collections; import java.util.Map; @@ -96,11 +96,16 @@ class EphemeralBuilderTests extends AbstractJsonTests { } @Test - void getArchiveHasCreateDate() throws Exception { - Clock clock = Clock.fixed(Instant.now(), ZoneOffset.UTC); - EphemeralBuilder builder = new EphemeralBuilder(clock, this.owner, this.image, this.metadata, this.creator, - this.env); - assertThat(builder.getArchive().getCreateDate()).isEqualTo(Instant.now(clock)); + void getArchiveHasFixedCreateDate() throws Exception { + EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env); + Instant createInstant = builder.getArchive().getCreateDate(); + OffsetDateTime createDateTime = OffsetDateTime.ofInstant(createInstant, ZoneId.of("UTC")); + assertThat(createDateTime.getYear()).isEqualTo(1980); + assertThat(createDateTime.getMonthValue()).isEqualTo(1); + assertThat(createDateTime.getDayOfMonth()).isEqualTo(1); + assertThat(createDateTime.getHour()).isEqualTo(0); + assertThat(createDateTime.getMinute()).isEqualTo(0); + assertThat(createDateTime.getSecond()).isEqualTo(1); } @Test diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveTests.java index ded72426f9..fa676f3a4a 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveTests.java @@ -20,9 +20,6 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; @@ -39,17 +36,15 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link ImageArchive}. * * @author Phillip Webb + * @author Scott Frederick */ class ImageArchiveTests extends AbstractJsonTests { - static final Instant CREATE_DATE = OffsetDateTime.of(1906, 12, 9, 11, 30, 0, 0, ZoneOffset.UTC).toInstant(); - @Test void fromImageWritesToValidArchiveTar() throws Exception { Image image = Image.of(getContent("image.json")); ImageArchive archive = ImageArchive.from(image, (update) -> { update.withNewLayer(Layer.of((layout) -> layout.folder("/spring", Owner.ROOT))); - update.withCreateDate(CREATE_DATE); update.withTag(ImageReference.of("pack.local/builder/6b7874626575656b6162")); }); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); @@ -78,7 +73,7 @@ class ImageArchiveTests extends AbstractJsonTests { } private void assertExpectedConfig(TarArchiveEntry entry, byte[] content) throws Exception { - assertThat(entry.getName()).isEqualTo("/d1872169d781cff5e1aa22d111f636bef0c57e1c358ca3861e3d33a5bdb1b4a5.json"); + assertThat(entry.getName()).isEqualTo("/682f8d24b9d9c313d1190a0e955dcb5e65ec9beea40420999839c6f0cbb38382.json"); String actualJson = new String(content, StandardCharsets.UTF_8); String expectedJson = StreamUtils.copyToString(getContent("image-archive-config.json"), StandardCharsets.UTF_8); JSONAssert.assertEquals(expectedJson, actualJson, false); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/docker/type/image-archive-config.json b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/docker/type/image-archive-config.json index bb03de98e1..395bbcda70 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/docker/type/image-archive-config.json +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/docker/type/image-archive-config.json @@ -25,7 +25,7 @@ "io.buildpacks.stack.id": "org.cloudfoundry.stacks.cflinuxfs3" } }, - "created": "1906-12-09T11:30:00Z", + "created": "1980-01-01T00:00:01Z", "history": [ { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/docker/type/image-archive-manifest.json b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/docker/type/image-archive-manifest.json index 9db29058c1..0e3a641407 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/docker/type/image-archive-manifest.json +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/docker/type/image-archive-manifest.json @@ -1,6 +1,6 @@ [ { - "Config": "/d1872169d781cff5e1aa22d111f636bef0c57e1c358ca3861e3d33a5bdb1b4a5.json", + "Config": "/682f8d24b9d9c313d1190a0e955dcb5e65ec9beea40420999839c6f0cbb38382.json", "Layers": [ "", "",