Skip to content

Commit e294d26

Browse files
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
1 parent 191dce3 commit e294d26

File tree

6 files changed

+22
-35
lines changed

6 files changed

+22
-35
lines changed

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
package org.springframework.boot.buildpack.platform.build;
1818

1919
import java.io.IOException;
20-
import java.time.Clock;
21-
import java.time.Instant;
2220
import java.util.Map;
2321

2422
import org.springframework.boot.buildpack.platform.docker.type.Image;
@@ -55,29 +53,13 @@ class EphemeralBuilder {
5553
*/
5654
EphemeralBuilder(BuildOwner buildOwner, Image builderImage, BuilderMetadata builderMetadata, Creator creator,
5755
Map<String, String> env) throws IOException {
58-
this(Clock.systemUTC(), buildOwner, builderImage, builderMetadata, creator, env);
59-
}
60-
61-
/**
62-
* Create a new {@link EphemeralBuilder} instance with a specific clock.
63-
* @param clock the clock used for the current time
64-
* @param buildOwner the build owner
65-
* @param builderImage the image
66-
* @param builderMetadata the builder metadata
67-
* @param creator the builder creator
68-
* @param env the builder env
69-
* @throws IOException on IO error
70-
*/
71-
EphemeralBuilder(Clock clock, BuildOwner buildOwner, Image builderImage, BuilderMetadata builderMetadata,
72-
Creator creator, Map<String, String> env) throws IOException {
7356
ImageReference name = ImageReference.random("pack.local/builder/").inTaggedForm();
7457
this.buildOwner = buildOwner;
7558
this.creator = creator;
7659
this.builderMetadata = builderMetadata.copy(this::updateMetadata);
7760
this.archive = ImageArchive.from(builderImage, (update) -> {
7861
update.withUpdatedConfig(this.builderMetadata::attachTo);
7962
update.withTag(name);
80-
update.withCreateDate(Instant.now(clock));
8163
if (env != null && !env.isEmpty()) {
8264
update.withNewLayer(getEnvLayer(env));
8365
}

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchive.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.security.MessageDigest;
2222
import java.security.NoSuchAlgorithmException;
2323
import java.time.Instant;
24+
import java.time.OffsetDateTime;
2425
import java.time.ZoneOffset;
2526
import java.time.format.DateTimeFormatter;
2627
import java.util.ArrayList;
@@ -46,13 +47,17 @@
4647
* An image archive that can be loaded into Docker.
4748
*
4849
* @author Phillip Webb
50+
* @author Scott Frederick
4951
* @since 2.3.0
5052
* @see #from(Image, IOConsumer)
5153
* @see <a href="https://github.com/moby/moby/blob/master/image/spec/v1.2.md">Docker Image
5254
* Specification</a>
5355
*/
5456
public class ImageArchive implements TarArchive {
5557

58+
private static final Instant WINDOWS_EPOCH_PLUS_SECOND = OffsetDateTime.of(1980, 1, 1, 0, 0, 1, 0, ZoneOffset.UTC)
59+
.toInstant();
60+
5661
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ISO_ZONED_DATE_TIME
5762
.withZone(ZoneOffset.UTC);
5863

@@ -248,7 +253,7 @@ private Update(Image image) {
248253

249254
private ImageArchive applyTo(IOConsumer<Update> update) throws IOException {
250255
update.accept(this);
251-
Instant createDate = (this.createDate != null) ? this.createDate : Instant.now();
256+
Instant createDate = (this.createDate != null) ? this.createDate : WINDOWS_EPOCH_PLUS_SECOND;
252257
return new ImageArchive(SharedObjectMapper.get(), this.config, createDate, this.tag, this.image.getOs(),
253258
this.image.getLayers(), Collections.unmodifiableList(this.newLayers));
254259
}

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
import java.io.FileOutputStream;
2323
import java.io.OutputStream;
2424
import java.nio.charset.StandardCharsets;
25-
import java.time.Clock;
2625
import java.time.Instant;
27-
import java.time.ZoneOffset;
26+
import java.time.OffsetDateTime;
27+
import java.time.ZoneId;
2828
import java.util.Collections;
2929
import java.util.Map;
3030

@@ -96,11 +96,16 @@ void getArchiveHasTag() throws Exception {
9696
}
9797

9898
@Test
99-
void getArchiveHasCreateDate() throws Exception {
100-
Clock clock = Clock.fixed(Instant.now(), ZoneOffset.UTC);
101-
EphemeralBuilder builder = new EphemeralBuilder(clock, this.owner, this.image, this.metadata, this.creator,
102-
this.env);
103-
assertThat(builder.getArchive().getCreateDate()).isEqualTo(Instant.now(clock));
99+
void getArchiveHasFixedCreateDate() throws Exception {
100+
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env);
101+
Instant createInstant = builder.getArchive().getCreateDate();
102+
OffsetDateTime createDateTime = OffsetDateTime.ofInstant(createInstant, ZoneId.of("UTC"));
103+
assertThat(createDateTime.getYear()).isEqualTo(1980);
104+
assertThat(createDateTime.getMonthValue()).isEqualTo(1);
105+
assertThat(createDateTime.getDayOfMonth()).isEqualTo(1);
106+
assertThat(createDateTime.getHour()).isEqualTo(0);
107+
assertThat(createDateTime.getMinute()).isEqualTo(0);
108+
assertThat(createDateTime.getSecond()).isEqualTo(1);
104109
}
105110

106111
@Test

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchiveTests.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@
2020
import java.io.ByteArrayOutputStream;
2121
import java.io.IOException;
2222
import java.nio.charset.StandardCharsets;
23-
import java.time.Instant;
24-
import java.time.OffsetDateTime;
25-
import java.time.ZoneOffset;
2623

2724
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
2825
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
@@ -39,17 +36,15 @@
3936
* Tests for {@link ImageArchive}.
4037
*
4138
* @author Phillip Webb
39+
* @author Scott Frederick
4240
*/
4341
class ImageArchiveTests extends AbstractJsonTests {
4442

45-
static final Instant CREATE_DATE = OffsetDateTime.of(1906, 12, 9, 11, 30, 0, 0, ZoneOffset.UTC).toInstant();
46-
4743
@Test
4844
void fromImageWritesToValidArchiveTar() throws Exception {
4945
Image image = Image.of(getContent("image.json"));
5046
ImageArchive archive = ImageArchive.from(image, (update) -> {
5147
update.withNewLayer(Layer.of((layout) -> layout.folder("/spring", Owner.ROOT)));
52-
update.withCreateDate(CREATE_DATE);
5348
update.withTag(ImageReference.of("pack.local/builder/6b7874626575656b6162"));
5449
});
5550
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@@ -78,7 +73,7 @@ private void assertExpectedLayer(TarArchiveEntry entry, byte[] content) throws E
7873
}
7974

8075
private void assertExpectedConfig(TarArchiveEntry entry, byte[] content) throws Exception {
81-
assertThat(entry.getName()).isEqualTo("/d1872169d781cff5e1aa22d111f636bef0c57e1c358ca3861e3d33a5bdb1b4a5.json");
76+
assertThat(entry.getName()).isEqualTo("/682f8d24b9d9c313d1190a0e955dcb5e65ec9beea40420999839c6f0cbb38382.json");
8277
String actualJson = new String(content, StandardCharsets.UTF_8);
8378
String expectedJson = StreamUtils.copyToString(getContent("image-archive-config.json"), StandardCharsets.UTF_8);
8479
JSONAssert.assertEquals(expectedJson, actualJson, false);

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/docker/type/image-archive-config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"io.buildpacks.stack.id": "org.cloudfoundry.stacks.cflinuxfs3"
2626
}
2727
},
28-
"created": "1906-12-09T11:30:00Z",
28+
"created": "1980-01-01T00:00:01Z",
2929
"history": [
3030
{
3131

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/resources/org/springframework/boot/buildpack/platform/docker/type/image-archive-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[
22
{
3-
"Config": "/d1872169d781cff5e1aa22d111f636bef0c57e1c358ca3861e3d33a5bdb1b4a5.json",
3+
"Config": "/682f8d24b9d9c313d1190a0e955dcb5e65ec9beea40420999839c6f0cbb38382.json",
44
"Layers": [
55
"",
66
"",

0 commit comments

Comments
 (0)