Skip to content

Commit

Permalink
feat (jkube-kit/common) : Add CLIDownloaderUtil to download cli binar…
Browse files Browse the repository at this point in the history
…ies (eclipse-jkube#2454)

Add CLIDownloaderUtil to download CLI binaries from remote sources.

Signed-off-by: Rohan Kumar <rohaan@redhat.com>
  • Loading branch information
rohanKanojia committed Nov 28, 2023
1 parent 031bca4 commit cb67e87
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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 org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;

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 binaryPrefix, String artifactName, File outputDirectory) throws IOException {
URL downloadUrl = new URL(String.format("%s/%s", baseDownloadUrl, artifactName));
File targetExtractionDir = outputDirectory.toPath().resolve(removeExtension(artifactName)).toFile();

downloadAndExtractTo(downloadUrl, targetExtractionDir);

return getCliBinaryPathFromExtractedDir(targetExtractionDir, binaryPrefix);
}

private static void downloadAndExtractTo(URL downloadUrl, File target) throws IOException {
try (HttpClient client = HttpClientUtils.createHttpClient(Config.empty()).newBuilder().build()) {
final HttpResponse<InputStream> 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;
}

public static boolean isCliDownloadPlatformWindows() {
return SystemUtils.IS_OS_WINDOWS;
}

public static String getCliDownloadPlatformApplicableBinary(String binaryName) {
return isCliDownloadPlatformWindows() ? binaryName + ".exe" : binaryName;
}

public static boolean isCliDownloadPlatformProcessorArchitectureARM() {
String architecture = System.getProperty("os.arch");
return StringUtils.isNotBlank(architecture) && architecture.contains("aarch");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* 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.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
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;

import static org.assertj.core.api.Assertions.assertThatIOException;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;

class CliDownloaderUtilTest {
@TempDir
private File temporaryFolder;

@Test
@EnabledOnOs(OS.LINUX)
void isCLIDownloadPlatformWindows_whenLinuxOs_thenReturnFalse() {
assertThat(CliDownloaderUtil.isCliDownloadPlatformWindows()).isFalse();
}

@Test
@EnabledOnOs(OS.LINUX)
void getCLIDownloadPlatformApplicableBinary_whenLinuxOs_thenReturnFalse() {
assertThat(CliDownloaderUtil.getCliDownloadPlatformApplicableBinary("foo")).isEqualTo("foo");
}

@ParameterizedTest
@CsvSource({"amd64,false", "aarch64,true"})
void isCLIDownloadPlatformProcessorArchitectureARM_whenArchitectureProvided_shouldReturnApplicableResult(String arch, boolean expectedResult) {
String defaultArchValue = System.getProperty("os.arch");
try {
// Given
System.setProperty("os.arch", arch);

// When
assertThat(CliDownloaderUtil.isCliDownloadPlatformProcessorArchitectureARM()).isEqualTo(expectedResult);

// Then
} finally {
System.setProperty("os.arch", defaultArchValue);
}
}

@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");
}
}
}
Binary file not shown.
Binary file not shown.

0 comments on commit cb67e87

Please sign in to comment.