Skip to content

Commit 75d321a

Browse files
committed
GoogleContainerTools#3158 - [Jib core] Tar archives with same contents are not reproducible
1 parent 5d19b1f commit 75d321a

File tree

3 files changed

+31
-20
lines changed

3 files changed

+31
-20
lines changed

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

+14-9
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,12 @@ public class ImageTarball {
4646
/** File name extension for the layer content files. */
4747
private static final String LAYER_FILE_EXTENSION = ".tar.gz";
4848

49+
/** Time that entry is set in the tar. */
50+
private static final Instant TAR_ENTRY_MODIFICATION_TIME = Instant.EPOCH;
51+
4952
private final Image image;
5053
private final ImageReference imageReference;
5154
private final ImmutableSet<String> allTargetImageTags;
52-
private static final Instant TAR_ENTRY_MODIFICATION_TIME = Instant.EPOCH;
5355

5456
/**
5557
* Instantiate with an {@link Image}.
@@ -91,7 +93,7 @@ private void ociWriteTo(OutputStream out) throws IOException {
9193
long size = layer.getBlobDescriptor().getSize();
9294

9395
tarStreamBuilder.addBlobEntry(
94-
layer.getBlob(), size, "blobs/sha256/" + digest.getHash(), creationTime);
96+
layer.getBlob(), size, "blobs/sha256/" + digest.getHash(), TAR_ENTRY_MODIFICATION_TIME);
9597
manifest.addLayer(size, digest);
9698
}
9799

@@ -103,25 +105,25 @@ private void ociWriteTo(OutputStream out) throws IOException {
103105
tarStreamBuilder.addByteEntry(
104106
JsonTemplateMapper.toByteArray(containerConfiguration),
105107
"blobs/sha256/" + configDescriptor.getDigest().getHash(),
106-
creationTime);
108+
TAR_ENTRY_MODIFICATION_TIME);
107109

108110
// Adds the manifest to the tarball
109111
BlobDescriptor manifestDescriptor = Digests.computeDigest(manifest);
110112
tarStreamBuilder.addByteEntry(
111113
JsonTemplateMapper.toByteArray(manifest),
112114
"blobs/sha256/" + manifestDescriptor.getDigest().getHash(),
113-
creationTime);
115+
TAR_ENTRY_MODIFICATION_TIME);
114116

115117
// Adds the oci-layout and index.json
116118
tarStreamBuilder.addByteEntry(
117119
"{\"imageLayoutVersion\": \"1.0.0\"}".getBytes(StandardCharsets.UTF_8),
118120
"oci-layout",
119-
creationTime);
121+
TAR_ENTRY_MODIFICATION_TIME);
120122
OciIndexTemplate index = new OciIndexTemplate();
121123
// TODO: figure out how to tag with allTargetImageTags
122124
index.addManifest(manifestDescriptor, imageReference.toStringWithQualifier());
123125
tarStreamBuilder.addByteEntry(
124-
JsonTemplateMapper.toByteArray(index), "index.json", creationTime);
126+
JsonTemplateMapper.toByteArray(index), "index.json", TAR_ENTRY_MODIFICATION_TIME);
125127

126128
tarStreamBuilder.writeAsTarArchiveTo(out);
127129
}
@@ -135,7 +137,10 @@ private void dockerWriteTo(OutputStream out) throws IOException {
135137
String layerName = layer.getBlobDescriptor().getDigest().getHash() + LAYER_FILE_EXTENSION;
136138

137139
tarStreamBuilder.addBlobEntry(
138-
layer.getBlob(), layer.getBlobDescriptor().getSize(), layerName, creationTime);
140+
layer.getBlob(),
141+
layer.getBlobDescriptor().getSize(),
142+
layerName,
143+
TAR_ENTRY_MODIFICATION_TIME);
139144
manifestTemplate.addLayerFile(layerName);
140145
}
141146

@@ -145,7 +150,7 @@ private void dockerWriteTo(OutputStream out) throws IOException {
145150
tarStreamBuilder.addByteEntry(
146151
JsonTemplateMapper.toByteArray(containerConfiguration),
147152
CONTAINER_CONFIGURATION_JSON_FILE_NAME,
148-
creationTime);
153+
TAR_ENTRY_MODIFICATION_TIME);
149154

150155
// Adds the manifest to tarball.
151156
for (String tag : allTargetImageTags) {
@@ -154,7 +159,7 @@ private void dockerWriteTo(OutputStream out) throws IOException {
154159
tarStreamBuilder.addByteEntry(
155160
JsonTemplateMapper.toByteArray(Collections.singletonList(manifestTemplate)),
156161
MANIFEST_JSON_FILE_NAME,
157-
creationTime);
162+
TAR_ENTRY_MODIFICATION_TIME);
158163

159164
tarStreamBuilder.writeAsTarArchiveTo(out);
160165
}

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public void addTarArchiveEntry(TarArchiveEntry entry) {
7777
public void addByteEntry(byte[] contents, String name, Instant modificationTime) {
7878
TarArchiveEntry entry = new TarArchiveEntry(name);
7979
entry.setSize(contents.length);
80-
entry.setModTime(modTime.getEpochSecond());
80+
entry.setModTime(modificationTime.getEpochSecond());
8181
archiveMap.put(entry, Blobs.from(outputStream -> outputStream.write(contents)));
8282
}
8383

@@ -88,12 +88,12 @@ public void addByteEntry(byte[] contents, String name, Instant modificationTime)
8888
* @param blob the {@link Blob} to add to the tarball
8989
* @param size the size (in bytes) of {@code blob}
9090
* @param name the name of the entry (i.e. filename)
91-
* @param modTime the time the entry is created
91+
* @param modificationTime the modification time of the entry
9292
*/
93-
public void addBlobEntry(Blob blob, long size, String name, Instant modTime) {
93+
public void addBlobEntry(Blob blob, long size, String name, Instant modificationTime) {
9494
TarArchiveEntry entry = new TarArchiveEntry(name);
9595
entry.setSize(size);
96-
entry.setModTime(modTime.getEpochSecond());
96+
entry.setModTime(modificationTime.getEpochSecond());
9797
archiveMap.put(entry, blob);
9898
}
9999
}

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

+13-7
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,10 @@ public void testToBlob_stringsAndTarArchiveEntriesWithCompression() throws IOExc
9797

9898
@Test
9999
public void testToBlob_multiByte() throws IOException {
100-
testTarStreamBuilder.addByteEntry("日本語".getBytes(StandardCharsets.UTF_8), "test", creationTime);
101100
testTarStreamBuilder.addByteEntry(
102-
"asdf".getBytes(StandardCharsets.UTF_8), "crepecake", creationTime);
101+
"日本語".getBytes(StandardCharsets.UTF_8), "test", Instant.EPOCH);
102+
testTarStreamBuilder.addByteEntry(
103+
"asdf".getBytes(StandardCharsets.UTF_8), "crepecake", Instant.EPOCH);
103104
testTarStreamBuilder.addBlobEntry(
104105
Blobs.from("jib"), "jib".getBytes(StandardCharsets.UTF_8).length, "jib", Instant.EPOCH);
105106

@@ -124,11 +125,13 @@ public void testToBlob_multiByte() throws IOException {
124125
Assert.assertEquals("crepecake", headerFile.getName());
125126
Assert.assertEquals(
126127
"asdf", new String(ByteStreams.toByteArray(tarArchiveInputStream), StandardCharsets.UTF_8));
128+
Assert.assertEquals(Instant.EPOCH, headerFile.getModTime().toInstant());
127129

128130
headerFile = tarArchiveInputStream.getNextTarEntry();
129131
Assert.assertEquals("jib", headerFile.getName());
130132
Assert.assertEquals(
131133
"jib", new String(ByteStreams.toByteArray(tarArchiveInputStream), StandardCharsets.UTF_8));
134+
Assert.assertEquals(Instant.EPOCH, headerFile.getModTime().toInstant());
132135

133136
Assert.assertNull(tarArchiveInputStream.getNextTarEntry());
134137
}
@@ -150,27 +153,27 @@ private void setUpWithTarEntries() {
150153
/** Creates a TarStreamBuilder using Strings. */
151154
private void setUpWithStrings() {
152155
// Prepares a test TarStreamBuilder.
153-
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA", creationTime);
154-
testTarStreamBuilder.addByteEntry(fileBContents, "crepecake", creationTime);
156+
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA", Instant.EPOCH);
157+
testTarStreamBuilder.addByteEntry(fileBContents, "crepecake", Instant.EPOCH);
155158
testTarStreamBuilder.addTarArchiveEntry(
156159
new TarArchiveEntry(directoryA.toFile(), "some/path/to"));
157160
testTarStreamBuilder.addByteEntry(
158161
fileAContents,
159162
"some/really/long/path/that/exceeds/100/characters/abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890",
160-
creationTime);
163+
Instant.EPOCH);
161164
}
162165

163166
/** Creates a TarStreamBuilder using Strings and TarArchiveEntries. */
164167
private void setUpWithStringsAndTarEntries() {
165168
// Prepares a test TarStreamBuilder.
166-
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA", creationTime);
169+
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA", Instant.EPOCH);
167170
testTarStreamBuilder.addTarArchiveEntry(new TarArchiveEntry(fileB.toFile(), "crepecake"));
168171
testTarStreamBuilder.addTarArchiveEntry(
169172
new TarArchiveEntry(directoryA.toFile(), "some/path/to"));
170173
testTarStreamBuilder.addByteEntry(
171174
fileAContents,
172175
"some/really/long/path/that/exceeds/100/characters/abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890",
173-
creationTime);
176+
Instant.EPOCH);
174177
}
175178

