From 4f1b4c98aef033a5a7cdf902b0fc63778b284ac6 Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Fri, 28 Aug 2020 15:18:02 -0500 Subject: [PATCH] Fail on Docker image load with empty response In some cases, a call to the Docker image load API will fail but return a 200 OK response status code and an empty response. This commit detects that the response from this call is empty and treats this condition as an error instead of a silent failure. Fixes gh-23130 --- .../buildpack/platform/docker/DockerApi.java | 27 ++++++++++++++++++- .../platform/docker/DockerApiTests.java | 11 ++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java index 8986c06b9ef..e630018faba 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/DockerApi.java @@ -178,11 +178,18 @@ public class DockerApi { Assert.notNull(archive, "Archive must not be null"); Assert.notNull(listener, "Listener must not be null"); URI loadUri = buildUrl("/images/load"); + StreamCaptureUpdateListener streamListener = new StreamCaptureUpdateListener(); listener.onStart(); try { try (Response response = http().post(loadUri, "application/x-tar", archive::writeTo)) { - jsonStream().get(response.getContent(), LoadImageUpdateEvent.class, listener::onUpdate); + jsonStream().get(response.getContent(), LoadImageUpdateEvent.class, (event) -> { + streamListener.onUpdate(event); + listener.onUpdate(event); + }); } + Assert.state(StringUtils.hasText(streamListener.getCapturedStream()), + "Invalid response received when loading image " + + ((archive.getTag() != null) ? "\"" + archive.getTag() + "\"" : "")); } finally { listener.onFinish(); @@ -352,4 +359,22 @@ public class DockerApi { } + /** + * {@link UpdateListener} used to ensure an image load response stream. + */ + private static class StreamCaptureUpdateListener implements UpdateListener { + + private String stream; + + @Override + public void onUpdate(LoadImageUpdateEvent event) { + this.stream = event.getStream(); + } + + String getCapturedStream() { + return this.stream; + } + + } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/DockerApiTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/DockerApiTests.java index 8abddb39960..c61cd6a4d38 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/DockerApiTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/DockerApiTests.java @@ -50,6 +50,7 @@ import org.springframework.boot.buildpack.platform.io.TarArchive; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; @@ -172,6 +173,16 @@ class DockerApiTests { .withMessage("Listener must not be null"); } + @Test // gh-23130 + void loadWithEmptyResponseThrowsException() throws Exception { + Image image = Image.of(getClass().getResourceAsStream("type/image.json")); + ImageArchive archive = ImageArchive.from(image); + URI loadUri = new URI(IMAGES_URL + "/load"); + given(http().post(eq(loadUri), eq("application/x-tar"), any())).willReturn(emptyResponse()); + assertThatIllegalStateException().isThrownBy(() -> this.api.load(archive, this.loadListener)) + .withMessageContaining("Invalid response received"); + } + @Test void loadLoadsImage() throws Exception { Image image = Image.of(getClass().getResourceAsStream("type/image.json"));