Skip to content

Commit 97f0e8d

Browse files
committed
Use Streams instead of java.io.File
1 parent 2c6a662 commit 97f0e8d

File tree

10 files changed

+92
-41
lines changed

10 files changed

+92
-41
lines changed

api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ContainerApi.kt

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import kotlinx.coroutines.flow.collect
4141
import kotlinx.coroutines.launch
4242
import kotlinx.coroutines.runBlocking
4343
import kotlinx.coroutines.withTimeout
44+
import okio.Source
4445
import okio.source
4546
import java.io.InputStream
4647
import java.net.HttpURLConnection.HTTP_NOT_FOUND
@@ -64,21 +65,21 @@ class ContainerApi(dockerClientConfig: DockerClientConfig = defaultClientConfig,
6465
* Get a tar archive of a resource in the filesystem of container id.
6566
* @param id ID or name of the container
6667
* @param path Resource in the container’s filesystem to archive.
67-
* @return void
68+
* @return InputStream
6869
* @throws UnsupportedOperationException If the API returns an informational or redirection response
6970
* @throws ClientException If the API returns a client error response
7071
* @throws ServerException If the API returns a server error response
7172
*/
7273
@Throws(UnsupportedOperationException::class, ClientException::class, ServerException::class)
73-
fun containerArchive(id: String, path: String): java.io.File {
74+
fun containerArchive(id: String, path: String): InputStream {
7475
val localVariableConfig = containerArchiveRequestConfig(id = id, path = path)
7576

76-
val localVarResponse = request<java.io.File?>(
77+
val localVarResponse = request<InputStream?>(
7778
localVariableConfig
7879
)
7980

8081
return when (localVarResponse.responseType) {
81-
ResponseType.Success -> (localVarResponse as Success<*>).data as java.io.File
82+
ResponseType.Success -> (localVarResponse as Success<*>).data as InputStream
8283
ResponseType.Informational -> throw UnsupportedOperationException("Client does not support Informational responses.")
8384
ResponseType.Redirection -> throw UnsupportedOperationException("Client does not support Redirection responses.")
8485
ResponseType.ClientError -> {
@@ -584,15 +585,15 @@ class ContainerApi(dockerClientConfig: DockerClientConfig = defaultClientConfig,
584585
* @throws ServerException If the API returns a server error response
585586
*/
586587
@Throws(UnsupportedOperationException::class, ClientException::class, ServerException::class)
587-
fun containerExport(id: String): java.io.File {
588+
fun containerExport(id: String): InputStream {
588589
val localVariableConfig = containerExportRequestConfig(id = id)
589590

590-
val localVarResponse = request<java.io.File>(
591+
val localVarResponse = request<InputStream>(
591592
localVariableConfig
592593
)
593594

594595
return when (localVarResponse.responseType) {
595-
ResponseType.Success -> (localVarResponse as Success<*>).data as java.io.File
596+
ResponseType.Success -> (localVarResponse as Success<*>).data as InputStream
596597
ResponseType.Informational -> throw UnsupportedOperationException("Client does not support Informational responses.")
597598
ResponseType.Redirection -> throw UnsupportedOperationException("Client does not support Redirection responses.")
598599
ResponseType.ClientError -> {
@@ -829,7 +830,7 @@ class ContainerApi(dockerClientConfig: DockerClientConfig = defaultClientConfig,
829830
* @param until Only return logs before this time, as a UNIX timestamp (optional, default to 0)
830831
* @param timestamps Add timestamps to every log line (optional, default to false)
831832
* @param tail Only return this number of log lines from the end of the logs. Specify as an integer or &#x60;all&#x60; to output all log lines. (optional, default to "all")
832-
* @return java.io.File
833+
* @return void
833834
* @throws UnsupportedOperationException If the API returns an informational or redirection response
834835
* @throws ClientException If the API returns a client error response
835836
* @throws ServerException If the API returns a server error response
@@ -1699,7 +1700,7 @@ class ContainerApi(dockerClientConfig: DockerClientConfig = defaultClientConfig,
16991700
* @return RequestConfig
17001701
*/
17011702
fun putContainerArchiveRequestConfig(id: String, path: String, inputStream: InputStream, noOverwriteDirNonDir: String?, copyUIDGID: String?): RequestConfig {
1702-
val localVariableBody: Any = inputStream.source()
1703+
val localVariableBody: Source = inputStream.source()
17031704
val localVariableQuery: MultiValueMap = mutableMapOf<String, List<String>>()
17041705
.apply {
17051706
put("path", listOf(path))

api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ImageApi.kt

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import kotlinx.coroutines.flow.collect
4646
import kotlinx.coroutines.launch
4747
import kotlinx.coroutines.runBlocking
4848
import kotlinx.coroutines.withTimeout
49+
import okio.Source
4950
import okio.source
5051
import java.io.InputStream
5152
import java.net.Proxy
@@ -741,37 +742,37 @@ class ImageApi(dockerClientConfig: DockerClientConfig = defaultClientConfig, pro
741742
* Export an image
742743
* Get a tarball containing all images and metadata for a repository. If &#x60;name&#x60; is a specific name and tag (e.g. &#x60;ubuntu:latest&#x60;), then only that image (and its parents) are returned. If &#x60;name&#x60; is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the &#x60;repositories&#x60; file in the tarball, as there were no image names referenced. ### Image tarball format An image tarball contains one directory per image layer (named using its long ID), each containing these files: - &#x60;VERSION&#x60;: currently &#x60;1.0&#x60; - the file format version - &#x60;json&#x60;: detailed layer information, similar to &#x60;docker inspect layer_id&#x60; - &#x60;layer.tar&#x60;: A tarfile containing the filesystem changes in this layer The &#x60;layer.tar&#x60; file contains &#x60;aufs&#x60; style &#x60;.wh..wh.aufs&#x60; files and directories for storing attribute changes and deletions. If the tarball defines a repository, the tarball should also include a &#x60;repositories&#x60; file at the root that contains a list of repository and tag names mapped to layer IDs. &#x60;&#x60;&#x60;json { \&quot;hello-world\&quot;: { \&quot;latest\&quot;: \&quot;565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1\&quot; } } &#x60;&#x60;&#x60;
743744
* @param name Image name or ID
744-
* @return java.io.File
745+
* @return InputStream
745746
* @throws UnsupportedOperationException If the API returns an informational or redirection response
746747
* @throws ClientException If the API returns a client error response
747748
* @throws ServerException If the API returns a server error response
748749
*/
749750
@Suppress("UNCHECKED_CAST")
750751
@Throws(UnsupportedOperationException::class, ClientException::class, ServerException::class)
751-
fun imageGet(name: String): java.io.File {
752+
fun imageGet(name: String): InputStream {
752753
return this.imageGetAll(Collections.singletonList(name))
753754
}
754755

755756
/**
756757
* Export several images
757758
* Get a tarball containing all images and metadata for several image repositories. For each value of the &#x60;names&#x60; parameter: if it is a specific name and tag (e.g. &#x60;ubuntu:latest&#x60;), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the &#39;repositories&#39; file for this image ID. For details on the format, see the [export image endpoint](#operation/ImageGet).
758759
* @param names Image names to filter by (optional)
759-
* @return java.io.File
760+
* @return InputStream
760761
* @throws UnsupportedOperationException If the API returns an informational or redirection response
761762
* @throws ClientException If the API returns a client error response
762763
* @throws ServerException If the API returns a server error response
763764
*/
764765
@Suppress("UNCHECKED_CAST")
765766
@Throws(UnsupportedOperationException::class, ClientException::class, ServerException::class)
766-
fun imageGetAll(names: List<String>?): java.io.File {
767+
fun imageGetAll(names: List<String>?): InputStream {
767768
val localVariableConfig = imageGetAllRequestConfig(names = names)
768769

769-
val localVarResponse = request<java.io.File>(
770+
val localVarResponse = request<InputStream>(
770771
localVariableConfig
771772
)
772773

773774
return when (localVarResponse.responseType) {
774-
ResponseType.Success -> (localVarResponse as Success<*>).data as java.io.File
775+
ResponseType.Success -> (localVarResponse as Success<*>).data as InputStream
775776
ResponseType.Informational -> throw UnsupportedOperationException("Client does not support Informational responses.")
776777
ResponseType.Redirection -> throw UnsupportedOperationException("Client does not support Redirection responses.")
777778
ResponseType.ClientError -> {
@@ -999,7 +1000,7 @@ class ImageApi(dockerClientConfig: DockerClientConfig = defaultClientConfig, pro
9991000
@Throws(UnsupportedOperationException::class, ClientException::class, ServerException::class)
10001001
@JvmOverloads
10011002
fun imageLoad(
1002-
quiet: Boolean?, imagesTarball: java.io.File?,
1003+
quiet: Boolean?, imagesTarball: InputStream?,
10031004
callback: StreamCallback<CreateImageInfo?>? = null, timeoutMillis: Long? = null /*= 24.hours.toLongMilliseconds()*/
10041005
) {
10051006
val localVariableConfig = imageLoadRequestConfig(quiet = quiet, imagesTarball = imagesTarball)
@@ -1048,8 +1049,8 @@ class ImageApi(dockerClientConfig: DockerClientConfig = defaultClientConfig, pro
10481049
* @param imagesTarball Tar archive containing images (optional)
10491050
* @return RequestConfig
10501051
*/
1051-
fun imageLoadRequestConfig(quiet: Boolean?, imagesTarball: java.io.File?): RequestConfig {
1052-
val localVariableBody: Any? = imagesTarball
1052+
fun imageLoadRequestConfig(quiet: Boolean?, imagesTarball: InputStream?): RequestConfig {
1053+
val localVariableBody: Source? = imagesTarball?.source()
10531054
val localVariableQuery: MultiValueMap = mutableMapOf<String, List<String>>()
10541055
.apply {
10551056
if (quiet != null) {

api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/PluginApi.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ import de.gesellix.docker.remote.api.core.ResponseType
2626
import de.gesellix.docker.remote.api.core.ServerError
2727
import de.gesellix.docker.remote.api.core.ServerException
2828
import de.gesellix.docker.remote.api.core.Success
29+
import okio.Source
30+
import okio.source
31+
import java.io.InputStream
2932
import java.net.Proxy
3033

3134
class PluginApi(dockerClientConfig: DockerClientConfig = defaultClientConfig, proxy: Proxy?) : ApiClient(dockerClientConfig, proxy) {
@@ -107,7 +110,7 @@ class PluginApi(dockerClientConfig: DockerClientConfig = defaultClientConfig, pr
107110
* @throws ServerException If the API returns a server error response
108111
*/
109112
@Throws(UnsupportedOperationException::class, ClientException::class, ServerException::class)
110-
fun pluginCreate(name: String, tarContext: java.io.File?) {
113+
fun pluginCreate(name: String, tarContext: InputStream?) {
111114
val localVariableConfig = pluginCreateRequestConfig(name = name, tarContext = tarContext)
112115

113116
val localVarResponse = request<Any?>(
@@ -136,8 +139,8 @@ class PluginApi(dockerClientConfig: DockerClientConfig = defaultClientConfig, pr
136139
* @param tarContext Path to tar containing plugin rootfs and manifest (optional)
137140
* @return RequestConfig
138141
*/
139-
fun pluginCreateRequestConfig(name: String, tarContext: java.io.File?): RequestConfig {
140-
val localVariableBody: Any? = tarContext
142+
fun pluginCreateRequestConfig(name: String, tarContext: InputStream?): RequestConfig {
143+
val localVariableBody: Source? = tarContext?.source()
141144
val localVariableQuery: MultiValueMap = mutableMapOf<String, List<String>>()
142145
.apply {
143146
put("name", listOf(name))

api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ServiceApi.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ class ServiceApi(dockerClientConfig: DockerClientConfig = defaultClientConfig, p
297297
* @param since Only return logs since this time, as a UNIX timestamp (optional, default to 0)
298298
* @param timestamps Add timestamps to every log line (optional, default to false)
299299
* @param tail Only return this number of log lines from the end of the logs. Specify as an integer or &#x60;all&#x60; to output all log lines. (optional, default to "all")
300-
* @return java.io.File
300+
* @return void
301301
* @throws UnsupportedOperationException If the API returns an informational or redirection response
302302
* @throws ClientException If the API returns a client error response
303303
* @throws ServerException If the API returns a server error response

api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/TaskApi.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ class TaskApi(dockerClientConfig: DockerClientConfig = defaultClientConfig, prox
168168
* @param since Only return logs since this time, as a UNIX timestamp (optional, default to 0)
169169
* @param timestamps Add timestamps to every log line (optional, default to false)
170170
* @param tail Only return this number of log lines from the end of the logs. Specify as an integer or &#x60;all&#x60; to output all log lines. (optional, default to "all")
171-
* @return java.io.File
171+
* @return void
172172
* @throws UnsupportedOperationException If the API returns an informational or redirection response
173173
* @throws ClientException If the API returns a client error response
174174
* @throws ServerException If the API returns a server error response

api-client/src/main/kotlin/de/gesellix/docker/remote/api/core/ApiClient.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import okhttp3.RequestBody.Companion.toRequestBody
2525
import okhttp3.ResponseBody
2626
import okio.Source
2727
import java.io.File
28+
import java.io.InputStream
2829
import java.lang.reflect.Type
2930
import java.net.Proxy
3031
import java.util.*
@@ -96,8 +97,8 @@ open class ApiClient(
9697
// if (mediaType == null && body.contentLength() == 0L) {
9798
// return null
9899
// }
99-
if (T::class.java == File::class.java) {
100-
return body.consumeFile() as T
100+
if (T::class.java == InputStream::class.java) {
101+
return body.consumeInputStream() as T
101102
}
102103
return when (mediaType) {
103104
JsonMediaType -> when (type) {

api-client/src/main/kotlin/de/gesellix/docker/remote/api/core/ResponseConsumer.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import okhttp3.internal.closeQuietly
1111
import okio.appendingSink
1212
import okio.buffer
1313
import java.io.File
14+
import java.io.InputStream
1415
import java.lang.reflect.Type
1516
import java.nio.file.Files
1617

@@ -27,6 +28,13 @@ fun ResponseBody?.consumeFile(): File? {
2728
return f
2829
}
2930

31+
fun ResponseBody?.consumeInputStream(): InputStream? {
32+
if (this == null) {
33+
return null
34+
}
35+
return source().inputStream()
36+
}
37+
3038
inline fun <reified T : Any?> ResponseBody?.consumeStream(mediaType: String?): Flow<T> {
3139
if (this == null) {
3240
return emptyFlow()

api-client/src/test/java/de/gesellix/docker/remote/api/client/ContainerApiIntegrationTest.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.slf4j.Logger;
3232

3333
import java.io.File;
34-
import java.io.FileInputStream;
3534
import java.io.IOException;
3635
import java.io.InputStream;
3736
import java.time.Duration;
@@ -171,7 +170,13 @@ public void containerExport() {
171170
null
172171
);
173172
containerApi.containerCreate(containerCreateRequest, "container-export");
174-
assertDoesNotThrow(() -> containerApi.containerExport("container-export"));
173+
Long size = assertDoesNotThrow(() -> {
174+
InputStream inputStream = containerApi.containerExport("container-export");
175+
long byteCount = Okio.buffer(Okio.source(inputStream)).readAll(Okio.blackhole());
176+
inputStream.close();
177+
return byteCount;
178+
});
179+
assertTrue(size > 0);
175180
removeContainer(engineApiClient, "container-export");
176181
}
177182

@@ -201,9 +206,10 @@ public void containerArchiveInfoGetAndPut() throws IOException {
201206
containerApi.containerCreate(containerCreateRequest, "container-archive-info-test");
202207
containerApi.containerStart("container-archive-info-test", null);
203208

204-
File archive = containerApi.containerArchive("container-archive-info-test", "/gattaca.txt");
209+
InputStream archive = containerApi.containerArchive("container-archive-info-test", "/gattaca.txt");
205210
File extractedDir = new TarUtil().unTar(archive);
206-
String fileContent = Okio.buffer(Okio.source(new File(extractedDir, "gattaca.txt"))).readUtf8();
211+
File gattaca = new File(extractedDir, "gattaca.txt");
212+
String fileContent = Okio.buffer(Okio.source(gattaca)).readUtf8();
207213
assertEquals("The wind\ncaught it.\n", fileContent.replaceAll("\r", ""));
208214

209215
IdResponse containerExec = engineApiClient.getExecApi().containerExec(
@@ -214,8 +220,7 @@ public void containerArchiveInfoGetAndPut() throws IOException {
214220
containerExec.getId(),
215221
new ExecStartConfig(null, null));
216222

217-
InputStream archiveStream = new FileInputStream(archive);
218-
containerApi.putContainerArchive("container-archive-info-test", "/tmp/test/", archiveStream, null, null);
223+
containerApi.putContainerArchive("container-archive-info-test", "/tmp/test/", new TarUtil().tar(gattaca), null, null);
219224

220225
Map<String, Object> archiveCopyInfo = (Map<String, Object>) containerApi.containerArchiveInfo("container-archive-info-test", "/tmp/test/gattaca.txt");
221226
assertEquals("gattaca.txt", archiveCopyInfo == null ? null : archiveCopyInfo.get("name"));

api-client/src/test/java/de/gesellix/docker/remote/api/client/ImageApiIntegrationTest.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import de.gesellix.docker.remote.api.testutil.TarUtil;
2525
import de.gesellix.docker.remote.api.testutil.TestImage;
2626
import de.gesellix.testutil.ResourceReader;
27+
import okio.Okio;
2728
import org.junit.jupiter.api.BeforeEach;
2829
import org.junit.jupiter.api.Test;
2930
import org.slf4j.Logger;
@@ -50,8 +51,6 @@
5051

5152
import static de.gesellix.docker.remote.api.testutil.Constants.LABEL_KEY;
5253
import static de.gesellix.docker.remote.api.testutil.Constants.LABEL_VALUE;
53-
import static java.nio.file.Files.readAttributes;
54-
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
5554
import static java.util.Arrays.asList;
5655
import static java.util.Collections.singletonList;
5756
import static java.util.Collections.singletonMap;
@@ -154,7 +153,7 @@ public void imageCreatePullFromRemote() {
154153

155154
@Test
156155
public void imageCreateImportFromUrl() throws IOException {
157-
File tarFile = imageApi.imageGet(testImage.getImageWithTag());
156+
InputStream tarFile = imageApi.imageGet(testImage.getImageWithTag());
158157
File destDir = new TarUtil().unTar(tarFile);
159158
File rootLayerTar = new ManifestUtil().getRootLayerLocation(destDir);
160159
URL importUrl = rootLayerTar.toURI().toURL();
@@ -172,7 +171,7 @@ public void imageCreateImportFromUrl() throws IOException {
172171

173172
@Test
174173
public void imageCreateImportFromInputStream() throws IOException {
175-
File tarFile = imageApi.imageGet(testImage.getImageWithTag());
174+
InputStream tarFile = imageApi.imageGet(testImage.getImageWithTag());
176175
File destDir = new TarUtil().unTar(tarFile);
177176
File rootLayerTar = new ManifestUtil().getRootLayerLocation(destDir);
178177
try (InputStream source = new FileInputStream(rootLayerTar)) {
@@ -227,8 +226,10 @@ public void imageDelete() {
227226
@Test
228227
public void imageGet() throws IOException {
229228
imageApi.imageTag(testImage.getImageWithTag(), "test", "export");
230-
File exportedImage = imageApi.imageGet("test:export");
231-
assertTrue(16896 < Long.parseLong(readAttributes(exportedImage.toPath(), "size", NOFOLLOW_LINKS).get("size").toString()));
229+
InputStream exportedImage = imageApi.imageGet("test:export");
230+
long byteCount = Okio.buffer(Okio.source(exportedImage)).readAll(Okio.blackhole());
231+
exportedImage.close();
232+
assertTrue(16896 < byteCount);
232233

233234
imageApi.imageDelete("test:export", null, null);
234235
}
@@ -238,8 +239,10 @@ public void imageGetAll() throws IOException {
238239
imageApi.imageTag(testImage.getImageWithTag(), "test", "export-all-1");
239240
imageApi.imageTag(testImage.getImageWithTag(), "test", "export-all-2");
240241

241-
File exportedImages = imageApi.imageGetAll(asList("test:export-all-1", "test:export-all-2"));
242-
assertTrue(22016 < Long.parseLong(readAttributes(exportedImages.toPath(), "size", NOFOLLOW_LINKS).get("size").toString()));
242+
InputStream exportedImages = imageApi.imageGetAll(asList("test:export-all-1", "test:export-all-2"));
243+
long byteCount = Okio.buffer(Okio.source(exportedImages)).readAll(Okio.blackhole());
244+
exportedImages.close();
245+
assertTrue(22016 < byteCount);
243246

244247
imageApi.imageDelete("test:export-all-1", null, null);
245248
imageApi.imageDelete("test:export-all-2", null, null);
@@ -250,7 +253,7 @@ public void imageLoad() {
250253
List<String> originalRepoDigests = imageApi.imageInspect(testImage.getImageWithTag()).getRepoDigests();
251254

252255
imageApi.imageTag(testImage.getImageWithTag(), "test", "load-image");
253-
File tarFile = imageApi.imageGet("test:load-image");
256+
InputStream tarFile = imageApi.imageGet("test:load-image");
254257
imageApi.imageDelete("test:load-image", null, null);
255258

256259
assertDoesNotThrow(() -> imageApi.imageLoad(false, tarFile));

0 commit comments

Comments
 (0)