From b7e4657ed6bb9fdb689f331cd77dff1c77b42822 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Wed, 22 Nov 2023 19:32:22 +0530 Subject: [PATCH] feat (jkube-kit/common) : Add utility class to decompress archive files (#2456) Add JKubeArchiveDecompressor utility class. It's extractArchive method would extract the utility class to specified folder. It only supports `.tgz` and `.zip` files at the moment. Signed-off-by: Rohan Kumar --- CHANGELOG.md | 1 + .../archive/JKubeArchiveDecompressor.java | 96 ++++++++++++++++++ .../archive/JKubeArchiveDecompressorTest.java | 65 ++++++++++++ .../resources/archives/nested-archive.tgz | Bin 0 -> 170 bytes .../resources/archives/nested-archive.zip | Bin 0 -> 500 bytes .../resources/archives/pack-v0.31.0-linux.tgz | Bin 0 -> 112 bytes .../archives/pack-v0.31.0-windows.zip | Bin 0 -> 166 bytes 7 files changed, 162 insertions(+) create mode 100644 jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/archive/JKubeArchiveDecompressor.java create mode 100644 jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/archive/JKubeArchiveDecompressorTest.java create mode 100644 jkube-kit/common/src/test/resources/archives/nested-archive.tgz create mode 100644 jkube-kit/common/src/test/resources/archives/nested-archive.zip create mode 100644 jkube-kit/common/src/test/resources/archives/pack-v0.31.0-linux.tgz create mode 100644 jkube-kit/common/src/test/resources/archives/pack-v0.31.0-windows.zip diff --git a/CHANGELOG.md b/CHANGELOG.md index 19bb758c45..1302b6fde2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Usage: * Fix #1690: Base images based on ubi9 * Fix #2390: support for all missing Chart.yaml fields * Fix #2444: Add support for Spring Boot application properties placeholders +* Fix #2456: Add utility class to decompress archive files ### 1.15.0 (2023-11-10) * Fix #2138: Support for Spring Boot Native Image diff --git a/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/archive/JKubeArchiveDecompressor.java b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/archive/JKubeArchiveDecompressor.java new file mode 100644 index 0000000000..412375e318 --- /dev/null +++ b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/archive/JKubeArchiveDecompressor.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.kit.common.archive; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; +import org.eclipse.jkube.kit.common.util.FileUtil; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class JKubeArchiveDecompressor { + private JKubeArchiveDecompressor() { } + + /** + * Extracts a given archive file to specified target directory + * + * @param inputArchiveFile input archive file + * @param targetDirectory target folder where you want to extract + * @throws IOException in case of failure while trying to create any directory + */ + public static void extractArchive(File inputArchiveFile, File targetDirectory) throws IOException { + if (targetDirectory.exists()) { + FileUtil.cleanDirectory(targetDirectory); + } + Files.createDirectory(targetDirectory.toPath()); + if (inputArchiveFile.getName().endsWith(".tgz")) { + extractTarArchive(inputArchiveFile, targetDirectory.toPath()); + } else if (inputArchiveFile.getName().endsWith(".zip")) { + extractZipArchive(inputArchiveFile, targetDirectory.toPath()); + } + } + + private static void extractTarArchive(File downloadedArchive, Path targetExtractionDir) throws IOException { + try (BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(downloadedArchive.toPath())); + TarArchiveInputStream tar = new TarArchiveInputStream(new GzipCompressorInputStream(inputStream))) { + ArchiveEntry entry; + while ((entry = tar.getNextEntry()) != null) { + Path extractTo = targetExtractionDir.resolve(entry.getName()); + if (entry.isDirectory()) { + Files.createDirectories(extractTo); + } else { + Files.copy(tar, extractTo); + } + } + } + } + + private static void extractZipArchive(File downloadedArchive, Path targetExtractionDir) throws IOException { + byte[] buffer = new byte[1024]; + try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(downloadedArchive.toPath()))) { + ZipEntry zipEntry = zis.getNextEntry(); + while (zipEntry != null) { + File newFile = new File(targetExtractionDir.toFile(), zipEntry.getName()); + if (zipEntry.isDirectory()) { + if (!newFile.isDirectory() && !newFile.mkdirs()) { + throw new IOException("Failed to create directory " + newFile); + } + } else { + // fix for Windows-created archives + File parent = newFile.getParentFile(); + if (!parent.isDirectory() && !parent.mkdirs()) { + throw new IOException("Failed to create directory " + parent); + } + + try (FileOutputStream fos = new FileOutputStream(newFile)) { + int len; + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + } + } + zipEntry = zis.getNextEntry(); + } + zis.closeEntry(); + } + } +} diff --git a/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/archive/JKubeArchiveDecompressorTest.java b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/archive/JKubeArchiveDecompressorTest.java new file mode 100644 index 0000000000..551d3b5a93 --- /dev/null +++ b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/archive/JKubeArchiveDecompressorTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.kit.common.archive; + +import org.eclipse.jkube.kit.common.assertj.FileAssertions; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.io.File; +import java.io.IOException; + +class JKubeArchiveDecompressorTest { + @TempDir + private File temporaryFolder; + + @ParameterizedTest + @CsvSource({ + "/archives/pack-v0.31.0-linux.tgz,pack", + "/archives/pack-v0.31.0-windows.zip,pack.exe" + }) + void extractArchive_whenArchiveWithSingleFileProvided_thenExtractToSpecifiedDir(String filePath, String expectedFileInExtractedArchiveName) throws IOException { + // Given + File input = new File(getClass().getResource(filePath).getFile()); + + // When + JKubeArchiveDecompressor.extractArchive(input, temporaryFolder); + + // Then + FileAssertions.assertThat(temporaryFolder) + .exists() + .fileTree() + .containsExactlyInAnyOrder(expectedFileInExtractedArchiveName); + } + + @ParameterizedTest + @CsvSource({ + "/archives/nested-archive.tgz,nested,nested/folder,nested/folder/artifact", + "/archives/nested-archive.zip,nested,nested/folder,nested/folder/artifact.exe" + }) + void extractArchive_whenArchiveWithNestedDir_thenExtractToSpecifiedDir(String filePath, String parentDir, String artifactParentDir, String artifact) throws IOException { + // Given + File input = new File(getClass().getResource(filePath).getFile()); + + // When + JKubeArchiveDecompressor.extractArchive(input, temporaryFolder); + + // Then + FileAssertions.assertThat(temporaryFolder) + .exists() + .fileTree() + .containsExactlyInAnyOrder(parentDir, artifactParentDir, artifact); + } +} diff --git a/jkube-kit/common/src/test/resources/archives/nested-archive.tgz b/jkube-kit/common/src/test/resources/archives/nested-archive.tgz new file mode 100644 index 0000000000000000000000000000000000000000..60dbb7eef175a4672314a47fbff0c2042f1544ea GIT binary patch literal 170 zcmb2|=3oE==C_wFay1!f5rFC3u&EK`g-5>k52vazxDb*#bv?LeVKQymmQc|%>AodbaBbXcgFXAfBpw? S4}`q2U0Si`oGOC`0|NkRDp3>w literal 0 HcmV?d00001 diff --git a/jkube-kit/common/src/test/resources/archives/nested-archive.zip b/jkube-kit/common/src/test/resources/archives/nested-archive.zip new file mode 100644 index 0000000000000000000000000000000000000000..ba902aca2d38eaa987ee980b1093cd9da6a834b3 GIT binary patch literal 500 zcmWIWW@h1H00G-YW#M23lwfC&VaQ7@E=f(%4-MgDV4f=ym#QWfms(oE&A`a=f|-E< zOa#CU;987o03X7DwEUcu)FOyE(m*rtn&XD%5Gf3E5{pVQ(-MDCOs^sv1~Lt57~JDPGeI85V0E7@A*azWtWr_|0XbXvIVxj+WV|)_oHidk(UmB z@?KqfKEt&BS#(#iuHf1yM$2Uk=X=fibn$rE?Y|d~n=CDi-j*#NU%tNzVla^Wu~_~3 LrE?|>8Vn2osOvC= literal 0 HcmV?d00001 diff --git a/jkube-kit/common/src/test/resources/archives/pack-v0.31.0-windows.zip b/jkube-kit/common/src/test/resources/archives/pack-v0.31.0-windows.zip new file mode 100644 index 0000000000000000000000000000000000000000..06cc3ed7c5363d079452f3578058ac6c92be97a6 GIT binary patch literal 166 zcmWIWW@h1H0D;%Dlf%IbD8a!X!%&czoUNByks2Dp$-r#**CiE%ODnh;7+GF0GcbUO o0B=SnIc8jDNWhI;(g