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
This commit is contained in:
parent
191dce3f5e
commit
e294d26458
|
@ -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<String, String> 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<String, String> 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));
|
||||
}
|
||||
|
|
|
@ -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 <a href="https://github.com/moby/moby/blob/master/image/spec/v1.2.md">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> 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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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": [
|
||||
{
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"Config": "/d1872169d781cff5e1aa22d111f636bef0c57e1c358ca3861e3d33a5bdb1b4a5.json",
|
||||
"Config": "/682f8d24b9d9c313d1190a0e955dcb5e65ec9beea40420999839c6f0cbb38382.json",
|
||||
"Layers": [
|
||||
"",
|
||||
"",
|
||||
|
|
Loading…
Reference in New Issue