Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#3158 - [Jib core] Tar archives with same contents are not reproducible #3159

Merged
merged 16 commits into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
562e2b8
#3158 - [Jib core] Tar archives with same contents are not reproducible
davidtron Mar 20, 2021
46d56e8
Merge pull request #1 from GoogleContainerTools/master
davidtron Mar 20, 2021
f4d6757
Revert "#3158 - [Jib core] Tar archives with same contents are not re…
davidtron Apr 8, 2021
438e2dc
#3158 - [Jib core] Tar archives with same contents are not reproducible
davidtron Apr 8, 2021
f1fd866
Update jib-core/src/main/java/com/google/cloud/tools/jib/image/ImageT…
davidtron Apr 9, 2021
11e744d
Update jib-core/src/test/java/com/google/cloud/tools/jib/tar/TarStrea…
davidtron Apr 9, 2021
3df1d7e
Update jib-core/src/test/java/com/google/cloud/tools/jib/tar/TarStrea…
davidtron Apr 9, 2021
f4bc94d
Update jib-core/src/main/java/com/google/cloud/tools/jib/tar/TarStrea…
davidtron Apr 9, 2021
5d19b1f
Update jib-core/src/main/java/com/google/cloud/tools/jib/tar/TarStrea…
davidtron Apr 9, 2021
75d321a
#3158 - [Jib core] Tar archives with same contents are not reproducible
davidtron Apr 9, 2021
9c1141b
#3158 - [Jib core] Tar archives with same contents are not reproducible
davidtron Apr 9, 2021
b12a4aa
#3158 - [Jib core] Tar archives with same contents are not reproducible
davidtron Apr 10, 2021
25a5ae2
#3158 - [Jib core] Tar archives with same contents are not reproducible
davidtron Apr 12, 2021
3bc801f
#3158 - [Jib core] Tar archives with same contents are not reproducible
davidtron Apr 12, 2021
cc87a86
#3158 - [Jib core] Tar archives with same contents are not reproducible
davidtron Apr 12, 2021
dd70bba
#3158 - [Jib core] Tar archives with same contents are not reproducible
davidtron Apr 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Collections;

/** Translates an {@link Image} to a tarball that can be loaded into Docker. */
Expand All @@ -45,6 +46,9 @@ 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;
Expand Down Expand Up @@ -88,7 +92,8 @@ private void ociWriteTo(OutputStream out) throws IOException {
DescriptorDigest digest = layer.getBlobDescriptor().getDigest();
long size = layer.getBlobDescriptor().getSize();

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

Expand All @@ -99,21 +104,26 @@ private void ociWriteTo(OutputStream out) throws IOException {
manifest.setContainerConfiguration(configDescriptor.getSize(), configDescriptor.getDigest());
tarStreamBuilder.addByteEntry(
JsonTemplateMapper.toByteArray(containerConfiguration),
"blobs/sha256/" + configDescriptor.getDigest().getHash());
"blobs/sha256/" + configDescriptor.getDigest().getHash(),
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());
"blobs/sha256/" + manifestDescriptor.getDigest().getHash(),
TAR_ENTRY_MODIFICATION_TIME);

// Adds the oci-layout and index.json
tarStreamBuilder.addByteEntry(
"{\"imageLayoutVersion\": \"1.0.0\"}".getBytes(StandardCharsets.UTF_8), "oci-layout");
"{\"imageLayoutVersion\": \"1.0.0\"}".getBytes(StandardCharsets.UTF_8),
"oci-layout",
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");
tarStreamBuilder.addByteEntry(
JsonTemplateMapper.toByteArray(index), "index.json", TAR_ENTRY_MODIFICATION_TIME);

tarStreamBuilder.writeAsTarArchiveTo(out);
}
Expand All @@ -127,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);
layer.getBlob(),
layer.getBlobDescriptor().getSize(),
layerName,
TAR_ENTRY_MODIFICATION_TIME);
manifestTemplate.addLayerFile(layerName);
}

