Restructure and polish docker code

Restructure and polish docker code to fix a package tangle and
provide better separation of concerns.
This commit is contained in:
Phillip Webb
2020-03-31 22:57:42 -07:00
parent 5d8d0bb159
commit 4dad56a491
34 changed files with 753 additions and 879 deletions

View File

@@ -33,7 +33,8 @@ import org.mockito.MockitoAnnotations;
import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi;
import org.springframework.boot.buildpack.platform.docker.DockerApi.ImageApi;
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
import org.springframework.boot.buildpack.platform.docker.Http.Response;
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
@@ -74,18 +75,18 @@ class DockerApiTests {
private static final String VOLUMES_URL = API_URL + "/volumes";
@Mock
private HttpClientHttp httpClient;
private HttpTransport http;
private DockerApi dockerApi;
@BeforeEach
void setup() {
MockitoAnnotations.initMocks(this);
this.dockerApi = new DockerApi(this.httpClient);
this.dockerApi = new DockerApi(this.http);
}
private HttpClientHttp httpClient() {
return this.httpClient;
private HttpTransport http() {
return this.http;
}
private Response emptyResponse() {
@@ -148,8 +149,8 @@ class DockerApiTests {
URI createUri = new URI(IMAGES_URL + "/create?fromImage=docker.io%2Fcloudfoundry%2Fcnb%3Abionic");
String imageHash = "4acb6bfd6c4f0cabaf7f3690e444afe51f1c7de54d51da7e63fac709c56f1c30";
URI imageUri = new URI(IMAGES_URL + "/docker.io/cloudfoundry/cnb@sha256:" + imageHash + "/json");
given(httpClient().post(createUri)).willReturn(responseOf("pull-stream.json"));
given(httpClient().get(imageUri)).willReturn(responseOf("type/image.json"));
given(http().post(createUri)).willReturn(responseOf("pull-stream.json"));
given(http().get(imageUri)).willReturn(responseOf("type/image.json"));
Image image = this.api.pull(reference, this.pullListener);
assertThat(image.getLayers()).hasSize(46);
InOrder ordered = inOrder(this.pullListener);
@@ -176,14 +177,13 @@ class DockerApiTests {
Image image = Image.of(getClass().getResourceAsStream("type/image.json"));
ImageArchive archive = ImageArchive.from(image);
URI loadUri = new URI(IMAGES_URL + "/load");
given(httpClient().post(eq(loadUri), eq("application/x-tar"), any()))
.willReturn(responseOf("load-stream.json"));
given(http().post(eq(loadUri), eq("application/x-tar"), any())).willReturn(responseOf("load-stream.json"));
this.api.load(archive, this.loadListener);
InOrder ordered = inOrder(this.loadListener);
ordered.verify(this.loadListener).onStart();
ordered.verify(this.loadListener).onUpdate(any());
ordered.verify(this.loadListener).onFinish();
verify(httpClient()).post(any(), any(), this.writer.capture());
verify(http()).post(any(), any(), this.writer.capture());
ByteArrayOutputStream out = new ByteArrayOutputStream();
this.writer.getValue().accept(out);
assertThat(out.toByteArray()).hasSizeGreaterThan(21000);
@@ -201,9 +201,9 @@ class DockerApiTests {
.of("ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
URI removeUri = new URI(IMAGES_URL
+ "/docker.io/library/ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
given(httpClient().delete(removeUri)).willReturn(emptyResponse());
given(http().delete(removeUri)).willReturn(emptyResponse());
this.api.remove(reference, false);
verify(httpClient()).delete(removeUri);
verify(http()).delete(removeUri);
}
@Test
@@ -212,9 +212,9 @@ class DockerApiTests {
.of("ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
URI removeUri = new URI(IMAGES_URL
+ "/docker.io/library/ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d?force=1");
given(httpClient().delete(removeUri)).willReturn(emptyResponse());
given(http().delete(removeUri)).willReturn(emptyResponse());
this.api.remove(reference, true);
verify(httpClient()).delete(removeUri);
verify(http()).delete(removeUri);
}
}
@@ -247,12 +247,12 @@ class DockerApiTests {
ImageReference imageReference = ImageReference.of("ubuntu:bionic");
ContainerConfig config = ContainerConfig.of(imageReference, (update) -> update.withCommand("/bin/bash"));
URI createUri = new URI(CONTAINERS_URL + "/create");
given(httpClient().post(eq(createUri), eq("application/json"), any()))
given(http().post(eq(createUri), eq("application/json"), any()))
.willReturn(responseOf("create-container-response.json"));
ContainerReference containerReference = this.api.create(config);
assertThat(containerReference.toString()).isEqualTo("e90e34656806");
ByteArrayOutputStream out = new ByteArrayOutputStream();
verify(httpClient()).post(any(), any(), this.writer.capture());
verify(http()).post(any(), any(), this.writer.capture());
this.writer.getValue().accept(out);
assertThat(out.toByteArray()).hasSizeGreaterThan(130);
}
@@ -267,17 +267,17 @@ class DockerApiTests {
});
ContainerContent content = ContainerContent.of(archive);
URI createUri = new URI(CONTAINERS_URL + "/create");
given(httpClient().post(eq(createUri), eq("application/json"), any()))
given(http().post(eq(createUri), eq("application/json"), any()))
.willReturn(responseOf("create-container-response.json"));
URI uploadUri = new URI(CONTAINERS_URL + "/e90e34656806/archive?path=%2F");
given(httpClient().put(eq(uploadUri), eq("application/x-tar"), any())).willReturn(emptyResponse());
given(http().put(eq(uploadUri), eq("application/x-tar"), any())).willReturn(emptyResponse());
ContainerReference containerReference = this.api.create(config, content);
assertThat(containerReference.toString()).isEqualTo("e90e34656806");
ByteArrayOutputStream out = new ByteArrayOutputStream();
verify(httpClient()).post(any(), any(), this.writer.capture());
verify(http()).post(any(), any(), this.writer.capture());
this.writer.getValue().accept(out);
assertThat(out.toByteArray()).hasSizeGreaterThan(130);
verify(httpClient()).put(any(), any(), this.writer.capture());
verify(http()).put(any(), any(), this.writer.capture());
this.writer.getValue().accept(out);
assertThat(out.toByteArray()).hasSizeGreaterThan(2000);
}
@@ -292,9 +292,9 @@ class DockerApiTests {
void startStartsContainer() throws Exception {
ContainerReference reference = ContainerReference.of("e90e34656806");
URI startContainerUri = new URI(CONTAINERS_URL + "/e90e34656806/start");
given(httpClient().post(startContainerUri)).willReturn(emptyResponse());
given(http().post(startContainerUri)).willReturn(emptyResponse());
this.api.start(reference);
verify(httpClient()).post(startContainerUri);
verify(http()).post(startContainerUri);
}
@Test
@@ -314,7 +314,7 @@ class DockerApiTests {
void logsProducesEvents() throws Exception {
ContainerReference reference = ContainerReference.of("e90e34656806");
URI logsUri = new URI(CONTAINERS_URL + "/e90e34656806/logs?stdout=1&stderr=1&follow=1");
given(httpClient().get(logsUri)).willReturn(responseOf("log-update-event.stream"));
given(http().get(logsUri)).willReturn(responseOf("log-update-event.stream"));
this.api.logs(reference, this.logListener);
InOrder ordered = inOrder(this.logListener);
ordered.verify(this.logListener).onStart();
@@ -332,7 +332,7 @@ class DockerApiTests {
void waitReturnsStatus() throws Exception {
ContainerReference reference = ContainerReference.of("e90e34656806");
URI waitUri = new URI(CONTAINERS_URL + "/e90e34656806/wait");
given(httpClient().post(waitUri)).willReturn(responseOf("container-wait-response.json"));
given(http().post(waitUri)).willReturn(responseOf("container-wait-response.json"));
ContainerStatus status = this.api.wait(reference);
assertThat(status.getStatusCode()).isEqualTo(1);
}
@@ -347,18 +347,18 @@ class DockerApiTests {
void removeRemovesContainer() throws Exception {
ContainerReference reference = ContainerReference.of("e90e34656806");
URI removeUri = new URI(CONTAINERS_URL + "/e90e34656806");
given(httpClient().delete(removeUri)).willReturn(emptyResponse());
given(http().delete(removeUri)).willReturn(emptyResponse());
this.api.remove(reference, false);
verify(httpClient()).delete(removeUri);
verify(http()).delete(removeUri);
}
@Test
void removeWhenForceIsTrueRemovesContainer() throws Exception {
ContainerReference reference = ContainerReference.of("e90e34656806");
URI removeUri = new URI(CONTAINERS_URL + "/e90e34656806?force=1");
given(httpClient().delete(removeUri)).willReturn(emptyResponse());
given(http().delete(removeUri)).willReturn(emptyResponse());
this.api.remove(reference, true);
verify(httpClient()).delete(removeUri);
verify(http()).delete(removeUri);
}
}
@@ -390,18 +390,18 @@ class DockerApiTests {
void deleteDeletesContainer() throws Exception {
VolumeName name = VolumeName.of("test");
URI removeUri = new URI(VOLUMES_URL + "/test");
given(httpClient().delete(removeUri)).willReturn(emptyResponse());
given(http().delete(removeUri)).willReturn(emptyResponse());
this.api.delete(name, false);
verify(httpClient()).delete(removeUri);
verify(http()).delete(removeUri);
}
@Test
void deleteWhenForceIsTrueDeletesContainer() throws Exception {
VolumeName name = VolumeName.of("test");
URI removeUri = new URI(VOLUMES_URL + "/test?force=1");
given(httpClient().delete(removeUri)).willReturn(emptyResponse());
given(http().delete(removeUri)).willReturn(emptyResponse());
this.api.delete(name, true);
verify(httpClient()).delete(removeUri);
verify(http()).delete(removeUri);
}
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright 2012-2020 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.httpclient;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpHost;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.httpclient.RemoteEnvironmentDockerHttpClientConnection.EnvironmentAccessor;
import org.springframework.boot.buildpack.platform.docker.ssl.SslContextFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link RemoteEnvironmentDockerHttpClientConnection}.
*
* @author Scott Frederick
*/
class RemoteEnvironmentDockerHttpClientConnectionTests {
private EnvironmentAccessor environment;
private RemoteEnvironmentDockerHttpClientConnection connection;
private SslContextFactory sslContextFactory;
@BeforeEach
void setUp() {
this.environment = mock(EnvironmentAccessor.class);
this.sslContextFactory = mock(SslContextFactory.class);
this.connection = new RemoteEnvironmentDockerHttpClientConnection(this.environment, this.sslContextFactory);
}
@Test
void notAcceptedWhenDockerHostNotSet() {
assertThat(this.connection.accept()).isFalse();
assertThatIllegalStateException().isThrownBy(() -> this.connection.getHttpHost());
assertThatIllegalStateException().isThrownBy(() -> this.connection.getHttpClient());
}
@Test
void acceptedWhenDockerHostIsSet() {
given(this.environment.getProperty("DOCKER_HOST")).willReturn("tcp://192.168.1.2:2376");
assertThat(this.connection.accept()).isTrue();
}
@Test
void invalidTlsConfigurationThrowsException() {
given(this.environment.getProperty("DOCKER_HOST")).willReturn("tcp://192.168.1.2:2376");
given(this.environment.getProperty("DOCKER_TLS_VERIFY")).willReturn("1");
assertThatIllegalArgumentException().isThrownBy(() -> this.connection.accept())
.withMessageContaining("DOCKER_CERT_PATH");
}
@Test
void hostProtocolIsHttpWhenNotSecure() {
given(this.environment.getProperty("DOCKER_HOST")).willReturn("tcp://192.168.1.2:2376");
assertThat(this.connection.accept()).isTrue();
HttpHost host = this.connection.getHttpHost();
assertThat(host).isNotNull();
assertThat(host.getSchemeName()).isEqualTo("http");
assertThat(host.getHostName()).isEqualTo("192.168.1.2");
assertThat(host.getPort()).isEqualTo(2376);
}
@Test
void hostProtocolIsHttpsWhenSecure() throws NoSuchAlgorithmException {
given(this.environment.getProperty("DOCKER_HOST")).willReturn("tcp://192.168.1.2:2376");
given(this.environment.getProperty("DOCKER_TLS_VERIFY")).willReturn("1");
given(this.environment.getProperty("DOCKER_CERT_PATH")).willReturn("/test-cert-path");
given(this.sslContextFactory.forPath("/test-cert-path")).willReturn(SSLContext.getDefault());
assertThat(this.connection.accept()).isTrue();
HttpHost host = this.connection.getHttpHost();
assertThat(host).isNotNull();
assertThat(host.getSchemeName()).isEqualTo("https");
assertThat(host.getHostName()).isEqualTo("192.168.1.2");
assertThat(host.getPort()).isEqualTo(2376);
}
}

View File

@@ -54,15 +54,12 @@ class KeyStoreFactoryTests {
throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException {
Path certPath = this.fileWriter.writeFile("cert.pem", PemFileWriter.CA_CERTIFICATE, PemFileWriter.CERTIFICATE);
KeyStore keyStore = KeyStoreFactory.create(certPath, null, "test-alias");
assertThat(keyStore.containsAlias("test-alias-0")).isTrue();
assertThat(keyStore.getCertificate("test-alias-0")).isNotNull();
assertThat(keyStore.getKey("test-alias-0", new char[] {})).isNull();
assertThat(keyStore.containsAlias("test-alias-1")).isTrue();
assertThat(keyStore.getCertificate("test-alias-1")).isNotNull();
assertThat(keyStore.getKey("test-alias-1", new char[] {})).isNull();
Files.delete(certPath);
}
@@ -72,11 +69,9 @@ class KeyStoreFactoryTests {
Path certPath = this.fileWriter.writeFile("cert.pem", PemFileWriter.CA_CERTIFICATE, PemFileWriter.CERTIFICATE);
Path keyPath = this.fileWriter.writeFile("key.pem", PemFileWriter.PRIVATE_KEY);
KeyStore keyStore = KeyStoreFactory.create(certPath, keyPath, "test-alias");
assertThat(keyStore.containsAlias("test-alias")).isTrue();
assertThat(keyStore.getCertificate("test-alias")).isNotNull();
assertThat(keyStore.getKey("test-alias", new char[] {})).isNotNull();
Files.delete(certPath);
Files.delete(keyPath);
}

View File

@@ -50,8 +50,7 @@ class SslContextFactoryTests {
this.fileWriter.writeFile("cert.pem", PemFileWriter.CERTIFICATE);
this.fileWriter.writeFile("key.pem", PemFileWriter.PRIVATE_KEY);
this.fileWriter.writeFile("ca.pem", PemFileWriter.CA_CERTIFICATE);
SSLContext sslContext = new SslContextFactory().forPath(this.fileWriter.getTempDir().toString());
SSLContext sslContext = new SslContextFactory().forDirectory(this.fileWriter.getTempDir().toString());
assertThat(sslContext).isNotNull();
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.buildpack.platform.docker;
package org.springframework.boot.buildpack.platform.docker.transport;
import java.net.URI;
import java.net.URISyntaxException;
@@ -26,12 +26,12 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link DockerException}.
* Tests for {@link DockerEngineException}.
*
* @author Phillip Webb
* @author Scott Frederick
*/
class DockerExceptionTests {
class DockerEngineExceptionTests {
private static final String HOST = "docker://localhost/";
@@ -51,20 +51,21 @@ class DockerExceptionTests {
@Test
void createWhenHostIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new DockerException(null, null, 404, null, NO_ERRORS))
assertThatIllegalArgumentException()
.isThrownBy(() -> new DockerEngineException(null, null, 404, null, NO_ERRORS))
.withMessage("host must not be null");
}
@Test
void createWhenUriIsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new DockerException(this.HOST, null, 404, null, NO_ERRORS))
.isThrownBy(() -> new DockerEngineException(HOST, null, 404, null, NO_ERRORS))
.withMessage("URI must not be null");
}
@Test
void create() {
DockerException exception = new DockerException(HOST, URI, 404, "missing", ERRORS);
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", ERRORS);
assertThat(exception.getMessage()).isEqualTo(
"Docker API call to 'docker://localhost/example' failed with status code 404 \"missing\" [code: message]");
assertThat(exception.getStatusCode()).isEqualTo(404);
@@ -74,7 +75,7 @@ class DockerExceptionTests {
@Test
void createWhenReasonPhraseIsNull() {
DockerException exception = new DockerException(HOST, URI, 404, null, ERRORS);
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, null, ERRORS);
assertThat(exception.getMessage()).isEqualTo(
"Docker API call to 'docker://localhost/example' failed with status code 404 [code: message]");
assertThat(exception.getStatusCode()).isEqualTo(404);
@@ -84,13 +85,13 @@ class DockerExceptionTests {
@Test
void createWhenErrorsIsNull() {
DockerException exception = new DockerException(HOST, URI, 404, "missing", null);
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", null);
assertThat(exception.getErrors()).isNull();
}
@Test
void createWhenErrorsIsEmpty() {
DockerException exception = new DockerException(HOST, URI, 404, "missing", NO_ERRORS);
DockerEngineException exception = new DockerEngineException(HOST, URI, 404, "missing", NO_ERRORS);
assertThat(exception.getMessage())
.isEqualTo("Docker API call to 'docker://localhost/example' failed with status code 404 \"missing\"");
assertThat(exception.getStatusCode()).isEqualTo(404);

View File

@@ -14,13 +14,13 @@
* limitations under the License.
*/
package org.springframework.boot.buildpack.platform.docker;
package org.springframework.boot.buildpack.platform.docker.transport;
import java.util.Iterator;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.Errors.Error;
import org.springframework.boot.buildpack.platform.docker.transport.Errors.Error;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.buildpack.platform.docker;
package org.springframework.boot.buildpack.platform.docker.transport;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -42,8 +42,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.buildpack.platform.docker.Http.Response;
import org.springframework.boot.buildpack.platform.docker.httpclient.DockerHttpClientConnection;
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
import org.springframework.util.StreamUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -53,13 +52,13 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link HttpClientHttp}.
* Tests for {@link HttpClientTransport}.
*
* @author Phillip Webb
* @author Mike Smithson
* @author Scott Frederick
*/
class HttpClientHttpTests {
class HttpClientTransportTests {
private static final String APPLICATION_JSON = "application/json";
@@ -84,7 +83,7 @@ class HttpClientHttpTests {
@Captor
private ArgumentCaptor<HttpUriRequest> requestCaptor;
private HttpClientHttp http;
private HttpClientTransport http;
private URI uri;
@@ -94,7 +93,7 @@ class HttpClientHttpTests {
given(this.client.execute(any(HttpHost.class), any(HttpRequest.class))).willReturn(this.response);
given(this.response.getEntity()).willReturn(this.entity);
given(this.response.getStatusLine()).willReturn(this.statusLine);
this.http = new HttpClientHttp(new TestClientConnection(this.client));
this.http = new TestHttpClientTransport(this.client);
this.uri = new URI("example");
}
@@ -181,14 +180,14 @@ class HttpClientHttpTests {
void executeWhenResposeIsIn400RangeShouldThrowDockerException() throws IOException {
given(this.entity.getContent()).willReturn(getClass().getResourceAsStream("errors.json"));
given(this.statusLine.getStatusCode()).willReturn(404);
assertThatExceptionOfType(DockerException.class).isThrownBy(() -> this.http.get(this.uri))
assertThatExceptionOfType(DockerEngineException.class).isThrownBy(() -> this.http.get(this.uri))
.satisfies((ex) -> assertThat(ex.getErrors()).hasSize(2));
}
@Test
void executeWhenResposeIsIn500RangeShouldThrowDockerException() {
given(this.statusLine.getStatusCode()).willReturn(500);
assertThatExceptionOfType(DockerException.class).isThrownBy(() -> this.http.get(this.uri))
assertThatExceptionOfType(DockerEngineException.class).isThrownBy(() -> this.http.get(this.uri))
.satisfies((ex) -> assertThat(ex.getErrors()).isNull());
}
@@ -196,8 +195,8 @@ class HttpClientHttpTests {
void executeWhenClientThrowsIOExceptionRethrowsAsDockerException() throws IOException {
given(this.client.execute(any(HttpHost.class), any(HttpRequest.class)))
.willThrow(new IOException("test IO exception"));
assertThatExceptionOfType(DockerException.class).isThrownBy(() -> this.http.get(this.uri))
.satisfies((ex) -> assertThat(ex.getErrors()).isNull()).satisfies(DockerException::getStatusCode)
assertThatExceptionOfType(DockerEngineException.class).isThrownBy(() -> this.http.get(this.uri))
.satisfies((ex) -> assertThat(ex.getErrors()).isNull()).satisfies(DockerEngineException::getStatusCode)
.withMessageContaining("500")
.satisfies((ex) -> assertThat(ex.getReasonPhrase()).contains("test IO exception"));
}
@@ -208,22 +207,13 @@ class HttpClientHttpTests {
return new String(out.toByteArray(), StandardCharsets.UTF_8);
}
private static final class TestClientConnection implements DockerHttpClientConnection {
/**
* Test {@link HttpClientTransport} implementation.
*/
static class TestHttpClientTransport extends HttpClientTransport {
private final CloseableHttpClient client;
private TestClientConnection(CloseableHttpClient client) {
this.client = client;
}
@Override
public HttpHost getHttpHost() {
return HttpHost.create("docker://localhost");
}
@Override
public CloseableHttpClient getHttpClient() {
return this.client;
protected TestHttpClientTransport(CloseableHttpClient client) {
super(client, HttpHost.create("docker://localhost"));
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2012-2020 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.util.Collections;
import java.util.Map;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link HttpTransport}.
*
* @author Phillip Webb
*/
class HttpTransportTests {
@Test
void createWhenHasDockerHostVariableReturnsRemote() {
Map<String, String> environment = Collections.singletonMap("DOCKER_HOST", "192.168.1.0");
HttpTransport transport = HttpTransport.create(environment::get);
assertThat(transport).isInstanceOf(RemoteHttpClientTransport.class);
}
@Test
void createWhenDoesNotHaveDockerHostVariableReturnsLocal() {
HttpTransport transport = HttpTransport.create((name) -> null);
assertThat(transport).isInstanceOf(LocalHttpClientTransport.class);
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2012-2020 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.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpHost;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.ssl.SslContextFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link RemoteHttpClientTransport}
*
* @author Scott Frederick
* @author Phillip Webb
*/
class RemoteHttpClientTransportTests {
private Map<String, String> environment = new LinkedHashMap<>();
@Test
void createIfPossibleWhenDockerHostIsNotSetReturnsNull() {
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get);
assertThat(transport).isNull();
}
@Test
void createIfPossibleWhenDockerHostIsSetReturnsTransport() {
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get);
assertThat(transport).isNotNull();
}
@Test
void createIfPossibleWhenTlsVerifyWithMissingCertPathThrowsException() {
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
this.environment.put("DOCKER_TLS_VERIFY", "1");
assertThatIllegalArgumentException()
.isThrownBy(() -> RemoteHttpClientTransport.createIfPossible(this.environment::get))
.withMessageContaining("DOCKER_CERT_PATH");
}
@Test
void createIfPossibleWhenNoTlsVerifyUsesHttp() {
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get);
assertThat(transport.getHost()).satisfies(hostOf("http", "192.168.1.2", 2376));
}
@Test
void createIfPossibleWhenTlsVerifyUsesHttps() throws Exception {
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
this.environment.put("DOCKER_TLS_VERIFY", "1");
this.environment.put("DOCKER_CERT_PATH", "/test-cert-path");
SslContextFactory sslContextFactory = mock(SslContextFactory.class);
given(sslContextFactory.forDirectory("/test-cert-path")).willReturn(SSLContext.getDefault());
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get,
sslContextFactory);
assertThat(transport.getHost()).satisfies(hostOf("https", "192.168.1.2", 2376));
}
private Consumer<HttpHost> hostOf(String scheme, String hostName, int port) {
return (host) -> {
assertThat(host).isNotNull();
assertThat(host.getSchemeName()).isEqualTo(scheme);
assertThat(host.getHostName()).isEqualTo(hostName);
assertThat(host.getPort()).isEqualTo(port);
};
}
}