Skip to content

Commit 6f095d6

Browse files
mikesmithsonscottfrederick
authored andcommitted
Improve error message from image building
Translate IOException to DockerException for a more meaningful error message when the Docker daemon is not available. Fixes gh-20151
1 parent e73ee7b commit 6f095d6

File tree

2 files changed

+44
-33
lines changed
  • spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src

2 files changed

+44
-33
lines changed

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

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.io.IOException;
2020
import java.io.InputStream;
2121
import java.io.OutputStream;
22+
import java.io.PrintWriter;
23+
import java.io.StringWriter;
2224
import java.net.URI;
2325

2426
import org.apache.http.HttpEntity;
@@ -36,7 +38,6 @@
3638
import org.apache.http.impl.client.CloseableHttpClient;
3739
import org.apache.http.impl.client.HttpClientBuilder;
3840
import org.apache.http.impl.client.HttpClients;
39-
import org.apache.http.util.EntityUtils;
4041

4142
import org.springframework.boot.buildpack.platform.io.Content;
4243
import org.springframework.boot.buildpack.platform.io.IOConsumer;
@@ -46,6 +47,7 @@
4647
* {@link Http} implementation backed by a {@link HttpClient}.
4748
*
4849
* @author Phillip Webb
50+
* @author Mike Smithson
4951
*/
5052
class HttpClientHttp implements Http {
5153

@@ -66,21 +68,19 @@ class HttpClientHttp implements Http {
6668
* Perform a HTTP GET operation.
6769
* @param uri the destination URI
6870
* @return the operation response
69-
* @throws IOException on IO error
7071
*/
7172
@Override
72-
public Response get(URI uri) throws IOException {
73+
public Response get(URI uri) {
7374
return execute(new HttpGet(uri));
7475
}
7576

7677
/**
7778
* Perform a HTTP POST operation.
7879
* @param uri the destination URI
7980
* @return the operation response
80-
* @throws IOException on IO error
8181
*/
8282
@Override
83-
public Response post(URI uri) throws IOException {
83+
public Response post(URI uri) {
8484
return execute(new HttpPost(uri));
8585
}
8686

@@ -90,11 +90,10 @@ public Response post(URI uri) throws IOException {
9090
* @param contentType the content type to write
9191
* @param writer a content writer
9292
* @return the operation response
93-
* @throws IOException on IO error
9493
*/
9594

9695
@Override
97-
public Response post(URI uri, String contentType, IOConsumer<OutputStream> writer) throws IOException {
96+
public Response post(URI uri, String contentType, IOConsumer<OutputStream> writer) {
9897
return execute(new HttpPost(uri), contentType, writer);
9998
}
10099

@@ -104,51 +103,55 @@ public Response post(URI uri, String contentType, IOConsumer<OutputStream> write
104103
* @param contentType the content type to write
105104
* @param writer a content writer
106105
* @return the operation response
107-
* @throws IOException on IO error
108106
*/
109107

110108
@Override
111-
public Response put(URI uri, String contentType, IOConsumer<OutputStream> writer) throws IOException {
109+
public Response put(URI uri, String contentType, IOConsumer<OutputStream> writer) {
112110
return execute(new HttpPut(uri), contentType, writer);
113111
}
114112

115113
/**
116114
* Perform a HTTP DELETE operation.
117115
* @param uri the destination URI
118116
* @return the operation response
119-
* @throws IOException on IO error
120117
*/
121118

122119
@Override
123-
public Response delete(URI uri) throws IOException {
120+
public Response delete(URI uri) {
124121
return execute(new HttpDelete(uri));
125122
}
126123

127124
private Response execute(HttpEntityEnclosingRequestBase request, String contentType,
128-
IOConsumer<OutputStream> writer) throws IOException {
125+
IOConsumer<OutputStream> writer) {
129126
request.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
130127
request.setEntity(new WritableHttpEntity(writer));
131128
return execute(request);
132129
}
133130

134-
private Response execute(HttpUriRequest request) throws IOException {
135-
CloseableHttpResponse response = this.client.execute(request);
136-
StatusLine statusLine = response.getStatusLine();
137-
int statusCode = statusLine.getStatusCode();
138-
HttpEntity entity = response.getEntity();
139-
if (statusCode >= 200 && statusCode < 300) {
140-
return new HttpClientResponse(response);
141-
}
142-
Errors errors = null;
143-
if (statusCode >= 400 && statusCode < 500) {
144-
try {
145-
errors = SharedObjectMapper.get().readValue(entity.getContent(), Errors.class);
131+
private Response execute(HttpUriRequest request) {
132+
CloseableHttpResponse response;
133+
try {
134+
response = this.client.execute(request);
135+
StatusLine statusLine = response.getStatusLine();
136+
int statusCode = statusLine.getStatusCode();
137+
HttpEntity entity = response.getEntity();
138+
139+
if (statusCode >= 400 && statusCode < 500) {
140+
Errors errors = SharedObjectMapper.get().readValue(entity.getContent(), Errors.class);
141+
throw new DockerException(request.getURI(), statusCode, statusLine.getReasonPhrase(), errors);
146142
}
147-
catch (Exception ex) {
143+
if (statusCode == 500) {
144+
throw new DockerException(request.getURI(), statusCode, statusLine.getReasonPhrase(), null);
148145
}
149146
}
150-
EntityUtils.consume(entity);
151-
throw new DockerException(request.getURI(), statusCode, statusLine.getReasonPhrase(), errors);
147+
catch (IOException ioe) {
148+
StringWriter stringWriter = new StringWriter();
149+
PrintWriter printWriter = new PrintWriter(stringWriter);
150+
ioe.printStackTrace(printWriter);
151+
throw new DockerException(request.getURI(), 500, stringWriter.toString(), null);
152+
}
153+
154+
return new HttpClientResponse(response);
152155
}
153156

154157
/**
@@ -175,7 +178,7 @@ public long getContentLength() {
175178
}
176179

177180
@Override
178-
public InputStream getContent() throws IOException, UnsupportedOperationException {
181+
public InputStream getContent() throws UnsupportedOperationException {
179182
throw new UnsupportedOperationException();
180183
}
181184

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/HttpClientHttpTests.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.apache.http.HttpEntityEnclosingRequest;
2727
import org.apache.http.HttpHeaders;
2828
import org.apache.http.StatusLine;
29-
import org.apache.http.client.ClientProtocolException;
3029
import org.apache.http.client.methods.CloseableHttpResponse;
3130
import org.apache.http.client.methods.HttpDelete;
3231
import org.apache.http.client.methods.HttpGet;
@@ -54,6 +53,7 @@
5453
* Tests for {@link HttpClientHttp}.
5554
*
5655
* @author Phillip Webb
56+
* @author Mike Smithson
5757
*/
5858
class HttpClientHttpTests {
5959

@@ -132,7 +132,7 @@ void postWithContentShouldExecuteHttpPost() throws Exception {
132132
assertThat(entity.isRepeatable()).isFalse();
133133
assertThat(entity.getContentLength()).isEqualTo(-1);
134134
assertThat(entity.isStreaming()).isTrue();
135-
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> entity.getContent());
135+
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(entity::getContent);
136136
assertThat(writeToString(entity)).isEqualTo("test");
137137
assertThat(response.getContent()).isSameAs(this.content);
138138
}
@@ -152,7 +152,7 @@ void putWithContentShouldExecuteHttpPut() throws Exception {
152152
assertThat(entity.isRepeatable()).isFalse();
153153
assertThat(entity.getContentLength()).isEqualTo(-1);
154154
assertThat(entity.isStreaming()).isTrue();
155-
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> entity.getContent());
155+
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(entity::getContent);
156156
assertThat(writeToString(entity)).isEqualTo("test");
157157
assertThat(response.getContent()).isSameAs(this.content);
158158
}
@@ -171,20 +171,28 @@ void deleteShouldExecuteHttpDelete() throws IOException {
171171
}
172172

173173
@Test
174-
void executeWhenResposeIsIn400RangeShouldThrowDockerException() throws ClientProtocolException, IOException {
174+
void executeWhenResposeIsIn400RangeShouldThrowDockerException() throws IOException {
175175
given(this.entity.getContent()).willReturn(getClass().getResourceAsStream("errors.json"));
176176
given(this.statusLine.getStatusCode()).willReturn(404);
177177
assertThatExceptionOfType(DockerException.class).isThrownBy(() -> this.http.get(this.uri))
178178
.satisfies((ex) -> assertThat(ex.getErrors()).hasSize(2));
179179
}
180180

181181
@Test
182-
void executeWhenResposeIsIn500RangeShouldThrowDockerException() throws ClientProtocolException, IOException {
182+
void executeWhenResposeIsIn500RangeShouldThrowDockerException() {
183183
given(this.statusLine.getStatusCode()).willReturn(500);
184184
assertThatExceptionOfType(DockerException.class).isThrownBy(() -> this.http.get(this.uri))
185185
.satisfies((ex) -> assertThat(ex.getErrors()).isNull());
186186
}
187187

188+
@Test
189+
void executeWhenClientExecutesRequestThrowsIOExceptionRethrowsAsDockerException() throws IOException {
190+
given(this.client.execute(any())).willThrow(IOException.class);
191+
assertThatExceptionOfType(DockerException.class).isThrownBy(() -> this.http.get(this.uri))
192+
.satisfies((ex) -> assertThat(ex.getErrors()).isNull()).satisfies(DockerException::getStatusCode)
193+
.withMessageContaining("500").satisfies((ex) -> assertThat(ex.getReasonPhrase())).isNotNull();
194+
}
195+
188196
private String writeToString(HttpEntity entity) throws IOException {
189197
ByteArrayOutputStream out = new ByteArrayOutputStream();
190198
entity.writeTo(out);

0 commit comments

Comments
 (0)