Skip to content

Commit

Permalink
feat (jkube-kit/common) : Add IoUtil.downloadArchive method to downlo…
Browse files Browse the repository at this point in the history
…ad archives (#2454)

+ Refactor IoUtil.download method to be able to extract response body
  for further consumption
+ Add IoUtil.downloadArchive method to download and extract archive to
  specified folder

Signed-off-by: Rohan Kumar <rohaan@redhat.com>
  • Loading branch information
rohanKanojia committed Nov 30, 2023
1 parent b3281a8 commit 00bc529
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 11 deletions.
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.

0 comments on commit 00bc529

Please sign in to comment.