Skip to content

Commit 7ad538c

Browse files
Add option to use configured Docker host in builder
This commit adds support for a `docker.bindHostToBuilder` option in the Maven and Gradle image building goal and task. Fixes gh-29384
1 parent 93622d1 commit 7ad538c

File tree

24 files changed

+621
-236
lines changed

24 files changed

+621
-236
lines changed

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Builder.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
import org.springframework.boot.buildpack.platform.docker.TotalProgressPushListener;
2828
import org.springframework.boot.buildpack.platform.docker.UpdateListener;
2929
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
30+
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
3031
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
3132
import org.springframework.boot.buildpack.platform.docker.type.Image;
3233
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
@@ -83,7 +84,8 @@ public Builder(BuildLog log) {
8384
* @since 2.4.0
8485
*/
8586
public Builder(BuildLog log, DockerConfiguration dockerConfiguration) {
86-
this(log, new DockerApi(dockerConfiguration), dockerConfiguration);
87+
this(log, new DockerApi((dockerConfiguration != null) ? dockerConfiguration.getHost() : null),
88+
dockerConfiguration);
8789
}
8890

8991
Builder(BuildLog log, DockerApi docker, DockerConfiguration dockerConfiguration) {
@@ -147,7 +149,11 @@ private Buildpacks getBuildpacks(BuildRequest request, ImageFetcher imageFetcher
147149
}
148150

149151
private void executeLifecycle(BuildRequest request, EphemeralBuilder builder) throws IOException {
150-
try (Lifecycle lifecycle = new Lifecycle(this.log, this.docker, request, builder)) {
152+
ResolvedDockerHost dockerHost = null;
153+
if (this.dockerConfiguration != null && this.dockerConfiguration.isBindHostToBuilder()) {
154+
dockerHost = ResolvedDockerHost.from(this.dockerConfiguration.getHost());
155+
}
156+
try (Lifecycle lifecycle = new Lifecycle(this.log, this.docker, dockerHost, request, builder)) {
151157
lifecycle.execute();
152158
}
153159
}

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Lifecycle.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222

2323
import org.springframework.boot.buildpack.platform.docker.DockerApi;
2424
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
25+
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
2526
import org.springframework.boot.buildpack.platform.docker.type.Binding;
2627
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
2728
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
@@ -47,10 +48,14 @@ class Lifecycle implements Closeable {
4748

4849
private static final String PLATFORM_API_VERSION_KEY = "CNB_PLATFORM_API";
4950

51+
private static final String DOMAIN_SOCKET_PATH = "/var/run/docker.sock";
52+
5053
private final BuildLog log;
5154

5255
private final DockerApi docker;
5356

57+
private final ResolvedDockerHost dockerHost;
58+
5459
private final BuildRequest request;
5560

5661
private final EphemeralBuilder builder;
@@ -75,12 +80,15 @@ class Lifecycle implements Closeable {
7580
* Create a new {@link Lifecycle} instance.
7681
* @param log build output log
7782
* @param docker the Docker API
83+
* @param dockerHost the Docker host information
7884
* @param request the request to process
7985
* @param builder the ephemeral builder used to run the phases
8086
*/
81-
Lifecycle(BuildLog log, DockerApi docker, BuildRequest request, EphemeralBuilder builder) {
87+
Lifecycle(BuildLog log, DockerApi docker, ResolvedDockerHost dockerHost, BuildRequest request,
88+
EphemeralBuilder builder) {
8289
this.log = log;
8390
this.docker = docker;
91+
this.dockerHost = dockerHost;
8492
this.request = request;
8593
this.builder = builder;
8694
this.lifecycleVersion = LifecycleVersion.parse(builder.getBuilderMetadata().getLifecycle().getVersion());
@@ -147,6 +155,7 @@ void execute() throws IOException {
147155
private Phase createPhase() {
148156
Phase phase = new Phase("creator", isVerboseLogging());
149157
phase.withDaemonAccess();
158+
configureDaemonAccess(phase);
150159
phase.withLogLevelArg();
151160
phase.withArgs("-app", Directory.APPLICATION);
152161
phase.withArgs("-platform", Directory.PLATFORM);
@@ -176,6 +185,24 @@ private Phase createPhase() {
176185
return phase;
177186
}
178187

188+
private void configureDaemonAccess(Phase phase) {
189+
if (this.dockerHost != null) {
190+
if (this.dockerHost.isRemote()) {
191+
phase.withEnv("DOCKER_HOST", this.dockerHost.getAddress());
192+
if (this.dockerHost.isSecure()) {
193+
phase.withEnv("DOCKER_TLS_VERIFY", "1");
194+
phase.withEnv("DOCKER_CERT_PATH", this.dockerHost.getCertificatePath());
195+
}
196+
}
197+
else {
198+
phase.withBinding(Binding.from(this.dockerHost.getAddress(), DOMAIN_SOCKET_PATH));
199+
}
200+
}
201+
else {
202+
phase.withBinding(Binding.from(DOMAIN_SOCKET_PATH, DOMAIN_SOCKET_PATH));
203+
}
204+
}
205+
179206
private boolean isVerboseLogging() {
180207
return this.request.isVerboseLogging() && this.lifecycleVersion.isEqualOrGreaterThan(LOGGING_MINIMUM_VERSION);
181208
}

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Phase.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,8 +35,6 @@
3535
*/
3636
class Phase {
3737

38-
private static final String DOMAIN_SOCKET_PATH = "/var/run/docker.sock";
39-
4038
private final String name;
4139

4240
private final boolean verboseLogging;
@@ -132,7 +130,6 @@ public String toString() {
132130
void apply(ContainerConfig.Update update) {
133131
if (this.daemonAccess) {
134132
update.withUser("root");
135-
update.withBinding(Binding.from(DOMAIN_SOCKET_PATH, DOMAIN_SOCKET_PATH));
136133
}
137134
update.withCommand("/cnb/lifecycle/" + this.name, StringUtils.toStringArray(this.args));
138135
update.withLabel("author", "spring-boot");

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@
2828
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
2929
import org.apache.http.client.utils.URIBuilder;
3030

31-
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
31+
import org.springframework.boot.buildpack.platform.docker.configuration.DockerHost;
3232
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
3333
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
3434
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
@@ -75,16 +75,16 @@ public class DockerApi {
7575
* Create a new {@link DockerApi} instance.
7676
*/
7777
public DockerApi() {
78-
this(new DockerConfiguration());
78+
this(HttpTransport.create(null));
7979
}
8080

8181
/**
8282
* Create a new {@link DockerApi} instance.
83-
* @param dockerConfiguration the docker configuration
83+
* @param dockerHost the Docker daemon host information
8484
* @since 2.4.0
8585
*/
86-
public DockerApi(DockerConfiguration dockerConfiguration) {
87-
this(HttpTransport.create((dockerConfiguration != null) ? dockerConfiguration.getHost() : null));
86+
public DockerApi(DockerHost dockerHost) {
87+
this(HttpTransport.create(dockerHost));
8888
}
8989

9090
/**

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,21 +33,28 @@ public final class DockerConfiguration {
3333

3434
private final DockerRegistryAuthentication publishAuthentication;
3535

36+
private final boolean bindHostToBuilder;
37+
3638
public DockerConfiguration() {
37-
this(null, null, null);
39+
this(null, null, null, false);
3840
}
3941

4042
private DockerConfiguration(DockerHost host, DockerRegistryAuthentication builderAuthentication,
41-
DockerRegistryAuthentication publishAuthentication) {
43+
DockerRegistryAuthentication publishAuthentication, boolean bindHostToBuilder) {
4244
this.host = host;
4345
this.builderAuthentication = builderAuthentication;
4446
this.publishAuthentication = publishAuthentication;
47+
this.bindHostToBuilder = bindHostToBuilder;
4548
}
4649

4750
public DockerHost getHost() {
4851
return this.host;
4952
}
5053

54+
public boolean isBindHostToBuilder() {
55+
return this.bindHostToBuilder;
56+
}
57+
5158
public DockerRegistryAuthentication getBuilderRegistryAuthentication() {
5259
return this.builderAuthentication;
5360
}
@@ -59,35 +66,40 @@ public DockerRegistryAuthentication getPublishRegistryAuthentication() {
5966
public DockerConfiguration withHost(String address, boolean secure, String certificatePath) {
6067
Assert.notNull(address, "Address must not be null");
6168
return new DockerConfiguration(new DockerHost(address, secure, certificatePath), this.builderAuthentication,
62-
this.publishAuthentication);
69+
this.publishAuthentication, this.bindHostToBuilder);
70+
}
71+
72+
public DockerConfiguration withBindHostToBuilder(boolean bindHostToBuilder) {
73+
return new DockerConfiguration(this.host, this.builderAuthentication, this.publishAuthentication,
74+
bindHostToBuilder);
6375
}
6476

6577
public DockerConfiguration withBuilderRegistryTokenAuthentication(String token) {
6678
Assert.notNull(token, "Token must not be null");
6779
return new DockerConfiguration(this.host, new DockerRegistryTokenAuthentication(token),
68-
this.publishAuthentication);
80+
this.publishAuthentication, this.bindHostToBuilder);
6981
}
7082

7183
public DockerConfiguration withBuilderRegistryUserAuthentication(String username, String password, String url,
7284
String email) {
7385
Assert.notNull(username, "Username must not be null");
7486
Assert.notNull(password, "Password must not be null");
7587
return new DockerConfiguration(this.host, new DockerRegistryUserAuthentication(username, password, url, email),
76-
this.publishAuthentication);
88+
this.publishAuthentication, this.bindHostToBuilder);
7789
}
7890

7991
public DockerConfiguration withPublishRegistryTokenAuthentication(String token) {
8092
Assert.notNull(token, "Token must not be null");
8193
return new DockerConfiguration(this.host, this.builderAuthentication,
82-
new DockerRegistryTokenAuthentication(token));
94+
new DockerRegistryTokenAuthentication(token), this.bindHostToBuilder);
8395
}
8496

8597
public DockerConfiguration withPublishRegistryUserAuthentication(String username, String password, String url,
8698
String email) {
8799
Assert.notNull(username, "Username must not be null");
88100
Assert.notNull(password, "Password must not be null");
89101
return new DockerConfiguration(this.host, this.builderAuthentication,
90-
new DockerRegistryUserAuthentication(username, password, url, email));
102+
new DockerRegistryUserAuthentication(username, password, url, email), this.bindHostToBuilder);
91103
}
92104

93105
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,10 @@ public class DockerHost {
3030

3131
private final String certificatePath;
3232

33+
public DockerHost(String address) {
34+
this(address, false, null);
35+
}
36+
3337
public DockerHost(String address, boolean secure, String certificatePath) {
3438
this.address = address;
3539
this.secure = secure;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright 2012-2022 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.configuration;
18+
19+
import java.nio.file.Files;
20+
import java.nio.file.Paths;
21+
22+
import com.sun.jna.Platform;
23+
24+
import org.springframework.boot.buildpack.platform.system.Environment;
25+
26+
/**
27+
* Resolves a {@link DockerHost} from the environment, configuration, or using defaults.
28+
*
29+
* @author Scott Frederick
30+
* @since 2.7.0
31+
*/
32+
public class ResolvedDockerHost extends DockerHost {
33+
34+
private static final String UNIX_SOCKET_PREFIX = "unix://";
35+
36+
private static final String DOMAIN_SOCKET_PATH = "/var/run/docker.sock";
37+
38+
private static final String WINDOWS_NAMED_PIPE_PATH = "//./pipe/docker_engine";
39+
40+
private static final String DOCKER_HOST = "DOCKER_HOST";
41+
42+
private static final String DOCKER_TLS_VERIFY = "DOCKER_TLS_VERIFY";
43+
44+
private static final String DOCKER_CERT_PATH = "DOCKER_CERT_PATH";
45+
46+
ResolvedDockerHost(String address, boolean secure, String certificatePath) {
47+
super(address, secure, certificatePath);
48+
}
49+
50+
@Override
51+
public String getAddress() {
52+
return super.getAddress().startsWith(UNIX_SOCKET_PREFIX)
53+
? super.getAddress().substring(UNIX_SOCKET_PREFIX.length()) : super.getAddress();
54+
}
55+
56+
public boolean isRemote() {
57+
return getAddress().startsWith("http") || getAddress().startsWith("tcp");
58+
}
59+
60+
public boolean isLocalFileReference() {
61+
try {
62+
return Files.exists(Paths.get(getAddress()));
63+
}
64+
catch (Exception ex) {
65+
return false;
66+
}
67+
}
68+
69+
public static ResolvedDockerHost from(DockerHost dockerHost) {
70+
return from(Environment.SYSTEM, dockerHost);
71+
}
72+
73+
static ResolvedDockerHost from(Environment environment, DockerHost dockerHost) {
74+
if (environment.get(DOCKER_HOST) != null) {
75+
return new ResolvedDockerHost(environment.get(DOCKER_HOST), isTrue(environment.get(DOCKER_TLS_VERIFY)),
76+
environment.get(DOCKER_CERT_PATH));
77+
}
78+
if (dockerHost != null && dockerHost.getAddress() != null) {
79+
return new ResolvedDockerHost(dockerHost.getAddress(), dockerHost.isSecure(),
80+
dockerHost.getCertificatePath());
81+
}
82+
return new ResolvedDockerHost(Platform.isWindows() ? WINDOWS_NAMED_PIPE_PATH : DOMAIN_SOCKET_PATH, false, null);
83+
}
84+
85+
private static boolean isTrue(String value) {
86+
try {
87+
return (value != null) && (Integer.parseInt(value) == 1);
88+
}
89+
catch (NumberFormatException ex) {
90+
return false;
91+
}
92+
}
93+
94+
}

0 commit comments

Comments
 (0)