Skip to content

Commit abdff95

Browse files
committed
Merge branch '3.2.x'
Closes gh-40526
2 parents 515bcca + 9e40970 commit abdff95

29 files changed

+1010
-135
lines changed

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Builder.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
1717
package org.springframework.boot.buildpack.platform.build;
1818

1919
import java.io.IOException;
20-
import java.nio.file.Path;
2120
import java.util.List;
2221
import java.util.function.Consumer;
2322

@@ -33,6 +32,7 @@
3332
import org.springframework.boot.buildpack.platform.docker.type.Image;
3433
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
3534
import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
35+
import org.springframework.boot.buildpack.platform.io.TarArchive;
3636
import org.springframework.util.Assert;
3737
import org.springframework.util.StringUtils;
3838

@@ -273,8 +273,9 @@ public Image fetchImage(ImageReference reference, ImageType imageType) throws IO
273273
}
274274

275275
@Override
276-
public void exportImageLayers(ImageReference reference, IOBiConsumer<String, Path> exports) throws IOException {
277-
Builder.this.docker.image().exportLayerFiles(reference, exports);
276+
public void exportImageLayers(ImageReference reference, IOBiConsumer<String, TarArchive> exports)
277+
throws IOException {
278+
Builder.this.docker.image().exportLayers(reference, exports);
278279
}
279280

280281
}

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildpackResolverContext.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,12 +17,12 @@
1717
package org.springframework.boot.buildpack.platform.build;
1818

1919
import java.io.IOException;
20-
import java.nio.file.Path;
2120
import java.util.List;
2221

2322
import org.springframework.boot.buildpack.platform.docker.type.Image;
2423
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
2524
import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
25+
import org.springframework.boot.buildpack.platform.io.TarArchive;
2626

2727
/**
2828
* Context passed to a {@link BuildpackResolver}.
@@ -52,6 +52,6 @@ interface BuildpackResolverContext {
5252
* during the callback)
5353
* @throws IOException on IO error
5454
*/
55-
void exportImageLayers(ImageReference reference, IOBiConsumer<String, Path> exports) throws IOException;
55+
void exportImageLayers(ImageReference reference, IOBiConsumer<String, TarArchive> exports) throws IOException;
5656

5757
}

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/ImageBuildpack.java

+20-19
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.boot.buildpack.platform.docker.type.Layer;
3737
import org.springframework.boot.buildpack.platform.docker.type.LayerId;
3838
import org.springframework.boot.buildpack.platform.io.IOConsumer;
39+
import org.springframework.boot.buildpack.platform.io.TarArchive;
3940
import org.springframework.util.StreamUtils;
4041

