Skip to content

Commit 438e2dc

Browse files
committed
GoogleContainerTools#3158 - [Jib core] Tar archives with same contents are not reproducible
1 parent f4d6757 commit 438e2dc

File tree

3 files changed

+38
-18
lines changed

3 files changed

+38
-18
lines changed

jib-core/src/main/java/com/google/cloud/tools/jib/image/ImageTarball.java

+18-8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.io.IOException;
3232
import java.io.OutputStream;
3333
import java.nio.charset.StandardCharsets;
34+
import java.time.Instant;
3435
import java.util.Collections;
3536

3637
/** Translates an {@link Image} to a tarball that can be loaded into Docker. */
@@ -48,6 +49,7 @@ public class ImageTarball {
4849
private final Image image;
4950
private final ImageReference imageReference;
5051
private final ImmutableSet<String> allTargetImageTags;
52+
private final Instant creationTime = Instant.EPOCH;
5153

5254
/**
5355
* Instantiate with an {@link Image}.
@@ -88,7 +90,8 @@ private void ociWriteTo(OutputStream out) throws IOException {
8890
DescriptorDigest digest = layer.getBlobDescriptor().getDigest();
8991
long size = layer.getBlobDescriptor().getSize();
9092

91-
tarStreamBuilder.addBlobEntry(layer.getBlob(), size, "blobs/sha256/" + digest.getHash());
93+
tarStreamBuilder.addBlobEntry(
94+
layer.getBlob(), size, "blobs/sha256/" + digest.getHash(), creationTime);
9295
manifest.addLayer(size, digest);
9396
}
9497

@@ -99,21 +102,26 @@ private void ociWriteTo(OutputStream out) throws IOException {
99102
manifest.setContainerConfiguration(configDescriptor.getSize(), configDescriptor.getDigest());
100103
tarStreamBuilder.addByteEntry(
101104
JsonTemplateMapper.toByteArray(containerConfiguration),
102-
"blobs/sha256/" + configDescriptor.getDigest().getHash());
105+
"blobs/sha256/" + configDescriptor.getDigest().getHash(),
106+
creationTime);
103107

104108
// Adds the manifest to the tarball
105109
BlobDescriptor manifestDescriptor = Digests.computeDigest(manifest);
106110
tarStreamBuilder.addByteEntry(
107111
JsonTemplateMapper.toByteArray(manifest),
108-
"blobs/sha256/" + manifestDescriptor.getDigest().getHash());
112+
"blobs/sha256/" + manifestDescriptor.getDigest().getHash(),
113+
creationTime);
109114

110115
// Adds the oci-layout and index.json
111116
tarStreamBuilder.addByteEntry(
112-
"{\"imageLayoutVersion\": \"1.0.0\"}".getBytes(StandardCharsets.UTF_8), "oci-layout");
117+
"{\"imageLayoutVersion\": \"1.0.0\"}".getBytes(StandardCharsets.UTF_8),
118+
"oci-layout",
119+
creationTime);
113120
OciIndexTemplate index = new OciIndexTemplate();
114121
// TODO: figure out how to tag with allTargetImageTags
115122
index.addManifest(manifestDescriptor, imageReference.toStringWithQualifier());
116-
tarStreamBuilder.addByteEntry(JsonTemplateMapper.toByteArray(index), "index.json");
123+
tarStreamBuilder.addByteEntry(
124+
JsonTemplateMapper.toByteArray(index), "index.json", creationTime);
117125

118126
tarStreamBuilder.writeAsTarArchiveTo(out);
119127
}
@@ -127,7 +135,7 @@ private void dockerWriteTo(OutputStream out) throws IOException {
127135
String layerName = layer.getBlobDescriptor().getDigest().getHash() + LAYER_FILE_EXTENSION;
128136

129137
tarStreamBuilder.addBlobEntry(
130-
layer.getBlob(), layer.getBlobDescriptor().getSize(), layerName);
138+
layer.getBlob(), layer.getBlobDescriptor().getSize(), layerName, creationTime);
131139
manifestTemplate.addLayerFile(layerName);
132140
}
133141

@@ -136,15 +144,17 @@ private void dockerWriteTo(OutputStream out) throws IOException {
136144
new ImageToJsonTranslator(image).getContainerConfiguration();
137145
tarStreamBuilder.addByteEntry(
138146
JsonTemplateMapper.toByteArray(containerConfiguration),
139-
CONTAINER_CONFIGURATION_JSON_FILE_NAME);
147+
CONTAINER_CONFIGURATION_JSON_FILE_NAME,
148+
creationTime);
140149

141150
// Adds the manifest to tarball.
142151
for (String tag : allTargetImageTags) {
143152
manifestTemplate.addRepoTag(imageReference.withQualifier(tag).toStringWithQualifier());
144153
}
145154
tarStreamBuilder.addByteEntry(
146155
JsonTemplateMapper.toByteArray(Collections.singletonList(manifestTemplate)),
147-
MANIFEST_JSON_FILE_NAME);
156+
MANIFEST_JSON_FILE_NAME,
157+
creationTime);
148158

149159
tarStreamBuilder.writeAsTarArchiveTo(out);
150160
}

jib-core/src/main/java/com/google/cloud/tools/jib/tar/TarStreamBuilder.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.IOException;
2222
import java.io.OutputStream;
2323
import java.nio.charset.StandardCharsets;
24+
import java.time.Instant;
2425
import java.util.LinkedHashMap;
2526
import java.util.Map;
2627
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
@@ -71,10 +72,12 @@ public void addTarArchiveEntry(TarArchiveEntry entry) {
7172
*
7273
* @param contents the bytes to add to the tarball
7374
* @param name the name of the entry (i.e. filename)
75+
* @param modTime the time the entry is created
7476
*/
75-
public void addByteEntry(byte[] contents, String name) {
77+
public void addByteEntry(byte[] contents, String name, Instant modTime) {
7678
TarArchiveEntry entry = new TarArchiveEntry(name);
7779
entry.setSize(contents.length);
80+
entry.setModTime(modTime.getEpochSecond());
7881
archiveMap.put(entry, Blobs.from(outputStream -> outputStream.write(contents)));
7982
}
8083

@@ -85,10 +88,12 @@ public void addByteEntry(byte[] contents, String name) {
8588
* @param blob the {@link Blob} to add to the tarball
8689
* @param size the size (in bytes) of {@code blob}
8790
* @param name the name of the entry (i.e. filename)
91+
* @param modTime the time the entry is created
8892
*/
89-
public void addBlobEntry(Blob blob, long size, String name) {
93+
public void addBlobEntry(Blob blob, long size, String name, Instant modTime) {
9094
TarArchiveEntry entry = new TarArchiveEntry(name);
9195
entry.setSize(size);
96+
entry.setModTime(modTime.getEpochSecond());
9297
archiveMap.put(entry, blob);
9398
}
9499
}

jib-core/src/test/java/com/google/cloud/tools/jib/tar/TarStreamBuilderTest.java

+13-8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.nio.file.Files;
3030
import java.nio.file.Path;
3131
import java.nio.file.Paths;
32+
import java.time.Instant;
3233
import java.util.zip.GZIPInputStream;
3334
import java.util.zip.GZIPOutputStream;
3435
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
@@ -46,6 +47,7 @@ public class TarStreamBuilderTest {
4647
private byte[] fileAContents;
4748
private byte[] fileBContents;
4849
private TarStreamBuilder testTarStreamBuilder = new TarStreamBuilder();
50+
private final Instant creationTime = Instant.EPOCH;
4951

5052
@Before
5153
public void setup() throws URISyntaxException, IOException {
@@ -96,10 +98,11 @@ public void testToBlob_stringsAndTarArchiveEntriesWithCompression() throws IOExc
9698

9799
@Test
98100
public void testToBlob_multiByte() throws IOException {
99-
testTarStreamBuilder.addByteEntry("日本語".getBytes(StandardCharsets.UTF_8), "test");
100-
testTarStreamBuilder.addByteEntry("asdf".getBytes(StandardCharsets.UTF_8), "crepecake");
101+
testTarStreamBuilder.addByteEntry("日本語".getBytes(StandardCharsets.UTF_8), "test", creationTime);
102+
testTarStreamBuilder.addByteEntry(
103+
"asdf".getBytes(StandardCharsets.UTF_8), "crepecake", creationTime);
101104
testTarStreamBuilder.addBlobEntry(
102-
Blobs.from("jib"), "jib".getBytes(StandardCharsets.UTF_8).length, "jib");
105+
Blobs.from("jib"), "jib".getBytes(StandardCharsets.UTF_8).length, "jib", creationTime);
103106

104107
// Writes the BLOB and captures the output.
105108
ByteArrayOutputStream tarByteOutputStream = new ByteArrayOutputStream();
@@ -148,25 +151,27 @@ private void setUpWithTarEntries() {
148151
/** Creates a TarStreamBuilder using Strings. */
149152
private void setUpWithStrings() {
150153
// Prepares a test TarStreamBuilder.
151-
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA");
152-
testTarStreamBuilder.addByteEntry(fileBContents, "crepecake");
154+
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA", creationTime);
155+
testTarStreamBuilder.addByteEntry(fileBContents, "crepecake", creationTime);
153156
testTarStreamBuilder.addTarArchiveEntry(
154157
new TarArchiveEntry(directoryA.toFile(), "some/path/to"));
155158
testTarStreamBuilder.addByteEntry(
156159
fileAContents,
157-
"some/really/long/path/that/exceeds/100/characters/abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890");
160+
"some/really/long/path/that/exceeds/100/characters/abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890",
161+
creationTime);
158162
}
159163

160164
/** Creates a TarStreamBuilder using Strings and TarArchiveEntries. */
161165
private void setUpWithStringsAndTarEntries() {
162166
// Prepares a test TarStreamBuilder.
163-
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA");
167+
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA", creationTime);
164168
testTarStreamBuilder.addTarArchiveEntry(new TarArchiveEntry(fileB.toFile(), "crepecake"));
165169
testTarStreamBuilder.addTarArchiveEntry(
166170
new TarArchiveEntry(directoryA.toFile(), "some/path/to"));
167171
testTarStreamBuilder.addByteEntry(
168172
fileAContents,
169-
"some/really/long/path/that/exceeds/100/characters/abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890");
173+
"some/really/long/path/that/exceeds/100/characters/abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890",
174+
creationTime);
170175
}
171176

172177
/** Creates a compressed blob from the TarStreamBuilder and verifies it. */

0 commit comments

Comments
 (0)