Skip to content

Commit

Permalink
GoogleContainerTools#3158 - [Jib core] Tar archives with same content…
Browse files Browse the repository at this point in the history
…s are not reproducible
  • Loading branch information
davidtron committed Apr 9, 2021
1 parent 5d19b1f commit 75d321a
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ public class ImageTarball {
/** File name extension for the layer content files. */
private static final String LAYER_FILE_EXTENSION = ".tar.gz";

/** Time that entry is set in the tar. */
private static final Instant TAR_ENTRY_MODIFICATION_TIME = Instant.EPOCH;

private final Image image;
private final ImageReference imageReference;
private final ImmutableSet<String> allTargetImageTags;
private static final Instant TAR_ENTRY_MODIFICATION_TIME = Instant.EPOCH;

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

tarStreamBuilder.addBlobEntry(
layer.getBlob(), size, "blobs/sha256/" + digest.getHash(), creationTime);
layer.getBlob(), size, "blobs/sha256/" + digest.getHash(), TAR_ENTRY_MODIFICATION_TIME);
manifest.addLayer(size, digest);
}

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

// Adds the manifest to the tarball
BlobDescriptor manifestDescriptor = Digests.computeDigest(manifest);
tarStreamBuilder.addByteEntry(
JsonTemplateMapper.toByteArray(manifest),
"blobs/sha256/" + manifestDescriptor.getDigest().getHash(),
creationTime);
TAR_ENTRY_MODIFICATION_TIME);

// Adds the oci-layout and index.json
tarStreamBuilder.addByteEntry(
"{\"imageLayoutVersion\": \"1.0.0\"}".getBytes(StandardCharsets.UTF_8),
"oci-layout",
creationTime);
TAR_ENTRY_MODIFICATION_TIME);
OciIndexTemplate index = new OciIndexTemplate();
// TODO: figure out how to tag with allTargetImageTags
index.addManifest(manifestDescriptor, imageReference.toStringWithQualifier());
tarStreamBuilder.addByteEntry(
JsonTemplateMapper.toByteArray(index), "index.json", creationTime);
JsonTemplateMapper.toByteArray(index), "index.json", TAR_ENTRY_MODIFICATION_TIME);

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

tarStreamBuilder.addBlobEntry(
layer.getBlob(), layer.getBlobDescriptor().getSize(), layerName, creationTime);
layer.getBlob(),
layer.getBlobDescriptor().getSize(),
layerName,
TAR_ENTRY_MODIFICATION_TIME);
manifestTemplate.addLayerFile(layerName);
}

Expand All @@ -145,7 +150,7 @@ private void dockerWriteTo(OutputStream out) throws IOException {
tarStreamBuilder.addByteEntry(
JsonTemplateMapper.toByteArray(containerConfiguration),
CONTAINER_CONFIGURATION_JSON_FILE_NAME,
creationTime);
TAR_ENTRY_MODIFICATION_TIME);

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

tarStreamBuilder.writeAsTarArchiveTo(out);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public void addTarArchiveEntry(TarArchiveEntry entry) {
public void addByteEntry(byte[] contents, String name, Instant modificationTime) {
TarArchiveEntry entry = new TarArchiveEntry(name);
entry.setSize(contents.length);
entry.setModTime(modTime.getEpochSecond());
entry.setModTime(modificationTime.getEpochSecond());
archiveMap.put(entry, Blobs.from(outputStream -> outputStream.write(contents)));
}

Expand All @@ -88,12 +88,12 @@ public void addByteEntry(byte[] contents, String name, Instant modificationTime)
* @param blob the {@link Blob} to add to the tarball
* @param size the size (in bytes) of {@code blob}
* @param name the name of the entry (i.e. filename)
* @param modTime the time the entry is created
* @param modificationTime the modification time of the entry
*/
public void addBlobEntry(Blob blob, long size, String name, Instant modTime) {
public void addBlobEntry(Blob blob, long size, String name, Instant modificationTime) {
TarArchiveEntry entry = new TarArchiveEntry(name);
entry.setSize(size);
entry.setModTime(modTime.getEpochSecond());
entry.setModTime(modificationTime.getEpochSecond());
archiveMap.put(entry, blob);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,10 @@ public void testToBlob_stringsAndTarArchiveEntriesWithCompression() throws IOExc

@Test
public void testToBlob_multiByte() throws IOException {
testTarStreamBuilder.addByteEntry("日本語".getBytes(StandardCharsets.UTF_8), "test", creationTime);
testTarStreamBuilder.addByteEntry(
"asdf".getBytes(StandardCharsets.UTF_8), "crepecake", creationTime);
"日本語".getBytes(StandardCharsets.UTF_8), "test", Instant.EPOCH);
testTarStreamBuilder.addByteEntry(
"asdf".getBytes(StandardCharsets.UTF_8), "crepecake", Instant.EPOCH);
testTarStreamBuilder.addBlobEntry(
Blobs.from("jib"), "jib".getBytes(StandardCharsets.UTF_8).length, "jib", Instant.EPOCH);

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

headerFile = tarArchiveInputStream.getNextTarEntry();
Assert.assertEquals("jib", headerFile.getName());
Assert.assertEquals(
"jib", new String(ByteStreams.toByteArray(tarArchiveInputStream), StandardCharsets.UTF_8));
Assert.assertEquals(Instant.EPOCH, headerFile.getModTime().toInstant());

Assert.assertNull(tarArchiveInputStream.getNextTarEntry());
}
Expand All @@ -150,27 +153,27 @@ private void setUpWithTarEntries() {
/** Creates a TarStreamBuilder using Strings. */
private void setUpWithStrings() {
// Prepares a test TarStreamBuilder.
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA", creationTime);
testTarStreamBuilder.addByteEntry(fileBContents, "crepecake", creationTime);
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA", Instant.EPOCH);
testTarStreamBuilder.addByteEntry(fileBContents, "crepecake", Instant.EPOCH);
testTarStreamBuilder.addTarArchiveEntry(
new TarArchiveEntry(directoryA.toFile(), "some/path/to"));
testTarStreamBuilder.addByteEntry(
fileAContents,
"some/really/long/path/that/exceeds/100/characters/abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890",
creationTime);
Instant.EPOCH);
}

/** Creates a TarStreamBuilder using Strings and TarArchiveEntries. */
private void setUpWithStringsAndTarEntries() {
// Prepares a test TarStreamBuilder.
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA", creationTime);
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA", Instant.EPOCH);
testTarStreamBuilder.addTarArchiveEntry(new TarArchiveEntry(fileB.toFile(), "crepecake"));
testTarStreamBuilder.addTarArchiveEntry(
new TarArchiveEntry(directoryA.toFile(), "some/path/to"));
testTarStreamBuilder.addByteEntry(
fileAContents,
"some/really/long/path/that/exceeds/100/characters/abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890",
creationTime);
Instant.EPOCH);
}

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

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

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

Expand Down

0 comments on commit 75d321a

Please sign in to comment.