|
16 | 16 |
|
17 | 17 | package org.springframework.boot.buildpack.platform.docker;
|
18 | 18 |
|
19 |
| -import java.io.BufferedReader; |
20 |
| -import java.io.ByteArrayInputStream; |
21 |
| -import java.io.FileInputStream; |
22 | 19 | import java.io.IOException;
|
23 |
| -import java.io.InputStream; |
24 |
| -import java.io.InputStreamReader; |
25 | 20 | import java.io.OutputStream;
|
26 | 21 | import java.net.URI;
|
27 | 22 | import java.net.URISyntaxException;
|
28 |
| -import java.nio.charset.StandardCharsets; |
29 | 23 | import java.nio.file.Files;
|
30 | 24 | import java.nio.file.Path;
|
31 | 25 | import java.util.Arrays;
|
32 | 26 | import java.util.Collection;
|
33 | 27 | import java.util.Collections;
|
34 | 28 | import java.util.List;
|
35 | 29 | import java.util.Objects;
|
36 |
| -import java.util.stream.Collectors; |
37 | 30 |
|
38 |
| -import org.apache.commons.compress.archivers.tar.TarArchiveEntry; |
39 |
| -import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; |
40 | 31 | import org.apache.hc.core5.net.URIBuilder;
|
41 | 32 |
|
42 | 33 | import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration.DockerHostConfiguration;
|
|
48 | 39 | import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
49 | 40 | import org.springframework.boot.buildpack.platform.docker.type.Image;
|
50 | 41 | import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
51 |
| -import org.springframework.boot.buildpack.platform.docker.type.ImageArchiveManifest; |
52 | 42 | import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
53 | 43 | import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
54 | 44 | import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
|
55 | 45 | import org.springframework.boot.buildpack.platform.io.TarArchive;
|
56 | 46 | import org.springframework.boot.buildpack.platform.json.JsonStream;
|
57 | 47 | import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
|
58 | 48 | import org.springframework.util.Assert;
|
59 |
| -import org.springframework.util.StreamUtils; |
60 | 49 | import org.springframework.util.StringUtils;
|
61 | 50 |
|
62 | 51 | /**
|
@@ -263,49 +252,50 @@ public void load(ImageArchive archive, UpdateListener<LoadImageUpdateEvent> list
|
263 | 252 | }
|
264 | 253 |
|
265 | 254 | /**
|
266 |
| - * Export the layers of an image as {@link TarArchive}s. |
| 255 | + * Export the layers of an image as paths to layer tar files. |
267 | 256 | * @param reference the reference to export
|
268 |
| - * @param exports a consumer to receive the layers (contents can only be accessed |
269 |
| - * during the callback) |
| 257 | + * @param exports a consumer to receive the layer tar file paths (file can only be |
| 258 | + * accessed during the callback) |
270 | 259 | * @throws IOException on IO error
|
| 260 | + * @since 2.7.10 |
| 261 | + * @deprecated since 3.2.6 for removal in 3.5.0 in favor of |
| 262 | + * {@link #exportLayers(ImageReference, IOBiConsumer)} |
271 | 263 | */
|
272 |
| - public void exportLayers(ImageReference reference, IOBiConsumer<String, TarArchive> exports) |
273 |
| - throws IOException { |
274 |
| - exportLayerFiles(reference, (name, path) -> { |
275 |
| - try (InputStream in = Files.newInputStream(path)) { |
276 |
| - TarArchive archive = (out) -> StreamUtils.copy(in, out); |
277 |
| - exports.accept(name, archive); |
| 264 | + @Deprecated(since = "3.2.6", forRemoval = true) |
| 265 | + public void exportLayerFiles(ImageReference reference, IOBiConsumer<String, Path> exports) throws IOException { |
| 266 | + Assert.notNull(reference, "Reference must not be null"); |
| 267 | + Assert.notNull(exports, "Exports must not be null"); |
| 268 | + exportLayers(reference, (name, archive) -> { |
| 269 | + Path path = Files.createTempFile("docker-export-layer-files-", null); |
| 270 | + try { |
| 271 | + try (OutputStream out = Files.newOutputStream(path)) { |
| 272 | + archive.writeTo(out); |
| 273 | + exports.accept(name, path); |
| 274 | + } |
| 275 | + } |
| 276 | + finally { |
| 277 | + Files.delete(path); |
278 | 278 | }
|
279 | 279 | });
|
280 | 280 | }
|
281 | 281 |
|
282 | 282 | /**
|
283 |
| - * Export the layers of an image as paths to layer tar files. |
| 283 | + * Export the layers of an image as {@link TarArchive TarArchives}. |
284 | 284 | * @param reference the reference to export
|
285 |
| - * @param exports a consumer to receive the layer tar file paths (file can only be |
286 |
| - * accessed during the callback) |
| 285 | + * @param exports a consumer to receive the layers (contents can only be accessed |
| 286 | + * during the callback) |
287 | 287 | * @throws IOException on IO error
|
288 |
| - * @since 2.7.10 |
289 | 288 | */
|
290 |
| - public void exportLayerFiles(ImageReference reference, IOBiConsumer<String, Path> exports) throws IOException { |
| 289 | + public void exportLayers(ImageReference reference, IOBiConsumer<String, TarArchive> exports) |
| 290 | + throws IOException { |
291 | 291 | Assert.notNull(reference, "Reference must not be null");
|
292 | 292 | Assert.notNull(exports, "Exports must not be null");
|
293 |
| - URI saveUri = buildUrl("/images/" + reference + "/get"); |
294 |
| - Response response = http().get(saveUri); |
295 |
| - Path exportFile = copyToTemp(response.getContent()); |
296 |
| - ImageArchiveManifest manifest = getManifest(reference, exportFile); |
297 |
| - try (TarArchiveInputStream tar = new TarArchiveInputStream(new FileInputStream(exportFile.toFile()))) { |
298 |
| - TarArchiveEntry entry = tar.getNextEntry(); |
299 |
| - while (entry != null) { |
300 |
| - if (manifestContainsLayerEntry(manifest, entry.getName())) { |
301 |
| - Path layerFile = copyToTemp(tar); |
302 |
| - exports.accept(entry.getName(), layerFile); |
303 |
| - Files.delete(layerFile); |
304 |
| - } |
305 |
| - entry = tar.getNextEntry(); |
| 293 | + URI uri = buildUrl("/images/" + reference + "/get"); |
| 294 | + try (Response response = http().get(uri)) { |
| 295 | + try (ExportedImageTar exportedImageTar = new ExportedImageTar(reference, response.getContent())) { |
| 296 | + exportedImageTar.exportLayers(exports); |
306 | 297 | }
|
307 | 298 | }
|
308 |
| - Files.delete(exportFile); |
309 | 299 | }
|
310 | 300 |
|
311 | 301 | /**
|
@@ -345,37 +335,6 @@ public void tag(ImageReference sourceReference, ImageReference targetReference)
|
345 | 335 | http().post(uri).close();
|
346 | 336 | }
|
347 | 337 |
|
348 |
| - private ImageArchiveManifest getManifest(ImageReference reference, Path exportFile) throws IOException { |
349 |
| - try (TarArchiveInputStream tar = new TarArchiveInputStream(new FileInputStream(exportFile.toFile()))) { |
350 |
| - TarArchiveEntry entry = tar.getNextEntry(); |
351 |
| - while (entry != null) { |
352 |
| - if (entry.getName().equals("manifest.json")) { |
353 |
| - return readManifest(tar); |
354 |
| - } |
355 |
| - entry = tar.getNextEntry(); |
356 |
| - } |
357 |
| - } |
358 |
| - throw new IllegalArgumentException("Manifest not found in image " + reference); |
359 |
| - } |
360 |
| - |
361 |
| - private ImageArchiveManifest readManifest(TarArchiveInputStream tar) throws IOException { |
362 |
| - String manifestContent = new BufferedReader(new InputStreamReader(tar, StandardCharsets.UTF_8)).lines() |
363 |
| - .collect(Collectors.joining()); |
364 |
| - return ImageArchiveManifest.of(new ByteArrayInputStream(manifestContent.getBytes(StandardCharsets.UTF_8))); |
365 |
| - } |
366 |
| - |
367 |
| - private Path copyToTemp(InputStream in) throws IOException { |
368 |
| - Path path = Files.createTempFile("create-builder-scratch-", null); |
369 |
| - try (OutputStream out = Files.newOutputStream(path)) { |
370 |
| - StreamUtils.copy(in, out); |
371 |
| - } |
372 |
| - return path; |
373 |
| - } |
374 |
| - |
375 |
| - private boolean manifestContainsLayerEntry(ImageArchiveManifest manifest, String layerId) { |
376 |
| - return manifest.getEntries().stream().anyMatch((content) -> content.getLayers().contains(layerId)); |
377 |
| - } |
378 |
| - |
379 | 338 | }
|
380 | 339 |
|
381 | 340 | /**
|
@@ -458,8 +417,9 @@ public void logs(ContainerReference reference, UpdateListener<LogUpdateEvent> li
|
458 | 417 | public ContainerStatus wait(ContainerReference reference) throws IOException {
|
459 | 418 | Assert.notNull(reference, "Reference must not be null");
|
460 | 419 | URI uri = buildUrl("/containers/" + reference + "/wait");
|
461 |
| - Response response = http().post(uri); |
462 |
| - return ContainerStatus.of(response.getContent()); |
| 420 | + try (Response response = http().post(uri)) { |
| 421 | + return ContainerStatus.of(response.getContent()); |
| 422 | + } |
463 | 423 | }
|
464 | 424 |
|
465 | 425 | /**
|
|
0 commit comments