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:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user