Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat (jkube-kit/common) : Add CliDownloaderUtil to download cli binaries (#2454) #2476

Merged
merged 1 commit into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
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 @@ -33,6 +33,7 @@
import org.eclipse.jkube.kit.common.KitLogger;

import static org.apache.commons.io.IOUtils.EOF;
import static org.eclipse.jkube.kit.common.archive.ArchiveDecompressor.extractArchive;

/**
*
Expand All @@ -54,14 +55,11 @@ private IoUtil() { }
* @throws IOException IO Exception
*/
public static void download(KitLogger log, URL downloadUrl, File target) throws IOException {
log.progressStart();
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();
final int length = Integer.parseInt(response.headers(StandardHttpHeaders.CONTENT_LENGTH)
.stream().findAny().orElse("-1"));
download(downloadUrl, response -> {
try (OutputStream out = Files.newOutputStream(target.toPath()); InputStream is = response.body()) {
log.progressStart();
final int length = Integer.parseInt(response.headers(StandardHttpHeaders.CONTENT_LENGTH)
.stream().findAny().orElse("-1"));
final byte[] buffer = new byte[8192];
long readBytes = 0;
int len;
Expand All @@ -70,14 +68,41 @@ public static void download(KitLogger log, URL downloadUrl, File target) throws
log.progressUpdate(target.getName(), "Downloading", getProgressBar(readBytes, length));
out.write(buffer, 0, len);
}
} finally {
log.progressFinished();
}
});
}

/**
* Download and extract a binary file packaged in an archive.
*
* @param downloadUrl download URL to archive containing binary file
* @param target target directory where this archive would be extracted to
* @throws IOException in case of error while downloading or extracting archive
*/
public static void downloadArchive(URL downloadUrl, File target) throws IOException {
download(downloadUrl, response -> {
try (InputStream is = response.body()) {
extractArchive(is, target);
}
});
}

private static void download(URL downloadUrl, HttpResponseConsumer<InputStream> responseConsumer) throws IOException {
try (HttpClient httpClient = HttpClientUtils.getHttpClientFactory().newBuilder(Config.empty()).build()) {
final HttpResponse<InputStream> response = httpClient.sendAsync(
httpClient.newHttpRequestBuilder().timeout(30, TimeUnit.MINUTES).url(downloadUrl).build(), InputStream.class)
.get();
if (!response.isSuccessful()) {
throw new IOException("Got (" + response.code() + ") while downloading from URL " + downloadUrl);
}
responseConsumer.consume(response);
} 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);
} finally {
log.progressFinished();
} catch (ExecutionException e) {
throw new IOException("Failed to download from URL " + downloadUrl, e);
}
}

Expand Down Expand Up @@ -154,4 +179,9 @@ private static String getProgressBar(long bytesRead, long length) {

return ret.toString();
}

@FunctionalInterface
private interface HttpResponseConsumer<T> {
void consume(HttpResponse<T> httpResponse) throws IOException;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,34 @@
*/
package org.eclipse.jkube.kit.common.util;

import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.URL;

import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.common.TestHttpStaticServer;
import org.eclipse.jkube.kit.common.assertj.FileAssertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIOException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

class IoUtilTest {
@TempDir
private File temporaryFolder;

private KitLogger kitLogger;

@BeforeEach
void setUp() {
kitLogger = spy(new KitLogger.SilentLogger());
}

@Test
void findOpenPort() throws IOException {
Expand Down Expand Up @@ -88,4 +107,74 @@ void testSanitizeFileName() {
assertThat(IoUtil.sanitizeFileName("s2i-env-docker.io/fabric8/java:latest")).isEqualTo("s2i-env-docker-io-fabric8-java-latest");
}

@Test
void download_whenRemoteFragmentProvided_thenDownloadToSpecifiedDir() throws IOException {
File remoteDirectory = new File(getClass().getResource("/remote-resources").getFile());
try (TestHttpStaticServer http = new TestHttpStaticServer(remoteDirectory)) {
// Given
URL downloadUrl = new URL(String.format("http://localhost:%d/deployment.yaml", http.getPort()));

// When
IoUtil.download(kitLogger, downloadUrl, new File(temporaryFolder, "deployment.yaml"));

// Then
verify(kitLogger).progressStart();
verify(kitLogger).progressFinished();
FileAssertions.assertThat(temporaryFolder)
.exists()
.fileTree()
.containsExactlyInAnyOrder("deployment.yaml");
}
}

@Test
void downloadArchive_whenUnixArtifactProvided_thenDownloadAndExtract() throws IOException {
File remoteDirectory = new File(getClass().getResource("/downloadable-artifacts").getFile());
try (TestHttpStaticServer http = new TestHttpStaticServer(remoteDirectory)) {
// Given
URL downloadUrl = new URL(String.format("http://localhost:%d/foo-v0.0.1-linux.tgz", http.getPort()));

// When
IoUtil.downloadArchive(downloadUrl, temporaryFolder);

// Then
FileAssertions.assertThat(temporaryFolder)
.exists()
.fileTree()
.containsExactlyInAnyOrder("linux-amd64", "linux-amd64/foo");
}
}

@Test
void downloadArchive_whenZipArtifactProvided_thenDownloadAndExtract() throws IOException {
File remoteDirectory = new File(getClass().getResource("/downloadable-artifacts").getFile());
try (TestHttpStaticServer http = new TestHttpStaticServer(remoteDirectory)) {
// Given
URL downloadUrl = new URL(String.format("http://localhost:%d/foo-v0.0.1-windows.zip", http.getPort()));

// When
IoUtil.downloadArchive(downloadUrl, temporaryFolder);

// Then
FileAssertions.assertThat(temporaryFolder)
.exists()
.fileTree()
.containsExactlyInAnyOrder("foo.exe");
}
}

@Test
void downloadArchive_whenArtifactNotAvailable_thenThrowException() throws IOException {
File remoteDirectory = new File(getClass().getResource("/downloadable-artifacts").getFile());
try (TestHttpStaticServer http = new TestHttpStaticServer(remoteDirectory)) {
// Given
URL downloadUrl = new URL(String.format("http://localhost:%d/idontexist-v0.0.1-linux.tgz", http.getPort()));

// When + Then
assertThatIOException()
.isThrownBy(() -> IoUtil.downloadArchive(downloadUrl, temporaryFolder))
.withMessageContaining("Got (404) while downloading from URL ");
}
}

}
Binary file not shown.
Binary file not shown.