/*
* Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.amazonaws.codepipeline.jenkinsplugin;
import hudson.model.BuildListener;
import hudson.util.IOUtils;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import com.amazonaws.codepipeline.jenkinsplugin.CodePipelineStateModel.CompressionType;
public final class CompressionTools {
private CompressionTools() {}
// Compressing the file to upload to S3 should use the same type of compression as the customer
// used for the input artifact.
public static File compressFile(
final String projectName,
final Path pathToCompress,
final CompressionType compressionType,
final BuildListener listener)
throws IOException {
File compressedArtifacts = null;
try {
switch (compressionType) {
case Zip:
compressedArtifacts = File.createTempFile(projectName + "-", ".zip");
compressZipFile(compressedArtifacts, pathToCompress, listener);
break;
case Tar:
compressedArtifacts = File.createTempFile(projectName + "-", ".tar");
compressTarFile(compressedArtifacts, pathToCompress, listener);
break;
case TarGz:
compressedArtifacts = File.createTempFile(projectName + "-", ".tar.gz");
compressTarGzFile(compressedArtifacts, pathToCompress, listener);
break;
case None:
throw new IllegalArgumentException("No compression type specified.");
}
} catch (final IOException e) {
if (compressedArtifacts != null) {
if (!compressedArtifacts.delete()) {
compressedArtifacts.deleteOnExit();
}
}
throw e;
}
return compressedArtifacts;
}
public static void compressZipFile(
final File temporaryZipFile,
final Path pathToCompress,
final BuildListener listener)
throws IOException {
try (final ZipArchiveOutputStream zipArchiveOutputStream =
new ZipArchiveOutputStream(
new BufferedOutputStream(
new FileOutputStream(temporaryZipFile)))) {
compressArchive(
pathToCompress,
zipArchiveOutputStream,
new ArchiveEntryFactory(CompressionType.Zip),
CompressionType.Zip,
listener);
}
}
public static void compressTarFile(
final File temporaryTarFile,
final Path pathToCompress,
final BuildListener listener)
throws IOException {
try (final TarArchiveOutputStream tarArchiveOutputStream =
new TarArchiveOutputStream(
new BufferedOutputStream(
new FileOutputStream(temporaryTarFile)))) {
compressArchive(
pathToCompress,
tarArchiveOutputStream,
new ArchiveEntryFactory(CompressionType.Tar),
CompressionType.Tar,
listener);
}
}
public static void compressTarGzFile(
final File temporaryTarGzFile,
final Path pathToCompress,
final BuildListener listener)
throws IOException {
try (final TarArchiveOutputStream tarGzArchiveOutputStream =
new TarArchiveOutputStream(
new BufferedOutputStream(
new GzipCompressorOutputStream(
new FileOutputStream(temporaryTarGzFile))))) {
compressArchive(
pathToCompress,
tarGzArchiveOutputStream,
new ArchiveEntryFactory(CompressionType.TarGz),
CompressionType.TarGz,
listener);
}
}
private static void compressArchive(
final Path pathToCompress,
final ArchiveOutputStream archiveOutputStream,
final ArchiveEntryFactory archiveEntryFactory,
final CompressionType compressionType,
final BuildListener listener)
throws IOException {
final List<File> files = addFilesToCompress(pathToCompress, listener);
LoggingHelper.log(listener, "Compressing directory '%s' as a '%s' archive",
pathToCompress.toString(),
compressionType.name());
for (final File file : files) {
final String newTarFileName = pathToCompress.relativize(file.toPath()).toString();
final ArchiveEntry archiveEntry = archiveEntryFactory.create(file, newTarFileName);
archiveOutputStream.putArchiveEntry(archiveEntry);
try (final FileInputStream fileInputStream = new FileInputStream(file)) {
IOUtils.copy(fileInputStream, archiveOutputStream);
}
archiveOutputStream.closeArchiveEntry();
}
}
public static List<File> addFilesToCompress(final Path pathToCompress, final BuildListener listener) throws IOException {
final List<File> files = new ArrayList<>();
if (pathToCompress != null) {
Files.walkFileTree(
pathToCompress,
EnumSet.of(FileVisitOption.FOLLOW_LINKS),
Integer.MAX_VALUE,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
files.add(file.toFile());
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(final Path file, final IOException e) throws IOException {
if (e != null) {
LoggingHelper.log(listener, "Failed to visit file '%s'. Error: %s.", file.toString(), e.getMessage());
LoggingHelper.log(listener, e);
throw e;
}
return FileVisitResult.CONTINUE;
}
});
}
return files;
}
public static Path resolveWorkspacePath(
final File workspace,
final String outputPath)
throws FileNotFoundException {
Path path = null;
if (workspace != null) {
if (!outputPath.contains(workspace.getAbsolutePath())) {
path = Paths.get(workspace.getAbsolutePath(), outputPath);
}
else {
path = Paths.get(outputPath);
}
}
else {
final String attemptPath;
final URL tmp = CompressionTools.class.getClassLoader().getResource("");
if (tmp != null) {
attemptPath = tmp.getPath();
}
else {
attemptPath = "";
}
if (attemptPath != null) {
path = Paths.get(attemptPath);
}
}
if (path == null) {
throw new FileNotFoundException("Could not resolve path for " + outputPath);
}
path.resolve(outputPath);
return path;
}
}