Browse Source

Merge branch '3.5.x'

Closes gh-48128
pull/48138/head
Phillip Webb 1 month ago
parent
commit
90d53b6d1b
  1. 2
      buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Builder.java
  2. 51
      buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java
  3. 59
      buildpack/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/DockerApiTests.java

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

@ -391,7 +391,7 @@ public class Builder { @@ -391,7 +391,7 @@ public class Builder {
@Override
public void exportImageLayers(ImageReference reference, IOBiConsumer<String, TarArchive> exports)
throws IOException {
Builder.this.docker.image().exportLayers(reference, exports);
Builder.this.docker.image().exportLayers(reference, this.platform, exports);
}
}

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

@ -124,6 +124,13 @@ public class DockerApi { @@ -124,6 +124,13 @@ public class DockerApi {
return this.jsonStream;
}
URI buildPlatformJsonUrl(Feature feature, @Nullable ImagePlatform platform, String path) {
if (platform != null && getApiVersion().supports(feature.minimumVersion())) {
return buildUrl(feature, path, "platform", platform.toJson());
}
return buildUrl(path);
}
private URI buildUrl(String path, @Nullable Collection<?> params) {
return buildUrl(Feature.BASELINE, path, (params != null) ? params.toArray() : null);
}
@ -229,9 +236,8 @@ public class DockerApi { @@ -229,9 +236,8 @@ public class DockerApi {
UpdateListener<PullImageUpdateEvent> listener, @Nullable String registryAuth) throws IOException {
Assert.notNull(reference, "'reference' must not be null");
Assert.notNull(listener, "'listener' must not be null");
URI createUri = (platform != null)
? buildUrl(Feature.PLATFORM, "/images/create", "fromImage", reference, "platform", platform)
: buildUrl("/images/create", "fromImage", reference);
URI createUri = (platform != null) ? buildUrl(Feature.PLATFORM_IMAGE_PULL, "/images/create", "fromImage",
reference, "platform", platform) : buildUrl("/images/create", "fromImage", reference);
DigestCaptureUpdateListener digestCapture = new DigestCaptureUpdateListener();
listener.onStart();
try {
@ -313,9 +319,24 @@ public class DockerApi { @@ -313,9 +319,24 @@ public class DockerApi {
*/
public void exportLayers(ImageReference reference, IOBiConsumer<String, TarArchive> exports)
throws IOException {
exportLayers(reference, null, exports);
}
/**
* Export the layers of an image as {@link TarArchive TarArchives}.
* @param reference the reference to export
* @param platform the platform (os/architecture/variant) of the image to export.
* Ignored on older versions of Docker.
* @param exports a consumer to receive the layers (contents can only be accessed
* during the callback)
* @throws IOException on IO error
* @since 3.4.12
*/
public void exportLayers(ImageReference reference, @Nullable ImagePlatform platform,
IOBiConsumer<String, TarArchive> exports) throws IOException {
Assert.notNull(reference, "'reference' must not be null");
Assert.notNull(exports, "'exports' must not be null");
URI uri = buildUrl("/images/" + reference + "/get");
URI uri = buildPlatformJsonUrl(Feature.PLATFORM_IMAGE_EXPORT, platform, "/images/" + reference + "/get");
try (Response response = http().get(uri)) {
try (ExportedImageTar exportedImageTar = new ExportedImageTar(reference, response.getContent())) {
exportedImageTar.exportLayers(exports);
@ -359,20 +380,13 @@ public class DockerApi { @@ -359,20 +380,13 @@ public class DockerApi {
// The Docker documentation is incomplete but platform parameters
// are supported since 1.49 (see https://github.com/moby/moby/pull/49586)
Assert.notNull(reference, "'reference' must not be null");
URI inspectUrl = inspectUrl(reference, platform);
URI inspectUrl = buildPlatformJsonUrl(Feature.PLATFORM_IMAGE_INSPECT, platform,
"/images/" + reference + "/json");
try (Response response = http().get(inspectUrl)) {
return Image.of(response.getContent());
}
}
private URI inspectUrl(ImageReference reference, @Nullable ImagePlatform platform) {
String path = "/images/" + reference + "/json";
if (platform != null && getApiVersion().supports(Feature.PLATFORM_INSPECT.minimumVersion())) {
return buildUrl(Feature.PLATFORM_INSPECT, path, "platform", platform.toJson());
}
return buildUrl(path);
}
public void tag(ImageReference sourceReference, ImageReference targetReference) throws IOException {
Assert.notNull(sourceReference, "'sourceReference' must not be null");
Assert.notNull(targetReference, "'targetReference' must not be null");
@ -415,7 +429,8 @@ public class DockerApi { @@ -415,7 +429,8 @@ public class DockerApi {
private ContainerReference createContainer(ContainerConfig config, @Nullable ImagePlatform platform)
throws IOException {
URI createUri = (platform != null) ? buildUrl(Feature.PLATFORM, "/containers/create", "platform", platform)
URI createUri = (platform != null)
? buildUrl(Feature.PLATFORM_CONTAINER_CREATE, "/containers/create", "platform", platform)
: buildUrl("/containers/create");
try (Response response = http().post(createUri, "application/json", config::writeTo)) {
return ContainerReference
@ -624,9 +639,13 @@ public class DockerApi { @@ -624,9 +639,13 @@ public class DockerApi {
BASELINE(ApiVersion.of(1, 24)),
PLATFORM(ApiVersion.of(1, 41)),
PLATFORM_IMAGE_PULL(ApiVersion.of(1, 41)),
PLATFORM_CONTAINER_CREATE(ApiVersion.of(1, 41)),
PLATFORM_IMAGE_INSPECT(ApiVersion.of(1, 49)),
PLATFORM_INSPECT(ApiVersion.of(1, 49));
PLATFORM_IMAGE_EXPORT(ApiVersion.of(1, 48));
private final ApiVersion minimumVersion;

59
buildpack/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/DockerApiTests.java

@ -501,6 +501,65 @@ class DockerApiTests { @@ -501,6 +501,65 @@ class DockerApiTests {
.containsExactly("/cnb/stack.toml");
}
@Test
void exportLayersExportsLayerTarsWithPlatformWhenSupportedVersion() throws Exception {
setVersion("1.48");
ImageReference reference = ImageReference.of("docker.io/paketobuildpacks/builder:base");
URI exportUri = new URI("/v1.48/images/docker.io/paketobuildpacks/builder:base/get?platform="
+ ENCODED_LINUX_ARM64_PLATFORM_JSON);
given(DockerApiTests.this.http.get(exportUri)).willReturn(responseOf("export.tar"));
MultiValueMap<String, String> contents = new LinkedMultiValueMap<>();
this.api.exportLayers(reference, LINUX_ARM64_PLATFORM, (name, archive) -> {
ByteArrayOutputStream out = new ByteArrayOutputStream();
archive.writeTo(out);
try (TarArchiveInputStream in = new TarArchiveInputStream(
new ByteArrayInputStream(out.toByteArray()))) {
TarArchiveEntry entry = in.getNextEntry();
while (entry != null) {
contents.add(name, entry.getName());
entry = in.getNextEntry();
}
}
});
assertThat(contents).hasSize(3)
.containsKeys("70bb7a3115f3d5c01099852112c7e05bf593789e510468edb06b6a9a11fa3b73/layer.tar",
"74a9a50ece13c025cf10e9110d9ddc86c995079c34e2a22a28d1a3d523222c6e/layer.tar",
"a69532b5b92bb891fbd9fa1a6b3af9087ea7050255f59ba61a796f8555ecd783/layer.tar");
assertThat(contents.get("70bb7a3115f3d5c01099852112c7e05bf593789e510468edb06b6a9a11fa3b73/layer.tar"))
.containsExactly("/cnb/order.toml");
assertThat(contents.get("74a9a50ece13c025cf10e9110d9ddc86c995079c34e2a22a28d1a3d523222c6e/layer.tar"))
.containsExactly("/cnb/stack.toml");
}
@Test
void exportLayersExportsLayerTarsWithPlatformWhenOldVersionInspectImage() throws Exception {
setVersion("1.47");
ImageReference reference = ImageReference.of("docker.io/paketobuildpacks/builder:base");
URI exportUri = new URI("/v1.47/images/docker.io/paketobuildpacks/builder:base/get");
given(DockerApiTests.this.http.get(exportUri)).willReturn(responseOf("export.tar"));
MultiValueMap<String, String> contents = new LinkedMultiValueMap<>();
this.api.exportLayers(reference, LINUX_ARM64_PLATFORM, (name, archive) -> {
ByteArrayOutputStream out = new ByteArrayOutputStream();
archive.writeTo(out);
try (TarArchiveInputStream in = new TarArchiveInputStream(
new ByteArrayInputStream(out.toByteArray()))) {
TarArchiveEntry entry = in.getNextEntry();
while (entry != null) {
contents.add(name, entry.getName());
entry = in.getNextEntry();
}
}
});
assertThat(contents).hasSize(3)
.containsKeys("70bb7a3115f3d5c01099852112c7e05bf593789e510468edb06b6a9a11fa3b73/layer.tar",
"74a9a50ece13c025cf10e9110d9ddc86c995079c34e2a22a28d1a3d523222c6e/layer.tar",
"a69532b5b92bb891fbd9fa1a6b3af9087ea7050255f59ba61a796f8555ecd783/layer.tar");
assertThat(contents.get("70bb7a3115f3d5c01099852112c7e05bf593789e510468edb06b6a9a11fa3b73/layer.tar"))
.containsExactly("/cnb/order.toml");
assertThat(contents.get("74a9a50ece13c025cf10e9110d9ddc86c995079c34e2a22a28d1a3d523222c6e/layer.tar"))
.containsExactly("/cnb/stack.toml");
}
@Test
void exportLayersWithSymlinksExportsLayerTars() throws Exception {
ImageReference reference = ImageReference.of("docker.io/paketobuildpacks/builder:base");

Loading…
Cancel
Save