commit
c6e19517be
|
@ -270,7 +270,7 @@ 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();
|
LoadImageUpdateListener streamListener = new LoadImageUpdateListener(archive);
|
||||||
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)) {
|
||||||
|
@ -279,9 +279,7 @@ public class DockerApi {
|
||||||
listener.onUpdate(event);
|
listener.onUpdate(event);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Assert.state(StringUtils.hasText(streamListener.getCapturedStream()),
|
streamListener.assertValidResponseReceived();
|
||||||
"Invalid response received when loading image "
|
|
||||||
+ ((archive.getTag() != null) ? "\"" + archive.getTag() + "\"" : ""));
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
listener.onFinish();
|
listener.onFinish();
|
||||||
|
@ -557,19 +555,33 @@ public class DockerApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link UpdateListener} used to ensure an image load response stream.
|
* {@link UpdateListener} for an image load response stream.
|
||||||
*/
|
*/
|
||||||
private static final class StreamCaptureUpdateListener implements UpdateListener<LoadImageUpdateEvent> {
|
private static final class LoadImageUpdateListener implements UpdateListener<LoadImageUpdateEvent> {
|
||||||
|
|
||||||
|
private final ImageArchive archive;
|
||||||
|
|
||||||
private String stream;
|
private String stream;
|
||||||
|
|
||||||
|
private LoadImageUpdateListener(ImageArchive archive) {
|
||||||
|
this.archive = archive;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpdate(LoadImageUpdateEvent event) {
|
public void onUpdate(LoadImageUpdateEvent event) {
|
||||||
|
Assert.state(event.getErrorDetail() == null,
|
||||||
|
() -> "Error response received when loading image" + image() + ": " + event.getErrorDetail());
|
||||||
this.stream = event.getStream();
|
this.stream = event.getStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
String getCapturedStream() {
|
private String image() {
|
||||||
return this.stream;
|
ImageReference tag = this.archive.getTag();
|
||||||
|
return (tag != null) ? " \"" + tag + "\"" : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertValidResponseReceived() {
|
||||||
|
Assert.state(StringUtils.hasText(this.stream),
|
||||||
|
() -> "Invalid response received when loading image" + image());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2020 the original author or authors.
|
* Copyright 2012-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.boot.buildpack.platform.docker;
|
package org.springframework.boot.buildpack.platform.docker;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ProgressUpdateEvent} fired as an image is loaded.
|
* A {@link ProgressUpdateEvent} fired as an image is loaded.
|
||||||
|
@ -28,10 +29,14 @@ public class LoadImageUpdateEvent extends ProgressUpdateEvent {
|
||||||
|
|
||||||
private final String stream;
|
private final String stream;
|
||||||
|
|
||||||
|
private final ErrorDetail errorDetail;
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public LoadImageUpdateEvent(String stream, String status, ProgressDetail progressDetail, String progress) {
|
public LoadImageUpdateEvent(String stream, String status, ProgressDetail progressDetail, String progress,
|
||||||
|
ErrorDetail errorDetail) {
|
||||||
super(status, progressDetail, progress);
|
super(status, progressDetail, progress);
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
|
this.errorDetail = errorDetail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,4 +47,42 @@ public class LoadImageUpdateEvent extends ProgressUpdateEvent {
|
||||||
return this.stream;
|
return this.stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the error detail or {@code null} if no error occurred.
|
||||||
|
* @return the error detail, if any
|
||||||
|
* @since 3.2.12
|
||||||
|
*/
|
||||||
|
public ErrorDetail getErrorDetail() {
|
||||||
|
return this.errorDetail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Details of an error embedded in a response stream.
|
||||||
|
*
|
||||||
|
* @since 3.2.12
|
||||||
|
*/
|
||||||
|
public static class ErrorDetail {
|
||||||
|
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public ErrorDetail(@JsonProperty("message") String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the message field from the error detail.
|
||||||
|
* @return the message
|
||||||
|
*/
|
||||||
|
public String getMessage() {
|
||||||
|
return this.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -319,6 +319,16 @@ class DockerApiTests {
|
||||||
.withMessageContaining("Invalid response received");
|
.withMessageContaining("Invalid response received");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // gh-31243
|
||||||
|
void loadWithErrorResponseThrowsException() 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(responseOf("load-error.json"));
|
||||||
|
assertThatIllegalStateException().isThrownBy(() -> this.api.load(archive, this.loadListener))
|
||||||
|
.withMessageContaining("Error 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"));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2020 the original author or authors.
|
* Copyright 2012-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,6 +18,7 @@ package org.springframework.boot.buildpack.platform.docker;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.LoadImageUpdateEvent.ErrorDetail;
|
||||||
import org.springframework.boot.buildpack.platform.docker.ProgressUpdateEvent.ProgressDetail;
|
import org.springframework.boot.buildpack.platform.docker.ProgressUpdateEvent.ProgressDetail;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -36,9 +37,16 @@ class LoadImageUpdateEventTests extends ProgressUpdateEventTests<LoadImageUpdate
|
||||||
assertThat(event.getStream()).isEqualTo("stream");
|
assertThat(event.getStream()).isEqualTo("stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getErrorDetailReturnsErrorDetail() {
|
||||||
|
LoadImageUpdateEvent event = createEvent();
|
||||||
|
assertThat(event.getErrorDetail()).extracting(ErrorDetail::getMessage).isEqualTo("max depth exceeded");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LoadImageUpdateEvent createEvent(String status, ProgressDetail progressDetail, String progress) {
|
protected LoadImageUpdateEvent createEvent(String status, ProgressDetail progressDetail, String progress) {
|
||||||
return new LoadImageUpdateEvent("stream", status, progressDetail, progress);
|
return new LoadImageUpdateEvent("stream", status, progressDetail, progress,
|
||||||
|
new ErrorDetail("max depth exceeded"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"errorDetail":{"message":"max depth exceeded"}}
|
Loading…
Reference in New Issue