Polish "Support authentication to private Docker registry"
See gh-22972
This commit is contained in:
@@ -54,7 +54,7 @@ public class Builder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Builder(BuildLog log) {
|
public Builder(BuildLog log) {
|
||||||
this(log, new DockerApi(new DockerConfiguration()));
|
this(log, new DockerApi());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder(BuildLog log, DockerConfiguration dockerConfiguration) {
|
public Builder(BuildLog log, DockerConfiguration dockerConfiguration) {
|
||||||
|
|||||||
@@ -24,12 +24,9 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.http.Header;
|
|
||||||
import org.apache.http.client.utils.URIBuilder;
|
import org.apache.http.client.utils.URIBuilder;
|
||||||
import org.apache.http.message.BasicHeader;
|
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryConfiguration;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
|
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.transport.HttpTransport.Response;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||||
@@ -72,15 +69,15 @@ public class DockerApi {
|
|||||||
* Create a new {@link DockerApi} instance.
|
* Create a new {@link DockerApi} instance.
|
||||||
*/
|
*/
|
||||||
public DockerApi() {
|
public DockerApi() {
|
||||||
this(new DockerConfiguration());
|
this(DockerConfiguration.withDefaults());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link DockerApi} instance.
|
* Create a new {@link DockerApi} instance.
|
||||||
* @param dockerConfiguration the Docker configuration options.
|
* @param dockerConfiguration the Docker configuration options
|
||||||
*/
|
*/
|
||||||
public DockerApi(DockerConfiguration dockerConfiguration) {
|
public DockerApi(DockerConfiguration dockerConfiguration) {
|
||||||
this(HttpTransport.create(createDockerEngineAuthenticationHeaders(dockerConfiguration)));
|
this(HttpTransport.create(dockerConfiguration));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,22 +93,6 @@ public class DockerApi {
|
|||||||
this.volume = new VolumeApi();
|
this.volume = new VolumeApi();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Collection<Header> createDockerEngineAuthenticationHeaders(DockerConfiguration dockerConfiguration) {
|
|
||||||
Assert.notNull(dockerConfiguration, "Docker configuration must not be null");
|
|
||||||
|
|
||||||
DockerRegistryConfiguration dockerRegistryConfiguration = dockerConfiguration.getDockerRegistryConfiguration();
|
|
||||||
if (dockerRegistryConfiguration == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
String dockerRegistryAuthToken = dockerRegistryConfiguration.createDockerRegistryAuthToken();
|
|
||||||
if (StringUtils.isEmpty(dockerRegistryAuthToken)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Arrays.asList(new BasicHeader("X-Registry-Auth", dockerRegistryAuthToken));
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpTransport http() {
|
private HttpTransport http() {
|
||||||
return this.http;
|
return this.http;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,34 +16,41 @@
|
|||||||
|
|
||||||
package org.springframework.boot.buildpack.platform.docker.configuration;
|
package org.springframework.boot.buildpack.platform.docker.configuration;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Docker configuration options.
|
* Docker configuration options.
|
||||||
*
|
*
|
||||||
* @author Wei Jiang
|
* @author Wei Jiang
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 2.4.0
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public class DockerConfiguration {
|
public final class DockerConfiguration {
|
||||||
|
|
||||||
/**
|
private final DockerRegistryAuthentication authentication;
|
||||||
* The docker registry configuration.
|
|
||||||
*/
|
|
||||||
private DockerRegistryConfiguration dockerRegistryConfiguration;
|
|
||||||
|
|
||||||
public DockerConfiguration() {
|
private DockerConfiguration(DockerRegistryAuthentication authentication) {
|
||||||
super();
|
this.authentication = authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerConfiguration(DockerRegistryConfiguration dockerRegistryConfiguration) {
|
public DockerRegistryAuthentication getRegistryAuthentication() {
|
||||||
super();
|
return this.authentication;
|
||||||
this.dockerRegistryConfiguration = dockerRegistryConfiguration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerRegistryConfiguration getDockerRegistryConfiguration() {
|
public static DockerConfiguration withDefaults() {
|
||||||
return this.dockerRegistryConfiguration;
|
return new DockerConfiguration(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDockerRegistryConfiguration(DockerRegistryConfiguration dockerRegistryConfiguration) {
|
public static DockerConfiguration withRegistryTokenAuthentication(String token) {
|
||||||
this.dockerRegistryConfiguration = dockerRegistryConfiguration;
|
Assert.notNull(token, "Token must not be null");
|
||||||
|
return new DockerConfiguration(new DockerRegistryTokenAuthentication(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DockerConfiguration withRegistryUserAuthentication(String username, String password, String url,
|
||||||
|
String email) {
|
||||||
|
Assert.notNull(username, "Username must not be null");
|
||||||
|
Assert.notNull(password, "Password must not be null");
|
||||||
|
return new DockerConfiguration(new DockerRegistryUserAuthentication(username, password, url, email));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Docker registry authentication configuration.
|
||||||
|
*
|
||||||
|
* @author Scott Frederick
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public abstract class DockerRegistryAuthentication {
|
||||||
|
|
||||||
|
public String createAuthHeader() {
|
||||||
|
try {
|
||||||
|
return Base64Utils.encodeToUrlSafeString(SharedObjectMapper.get().writeValueAsBytes(this));
|
||||||
|
}
|
||||||
|
catch (JsonProcessingException ex) {
|
||||||
|
throw new IllegalStateException("Error creating Docker registry authentication header", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,129 +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.configuration;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
|
|
||||||
import org.springframework.util.Base64Utils;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry configuration options.
|
|
||||||
*
|
|
||||||
* @author Wei Jiang
|
|
||||||
* @since 2.4.0
|
|
||||||
*/
|
|
||||||
public class DockerRegistryConfiguration {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry server address.
|
|
||||||
*/
|
|
||||||
@JsonProperty("serveraddress")
|
|
||||||
private String url;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry authentication username.
|
|
||||||
*/
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry authentication password.
|
|
||||||
*/
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry authentication email.
|
|
||||||
*/
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry authentication identity token.
|
|
||||||
*/
|
|
||||||
@JsonIgnore
|
|
||||||
private String token;
|
|
||||||
|
|
||||||
public DockerRegistryConfiguration() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DockerRegistryConfiguration(String url, String username, String password, String email, String token) {
|
|
||||||
super();
|
|
||||||
this.url = url;
|
|
||||||
this.username = username;
|
|
||||||
this.password = password;
|
|
||||||
this.email = email;
|
|
||||||
this.token = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String createDockerRegistryAuthToken() {
|
|
||||||
if (!StringUtils.isEmpty(this.getToken())) {
|
|
||||||
return this.getToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return Base64Utils.encodeToString(SharedObjectMapper.get().writeValueAsBytes(this));
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new IllegalStateException("create docker registry authentication token failed.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrl() {
|
|
||||||
return this.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUrl(String url) {
|
|
||||||
this.url = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return this.username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsername(String username) {
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPassword() {
|
|
||||||
return this.password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPassword(String password) {
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEmail() {
|
|
||||||
return this.email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEmail(String email) {
|
|
||||||
this.email = email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getToken() {
|
|
||||||
return this.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setToken(String token) {
|
|
||||||
this.token = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Docker registry authentication configuration using a token.
|
||||||
|
*
|
||||||
|
* @author Scott Frederick
|
||||||
|
*/
|
||||||
|
class DockerRegistryTokenAuthentication extends DockerRegistryAuthentication {
|
||||||
|
|
||||||
|
@JsonProperty("identitytoken")
|
||||||
|
private final String token;
|
||||||
|
|
||||||
|
DockerRegistryTokenAuthentication(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getToken() {
|
||||||
|
return this.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Docker registry authentication configuration using user credentials.
|
||||||
|
*
|
||||||
|
* @author Scott Frederick
|
||||||
|
*/
|
||||||
|
class DockerRegistryUserAuthentication extends DockerRegistryAuthentication {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private final String password;
|
||||||
|
|
||||||
|
@JsonProperty("serveraddress")
|
||||||
|
private final String url;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private final String email;
|
||||||
|
|
||||||
|
DockerRegistryUserAuthentication(String username, String password, String url, String email) {
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
this.url = url;
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getUsername() {
|
||||||
|
return this.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPassword() {
|
||||||
|
return this.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getUrl() {
|
||||||
|
return this.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getEmail() {
|
||||||
|
return this.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -36,10 +36,12 @@ import org.apache.http.client.methods.HttpUriRequest;
|
|||||||
import org.apache.http.entity.AbstractHttpEntity;
|
import org.apache.http.entity.AbstractHttpEntity;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
||||||
import org.springframework.boot.buildpack.platform.io.Content;
|
import org.springframework.boot.buildpack.platform.io.Content;
|
||||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||||
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
|
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for {@link HttpTransport} implementations backed by a
|
* Abstract base class for {@link HttpTransport} implementations backed by a
|
||||||
@@ -55,11 +57,14 @@ abstract class HttpClientTransport implements HttpTransport {
|
|||||||
|
|
||||||
private final HttpHost host;
|
private final HttpHost host;
|
||||||
|
|
||||||
protected HttpClientTransport(CloseableHttpClient client, HttpHost host) {
|
private final String registryAuthHeader;
|
||||||
|
|
||||||
|
protected HttpClientTransport(CloseableHttpClient client, HttpHost host, DockerConfiguration dockerConfiguration) {
|
||||||
Assert.notNull(client, "Client must not be null");
|
Assert.notNull(client, "Client must not be null");
|
||||||
Assert.notNull(host, "Host must not be null");
|
Assert.notNull(host, "Host must not be null");
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.host = host;
|
this.host = host;
|
||||||
|
this.registryAuthHeader = buildRegistryAuthHeader(dockerConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,6 +121,15 @@ abstract class HttpClientTransport implements HttpTransport {
|
|||||||
return execute(new HttpDelete(uri));
|
return execute(new HttpDelete(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String buildRegistryAuthHeader(DockerConfiguration dockerConfiguration) {
|
||||||
|
if (dockerConfiguration == null || dockerConfiguration.getRegistryAuthentication() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String authHeader = dockerConfiguration.getRegistryAuthentication().createAuthHeader();
|
||||||
|
return (StringUtils.hasText(authHeader)) ? authHeader : null;
|
||||||
|
}
|
||||||
|
|
||||||
private Response execute(HttpEntityEnclosingRequestBase request, String contentType,
|
private Response execute(HttpEntityEnclosingRequestBase request, String contentType,
|
||||||
IOConsumer<OutputStream> writer) {
|
IOConsumer<OutputStream> writer) {
|
||||||
request.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
|
request.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
|
||||||
@@ -125,6 +139,9 @@ abstract class HttpClientTransport implements HttpTransport {
|
|||||||
|
|
||||||
private Response execute(HttpUriRequest request) {
|
private Response execute(HttpUriRequest request) {
|
||||||
try {
|
try {
|
||||||
|
if (this.registryAuthHeader != null) {
|
||||||
|
request.addHeader("X-Registry-Auth", this.registryAuthHeader);
|
||||||
|
}
|
||||||
CloseableHttpResponse response = this.client.execute(this.host, request);
|
CloseableHttpResponse response = this.client.execute(this.host, request);
|
||||||
StatusLine statusLine = response.getStatusLine();
|
StatusLine statusLine = response.getStatusLine();
|
||||||
int statusCode = statusLine.getStatusCode();
|
int statusCode = statusLine.getStatusCode();
|
||||||
|
|||||||
@@ -21,11 +21,8 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import org.apache.http.Header;
|
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
||||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||||
import org.springframework.boot.buildpack.platform.system.Environment;
|
import org.springframework.boot.buildpack.platform.system.Environment;
|
||||||
|
|
||||||
@@ -88,17 +85,17 @@ public interface HttpTransport {
|
|||||||
* @return a {@link HttpTransport} instance
|
* @return a {@link HttpTransport} instance
|
||||||
*/
|
*/
|
||||||
static HttpTransport create() {
|
static HttpTransport create() {
|
||||||
return create(Collections.emptyList());
|
return create(DockerConfiguration.withDefaults());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the most suitable {@link HttpTransport} based on the
|
* Create the most suitable {@link HttpTransport} based on the
|
||||||
* {@link Environment#SYSTEM system environment}.
|
* {@link Environment#SYSTEM system environment}.
|
||||||
* @param dockerEngineAuthenticationHeaders authentication headerS for Docker engine.
|
* @param dockerConfiguration the Docker engine configuration
|
||||||
* @return a {@link HttpTransport} instance
|
* @return a {@link HttpTransport} instance
|
||||||
*/
|
*/
|
||||||
static HttpTransport create(Collection<Header> dockerEngineAuthenticationHeaders) {
|
static HttpTransport create(DockerConfiguration dockerConfiguration) {
|
||||||
return create(Environment.SYSTEM, dockerEngineAuthenticationHeaders);
|
return create(Environment.SYSTEM, dockerConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -108,21 +105,19 @@ public interface HttpTransport {
|
|||||||
* @return a {@link HttpTransport} instance
|
* @return a {@link HttpTransport} instance
|
||||||
*/
|
*/
|
||||||
static HttpTransport create(Environment environment) {
|
static HttpTransport create(Environment environment) {
|
||||||
return create(environment, Collections.emptyList());
|
return create(environment, DockerConfiguration.withDefaults());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the most suitable {@link HttpTransport} based on the given
|
* Create the most suitable {@link HttpTransport} based on the given
|
||||||
* {@link Environment}.
|
* {@link Environment} and {@link DockerConfiguration}.
|
||||||
* @param environment the source environment
|
* @param environment the source environment
|
||||||
* @param dockerEngineAuthenticationHeaders authentication headerS for Docker engine.
|
* @param dockerConfiguration the Docker engine configuration
|
||||||
* @return a {@link HttpTransport} instance
|
* @return a {@link HttpTransport} instance
|
||||||
*/
|
*/
|
||||||
static HttpTransport create(Environment environment, Collection<Header> dockerEngineAuthenticationHeaders) {
|
static HttpTransport create(Environment environment, DockerConfiguration dockerConfiguration) {
|
||||||
HttpTransport remote = RemoteHttpClientTransport.createIfPossible(environment,
|
HttpTransport remote = RemoteHttpClientTransport.createIfPossible(environment, dockerConfiguration);
|
||||||
dockerEngineAuthenticationHeaders);
|
return (remote != null) ? remote : LocalHttpClientTransport.create(environment, dockerConfiguration);
|
||||||
return (remote != null) ? remote
|
|
||||||
: LocalHttpClientTransport.create(environment, dockerEngineAuthenticationHeaders);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -21,10 +21,8 @@ import java.net.InetAddress;
|
|||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import com.sun.jna.Platform;
|
import com.sun.jna.Platform;
|
||||||
import org.apache.http.Header;
|
|
||||||
import org.apache.http.HttpHost;
|
import org.apache.http.HttpHost;
|
||||||
import org.apache.http.config.Registry;
|
import org.apache.http.config.Registry;
|
||||||
import org.apache.http.config.RegistryBuilder;
|
import org.apache.http.config.RegistryBuilder;
|
||||||
@@ -40,10 +38,10 @@ import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
|
|||||||
import org.apache.http.protocol.HttpContext;
|
import org.apache.http.protocol.HttpContext;
|
||||||
import org.apache.http.util.Args;
|
import org.apache.http.util.Args;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
||||||
import org.springframework.boot.buildpack.platform.socket.DomainSocket;
|
import org.springframework.boot.buildpack.platform.socket.DomainSocket;
|
||||||
import org.springframework.boot.buildpack.platform.socket.NamedPipeSocket;
|
import org.springframework.boot.buildpack.platform.socket.NamedPipeSocket;
|
||||||
import org.springframework.boot.buildpack.platform.system.Environment;
|
import org.springframework.boot.buildpack.platform.system.Environment;
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link HttpClientTransport} that talks to local Docker.
|
* {@link HttpClientTransport} that talks to local Docker.
|
||||||
@@ -59,19 +57,15 @@ final class LocalHttpClientTransport extends HttpClientTransport {
|
|||||||
|
|
||||||
private static final HttpHost LOCAL_DOCKER_HOST = HttpHost.create("docker://localhost");
|
private static final HttpHost LOCAL_DOCKER_HOST = HttpHost.create("docker://localhost");
|
||||||
|
|
||||||
private LocalHttpClientTransport(CloseableHttpClient client) {
|
private LocalHttpClientTransport(CloseableHttpClient client, DockerConfiguration dockerConfiguration) {
|
||||||
super(client, LOCAL_DOCKER_HOST);
|
super(client, LOCAL_DOCKER_HOST, dockerConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
static LocalHttpClientTransport create(Environment environment,
|
static LocalHttpClientTransport create(Environment environment, DockerConfiguration dockerConfiguration) {
|
||||||
Collection<Header> dockerEngineAuthenticationHeaders) {
|
|
||||||
HttpClientBuilder builder = HttpClients.custom();
|
HttpClientBuilder builder = HttpClients.custom();
|
||||||
builder.setConnectionManager(new LocalConnectionManager(socketFilePath(environment)));
|
builder.setConnectionManager(new LocalConnectionManager(socketFilePath(environment)));
|
||||||
builder.setSchemePortResolver(new LocalSchemePortResolver());
|
builder.setSchemePortResolver(new LocalSchemePortResolver());
|
||||||
if (!CollectionUtils.isEmpty(dockerEngineAuthenticationHeaders)) {
|
return new LocalHttpClientTransport(builder.build(), dockerConfiguration);
|
||||||
builder.setDefaultHeaders(dockerEngineAuthenticationHeaders);
|
|
||||||
}
|
|
||||||
return new LocalHttpClientTransport(builder.build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String socketFilePath(Environment environment) {
|
private static String socketFilePath(Environment environment) {
|
||||||
|
|||||||
@@ -18,12 +18,9 @@ package org.springframework.boot.buildpack.platform.docker.transport;
|
|||||||
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
|
||||||
import org.apache.http.Header;
|
|
||||||
import org.apache.http.HttpHost;
|
import org.apache.http.HttpHost;
|
||||||
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
|
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
|
||||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||||
@@ -31,10 +28,10 @@ import org.apache.http.impl.client.CloseableHttpClient;
|
|||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
import org.apache.http.impl.client.HttpClients;
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
||||||
import org.springframework.boot.buildpack.platform.docker.ssl.SslContextFactory;
|
import org.springframework.boot.buildpack.platform.docker.ssl.SslContextFactory;
|
||||||
import org.springframework.boot.buildpack.platform.system.Environment;
|
import org.springframework.boot.buildpack.platform.system.Environment;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link HttpClientTransport} that talks to a remote Docker.
|
* {@link HttpClientTransport} that talks to a remote Docker.
|
||||||
@@ -52,30 +49,23 @@ final class RemoteHttpClientTransport extends HttpClientTransport {
|
|||||||
|
|
||||||
private static final String DOCKER_CERT_PATH = "DOCKER_CERT_PATH";
|
private static final String DOCKER_CERT_PATH = "DOCKER_CERT_PATH";
|
||||||
|
|
||||||
private RemoteHttpClientTransport(CloseableHttpClient client, HttpHost host) {
|
private RemoteHttpClientTransport(CloseableHttpClient client, HttpHost host,
|
||||||
super(client, host);
|
DockerConfiguration dockerConfiguration) {
|
||||||
}
|
super(client, host, dockerConfiguration);
|
||||||
|
|
||||||
static RemoteHttpClientTransport createIfPossible(Environment environment) {
|
|
||||||
return createIfPossible(environment, Collections.emptyList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static RemoteHttpClientTransport createIfPossible(Environment environment,
|
static RemoteHttpClientTransport createIfPossible(Environment environment,
|
||||||
Collection<Header> dockerEngineAuthenticationHeaders) {
|
DockerConfiguration dockerConfiguration) {
|
||||||
return createIfPossible(environment, new SslContextFactory(), dockerEngineAuthenticationHeaders);
|
return createIfPossible(environment, dockerConfiguration, new SslContextFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
static RemoteHttpClientTransport createIfPossible(Environment environment, SslContextFactory sslContextFactory) {
|
static RemoteHttpClientTransport createIfPossible(Environment environment, DockerConfiguration dockerConfiguration,
|
||||||
return createIfPossible(environment, sslContextFactory, Collections.emptyList());
|
SslContextFactory sslContextFactory) {
|
||||||
}
|
|
||||||
|
|
||||||
static RemoteHttpClientTransport createIfPossible(Environment environment, SslContextFactory sslContextFactory,
|
|
||||||
Collection<Header> dockerEngineAuthenticationHeaders) {
|
|
||||||
String host = environment.get(DOCKER_HOST);
|
String host = environment.get(DOCKER_HOST);
|
||||||
if (host == null || isLocalFileReference(host)) {
|
if (host == null || isLocalFileReference(host)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return create(environment, sslContextFactory, HttpHost.create(host), dockerEngineAuthenticationHeaders);
|
return create(environment, sslContextFactory, HttpHost.create(host), dockerConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isLocalFileReference(String host) {
|
private static boolean isLocalFileReference(String host) {
|
||||||
@@ -89,18 +79,15 @@ final class RemoteHttpClientTransport extends HttpClientTransport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static RemoteHttpClientTransport create(Environment environment, SslContextFactory sslContextFactory,
|
private static RemoteHttpClientTransport create(Environment environment, SslContextFactory sslContextFactory,
|
||||||
HttpHost tcpHost, Collection<Header> dockerEngineAuthenticationHeaders) {
|
HttpHost tcpHost, DockerConfiguration dockerConfiguration) {
|
||||||
HttpClientBuilder builder = HttpClients.custom();
|
HttpClientBuilder builder = HttpClients.custom();
|
||||||
boolean secure = isSecure(environment);
|
boolean secure = isSecure(environment);
|
||||||
if (secure) {
|
if (secure) {
|
||||||
builder.setSSLSocketFactory(getSecureConnectionSocketFactory(environment, sslContextFactory));
|
builder.setSSLSocketFactory(getSecureConnectionSocketFactory(environment, sslContextFactory));
|
||||||
}
|
}
|
||||||
if (!CollectionUtils.isEmpty(dockerEngineAuthenticationHeaders)) {
|
|
||||||
builder.setDefaultHeaders(dockerEngineAuthenticationHeaders);
|
|
||||||
}
|
|
||||||
String scheme = secure ? "https" : "http";
|
String scheme = secure ? "https" : "http";
|
||||||
HttpHost httpHost = new HttpHost(tcpHost.getHostName(), tcpHost.getPort(), scheme);
|
HttpHost httpHost = new HttpHost(tcpHost.getHostName(), tcpHost.getPort(), scheme);
|
||||||
return new RemoteHttpClientTransport(builder.build(), httpHost);
|
return new RemoteHttpClientTransport(builder.build(), httpHost, dockerConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LayeredConnectionSocketFactory getSecureConnectionSocketFactory(Environment environment,
|
private static LayeredConnectionSocketFactory getSecureConnectionSocketFactory(Environment environment,
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ 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.ImageApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
|
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
|
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||||
@@ -67,7 +66,7 @@ class BuilderTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createWithDockerConfiguration() {
|
void createWithDockerConfiguration() {
|
||||||
Builder builder = new Builder(BuildLog.toSystemOut(), new DockerConfiguration());
|
Builder builder = new Builder(BuildLog.toSystemOut());
|
||||||
assertThat(builder).isNotNull();
|
assertThat(builder).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import org.apache.http.Header;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -36,8 +34,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
|||||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi;
|
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.ImageApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryConfiguration;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
|
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.transport.HttpTransport.Response;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||||
@@ -52,7 +48,6 @@ import org.springframework.boot.buildpack.platform.io.Content;
|
|||||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||||
import org.springframework.util.Base64Utils;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
@@ -124,40 +119,6 @@ class DockerApiTests {
|
|||||||
assertThat(api).isNotNull();
|
assertThat(api).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void createDockerApiWithDockerConfiguration() {
|
|
||||||
DockerApi api = new DockerApi(new DockerConfiguration());
|
|
||||||
assertThat(api).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void createWhenDockerConfigurationIsNullThrowsException() {
|
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new DockerApi((DockerConfiguration) null))
|
|
||||||
.withMessage("Docker configuration must not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void createDockerEngineAuthenticationHeaders() {
|
|
||||||
DockerRegistryConfiguration dockerRegistryConfiguration = new DockerRegistryConfiguration();
|
|
||||||
dockerRegistryConfiguration.setUsername("username");
|
|
||||||
dockerRegistryConfiguration.setPassword("password");
|
|
||||||
dockerRegistryConfiguration.setEmail("mock@spring.com");
|
|
||||||
dockerRegistryConfiguration.setUrl("http://mock.docker.registry");
|
|
||||||
DockerConfiguration dockerConfiguration = new DockerConfiguration();
|
|
||||||
dockerConfiguration.setDockerRegistryConfiguration(dockerRegistryConfiguration);
|
|
||||||
Collection<Header> dockerEngineAuthenticationHeaders = DockerApi
|
|
||||||
.createDockerEngineAuthenticationHeaders(dockerConfiguration);
|
|
||||||
assertThat(dockerEngineAuthenticationHeaders.size() == 1).isTrue();
|
|
||||||
Header header = dockerEngineAuthenticationHeaders.iterator().next();
|
|
||||||
assertThat(header.getName()).isEqualTo("X-Registry-Auth");
|
|
||||||
assertThat(header.getValue()).isEqualTo(
|
|
||||||
"ewogICJ1c2VybmFtZSIgOiAidXNlcm5hbWUiLAogICJwYXNzd29yZCIgOiAicGFzc3dvcmQiLAogICJlbWFpbCIgOiAibW9ja0BzcHJpbmcuY29tIiwKICAic2VydmVyYWRkcmVzcyIgOiAiaHR0cDovL21vY2suZG9ja2VyLnJlZ2lzdHJ5Igp9");
|
|
||||||
assertThat(new String(Base64Utils.decodeFromString(header.getValue())))
|
|
||||||
.isEqualTo("{\n" + " \"username\" : \"username\",\n" + " \"password\" : \"password\",\n"
|
|
||||||
+ " \"email\" : \"mock@spring.com\",\n"
|
|
||||||
+ " \"serveraddress\" : \"http://mock.docker.registry\"\n" + "}");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class ImageDockerApiTests {
|
class ImageDockerApiTests {
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,38 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
* Tests for {@link DockerConfiguration}.
|
* Tests for {@link DockerConfiguration}.
|
||||||
*
|
*
|
||||||
* @author Wei Jiang
|
* @author Wei Jiang
|
||||||
|
* @author Scott Frederick
|
||||||
*/
|
*/
|
||||||
public class DockerConfigurationTests {
|
public class DockerConfigurationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createDockerConfiguration() {
|
void createDockerConfigurationWithDefaults() {
|
||||||
assertThat(new DockerConfiguration()).isNotNull();
|
DockerConfiguration configuration = DockerConfiguration.withDefaults();
|
||||||
|
assertThat(configuration.getRegistryAuthentication()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createDockerConfigurationWithUserAuth() {
|
||||||
|
DockerConfiguration configuration = DockerConfiguration.withRegistryUserAuthentication("user", "secret",
|
||||||
|
"https://docker.example.com", "docker@example.com");
|
||||||
|
DockerRegistryAuthentication auth = configuration.getRegistryAuthentication();
|
||||||
|
assertThat(auth).isNotNull();
|
||||||
|
assertThat(auth).isInstanceOf(DockerRegistryUserAuthentication.class);
|
||||||
|
DockerRegistryUserAuthentication userAuth = (DockerRegistryUserAuthentication) auth;
|
||||||
|
assertThat(userAuth.getUrl()).isEqualTo("https://docker.example.com");
|
||||||
|
assertThat(userAuth.getUsername()).isEqualTo("user");
|
||||||
|
assertThat(userAuth.getPassword()).isEqualTo("secret");
|
||||||
|
assertThat(userAuth.getEmail()).isEqualTo("docker@example.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createDockerConfigurationWithTokenAuth() {
|
||||||
|
DockerConfiguration configuration = DockerConfiguration.withRegistryTokenAuthentication("token");
|
||||||
|
DockerRegistryAuthentication auth = configuration.getRegistryAuthentication();
|
||||||
|
assertThat(auth).isNotNull();
|
||||||
|
assertThat(auth).isInstanceOf(DockerRegistryTokenAuthentication.class);
|
||||||
|
DockerRegistryTokenAuthentication tokenAuth = (DockerRegistryTokenAuthentication) auth;
|
||||||
|
assertThat(tokenAuth.getToken()).isEqualTo("token");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +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.configuration;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import org.springframework.util.Base64Utils;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link DockerRegistryConfiguration}.
|
|
||||||
*
|
|
||||||
* @author Wei Jiang
|
|
||||||
*/
|
|
||||||
public class DockerRegistryConfigurationTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void createDockerRegistryAuthTokenWithToken() {
|
|
||||||
DockerRegistryConfiguration dockerRegistryConfiguration = new DockerRegistryConfiguration();
|
|
||||||
dockerRegistryConfiguration.setToken("mockToken");
|
|
||||||
assertThat(dockerRegistryConfiguration.createDockerRegistryAuthToken()).isEqualTo("mockToken");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void createDockerRegistryAuthTokenWithoutToken() {
|
|
||||||
DockerRegistryConfiguration dockerRegistryConfiguration = new DockerRegistryConfiguration();
|
|
||||||
dockerRegistryConfiguration.setUsername("username");
|
|
||||||
dockerRegistryConfiguration.setPassword("password");
|
|
||||||
dockerRegistryConfiguration.setEmail("mock@spring.com");
|
|
||||||
dockerRegistryConfiguration.setUrl("http://mock.docker.registry");
|
|
||||||
String token = dockerRegistryConfiguration.createDockerRegistryAuthToken();
|
|
||||||
assertThat(token).isEqualTo(
|
|
||||||
"ewogICJ1c2VybmFtZSIgOiAidXNlcm5hbWUiLAogICJwYXNzd29yZCIgOiAicGFzc3dvcmQiLAogICJlbWFpbCIgOiAibW9ja0BzcHJpbmcuY29tIiwKICAic2VydmVyYWRkcmVzcyIgOiAiaHR0cDovL21vY2suZG9ja2VyLnJlZ2lzdHJ5Igp9");
|
|
||||||
assertThat(new String(Base64Utils.decodeFromString(token))).isEqualTo("{\n" + " \"username\" : \"username\",\n"
|
|
||||||
+ " \"password\" : \"password\",\n" + " \"email\" : \"mock@spring.com\",\n"
|
|
||||||
+ " \"serveraddress\" : \"http://mock.docker.registry\"\n" + "}");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void createDockerRegistryAuthTokenWithUsernameAndPassword() {
|
|
||||||
DockerRegistryConfiguration dockerRegistryConfiguration = new DockerRegistryConfiguration();
|
|
||||||
dockerRegistryConfiguration.setUsername("username");
|
|
||||||
dockerRegistryConfiguration.setPassword("password");
|
|
||||||
String token = dockerRegistryConfiguration.createDockerRegistryAuthToken();
|
|
||||||
assertThat(dockerRegistryConfiguration.getEmail()).isNull();
|
|
||||||
assertThat(dockerRegistryConfiguration.getUrl()).isNull();
|
|
||||||
assertThat(token).isEqualTo(
|
|
||||||
"ewogICJ1c2VybmFtZSIgOiAidXNlcm5hbWUiLAogICJwYXNzd29yZCIgOiAicGFzc3dvcmQiLAogICJlbWFpbCIgOiBudWxsLAogICJzZXJ2ZXJhZGRyZXNzIiA6IG51bGwKfQ==");
|
|
||||||
assertThat(new String(Base64Utils.decodeFromString(token))).isEqualTo("{\n" + " \"username\" : \"username\",\n"
|
|
||||||
+ " \"password\" : \"password\",\n" + " \"email\" : null,\n" + " \"serveraddress\" : null\n" + "}");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void createDockerRegistryAuthTokenWithTokenAndUsername() {
|
|
||||||
DockerRegistryConfiguration dockerRegistryConfiguration = new DockerRegistryConfiguration();
|
|
||||||
dockerRegistryConfiguration.setToken("mockToken");
|
|
||||||
dockerRegistryConfiguration.setUsername("username");
|
|
||||||
dockerRegistryConfiguration.setPassword("password");
|
|
||||||
dockerRegistryConfiguration.setEmail("mock@spring.com");
|
|
||||||
dockerRegistryConfiguration.setUrl("http://mock.docker.registry");
|
|
||||||
assertThat(dockerRegistryConfiguration.createDockerRegistryAuthToken()).isEqualTo("mockToken");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.configuration;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.skyscreamer.jsonassert.JSONAssert;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
import org.springframework.util.StreamUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DockerRegistryTokenAuthentication}.
|
||||||
|
*/
|
||||||
|
class DockerRegistryTokenAuthenticationTests extends AbstractJsonTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createAuthHeaderReturnsEncodedHeader() throws IOException, JSONException {
|
||||||
|
DockerRegistryTokenAuthentication auth = new DockerRegistryTokenAuthentication("tokenvalue");
|
||||||
|
String header = auth.createAuthHeader();
|
||||||
|
String expectedJson = StreamUtils.copyToString(getContent("auth-token.json"), StandardCharsets.UTF_8);
|
||||||
|
JSONAssert.assertEquals(expectedJson, new String(Base64Utils.decodeFromUrlSafeString(header)), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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.configuration;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.skyscreamer.jsonassert.JSONAssert;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
import org.springframework.util.StreamUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DockerRegistryUserAuthentication}.
|
||||||
|
*/
|
||||||
|
class DockerRegistryUserAuthenticationTests extends AbstractJsonTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createMinimalAuthHeaderReturnsEncodedHeader() throws IOException, JSONException {
|
||||||
|
DockerRegistryUserAuthentication auth = new DockerRegistryUserAuthentication("user", "secret",
|
||||||
|
"https://docker.example.com", "docker@example.com");
|
||||||
|
JSONAssert.assertEquals(jsonContent("auth-user-full.json"), decoded(auth.createAuthHeader()), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createFullAuthHeaderReturnsEncodedHeader() throws IOException, JSONException {
|
||||||
|
DockerRegistryUserAuthentication auth = new DockerRegistryUserAuthentication("user", "secret", null, null);
|
||||||
|
JSONAssert.assertEquals(jsonContent("auth-user-minimal.json"), decoded(auth.createAuthHeader()), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String jsonContent(String s) throws IOException {
|
||||||
|
return StreamUtils.copyToString(getContent(s), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String decoded(String header) {
|
||||||
|
return new String(Base64Utils.decodeFromUrlSafeString(header));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import java.io.InputStream;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.apache.http.Header;
|
||||||
import org.apache.http.HttpEntity;
|
import org.apache.http.HttpEntity;
|
||||||
import org.apache.http.HttpEntityEnclosingRequest;
|
import org.apache.http.HttpEntityEnclosingRequest;
|
||||||
import org.apache.http.HttpHeaders;
|
import org.apache.http.HttpHeaders;
|
||||||
@@ -43,7 +44,9 @@ import org.mockito.Captor;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
||||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
|
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
import org.springframework.util.StreamUtils;
|
import org.springframework.util.StreamUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@@ -234,6 +237,47 @@ class HttpClientTransportTests {
|
|||||||
.satisfies((ex) -> assertThat(ex.getMessage()).contains("test IO exception"));
|
.satisfies((ex) -> assertThat(ex.getMessage()).contains("test IO exception"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWithDockerRegistryUserAuthWillSendAuthHeader() throws IOException {
|
||||||
|
DockerConfiguration dockerConfiguration = DockerConfiguration.withRegistryUserAuthentication("user", "secret",
|
||||||
|
"https://docker.example.com", "docker@example.com");
|
||||||
|
this.http = new TestHttpClientTransport(this.client, dockerConfiguration);
|
||||||
|
givenClientWillReturnResponse();
|
||||||
|
given(this.entity.getContent()).willReturn(this.content);
|
||||||
|
given(this.statusLine.getStatusCode()).willReturn(200);
|
||||||
|
Response response = this.http.get(this.uri);
|
||||||
|
verify(this.client).execute(this.hostCaptor.capture(), this.requestCaptor.capture());
|
||||||
|
HttpUriRequest request = this.requestCaptor.getValue();
|
||||||
|
assertThat(request).isInstanceOf(HttpGet.class);
|
||||||
|
assertThat(request.getURI()).isEqualTo(this.uri);
|
||||||
|
Header[] registryAuthHeaders = request.getHeaders("X-Registry-Auth");
|
||||||
|
assertThat(registryAuthHeaders).isNotNull();
|
||||||
|
assertThat(new String(Base64Utils.decodeFromString(registryAuthHeaders[0].getValue())))
|
||||||
|
.contains("\"username\" : \"user\"").contains("\"password\" : \"secret\"")
|
||||||
|
.contains("\"email\" : \"docker@example.com\"")
|
||||||
|
.contains("\"serveraddress\" : \"https://docker.example.com\"");
|
||||||
|
assertThat(response.getContent()).isSameAs(this.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWithDockerRegistryTokenAuthWillSendAuthHeader() throws IOException {
|
||||||
|
DockerConfiguration dockerConfiguration = DockerConfiguration.withRegistryTokenAuthentication("token");
|
||||||
|
this.http = new TestHttpClientTransport(this.client, dockerConfiguration);
|
||||||
|
givenClientWillReturnResponse();
|
||||||
|
given(this.entity.getContent()).willReturn(this.content);
|
||||||
|
given(this.statusLine.getStatusCode()).willReturn(200);
|
||||||
|
Response response = this.http.get(this.uri);
|
||||||
|
verify(this.client).execute(this.hostCaptor.capture(), this.requestCaptor.capture());
|
||||||
|
HttpUriRequest request = this.requestCaptor.getValue();
|
||||||
|
assertThat(request).isInstanceOf(HttpGet.class);
|
||||||
|
assertThat(request.getURI()).isEqualTo(this.uri);
|
||||||
|
Header[] registryAuthHeaders = request.getHeaders("X-Registry-Auth");
|
||||||
|
assertThat(registryAuthHeaders).isNotNull();
|
||||||
|
assertThat(new String(Base64Utils.decodeFromString(registryAuthHeaders[0].getValue())))
|
||||||
|
.contains("\"identitytoken\" : \"token\"");
|
||||||
|
assertThat(response.getContent()).isSameAs(this.content);
|
||||||
|
}
|
||||||
|
|
||||||
private String writeToString(HttpEntity entity) throws IOException {
|
private String writeToString(HttpEntity entity) throws IOException {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
entity.writeTo(out);
|
entity.writeTo(out);
|
||||||
@@ -252,7 +296,11 @@ class HttpClientTransportTests {
|
|||||||
static class TestHttpClientTransport extends HttpClientTransport {
|
static class TestHttpClientTransport extends HttpClientTransport {
|
||||||
|
|
||||||
protected TestHttpClientTransport(CloseableHttpClient client) {
|
protected TestHttpClientTransport(CloseableHttpClient client) {
|
||||||
super(client, HttpHost.create("docker://localhost"));
|
super(client, HttpHost.create("docker://localhost"), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TestHttpClientTransport(CloseableHttpClient client, DockerConfiguration dockerConfiguration) {
|
||||||
|
super(client, HttpHost.create("docker://localhost"), dockerConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,9 @@ package org.springframework.boot.buildpack.platform.docker.transport;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.http.Header;
|
|
||||||
import org.apache.http.message.BasicHeader;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
@@ -69,12 +65,4 @@ class HttpTransportTests {
|
|||||||
assertThat(transport).isInstanceOf(LocalHttpClientTransport.class);
|
assertThat(transport).isInstanceOf(LocalHttpClientTransport.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void createWithDockerEngineAuthenticationHeaders() {
|
|
||||||
Collection<Header> dockerEngineAuthenticationHeaders = Arrays.asList(new BasicHeader("X-Registry-Auth",
|
|
||||||
"eyJ1c2VybmFtZSI6ICJ1c2VybmFtZSIsInBhc3N3b3JkIjogInBhc3N3b3JkIiwiZW1haWwiOiAibW9ja0BzcHJpbmcuY29tIiwic2VydmVyYWRkcmVzcyI6ICJodHRwOi8vbW9jay5kb2NrZXIucmVnaXN0cnkifQ=="));
|
|
||||||
HttpTransport transport = HttpTransport.create((name) -> null, dockerEngineAuthenticationHeaders);
|
|
||||||
assertThat(transport).isInstanceOf(LocalHttpClientTransport.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,20 +19,17 @@ package org.springframework.boot.buildpack.platform.docker.transport;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
|
||||||
import org.apache.http.Header;
|
|
||||||
import org.apache.http.HttpHost;
|
import org.apache.http.HttpHost;
|
||||||
import org.apache.http.message.BasicHeader;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
||||||
import org.springframework.boot.buildpack.platform.docker.ssl.SslContextFactory;
|
import org.springframework.boot.buildpack.platform.docker.ssl.SslContextFactory;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@@ -50,9 +47,12 @@ class RemoteHttpClientTransportTests {
|
|||||||
|
|
||||||
private final Map<String, String> environment = new LinkedHashMap<>();
|
private final Map<String, String> environment = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private final DockerConfiguration dockerConfiguration = DockerConfiguration.withDefaults();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createIfPossibleWhenDockerHostIsNotSetReturnsNull() {
|
void createIfPossibleWhenDockerHostIsNotSetReturnsNull() {
|
||||||
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get);
|
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get,
|
||||||
|
this.dockerConfiguration);
|
||||||
assertThat(transport).isNull();
|
assertThat(transport).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,24 +61,16 @@ class RemoteHttpClientTransportTests {
|
|||||||
String dummySocketFilePath = Files.createTempFile(tempDir, "remote-transport", null).toAbsolutePath()
|
String dummySocketFilePath = Files.createTempFile(tempDir, "remote-transport", null).toAbsolutePath()
|
||||||
.toString();
|
.toString();
|
||||||
this.environment.put("DOCKER_HOST", dummySocketFilePath);
|
this.environment.put("DOCKER_HOST", dummySocketFilePath);
|
||||||
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get);
|
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get,
|
||||||
|
this.dockerConfiguration);
|
||||||
assertThat(transport).isNull();
|
assertThat(transport).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createIfPossibleWhenDockerHostIsAddressReturnsTransport() {
|
void createIfPossibleWhenDockerHostIsAddressReturnsTransport() {
|
||||||
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
|
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
|
||||||
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get);
|
|
||||||
assertThat(transport).isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void createWithDockerEngineAuthenticationHeaders() {
|
|
||||||
Collection<Header> dockerEngineAuthenticationHeaders = Arrays.asList(new BasicHeader("X-Registry-Auth",
|
|
||||||
"eyJ1c2VybmFtZSI6ICJ1c2VybmFtZSIsInBhc3N3b3JkIjogInBhc3N3b3JkIiwiZW1haWwiOiAibW9ja0BzcHJpbmcuY29tIiwic2VydmVyYWRkcmVzcyI6ICJodHRwOi8vbW9jay5kb2NrZXIucmVnaXN0cnkifQ=="));
|
|
||||||
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
|
|
||||||
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get,
|
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get,
|
||||||
dockerEngineAuthenticationHeaders);
|
this.dockerConfiguration);
|
||||||
assertThat(transport).isNotNull();
|
assertThat(transport).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,15 +78,16 @@ class RemoteHttpClientTransportTests {
|
|||||||
void createIfPossibleWhenTlsVerifyWithMissingCertPathThrowsException() {
|
void createIfPossibleWhenTlsVerifyWithMissingCertPathThrowsException() {
|
||||||
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
|
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
|
||||||
this.environment.put("DOCKER_TLS_VERIFY", "1");
|
this.environment.put("DOCKER_TLS_VERIFY", "1");
|
||||||
assertThatIllegalArgumentException()
|
assertThatIllegalArgumentException().isThrownBy(
|
||||||
.isThrownBy(() -> RemoteHttpClientTransport.createIfPossible(this.environment::get))
|
() -> RemoteHttpClientTransport.createIfPossible(this.environment::get, this.dockerConfiguration))
|
||||||
.withMessageContaining("DOCKER_CERT_PATH");
|
.withMessageContaining("DOCKER_CERT_PATH");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createIfPossibleWhenNoTlsVerifyUsesHttp() {
|
void createIfPossibleWhenNoTlsVerifyUsesHttp() {
|
||||||
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
|
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
|
||||||
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get);
|
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get,
|
||||||
|
this.dockerConfiguration);
|
||||||
assertThat(transport.getHost()).satisfies(hostOf("http", "192.168.1.2", 2376));
|
assertThat(transport.getHost()).satisfies(hostOf("http", "192.168.1.2", 2376));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,10 +99,19 @@ class RemoteHttpClientTransportTests {
|
|||||||
SslContextFactory sslContextFactory = mock(SslContextFactory.class);
|
SslContextFactory sslContextFactory = mock(SslContextFactory.class);
|
||||||
given(sslContextFactory.forDirectory("/test-cert-path")).willReturn(SSLContext.getDefault());
|
given(sslContextFactory.forDirectory("/test-cert-path")).willReturn(SSLContext.getDefault());
|
||||||
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get,
|
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get,
|
||||||
sslContextFactory);
|
this.dockerConfiguration, sslContextFactory);
|
||||||
assertThat(transport.getHost()).satisfies(hostOf("https", "192.168.1.2", 2376));
|
assertThat(transport.getHost()).satisfies(hostOf("https", "192.168.1.2", 2376));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createIfPossibleWithDockerConfigurationUserAuthReturnsTransport() {
|
||||||
|
this.environment.put("DOCKER_HOST", "tcp://192.168.1.2:2376");
|
||||||
|
RemoteHttpClientTransport transport = RemoteHttpClientTransport.createIfPossible(this.environment::get,
|
||||||
|
DockerConfiguration.withRegistryUserAuthentication("user", "secret", "http://docker.example.com",
|
||||||
|
"docker@example.com"));
|
||||||
|
assertThat(transport).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
private Consumer<HttpHost> hostOf(String scheme, String hostName, int port) {
|
private Consumer<HttpHost> hostOf(String scheme, String hostName, int port) {
|
||||||
return (host) -> {
|
return (host) -> {
|
||||||
assertThat(host).isNotNull();
|
assertThat(host).isNotNull();
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"identitytoken": "tokenvalue"
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"username": "user",
|
||||||
|
"password": "secret",
|
||||||
|
"email": "docker@example.com",
|
||||||
|
"serveraddress": "https://docker.example.com"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"username": "user",
|
||||||
|
"password": "secret"
|
||||||
|
}
|
||||||
@@ -36,6 +36,37 @@ On Linux and macOS, these environment variables can be set using the command `ev
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[build-image-docker-registry]]
|
||||||
|
=== Docker Registry
|
||||||
|
If the Docker images specified by the `builder` or `runImage` parameters are stored in a private Docker image registry that requires authentication, the authentication credentials can be provided using `docker.registry` properties.
|
||||||
|
Properties are provided for user authentication or identity token authentication.
|
||||||
|
Consult the documentation for the Docker registry being used to store builder or run images for further information on supported authentication methods.
|
||||||
|
|
||||||
|
The following table summarizes the available properties:
|
||||||
|
|
||||||
|
|===
|
||||||
|
| Property | Description
|
||||||
|
|
||||||
|
| `username`
|
||||||
|
| Username for the Docker image registry user. Required for user authentication.
|
||||||
|
|
||||||
|
| `password`
|
||||||
|
| Password for the Docker image registry user. Required for user authentication.
|
||||||
|
|
||||||
|
| `url`
|
||||||
|
| Address of the Docker image registry. Optional for user authentication.
|
||||||
|
|
||||||
|
| `email`
|
||||||
|
| E-mail address for the Docker image registry user. Optional for user authentication.
|
||||||
|
|
||||||
|
| `token`
|
||||||
|
| Identity token for the Docker image registry user. Required for token authentication.
|
||||||
|
|===
|
||||||
|
|
||||||
|
For more details, see also <<build-image-example-docker,examples>>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[build-image-customization]]
|
[[build-image-customization]]
|
||||||
=== Image Customizations
|
=== Image Customizations
|
||||||
The plugin invokes a {buildpacks-reference}/concepts/components/builder/[builder] to orchestrate the generation of an image.
|
The plugin invokes a {buildpacks-reference}/concepts/components/builder/[builder] to orchestrate the generation of an image.
|
||||||
@@ -186,3 +217,33 @@ The image name can be specified on the command line as well, as shown in this ex
|
|||||||
----
|
----
|
||||||
$ gradle bootBuildImage --imageName=example.com/library/my-app:v1
|
$ gradle bootBuildImage --imageName=example.com/library/my-app:v1
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[[build-image-example-docker]]
|
||||||
|
==== Docker Configuration
|
||||||
|
If the builder or run image are stored in a private Docker registry that supports user authentication, authentication details can be provided as shown in the following example:
|
||||||
|
|
||||||
|
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
|
||||||
|
.Groovy
|
||||||
|
----
|
||||||
|
include::../gradle/packaging/boot-build-image-docker-auth-user.gradle[tags=docker-auth-user]
|
||||||
|
----
|
||||||
|
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,attributes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
include::../gradle/packaging/boot-build-image-docker-auth-user.gradle.kts[tags=docker-auth-user]
|
||||||
|
----
|
||||||
|
|
||||||
|
If the builder or run image is stored in a private Docker registry that supports token authentication, the token value can be provided as shown in the following example:
|
||||||
|
|
||||||
|
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
|
||||||
|
.Groovy
|
||||||
|
----
|
||||||
|
include::../gradle/packaging/boot-build-image-docker-auth-token.gradle[tags=docker-auth-token]
|
||||||
|
----
|
||||||
|
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,attributes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
include::../gradle/packaging/boot-build-image-docker-auth-token.gradle.kts[tags=docker-auth-token]
|
||||||
|
----
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'org.springframework.boot' version '{gradle-project-version}'
|
||||||
|
}
|
||||||
|
|
||||||
|
bootJar {
|
||||||
|
mainClassName 'com.example.ExampleApplication'
|
||||||
|
}
|
||||||
|
|
||||||
|
// tag::docker-auth-token[]
|
||||||
|
bootBuildImage {
|
||||||
|
docker {
|
||||||
|
registry {
|
||||||
|
token = "9cbaf023786cd7..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end::docker-auth-token[]
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import org.springframework.boot.gradle.tasks.bundling.BootJar
|
||||||
|
import org.springframework.boot.gradle.tasks.bundling.BootBuildImage
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
id("org.springframework.boot") version "{gradle-project-version}"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.getByName<BootJar>("bootJar") {
|
||||||
|
mainClassName = "com.example.ExampleApplication"
|
||||||
|
}
|
||||||
|
|
||||||
|
// tag::docker-auth-token[]
|
||||||
|
tasks.getByName<BootBuildImage>("bootBuildImage") {
|
||||||
|
docker {
|
||||||
|
registry {
|
||||||
|
token = "9cbaf023786cd7..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end::docker-auth-token[]
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'org.springframework.boot' version '{gradle-project-version}'
|
||||||
|
}
|
||||||
|
|
||||||
|
bootJar {
|
||||||
|
mainClassName 'com.example.ExampleApplication'
|
||||||
|
}
|
||||||
|
|
||||||
|
// tag::docker-auth-user[]
|
||||||
|
bootBuildImage {
|
||||||
|
docker {
|
||||||
|
registry {
|
||||||
|
username = "user"
|
||||||
|
password = "secret"
|
||||||
|
url = "https://docker.example.com/v1/"
|
||||||
|
email = "user@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end::docker-auth-user[]
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import org.springframework.boot.gradle.tasks.bundling.BootJar
|
||||||
|
import org.springframework.boot.gradle.tasks.bundling.BootBuildImage
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
id("org.springframework.boot") version "{gradle-project-version}"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.getByName<BootJar>("bootJar") {
|
||||||
|
mainClassName = "com.example.ExampleApplication"
|
||||||
|
}
|
||||||
|
|
||||||
|
// tag::docker-auth-user[]
|
||||||
|
tasks.getByName<BootBuildImage>("bootBuildImage") {
|
||||||
|
docker {
|
||||||
|
registry {
|
||||||
|
username = "user"
|
||||||
|
password = "secret"
|
||||||
|
url = "https://docker.example.com/v1/"
|
||||||
|
email = "user@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end::docker-auth-user[]
|
||||||
@@ -20,6 +20,8 @@ import java.io.IOException;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import groovy.lang.Closure;
|
||||||
|
import org.gradle.api.Action;
|
||||||
import org.gradle.api.DefaultTask;
|
import org.gradle.api.DefaultTask;
|
||||||
import org.gradle.api.JavaVersion;
|
import org.gradle.api.JavaVersion;
|
||||||
import org.gradle.api.Project;
|
import org.gradle.api.Project;
|
||||||
@@ -27,15 +29,16 @@ import org.gradle.api.Task;
|
|||||||
import org.gradle.api.file.RegularFileProperty;
|
import org.gradle.api.file.RegularFileProperty;
|
||||||
import org.gradle.api.provider.Property;
|
import org.gradle.api.provider.Property;
|
||||||
import org.gradle.api.tasks.Input;
|
import org.gradle.api.tasks.Input;
|
||||||
|
import org.gradle.api.tasks.Nested;
|
||||||
import org.gradle.api.tasks.Optional;
|
import org.gradle.api.tasks.Optional;
|
||||||
import org.gradle.api.tasks.TaskAction;
|
import org.gradle.api.tasks.TaskAction;
|
||||||
import org.gradle.api.tasks.options.Option;
|
import org.gradle.api.tasks.options.Option;
|
||||||
|
import org.gradle.util.ConfigureUtil;
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
||||||
import org.springframework.boot.buildpack.platform.build.Builder;
|
import org.springframework.boot.buildpack.platform.build.Builder;
|
||||||
import org.springframework.boot.buildpack.platform.build.Creator;
|
import org.springframework.boot.buildpack.platform.build.Creator;
|
||||||
import org.springframework.boot.buildpack.platform.build.PullPolicy;
|
import org.springframework.boot.buildpack.platform.build.PullPolicy;
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
|
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageName;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageName;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
@@ -73,7 +76,7 @@ public class BootBuildImage extends DefaultTask {
|
|||||||
|
|
||||||
private PullPolicy pullPolicy;
|
private PullPolicy pullPolicy;
|
||||||
|
|
||||||
private Docker docker;
|
private DockerSpec docker = new DockerSpec();
|
||||||
|
|
||||||
public BootBuildImage() {
|
public BootBuildImage() {
|
||||||
this.jar = getProject().getObjects().fileProperty();
|
this.jar = getProject().getObjects().fileProperty();
|
||||||
@@ -250,30 +253,36 @@ public class BootBuildImage extends DefaultTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Docker configuration with the builder will be used.
|
* Returns the Docker configuration the builder will use.
|
||||||
* @return docker configuration.
|
* @return docker configuration.
|
||||||
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
@Input
|
@Nested
|
||||||
@Optional
|
public DockerSpec getDocker() {
|
||||||
public Docker getDocker() {
|
|
||||||
return this.docker;
|
return this.docker;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the Docker configuration with the builder will be used.
|
* Configures the Docker connection using the given {@code action}.
|
||||||
* @param docker docker configuration.
|
* @param action the action to apply
|
||||||
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
@Option(option = "docker", description = "The Docker configuration to use")
|
public void docker(Action<DockerSpec> action) {
|
||||||
public void setDocker(Docker docker) {
|
action.execute(this.docker);
|
||||||
this.docker = docker;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the Docker connection using the given {@code closure}.
|
||||||
|
* @param closure the closure to apply
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public void docker(Closure<?> closure) {
|
||||||
|
docker(ConfigureUtil.configureUsing(closure));
|
||||||
}
|
}
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
void buildImage() throws DockerEngineException, IOException {
|
void buildImage() throws DockerEngineException, IOException {
|
||||||
DockerConfiguration dockerConfiguration = (this.docker != null) ? this.docker.getDockerConfiguration()
|
Builder builder = new Builder(this.docker.asDockerConfiguration());
|
||||||
: new DockerConfiguration();
|
|
||||||
|
|
||||||
Builder builder = new Builder(dockerConfiguration);
|
|
||||||
BuildRequest request = createRequest();
|
BuildRequest request = createRequest();
|
||||||
builder.build(request);
|
builder.build(request);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +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.gradle.tasks.bundling;
|
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryConfiguration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker configuration options.
|
|
||||||
*
|
|
||||||
* @author Wei Jiang
|
|
||||||
* @since 2.4.0
|
|
||||||
*/
|
|
||||||
public class Docker {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The docker registry configuration.
|
|
||||||
*/
|
|
||||||
private DockerRegistry registry;
|
|
||||||
|
|
||||||
public DockerRegistry getRegistry() {
|
|
||||||
return this.registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRegistry(DockerRegistry registry) {
|
|
||||||
this.registry = registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DockerConfiguration getDockerConfiguration() {
|
|
||||||
DockerRegistryConfiguration dockerRegistryConfiguration = null;
|
|
||||||
|
|
||||||
if (this.registry != null) {
|
|
||||||
dockerRegistryConfiguration = this.registry.getDockerRegistryConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DockerConfiguration(dockerRegistryConfiguration);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,98 +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.gradle.tasks.bundling;
|
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryConfiguration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry configuration options.
|
|
||||||
*
|
|
||||||
* @author Wei Jiang
|
|
||||||
* @since 2.4.0
|
|
||||||
*/
|
|
||||||
public class DockerRegistry {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry server address.
|
|
||||||
*/
|
|
||||||
private String url;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry authentication username.
|
|
||||||
*/
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry authentication password.
|
|
||||||
*/
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry authentication email.
|
|
||||||
*/
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry authentication identity token.
|
|
||||||
*/
|
|
||||||
private String token;
|
|
||||||
|
|
||||||
public String getUrl() {
|
|
||||||
return this.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUrl(String url) {
|
|
||||||
this.url = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return this.username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsername(String username) {
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPassword() {
|
|
||||||
return this.password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPassword(String password) {
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEmail() {
|
|
||||||
return this.email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEmail(String email) {
|
|
||||||
this.email = email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getToken() {
|
|
||||||
return this.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setToken(String token) {
|
|
||||||
this.token = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DockerRegistryConfiguration getDockerRegistryConfiguration() {
|
|
||||||
return new DockerRegistryConfiguration(this.url, this.username, this.password, this.email, this.token);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* 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.gradle.tasks.bundling;
|
||||||
|
|
||||||
|
import groovy.lang.Closure;
|
||||||
|
import org.gradle.api.Action;
|
||||||
|
import org.gradle.api.GradleException;
|
||||||
|
import org.gradle.api.tasks.Input;
|
||||||
|
import org.gradle.api.tasks.Nested;
|
||||||
|
import org.gradle.api.tasks.Optional;
|
||||||
|
import org.gradle.util.ConfigureUtil;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates Docker configuration options.
|
||||||
|
*
|
||||||
|
* @author Wei Jiang
|
||||||
|
* @author Scott Frederick
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public class DockerSpec {
|
||||||
|
|
||||||
|
private final DockerRegistrySpec registry;
|
||||||
|
|
||||||
|
public DockerSpec() {
|
||||||
|
this.registry = new DockerRegistrySpec();
|
||||||
|
}
|
||||||
|
|
||||||
|
DockerSpec(DockerRegistrySpec registry) {
|
||||||
|
this.registry = registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link DockerRegistrySpec} that configures registry authentication.
|
||||||
|
* @return the registry spec
|
||||||
|
*/
|
||||||
|
@Nested
|
||||||
|
public DockerRegistrySpec getRegistry() {
|
||||||
|
return this.registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customizes the {@link DockerRegistrySpec} that configures registry authentication.
|
||||||
|
* @param action the action to apply
|
||||||
|
*/
|
||||||
|
public void registry(Action<DockerRegistrySpec> action) {
|
||||||
|
action.execute(this.registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customizes the {@link DockerRegistrySpec} that configures registry authentication.
|
||||||
|
* @param closure the closure to apply
|
||||||
|
*/
|
||||||
|
public void registry(Closure<?> closure) {
|
||||||
|
registry(ConfigureUtil.configureUsing(closure));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this configuration as a {@link DockerConfiguration} instance. This method
|
||||||
|
* should only be called when the configuration is complete and will no longer be
|
||||||
|
* changed.
|
||||||
|
* @return the Docker configuration
|
||||||
|
*/
|
||||||
|
DockerConfiguration asDockerConfiguration() {
|
||||||
|
if (this.registry == null || this.registry.hasEmptyAuth()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (this.registry.hasTokenAuth() && !this.registry.hasUserAuth()) {
|
||||||
|
return DockerConfiguration.withRegistryTokenAuthentication(this.registry.getToken());
|
||||||
|
}
|
||||||
|
if (this.registry.hasUserAuth() && !this.registry.hasTokenAuth()) {
|
||||||
|
return DockerConfiguration.withRegistryUserAuthentication(this.registry.getUsername(),
|
||||||
|
this.registry.getPassword(), this.registry.getUrl(), this.registry.getEmail());
|
||||||
|
}
|
||||||
|
throw new GradleException(
|
||||||
|
"Invalid Docker registry configuration, either token or username/password must be provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates Docker registry authentication configuration options.
|
||||||
|
*/
|
||||||
|
public static class DockerRegistrySpec {
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the username to use when authenticating to the Docker registry.
|
||||||
|
* @return the registry username
|
||||||
|
*/
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public String getUsername() {
|
||||||
|
return this.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the username to use when authenticating to the Docker registry.
|
||||||
|
* @param username the registry username
|
||||||
|
*/
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the password to use when authenticating to the Docker registry.
|
||||||
|
* @return the registry password
|
||||||
|
*/
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public String getPassword() {
|
||||||
|
return this.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the password to use when authenticating to the Docker registry.
|
||||||
|
* @param password the registry username
|
||||||
|
*/
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Docker registry URL.
|
||||||
|
* @return the registry URL
|
||||||
|
*/
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public String getUrl() {
|
||||||
|
return this.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Docker registry URL.
|
||||||
|
* @param url the registry URL
|
||||||
|
*/
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the email address associated with the Docker registry username.
|
||||||
|
* @return the registry email address
|
||||||
|
*/
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public String getEmail() {
|
||||||
|
return this.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the email address associated with the Docker registry username.
|
||||||
|
* @param email the registry email address
|
||||||
|
*/
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the identity token to use when authenticating to the Docker registry.
|
||||||
|
* @return the registry identity token
|
||||||
|
*/
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public String getToken() {
|
||||||
|
return this.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the identity token to use when authenticating to the Docker registry.
|
||||||
|
* @param token the registry identity token
|
||||||
|
*/
|
||||||
|
public void setToken(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasEmptyAuth() {
|
||||||
|
return this.username == null && this.password == null && this.url == null && this.email == null
|
||||||
|
&& this.token == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasUserAuth() {
|
||||||
|
return this.getUsername() != null && this.getPassword() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasTokenAuth() {
|
||||||
|
return this.getToken() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,47 +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.gradle.tasks.bundling;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryConfiguration;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link DockerRegistry}.
|
|
||||||
*
|
|
||||||
* @author Wei Jiang
|
|
||||||
*/
|
|
||||||
public class DockerRegistryTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getDockerRegistryConfiguration() {
|
|
||||||
DockerRegistry dockerRegistry = new DockerRegistry();
|
|
||||||
dockerRegistry.setUsername("username");
|
|
||||||
dockerRegistry.setPassword("password");
|
|
||||||
dockerRegistry.setEmail("mock@spring.com");
|
|
||||||
dockerRegistry.setUrl("http://mock.docker.registry");
|
|
||||||
DockerRegistryConfiguration dockerRegistryConfiguration = dockerRegistry.getDockerRegistryConfiguration();
|
|
||||||
assertThat(dockerRegistryConfiguration).isNotNull();
|
|
||||||
assertThat(dockerRegistryConfiguration.getUsername()).isEqualTo(dockerRegistry.getUsername());
|
|
||||||
assertThat(dockerRegistryConfiguration.getPassword()).isEqualTo(dockerRegistry.getPassword());
|
|
||||||
assertThat(dockerRegistryConfiguration.getEmail()).isEqualTo(dockerRegistry.getEmail());
|
|
||||||
assertThat(dockerRegistryConfiguration.getUrl()).isEqualTo(dockerRegistry.getUrl());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* 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.gradle.tasks.bundling;
|
||||||
|
|
||||||
|
import org.gradle.api.GradleException;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryAuthentication;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DockerSpec}.
|
||||||
|
*
|
||||||
|
* @author Wei Jiang
|
||||||
|
* @author Scott Frederick
|
||||||
|
*/
|
||||||
|
public class DockerSpecTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asDockerConfigurationWithoutRegistry() {
|
||||||
|
DockerSpec dockerSpec = new DockerSpec();
|
||||||
|
assertThat(dockerSpec.asDockerConfiguration()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asDockerConfigurationWithEmptyRegistry() {
|
||||||
|
DockerSpec dockerSpec = new DockerSpec(new DockerSpec.DockerRegistrySpec());
|
||||||
|
assertThat(dockerSpec.asDockerConfiguration()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asDockerConfigurationWithUserAuth() {
|
||||||
|
DockerSpec.DockerRegistrySpec dockerRegistry = new DockerSpec.DockerRegistrySpec();
|
||||||
|
dockerRegistry.setUsername("user");
|
||||||
|
dockerRegistry.setPassword("secret");
|
||||||
|
dockerRegistry.setUrl("https://docker.example.com");
|
||||||
|
dockerRegistry.setEmail("docker@example.com");
|
||||||
|
DockerSpec dockerSpec = new DockerSpec(dockerRegistry);
|
||||||
|
DockerConfiguration dockerConfiguration = dockerSpec.asDockerConfiguration();
|
||||||
|
DockerRegistryAuthentication registryAuthentication = dockerConfiguration.getRegistryAuthentication();
|
||||||
|
assertThat(registryAuthentication).isNotNull();
|
||||||
|
assertThat(new String(Base64Utils.decodeFromString(registryAuthentication.createAuthHeader())))
|
||||||
|
.contains("\"username\" : \"user\"").contains("\"password\" : \"secret\"")
|
||||||
|
.contains("\"email\" : \"docker@example.com\"")
|
||||||
|
.contains("\"serveraddress\" : \"https://docker.example.com\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asDockerConfigurationWithIncompleteUserAuthFails() {
|
||||||
|
DockerSpec.DockerRegistrySpec dockerRegistry = new DockerSpec.DockerRegistrySpec();
|
||||||
|
dockerRegistry.setUsername("user");
|
||||||
|
dockerRegistry.setUrl("https://docker.example.com");
|
||||||
|
dockerRegistry.setEmail("docker@example.com");
|
||||||
|
DockerSpec dockerSpec = new DockerSpec(dockerRegistry);
|
||||||
|
assertThatExceptionOfType(GradleException.class).isThrownBy(dockerSpec::asDockerConfiguration)
|
||||||
|
.withMessageContaining("Invalid Docker registry configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asDockerConfigurationWithTokenAuth() {
|
||||||
|
DockerSpec.DockerRegistrySpec dockerRegistry = new DockerSpec.DockerRegistrySpec();
|
||||||
|
dockerRegistry.setToken("token");
|
||||||
|
DockerSpec dockerSpec = new DockerSpec(dockerRegistry);
|
||||||
|
DockerConfiguration dockerConfiguration = dockerSpec.asDockerConfiguration();
|
||||||
|
DockerRegistryAuthentication registryAuthentication = dockerConfiguration.getRegistryAuthentication();
|
||||||
|
assertThat(registryAuthentication).isNotNull();
|
||||||
|
assertThat(new String(Base64Utils.decodeFromString(registryAuthentication.createAuthHeader())))
|
||||||
|
.contains("\"identitytoken\" : \"token\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asDockerConfigurationWithUserAndTokenAuthFails() {
|
||||||
|
DockerSpec.DockerRegistrySpec dockerRegistry = new DockerSpec.DockerRegistrySpec();
|
||||||
|
dockerRegistry.setUsername("user");
|
||||||
|
dockerRegistry.setPassword("secret");
|
||||||
|
dockerRegistry.setToken("token");
|
||||||
|
DockerSpec dockerSpec = new DockerSpec(dockerRegistry);
|
||||||
|
assertThatExceptionOfType(GradleException.class).isThrownBy(dockerSpec::asDockerConfiguration)
|
||||||
|
.withMessageContaining("Invalid Docker registry configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -59,6 +59,37 @@ On Linux and macOS, these environment variables can be set using the command `ev
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[build-image-docker-registry]]
|
||||||
|
=== Docker Registry
|
||||||
|
If the Docker images specified by the `builder` or `runImage` parameters are stored in a private Docker image registry that requires authentication, the authentication credentials can be provided using `docker.registry` parameters.
|
||||||
|
Parameters are provided for user authentication or identity token authentication.
|
||||||
|
Consult the documentation for the Docker registry being used to store builder or run images for further information on supported authentication methods.
|
||||||
|
|
||||||
|
The following table summarizes the available parameters:
|
||||||
|
|
||||||
|
|===
|
||||||
|
| Parameter | Description
|
||||||
|
|
||||||
|
| `username`
|
||||||
|
| Username for the Docker image registry user. Required for user authentication.
|
||||||
|
|
||||||
|
| `password`
|
||||||
|
| Password for the Docker image registry user. Required for user authentication.
|
||||||
|
|
||||||
|
| `url`
|
||||||
|
| Address of the Docker image registry. Optional for user authentication.
|
||||||
|
|
||||||
|
| `email`
|
||||||
|
| E-mail address for the Docker image registry user. Optional for user authentication.
|
||||||
|
|
||||||
|
| `token`
|
||||||
|
| Identity token for the Docker image registry user. Required for token authentication.
|
||||||
|
|===
|
||||||
|
|
||||||
|
For more details, see also <<build-image-example-docker,examples>>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[build-image-customization]]
|
[[build-image-customization]]
|
||||||
=== Image Customizations
|
=== Image Customizations
|
||||||
The plugin invokes a {buildpacks-reference}/concepts/components/builder/[builder] to orchestrate the generation of an image.
|
The plugin invokes a {buildpacks-reference}/concepts/components/builder/[builder] to orchestrate the generation of an image.
|
||||||
@@ -252,3 +283,57 @@ The image name can be specified on the command line as well, as shown in this ex
|
|||||||
$ mvn spring-boot:build-image -Dspring-boot.build-image.imageName=example.com/library/my-app:v1
|
$ mvn spring-boot:build-image -Dspring-boot.build-image.imageName=example.com/library/my-app:v1
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[build-image-example-docker]]
|
||||||
|
==== Docker Configuration
|
||||||
|
If the builder or run image are stored in a private Docker registry that supports user authentication, authentication details can be provided as shown in the following example:
|
||||||
|
|
||||||
|
[source,xml,indent=0,subs="verbatim,attributes"]
|
||||||
|
----
|
||||||
|
<project>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>{gradle-project-version}</version>
|
||||||
|
<configuration>
|
||||||
|
<docker>
|
||||||
|
<registry>
|
||||||
|
<username>user</username>
|
||||||
|
<password>secret</password>
|
||||||
|
<url>https://docker.example.com/v1/</url>
|
||||||
|
<email>user@example.com</email>
|
||||||
|
</registry>
|
||||||
|
</docker>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
|
----
|
||||||
|
|
||||||
|
If the builder or run image is stored in a private Docker registry that supports token authentication, the token value can be provided as shown in the following example:
|
||||||
|
|
||||||
|
[source,xml,indent=0,subs="verbatim,attributes"]
|
||||||
|
----
|
||||||
|
<project>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>{gradle-project-version}</version>
|
||||||
|
<configuration>
|
||||||
|
<docker>
|
||||||
|
<registry>
|
||||||
|
<token>9cbaf023786cd7...</token>
|
||||||
|
</registry>
|
||||||
|
</docker>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
|
----
|
||||||
|
|||||||
@@ -159,8 +159,8 @@ public class BuildImageMojo extends AbstractPackagerMojo {
|
|||||||
private void buildImage() throws MojoExecutionException {
|
private void buildImage() throws MojoExecutionException {
|
||||||
Libraries libraries = getLibraries(Collections.emptySet());
|
Libraries libraries = getLibraries(Collections.emptySet());
|
||||||
try {
|
try {
|
||||||
DockerConfiguration dockerConfiguration = (this.docker != null) ? this.docker.getDockerConfiguration()
|
DockerConfiguration dockerConfiguration = (this.docker != null) ? this.docker.asDockerConfiguration()
|
||||||
: new DockerConfiguration();
|
: null;
|
||||||
Builder builder = new Builder(new MojoBuildLog(this::getLog), dockerConfiguration);
|
Builder builder = new Builder(new MojoBuildLog(this::getLog), dockerConfiguration);
|
||||||
BuildRequest request = getBuildRequest(libraries);
|
BuildRequest request = getBuildRequest(libraries);
|
||||||
builder.build(request);
|
builder.build(request);
|
||||||
|
|||||||
@@ -17,37 +17,116 @@
|
|||||||
package org.springframework.boot.maven;
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryConfiguration;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Docker configuration options.
|
* Docker configuration options.
|
||||||
*
|
*
|
||||||
* @author Wei Jiang
|
* @author Wei Jiang
|
||||||
|
* @author Scott Frederick
|
||||||
* @since 2.4.0
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public class Docker {
|
public class Docker {
|
||||||
|
|
||||||
/**
|
|
||||||
* The docker registry configuration.
|
|
||||||
*/
|
|
||||||
private DockerRegistry registry;
|
private DockerRegistry registry;
|
||||||
|
|
||||||
public DockerRegistry getRegistry() {
|
/**
|
||||||
return this.registry;
|
* Sets the {@link DockerRegistry} that configures registry authentication.
|
||||||
}
|
* @param registry the registry configuration
|
||||||
|
*/
|
||||||
public void setRegistry(DockerRegistry registry) {
|
public void setRegistry(DockerRegistry registry) {
|
||||||
this.registry = registry;
|
this.registry = registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DockerConfiguration getDockerConfiguration() {
|
/**
|
||||||
DockerRegistryConfiguration dockerRegistryConfiguration = null;
|
* Returns this configuration as a {@link DockerConfiguration} instance. This method
|
||||||
|
* should only be called when the configuration is complete and will no longer be
|
||||||
if (this.registry != null) {
|
* changed.
|
||||||
dockerRegistryConfiguration = this.registry.getDockerRegistryConfiguration();
|
* @return the Docker configuration
|
||||||
|
*/
|
||||||
|
DockerConfiguration asDockerConfiguration() {
|
||||||
|
if (this.registry == null || this.registry.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (this.registry.hasTokenAuth() && !this.registry.hasUserAuth()) {
|
||||||
|
return DockerConfiguration.withRegistryTokenAuthentication(this.registry.getToken());
|
||||||
|
}
|
||||||
|
if (this.registry.hasUserAuth() && !this.registry.hasTokenAuth()) {
|
||||||
|
return DockerConfiguration.withRegistryUserAuthentication(this.registry.getUsername(),
|
||||||
|
this.registry.getPassword(), this.registry.getUrl(), this.registry.getEmail());
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid Docker registry configuration, either token or username/password must be provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates Docker registry authentication configuration options.
|
||||||
|
*/
|
||||||
|
public static class DockerRegistry {
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
String getUsername() {
|
||||||
|
return this.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPassword() {
|
||||||
|
return this.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getEmail() {
|
||||||
|
return this.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getUrl() {
|
||||||
|
return this.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getToken() {
|
||||||
|
return this.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToken(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isEmpty() {
|
||||||
|
return this.username == null && this.password == null && this.url == null && this.email == null
|
||||||
|
&& this.token == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasTokenAuth() {
|
||||||
|
return this.token != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasUserAuth() {
|
||||||
|
return this.username != null && this.password != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DockerConfiguration(dockerRegistryConfiguration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,98 +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.maven;
|
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryConfiguration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry configuration options.
|
|
||||||
*
|
|
||||||
* @author Wei Jiang
|
|
||||||
* @since 2.4.0
|
|
||||||
*/
|
|
||||||
public class DockerRegistry {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry server address.
|
|
||||||
*/
|
|
||||||
private String url;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry authentication username.
|
|
||||||
*/
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry authentication password.
|
|
||||||
*/
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry authentication email.
|
|
||||||
*/
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Docker registry authentication identity token.
|
|
||||||
*/
|
|
||||||
private String token;
|
|
||||||
|
|
||||||
public String getUrl() {
|
|
||||||
return this.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUrl(String url) {
|
|
||||||
this.url = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return this.username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsername(String username) {
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPassword() {
|
|
||||||
return this.password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPassword(String password) {
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEmail() {
|
|
||||||
return this.email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEmail(String email) {
|
|
||||||
this.email = email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getToken() {
|
|
||||||
return this.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setToken(String token) {
|
|
||||||
this.token = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DockerRegistryConfiguration getDockerRegistryConfiguration() {
|
|
||||||
return new DockerRegistryConfiguration(this.url, this.username, this.password, this.email, this.token);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,47 +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.maven;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryConfiguration;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link DockerRegistry}.
|
|
||||||
*
|
|
||||||
* @author Wei Jiang
|
|
||||||
*/
|
|
||||||
public class DockerRegistryTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getDockerRegistryConfiguration() {
|
|
||||||
DockerRegistry dockerRegistry = new DockerRegistry();
|
|
||||||
dockerRegistry.setUsername("username");
|
|
||||||
dockerRegistry.setPassword("password");
|
|
||||||
dockerRegistry.setEmail("mock@spring.com");
|
|
||||||
dockerRegistry.setUrl("http://mock.docker.registry");
|
|
||||||
DockerRegistryConfiguration dockerRegistryConfiguration = dockerRegistry.getDockerRegistryConfiguration();
|
|
||||||
assertThat(dockerRegistryConfiguration).isNotNull();
|
|
||||||
assertThat(dockerRegistryConfiguration.getUsername()).isEqualTo(dockerRegistry.getUsername());
|
|
||||||
assertThat(dockerRegistryConfiguration.getPassword()).isEqualTo(dockerRegistry.getPassword());
|
|
||||||
assertThat(dockerRegistryConfiguration.getEmail()).isEqualTo(dockerRegistry.getEmail());
|
|
||||||
assertThat(dockerRegistryConfiguration.getUrl()).isEqualTo(dockerRegistry.getUrl());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* 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.maven;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryAuthentication;
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link Docker}.
|
||||||
|
*
|
||||||
|
* @author Wei Jiang
|
||||||
|
* @author Scott Frederick
|
||||||
|
*/
|
||||||
|
public class DockerTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asDockerConfigurationWithoutRegistry() {
|
||||||
|
Docker docker = new Docker();
|
||||||
|
assertThat(docker.asDockerConfiguration()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asDockerConfigurationWithEmptyRegistry() {
|
||||||
|
Docker.DockerRegistry dockerRegistry = new Docker.DockerRegistry();
|
||||||
|
Docker docker = new Docker();
|
||||||
|
docker.setRegistry(dockerRegistry);
|
||||||
|
assertThat(docker.asDockerConfiguration()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asDockerConfigurationWithUserAuth() {
|
||||||
|
Docker.DockerRegistry dockerRegistry = new Docker.DockerRegistry();
|
||||||
|
dockerRegistry.setUsername("user");
|
||||||
|
dockerRegistry.setPassword("secret");
|
||||||
|
dockerRegistry.setUrl("https://docker.example.com");
|
||||||
|
dockerRegistry.setEmail("docker@example.com");
|
||||||
|
Docker docker = new Docker();
|
||||||
|
docker.setRegistry(dockerRegistry);
|
||||||
|
DockerConfiguration dockerConfiguration = docker.asDockerConfiguration();
|
||||||
|
DockerRegistryAuthentication registryAuthentication = dockerConfiguration.getRegistryAuthentication();
|
||||||
|
assertThat(registryAuthentication).isNotNull();
|
||||||
|
assertThat(new String(Base64Utils.decodeFromString(registryAuthentication.createAuthHeader())))
|
||||||
|
.contains("\"username\" : \"user\"").contains("\"password\" : \"secret\"")
|
||||||
|
.contains("\"email\" : \"docker@example.com\"")
|
||||||
|
.contains("\"serveraddress\" : \"https://docker.example.com\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asDockerConfigurationWithIncompleteUserAuthFails() {
|
||||||
|
Docker.DockerRegistry dockerRegistry = new Docker.DockerRegistry();
|
||||||
|
dockerRegistry.setUsername("user");
|
||||||
|
dockerRegistry.setUrl("https://docker.example.com");
|
||||||
|
dockerRegistry.setEmail("docker@example.com");
|
||||||
|
Docker docker = new Docker();
|
||||||
|
docker.setRegistry(dockerRegistry);
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(docker::asDockerConfiguration)
|
||||||
|
.withMessageContaining("Invalid Docker registry configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asDockerConfigurationWithTokenAuth() {
|
||||||
|
Docker.DockerRegistry dockerRegistry = new Docker.DockerRegistry();
|
||||||
|
dockerRegistry.setToken("token");
|
||||||
|
Docker docker = new Docker();
|
||||||
|
docker.setRegistry(dockerRegistry);
|
||||||
|
DockerConfiguration dockerConfiguration = docker.asDockerConfiguration();
|
||||||
|
DockerRegistryAuthentication registryAuthentication = dockerConfiguration.getRegistryAuthentication();
|
||||||
|
assertThat(registryAuthentication).isNotNull();
|
||||||
|
assertThat(new String(Base64Utils.decodeFromString(registryAuthentication.createAuthHeader())))
|
||||||
|
.contains("\"identitytoken\" : \"token\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void asDockerConfigurationWithUserAndTokenAuthFails() {
|
||||||
|
Docker.DockerRegistry dockerRegistry = new Docker.DockerRegistry();
|
||||||
|
dockerRegistry.setUsername("user");
|
||||||
|
dockerRegistry.setPassword("secret");
|
||||||
|
dockerRegistry.setToken("token");
|
||||||
|
Docker docker = new Docker();
|
||||||
|
docker.setRegistry(dockerRegistry);
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(docker::asDockerConfiguration)
|
||||||
|
.withMessageContaining("Invalid Docker registry configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user