Commit 4f1b4c98 authored by Scott Frederick's avatar Scott Frederick

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
parent 6186db73
...@@ -178,11 +178,18 @@ public class DockerApi { ...@@ -178,11 +178,18 @@ public class DockerApi {
Assert.notNull(archive, "Archive must not be null"); Assert.notNull(archive, "Archive must not be null");
Assert.notNull(listener, "Listener must not be null"); Assert.notNull(listener, "Listener must not be null");
URI loadUri = buildUrl("/images/load"); URI loadUri = buildUrl("/images/load");
StreamCaptureUpdateListener streamListener = new StreamCaptureUpdateListener();
listener.onStart(); listener.onStart();
try { try {
try (Response response = http().post(loadUri, "application/x-tar", archive::writeTo)) { 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 { finally {
listener.onFinish(); listener.onFinish();
...@@ -352,4 +359,22 @@ public class DockerApi { ...@@ -352,4 +359,22 @@ public class DockerApi {
} }
/**
* {@link UpdateListener} used to ensure an image load response stream.
*/
private static class StreamCaptureUpdateListener implements UpdateListener<LoadImageUpdateEvent> {
private String stream;
@Override
public void onUpdate(LoadImageUpdateEvent event) {
this.stream = event.getStream();
}
String getCapturedStream() {
return this.stream;
}
}
} }
...@@ -50,6 +50,7 @@ import org.springframework.boot.buildpack.platform.io.TarArchive; ...@@ -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.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; 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.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
...@@ -172,6 +173,16 @@ class DockerApiTests { ...@@ -172,6 +173,16 @@ class DockerApiTests {
.withMessage("Listener must not be null"); .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 @Test
void loadLoadsImage() throws Exception { void loadLoadsImage() throws Exception {
Image image = Image.of(getClass().getResourceAsStream("type/image.json")); Image image = Image.of(getClass().getResourceAsStream("type/image.json"));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment