Skip to content
Merged
Changes from all commits
Commits
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 @@ -2,93 +2,325 @@

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.io.IOUtils;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;

import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

public class CompressArchiveUtilTest {

@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();

@Test
public void testExecutableFlagIsPreserved() throws Exception {
public void tarWithRegularFileAsInput() throws Exception {
Path archiveSourceFile = tempFolder.getRoot().toPath().resolve("sourceFile");
createFileWithContent(archiveSourceFile);

// ChildrenOnly = false
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceFile, tarGzFile, true, false);
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "sourceFile");

// ChildrenOnly = true, this option make no sense when input is a file but still, let's test it
// to make sure it behaves as expected
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceFile, tarGzFile, true, false);
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "sourceFile");
}

@Test
public void tarWithExecutableFileAsInput() throws Exception {
Path archiveSourceFile = tempFolder.getRoot().toPath().resolve("executableFile.sh");
createFileWithContent(archiveSourceFile);
archiveSourceFile.toFile().setExecutable(true);

// ChildrenOnly = false
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceFile, tarGzFile, true, false);
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsExecutableFile(tarGzFile.toFile(), "executableFile.sh");

// ChildrenOnly = true, this option make no sense when input is a file but still, let's test it
// to make sure it behaves as expected
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceFile, tarGzFile, true, false);
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsExecutableFile(tarGzFile.toFile(), "executableFile.sh");
}

@Test
@Ignore("Symlink creation is broken so test do not pass")
public void tarWithSymbolicLinkFileAsInput() throws IOException {
Path archiveSourceFile = tempFolder.getRoot().toPath().resolve("symlinkFile");
Path linkTargetFile = tempFolder.newFile("link-target").toPath();
Files.createSymbolicLink(archiveSourceFile, linkTargetFile);

// ChildrenOnly = false
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceFile, tarGzFile, true, false);
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsSymlink(tarGzFile.toFile(), "symlinkFile", linkTargetFile.toString());

// ChildrenOnly = true, this option make no sense when input is a file but still, let's test it
// to make sure it behaves as expected
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceFile, tarGzFile, true, false);
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsSymlink(tarGzFile.toFile(), "symlinkFile", linkTargetFile.toString());
}

@Test
public void tarWithfolderAsInput() throws Exception {
Path archiveSourceDir = tempFolder.newFolder("archive-source").toPath();
createFoldersAndSubFolderWithFiles(archiveSourceDir);

// ChildrenOnly = false
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, false);
assertEquals(7, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "archive-source");
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "folderA");
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "folderB");
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "subFolderB");
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "fileA");
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "fileB");
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "subFileB");

// ChildrenOnly = true
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, true);
assertEquals(6, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "folderA");
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "folderB");
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "subFolderB");
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "fileA");
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "fileB");
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "subFileB");
}

@Test
public void tarWithfolderAsInputAndNestedExecutableFile() throws Exception {
Path archiveSourceDir = tempFolder.newFolder("archive-source").toPath();
Path executableFile = archiveSourceDir.resolve("executableFile.sh");
createFileWithContent(executableFile);
executableFile.toFile().setExecutable(true);

// ChildrenOnly = false
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, false);
assertEquals(2, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "archive-source");
assertTarArchiveEntryIsExecutableFile(tarGzFile.toFile(), "executableFile.sh");

// ChildrenOnly = true
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, true);
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsExecutableFile(tarGzFile.toFile(), "executableFile.sh");
}

@Test
@Ignore("Symlink creation is broken so test do not pass")
public void tarWithfolderAsInputAndNestedSymbolicLinkFile() throws Exception {
Path archiveSourceDir = tempFolder.newFolder("archive-source").toPath();
Path linkTargetFile = tempFolder.newFile("link-target").toPath();
Path symlinkFile = archiveSourceDir.resolve("symlinkFile");
Files.createSymbolicLink(symlinkFile, linkTargetFile);

// ChildrenOnly = false
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, false);
assertEquals(2, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "archive-source");
assertTarArchiveEntryIsSymlink(tarGzFile.toFile(), "symlinkFile", linkTargetFile.toString());

// ChildrenOnly = true
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, true);
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsSymlink(tarGzFile.toFile(), "symlinkFile", linkTargetFile.toString());
}

@Test
@Ignore("Symlink creation is broken so test do not pass")
public void tarWithfolderAsInputAndNestedSymbolicLinkDir() throws Exception {
Path archiveSourceDir = tempFolder.newFolder("archive-source").toPath();
Path linkTargetDir = tempFolder.newFolder("link-target").toPath();
Path symlinkFile = archiveSourceDir.resolve("symlinkFile");
Files.createSymbolicLink(symlinkFile, linkTargetDir);

// ChildrenOnly = false
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, false);
assertEquals(2, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "archive-source");
assertTarArchiveEntryIsSymlink(tarGzFile.toFile(), "symlinkFile", linkTargetDir.toString());