176179
/** Creates a compressed blob from the TarStreamBuilder and verifies it. */
@@ -215,18 +218,21 @@ private void verifyTarArchive(TarArchiveInputStream tarArchiveInputStream) throw
215218
// Verifies fileB was archived correctly.
216219
TarArchiveEntry headerFileB = tarArchiveInputStream.getNextTarEntry();
217220
Assert.assertEquals("crepecake", headerFileB.getName());
221+
Assert.assertEquals(Instant.EPOCH, headerFileB.getModTime().toInstant());
218222
byte[] fileBString = ByteStreams.toByteArray(tarArchiveInputStream);
219223
Assert.assertArrayEquals(fileBContents, fileBString);
220224

221225
// Verifies directoryA was archived correctly.
222226
TarArchiveEntry headerDirectoryA = tarArchiveInputStream.getNextTarEntry();
223227
Assert.assertEquals("some/path/to/", headerDirectoryA.getName());
228+
Assert.assertEquals(Instant.EPOCH, headerDirectoryA.getModTime().toInstant());
224229

225230
// Verifies the long file was archived correctly.
226231
TarArchiveEntry headerFileALong = tarArchiveInputStream.getNextTarEntry();
227232
Assert.assertEquals(
228233
"some/really/long/path/that/exceeds/100/characters/abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890",
229234
headerFileALong.getName());
235+
Assert.assertEquals(Instant.EPOCH, headerFileALong);
230236
byte[] fileALongString = ByteStreams.toByteArray(tarArchiveInputStream);
231237
Assert.assertArrayEquals(fileAContents, fileALongString);
232238

0 commit comments

Comments
 (0)