Expand All @@ -136,15 +149,17 @@ private void dockerWriteTo(OutputStream out) throws IOException {
new ImageToJsonTranslator(image).getContainerConfiguration();
tarStreamBuilder.addByteEntry(
JsonTemplateMapper.toByteArray(containerConfiguration),
CONTAINER_CONFIGURATION_JSON_FILE_NAME);
CONTAINER_CONFIGURATION_JSON_FILE_NAME,
TAR_ENTRY_MODIFICATION_TIME);

// Adds the manifest to tarball.
for (String tag : allTargetImageTags) {
manifestTemplate.addRepoTag(imageReference.withQualifier(tag).toStringWithQualifier());
}
tarStreamBuilder.addByteEntry(
JsonTemplateMapper.toByteArray(Collections.singletonList(manifestTemplate)),
MANIFEST_JSON_FILE_NAME);
MANIFEST_JSON_FILE_NAME,
TAR_ENTRY_MODIFICATION_TIME);

tarStreamBuilder.writeAsTarArchiveTo(out);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
Expand Down Expand Up @@ -71,10 +72,12 @@ public void addTarArchiveEntry(TarArchiveEntry entry) {
*
* @param contents the bytes to add to the tarball
* @param name the name of the entry (i.e. filename)
* @param modificationTime the modification time of the entry
*/
public void addByteEntry(byte[] contents, String name) {
public void addByteEntry(byte[] contents, String name, Instant modificationTime) {
TarArchiveEntry entry = new TarArchiveEntry(name);
entry.setSize(contents.length);
entry.setModTime(modificationTime.getEpochSecond());
chanseokoh marked this conversation as resolved.
Show resolved Hide resolved
archiveMap.put(entry, Blobs.from(outputStream -> outputStream.write(contents)));
}

Expand All @@ -85,10 +88,12 @@ public void addByteEntry(byte[] contents, String name) {
* @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 modificationTime the modification time of the entry
*/
public void addBlobEntry(Blob blob, long size, String name) {
public void addBlobEntry(Blob blob, long size, String name, Instant modificationTime) {
TarArchiveEntry entry = new TarArchiveEntry(name);
entry.setSize(size);
entry.setModTime(modificationTime.getEpochSecond());
archiveMap.put(entry, blob);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
Expand Down Expand Up @@ -96,10 +97,12 @@ public void testToBlob_stringsAndTarArchiveEntriesWithCompression() throws IOExc

@Test
public void testToBlob_multiByte() throws IOException {
testTarStreamBuilder.addByteEntry("日本語".getBytes(StandardCharsets.UTF_8), "test");
testTarStreamBuilder.addByteEntry("asdf".getBytes(StandardCharsets.UTF_8), "crepecake");
testTarStreamBuilder.addByteEntry(
"日本語".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");
Blobs.from("jib"), "jib".getBytes(StandardCharsets.UTF_8).length, "jib", Instant.EPOCH);

// Writes the BLOB and captures the output.
ByteArrayOutputStream tarByteOutputStream = new ByteArrayOutputStream();
Expand All @@ -122,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 @@ -148,25 +153,27 @@ private void setUpWithTarEntries() {
/** Creates a TarStreamBuilder using Strings. */
private void setUpWithStrings() {
// Prepares a test TarStreamBuilder.
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA");
testTarStreamBuilder.addByteEntry(fileBContents, "crepecake");
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");
"some/really/long/path/that/exceeds/100/characters/abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890",
Instant.EPOCH);
}

/** Creates a TarStreamBuilder using Strings and TarArchiveEntries. */
private void setUpWithStringsAndTarEntries() {
// Prepares a test TarStreamBuilder.
testTarStreamBuilder.addByteEntry(fileAContents, "some/path/to/resourceFileA");
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");
"some/really/long/path/that/exceeds/100/characters/abcdefghijklmnopqrstuvwxyz0123456789012345678901234567890",
Instant.EPOCH);
}

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