// ChildrenOnly = true
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, true);
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
assertTarArchiveEntryIsSymlink(tarGzFile.toFile(), "symlinkFile", linkTargetDir.toString());
}

@Test
public void archiveTARFilesWithFolderAndFiles() throws Exception {
File archive = CompressArchiveUtil.archiveTARFiles(tempFolder.getRoot(),
createFoldersAndSubFolderWithFiles(tempFolder.getRoot().toPath()), "archive");
assertEquals(6, getNumberOfEntryInArchive(archive));
assertTarArchiveEntryIsDirectory(archive, "folderA");
assertTarArchiveEntryIsDirectory(archive, "folderB");
assertTarArchiveEntryIsDirectory(archive, "subFolderB");
assertTarArchiveEntryIsNonEmptyFile(archive, "fileA");
assertTarArchiveEntryIsNonEmptyFile(archive, "fileB");
assertTarArchiveEntryIsNonEmptyFile(archive, "subFileB");
}

@Test
public void archiveTARFilesWithExecutableFile() throws Exception {
File executableFile = tempFolder.newFile("executableFile.sh");
executableFile.setExecutable(true);
assertThat(executableFile.canExecute(), is(true));

File archive = CompressArchiveUtil.archiveTARFiles(tempFolder.getRoot(), asList(executableFile),
"archive");
File expectedFile = extractFileByName(archive, "executableFile.sh", "executableFile.sh.result");

assertThat("should be executable", expectedFile.canExecute());
}

private File extractFileByName(File archive, String filenameToExtract, String outputName) throws IOException {
File expectedFile = new File(tempFolder.newFolder(), outputName);
expectedFile.delete();
assertThat(expectedFile.exists(), is(false));

TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(new GZIPInputStream(
new BufferedInputStream(new FileInputStream(archive))));
TarArchiveEntry entry;
boolean found = false;
while ((entry = tarArchiveInputStream.getNextTarEntry()) != null) {
String individualFiles = entry.getName();
if (individualFiles.equals(filenameToExtract) || individualFiles.endsWith("/" + filenameToExtract)) {
found = true;
IOUtils.copy(tarArchiveInputStream, new FileOutputStream(expectedFile));
if ((entry.getMode() & 0755) == 0755) {
expectedFile.setExecutable(true);
}
break;
}
}
assertThat("should extracted the file", found);
tarArchiveInputStream.close();
return expectedFile;

File archive = CompressArchiveUtil.archiveTARFiles(tempFolder.getRoot(), asList(executableFile), "archive");
assertEquals(1, getNumberOfEntryInArchive(archive));
assertTarArchiveEntryIsExecutableFile(archive, "executableFile.sh");
}