4142
/**
@@ -115,31 +116,31 @@ private static class ExportedLayers {
115116

116117
ExportedLayers(BuildpackResolverContext context, ImageReference imageReference) throws IOException {
117118
List<Path> layerFiles = new ArrayList<>();
118-
context.exportImageLayers(imageReference, (name, path) -> layerFiles.add(copyToTemp(path)));
119+
context.exportImageLayers(imageReference,
120+
(name, tarArchive) -> layerFiles.add(createLayerFile(tarArchive)));
119121
this.layerFiles = Collections.unmodifiableList(layerFiles);
120122
}
121123

122-
private Path copyToTemp(Path path) throws IOException {
123-
Path outputPath = Files.createTempFile("create-builder-scratch-", null);
124-
try (OutputStream out = Files.newOutputStream(outputPath)) {
125-
copyLayerTar(path, out);
124+
private Path createLayerFile(TarArchive tarArchive) throws IOException {
125+
Path sourceTarFile = Files.createTempFile("create-builder-scratch-source-", null);
126+
try (OutputStream out = Files.newOutputStream(sourceTarFile)) {
127+
tarArchive.writeTo(out);
126128
}
127-
return outputPath;
128-
}
129-
130-
private void copyLayerTar(Path path, OutputStream out) throws IOException {
131-
try (TarArchiveInputStream tarIn = new TarArchiveInputStream(Files.newInputStream(path));
132-
TarArchiveOutputStream tarOut = new TarArchiveOutputStream(out)) {
133-
tarOut.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
134-
TarArchiveEntry entry = tarIn.getNextEntry();
135-
while (entry != null) {
136-
tarOut.putArchiveEntry(entry);
137-
StreamUtils.copy(tarIn, tarOut);
138-
tarOut.closeArchiveEntry();
139-
entry = tarIn.getNextEntry();
129+
Path layerFile = Files.createTempFile("create-builder-scratch-", null);
130+
try (TarArchiveOutputStream out = new TarArchiveOutputStream(Files.newOutputStream(layerFile))) {
131+
try (TarArchiveInputStream in = new TarArchiveInputStream(Files.newInputStream(sourceTarFile))) {
132+
out.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
133+
TarArchiveEntry entry = in.getNextEntry();
134+
while (entry != null) {
135+
out.putArchiveEntry(entry);
136+
StreamUtils.copy(in, out);
137+
out.closeArchiveEntry();
138+
entry = in.getNextEntry();
139+
}
140+
out.finish();
140141
}
141-
tarOut.finish();
142142
}
143+
return layerFile;
143144
}
144145

145146
void apply(IOConsumer<Layer> layers) throws IOException {

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java

+32-72
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,18 @@
1616

1717
package org.springframework.boot.buildpack.platform.docker;
1818

19-
import java.io.BufferedReader;
20-
import java.io.ByteArrayInputStream;
21-
import java.io.FileInputStream;
2219
import java.io.IOException;
23-
import java.io.InputStream;
24-
import java.io.InputStreamReader;
2520
import java.io.OutputStream;
2621
import java.net.URI;
2722
import java.net.URISyntaxException;
28-
import java.nio.charset.StandardCharsets;
2923
import java.nio.file.Files;
3024
import java.nio.file.Path;
3125
import java.util.Arrays;
3226
import java.util.Collection;
3327
import java.util.Collections;
3428
import java.util.List;
3529
import java.util.Objects;
36-
import java.util.stream.Collectors;
3730

38-
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
39-
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
4031
import org.apache.hc.core5.net.URIBuilder;
4132

4233
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration.DockerHostConfiguration;
@@ -48,15 +39,13 @@
4839
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
4940
import org.springframework.boot.buildpack.platform.docker.type.Image;
5041
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
51-
import org.springframework.boot.buildpack.platform.docker.type.ImageArchiveManifest;
5242
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
5343
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
5444
import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
5545
import org.springframework.boot.buildpack.platform.io.TarArchive;
5646
import org.springframework.boot.buildpack.platform.json.JsonStream;
5747
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
5848
import org.springframework.util.Assert;
59-
import org.springframework.util.StreamUtils;
6049
import org.springframework.util.StringUtils;
6150

6251
/**
@@ -263,49 +252,50 @@ public void load(ImageArchive archive, UpdateListener<LoadImageUpdateEvent> list
263252
}
264253

265254
/**
266-
* Export the layers of an image as {@link TarArchive}s.
255+
* Export the layers of an image as paths to layer tar files.
267256
* @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)
270259
* @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)}
271263
*/
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);
278278
}
279279
});
280280
}
281281

282282
/**
283-
* Export the layers of an image as paths to layer tar files.
283+
* Export the layers of an image as {@link TarArchive TarArchives}.
284284
* @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)
287287
* @throws IOException on IO error
288-
* @since 2.7.10
289288
*/
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 {
291291
Assert.notNull(reference, "Reference must not be null");
292292
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);
306297
}
307298
}
308-
Files.delete(exportFile);
309299
}
310300

311301
/**
@@ -345,37 +335,6 @@ public void tag(ImageReference sourceReference, ImageReference targetReference)
345335
http().post(uri).close();
346336
}
347337

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-
379338
}
380339

381340
/**
@@ -458,8 +417,9 @@ public void logs(ContainerReference reference, UpdateListener<LogUpdateEvent> li
458417
public ContainerStatus wait(ContainerReference reference) throws IOException {
459418
Assert.notNull(reference, "Reference must not be null");
460419
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+
}
463423
}
464424

465425
/**

0 commit comments

Comments
 (0)