Browse Source
This commit adds support for a `docker.bindHostToBuilder` option in the Maven and Gradle image building goal and task. Fixes gh-29384pull/30406/head
24 changed files with 621 additions and 236 deletions
@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.buildpack.platform.docker.configuration; |
||||
|
||||
import java.nio.file.Files; |
||||
import java.nio.file.Paths; |
||||
|
||||
import com.sun.jna.Platform; |
||||
|
||||
import org.springframework.boot.buildpack.platform.system.Environment; |
||||
|
||||
/** |
||||
* Resolves a {@link DockerHost} from the environment, configuration, or using defaults. |
||||
* |
||||
* @author Scott Frederick |
||||
* @since 2.7.0 |
||||
*/ |
||||
public class ResolvedDockerHost extends DockerHost { |
||||
|
||||
private static final String UNIX_SOCKET_PREFIX = "unix://"; |
||||
|
||||
private static final String DOMAIN_SOCKET_PATH = "/var/run/docker.sock"; |
||||
|
||||
private static final String WINDOWS_NAMED_PIPE_PATH = "//./pipe/docker_engine"; |
||||
|
||||
private static final String DOCKER_HOST = "DOCKER_HOST"; |
||||
|
||||
private static final String DOCKER_TLS_VERIFY = "DOCKER_TLS_VERIFY"; |
||||
|
||||
private static final String DOCKER_CERT_PATH = "DOCKER_CERT_PATH"; |
||||
|
||||
ResolvedDockerHost(String address, boolean secure, String certificatePath) { |
||||
super(address, secure, certificatePath); |
||||
} |
||||
|
||||
@Override |
||||
public String getAddress() { |
||||
return super.getAddress().startsWith(UNIX_SOCKET_PREFIX) |
||||
? super.getAddress().substring(UNIX_SOCKET_PREFIX.length()) : super.getAddress(); |
||||
} |
||||
|
||||
public boolean isRemote() { |
||||
return getAddress().startsWith("http") || getAddress().startsWith("tcp"); |
||||
} |
||||
|
||||
public boolean isLocalFileReference() { |
||||
try { |
||||
return Files.exists(Paths.get(getAddress())); |
||||
} |
||||
catch (Exception ex) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public static ResolvedDockerHost from(DockerHost dockerHost) { |
||||
return from(Environment.SYSTEM, dockerHost); |
||||
} |
||||
|
||||
static ResolvedDockerHost from(Environment environment, DockerHost dockerHost) { |
||||
if (environment.get(DOCKER_HOST) != null) { |
||||
return new ResolvedDockerHost(environment.get(DOCKER_HOST), isTrue(environment.get(DOCKER_TLS_VERIFY)), |
||||
environment.get(DOCKER_CERT_PATH)); |
||||
} |
||||
if (dockerHost != null && dockerHost.getAddress() != null) { |
||||
return new ResolvedDockerHost(dockerHost.getAddress(), dockerHost.isSecure(), |
||||
dockerHost.getCertificatePath()); |
||||
} |
||||
return new ResolvedDockerHost(Platform.isWindows() ? WINDOWS_NAMED_PIPE_PATH : DOMAIN_SOCKET_PATH, false, null); |
||||
} |
||||
|
||||
private static boolean isTrue(String value) { |
||||
try { |
||||
return (value != null) && (Integer.parseInt(value) == 1); |
||||
} |
||||
catch (NumberFormatException ex) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,166 @@
@@ -0,0 +1,166 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.buildpack.platform.docker.configuration; |
||||
|
||||
import java.io.IOException; |
||||
import java.nio.file.Files; |
||||
import java.nio.file.Path; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.condition.DisabledOnOs; |
||||
import org.junit.jupiter.api.condition.EnabledOnOs; |
||||
import org.junit.jupiter.api.condition.OS; |
||||
import org.junit.jupiter.api.io.TempDir; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link ResolvedDockerHost}. |
||||
* |
||||
* @author Scott Frederick |
||||
*/ |
||||
class ResolvedDockerHostTests { |
||||
|
||||
private final Map<String, String> environment = new LinkedHashMap<>(); |
||||
|
||||
@Test |
||||
@DisabledOnOs(OS.WINDOWS) |
||||
void resolveWhenDockerHostIsNullReturnsLinuxDefault() { |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, null); |
||||
assertThat(dockerHost.getAddress()).isEqualTo("/var/run/docker.sock"); |
||||
assertThat(dockerHost.isSecure()).isFalse(); |
||||
assertThat(dockerHost.getCertificatePath()).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
@EnabledOnOs(OS.WINDOWS) |
||||
void resolveWhenDockerHostIsNullReturnsWindowsDefault() { |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, null); |
||||
assertThat(dockerHost.getAddress()).isEqualTo("//./pipe/docker_engine"); |
||||
assertThat(dockerHost.isSecure()).isFalse(); |
||||
assertThat(dockerHost.getCertificatePath()).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
@DisabledOnOs(OS.WINDOWS) |
||||
void resolveWhenDockerHostAddressIsNullReturnsLinuxDefault() { |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, new DockerHost(null)); |
||||
assertThat(dockerHost.getAddress()).isEqualTo("/var/run/docker.sock"); |
||||
assertThat(dockerHost.isSecure()).isFalse(); |
||||
assertThat(dockerHost.getCertificatePath()).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void resolveWhenDockerHostAddressIsLocalReturnsAddress(@TempDir Path tempDir) throws IOException { |
||||
String socketFilePath = Files.createTempFile(tempDir, "remote-transport", null).toAbsolutePath().toString(); |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, |
||||
new DockerHost(socketFilePath, false, null)); |
||||
assertThat(dockerHost.isLocalFileReference()).isTrue(); |
||||
assertThat(dockerHost.isRemote()).isFalse(); |
||||
assertThat(dockerHost.getAddress()).isEqualTo(socketFilePath); |
||||
assertThat(dockerHost.isSecure()).isFalse(); |
||||
assertThat(dockerHost.getCertificatePath()).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void resolveWhenDockerHostAddressIsLocalWithSchemeReturnsAddress(@TempDir Path tempDir) throws IOException { |
||||
String socketFilePath = Files.createTempFile(tempDir, "remote-transport", null).toAbsolutePath().toString(); |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, |
||||
new DockerHost("unix://" + socketFilePath, false, null)); |
||||
assertThat(dockerHost.isLocalFileReference()).isTrue(); |
||||
assertThat(dockerHost.isRemote()).isFalse(); |
||||
assertThat(dockerHost.getAddress()).isEqualTo(socketFilePath); |
||||
assertThat(dockerHost.isSecure()).isFalse(); |
||||
assertThat(dockerHost.getCertificatePath()).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void resolveWhenDockerHostAddressIsHttpReturnsAddress() { |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, |
||||
new DockerHost("http://docker.example.com", false, null)); |
||||
assertThat(dockerHost.isLocalFileReference()).isFalse(); |
||||
assertThat(dockerHost.isRemote()).isTrue(); |
||||
assertThat(dockerHost.getAddress()).isEqualTo("http://docker.example.com"); |
||||
assertThat(dockerHost.isSecure()).isFalse(); |
||||
assertThat(dockerHost.getCertificatePath()).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void resolveWhenDockerHostAddressIsHttpsReturnsAddress() { |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, |
||||
new DockerHost("https://docker.example.com", true, "/cert-path")); |
||||
assertThat(dockerHost.isLocalFileReference()).isFalse(); |
||||
assertThat(dockerHost.isRemote()).isTrue(); |
||||
assertThat(dockerHost.getAddress()).isEqualTo("https://docker.example.com"); |
||||
assertThat(dockerHost.isSecure()).isTrue(); |
||||
assertThat(dockerHost.getCertificatePath()).isEqualTo("/cert-path"); |
||||
} |
||||
|
||||
@Test |
||||
void resolveWhenDockerHostAddressIsTcpReturnsAddress() { |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, |
||||
new DockerHost("tcp://192.168.99.100:2376", true, "/cert-path")); |
||||
assertThat(dockerHost.isLocalFileReference()).isFalse(); |
||||
assertThat(dockerHost.isRemote()).isTrue(); |
||||
assertThat(dockerHost.getAddress()).isEqualTo("tcp://192.168.99.100:2376"); |
||||
assertThat(dockerHost.isSecure()).isTrue(); |
||||
assertThat(dockerHost.getCertificatePath()).isEqualTo("/cert-path"); |
||||
} |
||||
|
||||
@Test |
||||
void resolveWhenEnvironmentAddressIsLocalReturnsAddress(@TempDir Path tempDir) throws IOException { |
||||
String socketFilePath = Files.createTempFile(tempDir, "remote-transport", null).toAbsolutePath().toString(); |
||||
this.environment.put("DOCKER_HOST", socketFilePath); |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, |
||||
new DockerHost("/unused", true, "/unused")); |
||||
assertThat(dockerHost.isLocalFileReference()).isTrue(); |
||||
assertThat(dockerHost.isRemote()).isFalse(); |
||||
assertThat(dockerHost.getAddress()).isEqualTo(socketFilePath); |
||||
assertThat(dockerHost.isSecure()).isFalse(); |
||||
assertThat(dockerHost.getCertificatePath()).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void resolveWhenEnvironmentAddressIsLocalWithSchemeReturnsAddress(@TempDir Path tempDir) throws IOException { |
||||
String socketFilePath = Files.createTempFile(tempDir, "remote-transport", null).toAbsolutePath().toString(); |
||||
this.environment.put("DOCKER_HOST", "unix://" + socketFilePath); |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, |
||||
new DockerHost("/unused", true, "/unused")); |
||||
assertThat(dockerHost.isLocalFileReference()).isTrue(); |
||||
assertThat(dockerHost.isRemote()).isFalse(); |
||||
assertThat(dockerHost.getAddress()).isEqualTo(socketFilePath); |
||||
assertThat(dockerHost.isSecure()).isFalse(); |
||||
assertThat(dockerHost.getCertificatePath()).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void resolveWhenEnvironmentAddressIsTcpReturnsAddress() { |
||||
this.environment.put("DOCKER_HOST", "tcp://192.168.99.100:2376"); |
||||
this.environment.put("DOCKER_TLS_VERIFY", "1"); |
||||
this.environment.put("DOCKER_CERT_PATH", "/cert-path"); |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(this.environment::get, |
||||
new DockerHost("tcp://1.1.1.1", false, "/unused")); |
||||
assertThat(dockerHost.isLocalFileReference()).isFalse(); |
||||
assertThat(dockerHost.isRemote()).isTrue(); |
||||
assertThat(dockerHost.getAddress()).isEqualTo("tcp://192.168.99.100:2376"); |
||||
assertThat(dockerHost.isSecure()).isTrue(); |
||||
assertThat(dockerHost.getCertificatePath()).isEqualTo("/cert-path"); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
/* |
||||
* Copyright 2012-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.buildpack.platform.docker.transport; |
||||
|
||||
import java.io.IOException; |
||||
import java.nio.file.Files; |
||||
import java.nio.file.Path; |
||||
import java.nio.file.Paths; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.io.TempDir; |
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerHost; |
||||
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link LocalHttpClientTransport} |
||||
* |
||||
* @author Scott Frederick |
||||
*/ |
||||
class LocalHttpClientTransportTests { |
||||
|
||||
@Test |
||||
void createWhenDockerHostIsFileReturnsTransport(@TempDir Path tempDir) throws IOException { |
||||
String socketFilePath = Files.createTempFile(tempDir, "remote-transport", null).toAbsolutePath().toString(); |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(new DockerHost(socketFilePath)); |
||||
LocalHttpClientTransport transport = LocalHttpClientTransport.create(dockerHost); |
||||
assertThat(transport).isNotNull(); |
||||
} |
||||
|
||||
@Test |
||||
void createWhenDockerHostIsFileThatDoesNotExistReturnsTransport(@TempDir Path tempDir) { |
||||
String socketFilePath = Paths.get(tempDir.toString(), "dummy").toAbsolutePath().toString(); |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(new DockerHost(socketFilePath)); |
||||
LocalHttpClientTransport transport = LocalHttpClientTransport.create(dockerHost); |
||||
assertThat(transport).isNotNull(); |
||||
} |
||||
|
||||
@Test |
||||
void createWhenDockerHostIsAddressReturnsTransport() { |
||||
ResolvedDockerHost dockerHost = ResolvedDockerHost.from(new DockerHost("tcp://192.168.1.2:2376")); |
||||
LocalHttpClientTransport transport = LocalHttpClientTransport.create(dockerHost); |
||||
assertThat(transport).isNotNull(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
{ |
||||
"User": "root", |
||||
"Image": "pack.local/ephemeral-builder", |
||||
"Cmd": [ |
||||
"/cnb/lifecycle/creator", |
||||
"-app", |
||||
"/workspace", |
||||
"-platform", |
||||
"/platform", |
||||
"-run-image", |
||||
"docker.io/cloudfoundry/run:latest", |
||||
"-layers", |
||||
"/layers", |
||||
"-cache-dir", |
||||
"/cache", |
||||
"-launch-cache", |
||||
"/launch-cache", |
||||
"-daemon", |
||||
"docker.io/library/my-application:latest" |
||||
], |
||||
"Env": [ |
||||
"CNB_PLATFORM_API=0.8" |
||||
], |
||||
"Labels": { |
||||
"author": "spring-boot" |
||||
}, |
||||
"HostConfig": { |
||||
"Binds": [ |
||||
"/var/alt.sock:/var/run/docker.sock", |
||||
"pack-layers-aaaaaaaaaa:/layers", |
||||
"pack-app-aaaaaaaaaa:/workspace", |
||||
"pack-cache-b35197ac41ea.build:/cache", |
||||
"pack-cache-b35197ac41ea.launch:/launch-cache" |
||||
] |
||||
} |
||||
} |
||||
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
{ |
||||
"User": "root", |
||||
"Image": "pack.local/ephemeral-builder", |
||||
"Cmd": [ |
||||
"/cnb/lifecycle/creator", |
||||
"-app", |
||||
"/workspace", |
||||
"-platform", |
||||
"/platform", |
||||
"-run-image", |
||||
"docker.io/cloudfoundry/run:latest", |
||||
"-layers", |
||||
"/layers", |
||||
"-cache-dir", |
||||
"/cache", |
||||
"-launch-cache", |
||||
"/launch-cache", |
||||
"-daemon", |
||||
"docker.io/library/my-application:latest" |
||||
], |
||||
"Env": [ |
||||
"DOCKER_HOST=tcp://192.168.1.2:2376", |
||||
"CNB_PLATFORM_API=0.8" |
||||
], |
||||
"Labels": { |
||||
"author": "spring-boot" |
||||
}, |
||||
"HostConfig": { |
||||
"Binds": [ |
||||
"pack-layers-aaaaaaaaaa:/layers", |
||||
"pack-app-aaaaaaaaaa:/workspace", |
||||
"pack-cache-b35197ac41ea.build:/cache", |
||||
"pack-cache-b35197ac41ea.launch:/launch-cache" |
||||
] |
||||
} |
||||
} |
||||
Loading…
Reference in new issue