@Test
public void testSymbolicLinkDir() throws IOException {
Path uploadDir = tempFolder.newFolder("upload").toPath();
Path linkTarget = tempFolder.newFolder("link-target").toPath();
Path tmpFile = Files.createTempFile(linkTarget, "link-dir", "rand");
Files.createSymbolicLink(uploadDir.resolve("link-folder"), linkTarget);
Path tarGzFile = tempFolder.newFile("docker-java.tar.gz").toPath();
//follow link only works for childrenOnly=false
CompressArchiveUtil.tar(uploadDir, tarGzFile, true, false);
File expectedFile = extractFileByName(tarGzFile.toFile(), tmpFile.toFile().getName(), "foo1");
assertThat(expectedFile.canRead(), is(true));
@Ignore("Symlink creation is broken so test do not pass")
public void archiveTARFilesWithSymbolicLinkFile() throws Exception {
Path linkTargetFile = tempFolder.newFile("link-target").toPath();
Path symlinkFile = tempFolder.getRoot().toPath().resolve("symlinkFile");
Files.createSymbolicLink(symlinkFile, linkTargetFile);

File archive = CompressArchiveUtil.archiveTARFiles(tempFolder.getRoot(), asList(symlinkFile.toFile()), "archive");
assertEquals(1, getNumberOfEntryInArchive(archive));
assertTarArchiveEntryIsSymlink(archive, "symlinkFile", linkTargetFile.toString());
}

@Test
public void testSymbolicLinkFile() throws IOException {
Path uploadDir = tempFolder.newFolder("upload").toPath();
Path tmpFile = tempFolder.newFile("src").toPath();
Files.createSymbolicLink(uploadDir.resolve("link-file"), tmpFile);
Path tarGzFile = tempFolder.newFile("docker-java.tar.gz").toPath();
boolean childrenOnly = false;
CompressArchiveUtil.tar(uploadDir, tarGzFile, true, childrenOnly);
File expectedFile = extractFileByName(tarGzFile.toFile(), "link-file", "foo1");
assertThat(expectedFile.canRead(), is(true));
childrenOnly = true;
CompressArchiveUtil.tar(uploadDir, tarGzFile, true, childrenOnly);
extractFileByName(tarGzFile.toFile(), "link-file", "foo1");
assertThat(expectedFile.canRead(), is(true));
@Ignore("Symlink creation is broken so test do not pass")
public void archiveTARFilesWithSymbolicLinkDir() throws Exception {
Path linkTargetDir = tempFolder.newFolder("link-target").toPath();
Path symlinkFile = tempFolder.getRoot().toPath().resolve("symlinkFile");
Files.createSymbolicLink(symlinkFile, linkTargetDir);

File archive = CompressArchiveUtil.archiveTARFiles(tempFolder.getRoot(), asList(symlinkFile.toFile()), "archive");
assertEquals(1, getNumberOfEntryInArchive(archive));
assertTarArchiveEntryIsSymlink(archive, "symlinkFile", linkTargetDir.toString());
}

private static void assertTarArchiveEntryIsDirectory(File archive, String directoryName) throws IOException {
TarArchiveEntry tarArchiveEntry = getTarArchiveEntry(archive, directoryName);
assertNotNull(tarArchiveEntry);
assertTrue(tarArchiveEntry.isDirectory());
}

private static void assertTarArchiveEntryIsNonEmptyFile(File archive, String fileName) throws IOException {
TarArchiveEntry tarArchiveEntry = getTarArchiveEntry(archive, fileName);
assertNotNull(tarArchiveEntry);
assertTrue(tarArchiveEntry.isFile());
assertTrue(tarArchiveEntry.getSize()>0);
}

private static void assertTarArchiveEntryIsExecutableFile(File archive, String fileName) throws IOException {
TarArchiveEntry tarArchiveEntry = getTarArchiveEntry(archive, fileName);
assertNotNull(tarArchiveEntry);
assertTrue(tarArchiveEntry.isFile());
assertEquals("should be executable", (tarArchiveEntry.getMode() & 0755), 0755);
}

private static void assertTarArchiveEntryIsSymlink(File archive, String fileName, String expectedTarget) throws IOException {
TarArchiveEntry tarArchiveEntry = getTarArchiveEntry(archive, fileName);
assertNotNull(tarArchiveEntry);
assertTrue("should be a symbolic link", tarArchiveEntry.isSymbolicLink());
assertEquals(expectedTarget, tarArchiveEntry.getLinkName());
}

/**
* Creates the following directory structure with files in the specified
* destination folder
*
* destinationFolder
* |__folderA
* | |__fileA
* |__folderB
* |__fileB
* |__subFolderB
* |__subFileB
*
*
* @param destinationFolder where to create the folder/files.
* @return the list of created files.
* @throws IOException if an error occurs while creating the folders/files.
*/
private static List<File> createFoldersAndSubFolderWithFiles(Path destinationFolder) throws IOException {
List<File> createdFiles = new ArrayList<File>();
Path folderA = destinationFolder.resolve("folderA");
createdFiles.add(Files.createDirectories(folderA).toFile());
createdFiles.add(createFileWithContent(folderA.resolve("fileA")));

Path folderB = destinationFolder.resolve("folderB");
createdFiles.add(Files.createDirectories(folderB).toFile());
createdFiles.add(createFileWithContent(folderB.resolve("fileB")));

Path subFolderB = folderB.resolve("subFolderB");
createdFiles.add(Files.createDirectories(subFolderB).toFile());
createdFiles.add(createFileWithContent(folderA.resolve("subFileB")));
return createdFiles;
}

private static File createFileWithContent(Path fileToCreate) throws IOException {
try (InputStream in = new ByteArrayInputStream("some content".getBytes())) {
Files.copy(in, fileToCreate);
}
return fileToCreate.toFile();
}

private static TarArchiveEntry getTarArchiveEntry(File tarArchive, String filename) throws IOException {
try (TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(
new GZIPInputStream(new BufferedInputStream(new FileInputStream(tarArchive))))) {
TarArchiveEntry entry;
while ((entry = tarArchiveInputStream.getNextTarEntry()) != null) {
if (entry.getName().equals(filename)
|| entry.getName().endsWith("/" + filename)
|| entry.getName().equals(filename + "/")
|| entry.getName().endsWith("/" + filename + "/")) {
return entry;
}
}
}
return null;
}

private static int getNumberOfEntryInArchive(File tarArchive) throws IOException {
int numberOfEntries = 0;
try (TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(
new GZIPInputStream(new BufferedInputStream(new FileInputStream(tarArchive))))) {
while ((tarArchiveInputStream.getNextTarEntry()) != null) {
numberOfEntries++;
}
}
return numberOfEntries;
}
}