Skip to content

Commit 90ce472

Browse files
Support local socket path in DOCKER_HOST
Prior to this commit, if a DOCKER_HOST environment variable was present when attempting to communicate with a Docker daemon, it was assumed that the value of that variable was an address that could be used to create an HTTP connection to a remote daemon. In some cases, the value of the variable is the path to a local socket file, which would cause the HTTP connection to fail. This commit adds additional validation of the value of the DOCKER_HOST environment variable to determine whether it is a remote address or a local socket file and create the appropriate connection type. Fixes gh-21173
1 parent 86e6ec0 commit 90ce472

File tree

5 files changed

+56
-14
lines changed

5 files changed

+56
-14
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
* HTTP transport used for docker access.
3030
*
3131
* @author Phillip Webb
32+
* @author Scott Frederick
3233
* @since 2.3.0
3334
*/
3435
public interface HttpTransport {
@@ -94,7 +95,7 @@ static HttpTransport create() {
9495
*/
9596
static HttpTransport create(Environment environment) {
9697
HttpTransport remote = RemoteHttpClientTransport.createIfPossible(environment);
97-
return (remote != null) ? remote : LocalHttpClientTransport.create();
98+
return (remote != null) ? remote : LocalHttpClientTransport.create(environment);
9899
}
99100

100101
/**

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
import org.springframework.boot.buildpack.platform.socket.DomainSocket;
4242
import org.springframework.boot.buildpack.platform.socket.NamedPipeSocket;
43+
import org.springframework.boot.buildpack.platform.system.Environment;
4344

4445
/**
4546
* {@link HttpClientTransport} that talks to local Docker.
@@ -49,15 +50,17 @@
4950
*/
5051
final class LocalHttpClientTransport extends HttpClientTransport {
5152

53+
private static final String DOCKER_HOST = "DOCKER_HOST";
54+
5255
private static final HttpHost LOCAL_DOCKER_HOST = HttpHost.create("docker://localhost");
5356

5457
private LocalHttpClientTransport(CloseableHttpClient client) {
5558
super(client, LOCAL_DOCKER_HOST);
5659
}
5760

58-
static LocalHttpClientTransport create() {
61+
static LocalHttpClientTransport create(Environment environment) {
5962
HttpClientBuilder builder = HttpClients.custom();
60-
builder.setConnectionManager(new LocalConnectionManager());
63+
builder.setConnectionManager(new LocalConnectionManager(environment.get(DOCKER_HOST)));
6164
builder.setSchemePortResolver(new LocalSchemePortResolver());
6265
return new LocalHttpClientTransport(builder.build());
6366
}
@@ -67,13 +70,13 @@ static LocalHttpClientTransport create() {
6770
*/
6871
private static class LocalConnectionManager extends BasicHttpClientConnectionManager {
6972

70-
LocalConnectionManager() {
71-
super(getRegistry(), null, null, new LocalDnsResolver());
73+
LocalConnectionManager(String host) {
74+
super(getRegistry(host), null, null, new LocalDnsResolver());
7275
}
7376

74-
private static Registry<ConnectionSocketFactory> getRegistry() {
77+
private static Registry<ConnectionSocketFactory> getRegistry(String host) {
7578
RegistryBuilder<ConnectionSocketFactory> builder = RegistryBuilder.create();
76-
builder.register("docker", new LocalConnectionSocketFactory());
79+
builder.register("docker", new LocalConnectionSocketFactory(host));
7780
return builder.build();
7881
}
7982

@@ -103,12 +106,18 @@ private static class LocalConnectionSocketFactory implements ConnectionSocketFac
103106

104107
private static final String WINDOWS_NAMED_PIPE_PATH = "//./pipe/docker_engine";
105108

109+
private final String host;
110+
111+
LocalConnectionSocketFactory(String host) {
112+
this.host = host;
113+
}
114+
106115
@Override
107116
public Socket createSocket(HttpContext context) throws IOException {
108117
if (Platform.isWindows()) {
109-
return NamedPipeSocket.get(WINDOWS_NAMED_PIPE_PATH);
118+
return NamedPipeSocket.get((this.host != null) ? this.host : WINDOWS_NAMED_PIPE_PATH);
110119
}
111-
return DomainSocket.get(DOMAIN_SOCKET_PATH);
120+
return DomainSocket.get((this.host != null) ? this.host : DOMAIN_SOCKET_PATH);
112121
}
113122

114123
@Override

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package org.springframework.boot.buildpack.platform.docker.transport;
1818

19+
import java.nio.file.Files;
20+
import java.nio.file.Paths;
21+
1922
import javax.net.ssl.SSLContext;
2023

2124
import org.apache.http.HttpHost;
@@ -53,7 +56,10 @@ static RemoteHttpClientTransport createIfPossible(Environment environment) {
5356

5457
static RemoteHttpClientTransport createIfPossible(Environment environment, SslContextFactory sslContextFactory) {
5558
String host = environment.get(DOCKER_HOST);
56-
return (host != null) ? create(environment, sslContextFactory, HttpHost.create(host)) : null;
59+
if (host == null || Files.exists(Paths.get(host))) {
60+
return null;
61+
}
62+
return create(environment, sslContextFactory, HttpHost.create(host));
5763
}
5864

5965
private static RemoteHttpClientTransport create(Environment environment, SslContextFactory sslContextFactory,

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,40 @@
1616

1717
package org.springframework.boot.buildpack.platform.docker.transport;
1818

19+
import java.io.IOException;
20+
import java.nio.file.Files;
21+
import java.nio.file.Path;
1922
import java.util.Collections;
2023
import java.util.Map;
2124

2225
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.api.io.TempDir;
2327

2428
import static org.assertj.core.api.Assertions.assertThat;
2529

2630
/**
2731
* Tests for {@link HttpTransport}.
2832
*
2933
* @author Phillip Webb
34+
* @author Scott Frederick
3035
*/
3136
class HttpTransportTests {
3237

3338
@Test
34-
void createWhenHasDockerHostVariableReturnsRemote() {
35-
Map<String, String> environment = Collections.singletonMap("DOCKER_HOST", "192.168.1.0");
39+
void createWhenDockerHostVariableIsAddressReturnsRemote() {
40+
Map<String, String> environment = Collections.singletonMap("DOCKER_HOST", "tcp://192.168.1.0");
3641
HttpTransport transport = HttpTransport.create(environment::get);
3742
assertThat(transport).isInstanceOf(RemoteHttpClientTransport.class);
3843
}
3944

45+
@Test
46+
void createWhenDockerHostVariableIsFileReturnsLocal(@TempDir Path tempDir) throws IOException {
47+
String dummySocketFilePath = Files.createTempFile(tempDir, "http-transport", null).toAbsolutePath().toString();
48+
Map<String, String> environment = Collections.singletonMap("DOCKER_HOST", dummySocketFilePath);
49+
HttpTransport transport = HttpTransport.create(environment::get);
50+
assertThat(transport).isInstanceOf(LocalHttpClientTransport.class);
51+
}
52+
4053
@Test
4154
void createWhenDoesNotHaveDockerHostVariableReturnsLocal() {
4255
HttpTransport transport = HttpTransport.create((name) -> null);

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package org.springframework.boot.buildpack.platform.docker.transport;
1818

19+
import java.io.IOException;
20+
import java.nio.file.Files;
21+
import java.nio.file.Path;
1922
import java.util.LinkedHashMap;
2023
import java.util.Map;
2124
import java.util.function.Consumer;
@@ -24,6 +27,7 @@
2427

2528
import org.apache.http.HttpHost;
2629
import org.junit.jupiter.api.Test;
30+
import org.junit.jupiter.api.io.TempDir;
2731

2832
import org.springframework.boot.buildpack.platform.docker.ssl.SslContextFactory;
2933

@@ -40,7 +44,7 @@
4044
*/
4145
class RemoteHttpClientTransportTests {
4246

43-
private Map<String, String> environment = new LinkedHashMap<>();
47+
private final Map<String, String> environment = new LinkedHashMap<>();
4448

4549
@Test
4650
void createIfPossibleWhenDockerHostIsNotSetReturnsNull() {
@@ -49,7 +53,16 @@ void createIfPossibleWhenDockerHostIsNotSetReturnsNull() {
4953
}
5054

5155
@Test
52-
void createIfPossibleWhenDockerHostIsSetReturnsTransport() {
56+
void createIfPossibleWhenDockerHostIsFileReturnsNull(@TempDir Path tempDir) throws IOException {
57+
String dummySocketFilePath = Files.createTempFile(tempDir, "remote-transport", null).toAbsolutePath()
58+
.toString();
59+
this.environment.put("DOCKER_HOST", dummySocketFilePath);
60+
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get);
61+
assertThat(transport).isNull();
62+
}
63+
64+
@Test
65+
void createIfPossibleWhenDockerHostIsAddressReturnsTransport() {
5366
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
5467
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get);
5568
assertThat(transport).isNotNull();

0 commit comments

Comments
 (0)