diff --git a/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/CliDownloaderUtil.java b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/CliDownloaderUtil.java new file mode 100644 index 0000000000..9fa5a09cf4 --- /dev/null +++ b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/CliDownloaderUtil.java @@ -0,0 +1,70 @@ +/* + * 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.util; + +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.http.HttpClient; +import io.fabric8.kubernetes.client.http.HttpResponse; +import io.fabric8.kubernetes.client.utils.HttpClientUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static org.apache.commons.io.FilenameUtils.removeExtension; +import static org.eclipse.jkube.kit.common.archive.ArchiveDecompressor.extractArchive; + +public class CliDownloaderUtil { + private CliDownloaderUtil() { } + + public static String downloadCli(String baseDownloadUrl, String cliBinaryPrefix, String cliArtifactName, File outputDirectory) throws IOException { + URL downloadUrl = new URL(String.format("%s/%s", baseDownloadUrl, cliArtifactName)); + File targetExtractionDir = outputDirectory.toPath().resolve(removeExtension(cliArtifactName)).toFile(); + + downloadAndExtractTo(downloadUrl, targetExtractionDir); + + return getCliBinaryPathFromExtractedDir(targetExtractionDir, cliBinaryPrefix); + } + + private static void downloadAndExtractTo(URL downloadUrl, File target) throws IOException { + try (HttpClient client = HttpClientUtils.getHttpClientFactory().newBuilder(Config.empty()).build()) { + final HttpResponse response = client.sendAsync( + client.newHttpRequestBuilder().timeout(30, TimeUnit.MINUTES).url(downloadUrl).build(), InputStream.class) + .get(); + if (!response.isSuccessful()) { + throw new IOException("Server returned (" + response.code() + ") while downloading " + downloadUrl); + } + try (InputStream is = response.body()) { + extractArchive(is, target); + } + } catch(InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new IOException("Download interrupted", ex); + } catch (IOException | ExecutionException e) { + throw new IOException("Failed to download URL " + downloadUrl + " to " + target + ": " + e, e); + } + } + + private static String getCliBinaryPathFromExtractedDir(File targetExtractionDir, String binaryPrefix) { + String cliPath = null; + File[] cliArtifactList = targetExtractionDir.listFiles(f -> f.getName().startsWith(binaryPrefix)); + if (cliArtifactList != null && cliArtifactList.length >= 1) { + cliPath = cliArtifactList[0].getAbsolutePath(); + } + return cliPath; + } +} \ No newline at end of file diff --git a/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/CliDownloaderUtilTest.java b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/CliDownloaderUtilTest.java new file mode 100644 index 0000000000..a0ec9cdcbd --- /dev/null +++ b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/CliDownloaderUtilTest.java @@ -0,0 +1,82 @@ +/* + * 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.util; + +import org.eclipse.jkube.kit.common.TestHttpStaticServer; +import org.eclipse.jkube.kit.common.assertj.FileAssertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThatIOException; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +class CliDownloaderUtilTest { + @TempDir + private File temporaryFolder; + + @Test + void downloadCLI_whenUnixArtifactProvided_thenDownloadAndExtract() throws IOException { + File remoteDirectory = new File(getClass().getResource("/downloadable-artifacts").getFile()); + try (TestHttpStaticServer http = new TestHttpStaticServer(remoteDirectory)) { + // Given + String baseUrl = String.format("http://localhost:%d", http.getPort()); + + // When + String downloadPath = CliDownloaderUtil.downloadCli(baseUrl, "foo", "foo-v0.0.1-linux.tgz", temporaryFolder); + + // Then + assertThat(downloadPath).contains("foo-v0.0.1-linux", "foo"); + FileAssertions.assertThat(temporaryFolder) + .exists() + .fileTree() + .containsExactlyInAnyOrder("foo-v0.0.1-linux", "foo-v0.0.1-linux/foo"); + } + } + + @Test + void downloadCLI_whenZipArtifactProvided_thenDownloadAndExtract() throws IOException { + File remoteDirectory = new File(getClass().getResource("/downloadable-artifacts").getFile()); + try (TestHttpStaticServer http = new TestHttpStaticServer(remoteDirectory)) { + // Given + String baseUrl = String.format("http://localhost:%d", http.getPort()); + + // When + String downloadPath = CliDownloaderUtil.downloadCli(baseUrl, "foo", "foo-v0.0.1-windows.zip", temporaryFolder); + + // Then + assertThat(downloadPath).contains("foo-v0.0.1-windows", "foo.exe"); + FileAssertions.assertThat(temporaryFolder) + .exists() + .fileTree() + .containsExactlyInAnyOrder("foo-v0.0.1-windows", "foo-v0.0.1-windows/foo.exe"); + } + } + + @Test + void downloadCLI_whenArtifactNotAvailable_thenThrowException() throws IOException { + File remoteDirectory = new File(getClass().getResource("/downloadable-artifacts").getFile()); + try (TestHttpStaticServer http = new TestHttpStaticServer(remoteDirectory)) { + // Given + String baseUrl = String.format("http://localhost:%d", http.getPort()); + + // When + Then + assertThatIOException() + .isThrownBy(() -> CliDownloaderUtil.downloadCli(baseUrl, "idontexist", "idontexist-v0.0.1-linux.tgz", temporaryFolder)) + .withMessageContaining("Failed to download"); + } + } +} diff --git a/jkube-kit/common/src/test/resources/downloadable-artifacts/foo-v0.0.1-linux.tgz b/jkube-kit/common/src/test/resources/downloadable-artifacts/foo-v0.0.1-linux.tgz new file mode 100644 index 0000000000..a8ea74b25e Binary files /dev/null and b/jkube-kit/common/src/test/resources/downloadable-artifacts/foo-v0.0.1-linux.tgz differ diff --git a/jkube-kit/common/src/test/resources/downloadable-artifacts/foo-v0.0.1-windows.zip b/jkube-kit/common/src/test/resources/downloadable-artifacts/foo-v0.0.1-windows.zip new file mode 100644 index 0000000000..b4bafba62b Binary files /dev/null and b/jkube-kit/common/src/test/resources/downloadable-artifacts/foo-v0.0.1-windows.zip differ