Skip to content

Commit 4b2c6fa

Browse files
committed
Merge branch '3.5.x'
Closes gh-47901
2 parents 97daba7 + 4309ab0 commit 4b2c6fa

File tree

7 files changed

+88
-17
lines changed

7 files changed

+88
-17
lines changed

buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/DockerEngineException.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,21 @@
1717
package org.springframework.boot.buildpack.platform.docker.transport;
1818

1919
import java.net.URI;
20+
import java.nio.charset.StandardCharsets;
2021

22+
import org.apache.hc.core5.http.HttpStatus;
2123
import org.jspecify.annotations.Nullable;
2224

2325
import org.springframework.util.Assert;
26+
import org.springframework.util.ObjectUtils;
2427
import org.springframework.util.StringUtils;
2528

2629
/**
2730
* Exception thrown when a call to the Docker API fails.
2831
*
2932
* @author Phillip Webb
3033
* @author Scott Frederick
34+
* @author Siva Sai Udayagiri
3135
* @since 2.3.0
3236
*/
3337
public class DockerEngineException extends RuntimeException {
@@ -41,8 +45,8 @@ public class DockerEngineException extends RuntimeException {
4145
private final @Nullable Message responseMessage;
4246

4347
public DockerEngineException(String host, URI uri, int statusCode, @Nullable String reasonPhrase,
44-
@Nullable Errors errors, @Nullable Message responseMessage) {
45-
super(buildMessage(host, uri, statusCode, reasonPhrase, errors, responseMessage));
48+
@Nullable Errors errors, @Nullable Message responseMessage, byte @Nullable [] content) {
49+
super(buildMessage(host, uri, statusCode, reasonPhrase, errors, responseMessage, content));
4650
this.statusCode = statusCode;
4751
this.reasonPhrase = reasonPhrase;
4852
this.errors = errors;
@@ -84,7 +88,7 @@ public int getStatusCode() {
8488
}
8589

8690
private static String buildMessage(String host, URI uri, int statusCode, @Nullable String reasonPhrase,
87-
@Nullable Errors errors, @Nullable Message responseMessage) {
91+
@Nullable Errors errors, @Nullable Message responseMessage, byte @Nullable [] content) {
8892
Assert.notNull(host, "'host' must not be null");
8993
Assert.notNull(uri, "'uri' must not be null");
9094
StringBuilder message = new StringBuilder(
@@ -95,6 +99,10 @@ private static String buildMessage(String host, URI uri, int statusCode, @Nullab
9599
if (responseMessage != null && StringUtils.hasLength(responseMessage.getMessage())) {
96100
message.append(" and message \"").append(responseMessage.getMessage()).append("\"");
97101
}
102+
else if (statusCode == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED && !ObjectUtils.isEmpty(content)) {
103+
String contentString = new String(content, StandardCharsets.UTF_8);
104+
message.append(" and content \"").append(contentString.trim()).append("\"");
105+
}
98106
if (errors != null && !errors.isEmpty()) {
99107
message.append(" ").append(errors);
100108
}

buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpClientTransport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ private Response execute(HttpUriRequest request) {
168168
Errors errors = (statusCode != 500) ? deserializeErrors(content) : null;
169169
Message message = deserializeMessage(content);
170170
throw new DockerEngineException(this.host.toHostString(), request.getUri(), statusCode,
171-
response.getReasonPhrase(), errors, message);
171+
response.getReasonPhrase(), errors, message, content);
172172
}
173173
return new HttpClientResponse(response);
174174
}

buildpack/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/BuilderTests.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
3737
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
3838
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryAuthentication;
39-
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
39+
import org.springframework.boot.buildpack.platform.docker.transport.TestDockerEngineException;
4040
import org.springframework.boot.buildpack.platform.docker.type.Binding;
4141
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
4242
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
@@ -331,12 +331,12 @@ void buildInvokesBuilderWithIfNotPresentPullPolicy() throws Exception {
331331
given(docker.image().pull(eq(BASE_CNB), eq(ImagePlatform.from(builderImage)), any(), isNull()))
332332
.willAnswer(withPulledImage(runImage));
333333
given(docker.image().inspect(eq(DEFAULT_BUILDER)))
334-
.willThrow(
335-
new DockerEngineException("docker://localhost/", new URI("example"), 404, "NOT FOUND", null, null))
334+
.willThrow(new TestDockerEngineException("docker://localhost/", new URI("example"), 404, "NOT FOUND", null,
335+
null, null))
336336
.willReturn(builderImage);
337337
given(docker.image().inspect(eq(BASE_CNB)))
338-
.willThrow(
339-
new DockerEngineException("docker://localhost/", new URI("example"), 404, "NOT FOUND", null, null))
338+
.willThrow(new TestDockerEngineException("docker://localhost/", new URI("example"), 404, "NOT FOUND", null,
339+
null, null))
340340
.willReturn(runImage);
341341
Builder builder = new Builder(BuildLog.to(out), docker, null);
342342
BuildRequest request = getTestRequest().withPullPolicy(PullPolicy.IF_NOT_PRESENT);

buildpack/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/transport/DockerEngineExceptionTests.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.net.URI;
2020
import java.net.URISyntaxException;
21+
import java.nio.charset.StandardCharsets;
2122
import java.util.Collections;
2223

2324
import org.junit.jupiter.api.Test;
@@ -57,21 +58,21 @@ class DockerEngineExceptionTests {
5758
@SuppressWarnings("NullAway") // Test null check
5859
void createWhenHostIsNullThrowsException() {
5960
assertThatIllegalArgumentException()
60-
.isThrownBy(() -> new DockerEngineException(null, null, 404, null, NO_ERRORS, NO_MESSAGE))
61+
.isThrownBy(() -> new DockerEngineException(null, null, 404, null, NO_ERRORS, NO_MESSAGE, null))
6162
.withMessage("'host' must not be null");
6263
}
6364

6465
@Test
6566
@SuppressWarnings("NullAway") // Test null check
6667
void createWhenUriIsNullThrowsException() {
6768
assertThatIllegalArgumentException()
68-
.isThrownBy(() -> new DockerEngineException(HOST, null, 404, null, NO_ERRORS, NO_MESSAGE))
69+
.isThrownBy(() -> new DockerEngineException(HOST, null, 404, null, NO_ERRORS, NO_MESSAGE, null))
6970
.withMessage("'uri' must not be null");
7071
}
7172

7273
@Test
7374
void create() {
74-
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", ERRORS, MESSAGE);
75+
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", ERRORS, MESSAGE, null);
7576
assertThat(exception.getMessage()).isEqualTo(
7677
"Docker API call to 'docker://localhost/example' failed with status code 404 \"missing\" and message \"response message\" [code: message]");
7778
assertThat(exception.getStatusCode()).isEqualTo(404);
@@ -82,7 +83,7 @@ void create() {
8283

8384
@Test
8485
void createWhenReasonPhraseIsNull() {
85-
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, null, ERRORS, MESSAGE);
86+
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, null, ERRORS, MESSAGE, null);
8687
assertThat(exception.getMessage()).isEqualTo(
8788
"Docker API call to 'docker://localhost/example' failed with status code 404 and message \"response message\" [code: message]");
8889
assertThat(exception.getStatusCode()).isEqualTo(404);
@@ -93,15 +94,16 @@ void createWhenReasonPhraseIsNull() {
9394

9495
@Test
9596
void createWhenErrorsIsNull() {
96-
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", null, MESSAGE);
97+
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", null, MESSAGE, null);
9798
assertThat(exception.getMessage()).isEqualTo(
9899
"Docker API call to 'docker://localhost/example' failed with status code 404 \"missing\" and message \"response message\"");
99100
assertThat(exception.getErrors()).isNull();
100101
}
101102

102103
@Test
103104
void createWhenErrorsIsEmpty() {
104-
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", NO_ERRORS, MESSAGE);
105+
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", NO_ERRORS, MESSAGE,
106+
null);
105107
assertThat(exception.getMessage()).isEqualTo(
106108
"Docker API call to 'docker://localhost/example' failed with status code 404 \"missing\" and message \"response message\"");
107109
assertThat(exception.getStatusCode()).isEqualTo(404);
@@ -111,18 +113,28 @@ void createWhenErrorsIsEmpty() {
111113

112114
@Test
113115
void createWhenMessageIsNull() {
114-
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", ERRORS, null);
116+
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", ERRORS, null, null);
115117
assertThat(exception.getMessage()).isEqualTo(
116118
"Docker API call to 'docker://localhost/example' failed with status code 404 \"missing\" [code: message]");
117119
assertThat(exception.getResponseMessage()).isNull();
118120
}
119121

120122
@Test
121123
void createWhenMessageIsEmpty() {
122-
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", ERRORS, NO_MESSAGE);
124+
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", ERRORS, NO_MESSAGE,
125+
null);
123126
assertThat(exception.getMessage()).isEqualTo(
124127
"Docker API call to 'docker://localhost/example' failed with status code 404 \"missing\" [code: message]");
125128
assertThat(exception.getResponseMessage()).isSameAs(NO_MESSAGE);
126129
}
127130

131+
@Test
132+
void createWhenProxyAuthFailureWithTextContent() {
133+
DockerEngineException exception = new DockerEngineException(HOST, URI, 407, "Proxy Authentication Required",
134+
null, null, "Badness".getBytes(StandardCharsets.UTF_8));
135+
assertThat(exception.getMessage())
136+
.isEqualTo("Docker API call to 'docker://localhost/example' failed with status code 407 "
137+
+ "\"Proxy Authentication Required\" and content \"Badness\"");
138+
}
139+
128140
}

buildpack/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/transport/HttpClientTransportTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.apache.hc.core5.http.HttpEntity;
3434
import org.apache.hc.core5.http.HttpHeaders;
3535
import org.apache.hc.core5.http.HttpHost;
36+
import org.apache.hc.core5.http.HttpStatus;
3637
import org.assertj.core.api.ThrowingConsumer;
3738
import org.junit.jupiter.api.BeforeEach;
3839
import org.junit.jupiter.api.Test;
@@ -330,6 +331,19 @@ void shouldReturnErrorsAndMessage() throws IOException {
330331
});
331332
}
332333

334+
@Test
335+
void shouldReturnErrorsAndConentIfProxyAuthError() throws IOException {
336+
givenClientWillReturnResponse();
337+
given(this.entity.getContent()).willReturn(getClass().getResourceAsStream("proxy-error.txt"));
338+
given(this.response.getCode()).willReturn(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED);
339+
assertThatExceptionOfType(DockerEngineException.class).isThrownBy(() -> this.http.get(this.uri))
340+
.satisfies((ex) -> {
341+
assertThat(ex.getErrors()).isNull();
342+
assertThat(ex.getResponseMessage()).isNull();
343+
assertThat(ex.getMessage()).contains("Some kind of procy auth problem!");
344+
});
345+
}
346+
333347
@Test
334348
void executeWhenClientThrowsIOExceptionRethrowsAsDockerException() throws IOException {
335349
given(this.client.executeOpen(any(HttpHost.class), any(HttpUriRequest.class), isNull()))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.buildpack.platform.docker.transport;
18+
19+
import java.net.URI;
20+
21+
import org.jspecify.annotations.Nullable;
22+
23+
/**
24+
* Subclass of {@link DockerEngineException} for testing.
25+
*
26+
* @author Phillip Webb
27+
*/
28+
public class TestDockerEngineException extends DockerEngineException {
29+
30+
public TestDockerEngineException(String host, URI uri, int statusCode, @Nullable String reasonPhrase,
31+
@Nullable Errors errors, @Nullable Message responseMessage, byte @Nullable [] content) {
32+
super(host, uri, statusCode, reasonPhrase, errors, responseMessage, content);
33+
}
34+
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Some kind of procy auth problem!
2+

0 commit comments

Comments
 (0)