Skip to content

Commit d1338a6

Browse files
Merge pull request #22972 from wmz7year
* gh-22972: Polish "Support authentication to private Docker registry" Support authentication to private docker registry Closes gh-22972
2 parents f0dfff8 + 86fa814 commit d1338a6

File tree

33 files changed

+1384
-28
lines changed

33 files changed

+1384
-28
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
2525
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
2626
import org.springframework.boot.buildpack.platform.docker.UpdateListener;
27+
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
2728
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
2829
import org.springframework.boot.buildpack.platform.docker.type.Image;
2930
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
@@ -48,10 +49,18 @@ public Builder() {
4849
this(BuildLog.toSystemOut());
4950
}
5051

52+
public Builder(DockerConfiguration dockerConfiguration) {
53+
this(BuildLog.toSystemOut(), dockerConfiguration);
54+
}
55+
5156
public Builder(BuildLog log) {
5257
this(log, new DockerApi());
5358
}
5459

60+
public Builder(BuildLog log, DockerConfiguration dockerConfiguration) {
61+
this(log, new DockerApi(dockerConfiguration));
62+
}
63+
5564
Builder(BuildLog log, DockerApi docker) {
5665
Assert.notNull(log, "Log must not be null");
5766
this.log = log;

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.apache.http.client.utils.URIBuilder;
2828

29+
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
2930
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
3031
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
3132
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
@@ -68,7 +69,15 @@ public class DockerApi {
6869
* Create a new {@link DockerApi} instance.
6970
*/
7071
public DockerApi() {
71-
this(HttpTransport.create());
72+
this(DockerConfiguration.withDefaults());
73+
}
74+
75+
/**
76+
* Create a new {@link DockerApi} instance.
77+
* @param dockerConfiguration the Docker configuration options
78+
*/
79+
public DockerApi(DockerConfiguration dockerConfiguration) {
80+
this(HttpTransport.create(dockerConfiguration));
7281
}
7382

7483
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.buildpack.platform.docker.configuration;
18+
19+
import org.springframework.util.Assert;
20+
21+
/**
22+
* Docker configuration options.
23+
*
24+
* @author Wei Jiang
25+
* @author Scott Frederick
26+
* @since 2.4.0
27+
*/
28+
public final class DockerConfiguration {
29+
30+
private final DockerRegistryAuthentication authentication;
31+
32+
private DockerConfiguration(DockerRegistryAuthentication authentication) {
33+
this.authentication = authentication;
34+
}
35+
36+
public DockerRegistryAuthentication getRegistryAuthentication() {
37+
return this.authentication;
38+
}
39+
40+
public static DockerConfiguration withDefaults() {
41+
return new DockerConfiguration(null);
42+
}
43+
44+
public static DockerConfiguration withRegistryTokenAuthentication(String token) {
45+
Assert.notNull(token, "Token must not be null");
46+
return new DockerConfiguration(new DockerRegistryTokenAuthentication(token));
47+
}
48+
49+
public static DockerConfiguration withRegistryUserAuthentication(String username, String password, String url,
50+
String email) {
51+
Assert.notNull(username, "Username must not be null");
52+
Assert.notNull(password, "Password must not be null");
53+
return new DockerConfiguration(new DockerRegistryUserAuthentication(username, password, url, email));
54+
}
55+
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.buildpack.platform.docker.configuration;
18+
19+
import com.fasterxml.jackson.core.JsonProcessingException;
20+
21+
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
22+
import org.springframework.util.Base64Utils;
23+
24+
/**
25+
* Docker registry authentication configuration.
26+
*
27+
* @author Scott Frederick
28+
* @since 2.4.0
29+
*/
30+
public abstract class DockerRegistryAuthentication {
31+
32+
public String createAuthHeader() {
33+
try {
34+
return Base64Utils.encodeToUrlSafeString(SharedObjectMapper.get().writeValueAsBytes(this));
35+
}
36+
catch (JsonProcessingException ex) {
37+
throw new IllegalStateException("Error creating Docker registry authentication header", ex);
38+
}
39+
}
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.buildpack.platform.docker.configuration;
18+
19+
import com.fasterxml.jackson.annotation.JsonProperty;
20+
21+
/**
22+
* Docker registry authentication configuration using a token.
23+
*
24+
* @author Scott Frederick
25+
*/
26+
class DockerRegistryTokenAuthentication extends DockerRegistryAuthentication {
27+
28+
@JsonProperty("identitytoken")
29+
private final String token;
30+
31+
DockerRegistryTokenAuthentication(String token) {
32+
this.token = token;
33+
}
34+
35+
String getToken() {
36+
return this.token;
37+
}
38+
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.buildpack.platform.docker.configuration;
18+
19+
import com.fasterxml.jackson.annotation.JsonProperty;
20+
21+
/**
22+
* Docker registry authentication configuration using user credentials.
23+
*
24+
* @author Scott Frederick
25+
*/
26+
class DockerRegistryUserAuthentication extends DockerRegistryAuthentication {
27+
28+
@JsonProperty
29+
private final String username;
30+
31+
@JsonProperty
32+
private final String password;
33+
34+
@JsonProperty("serveraddress")
35+
private final String url;
36+
37+
@JsonProperty
38+
private final String email;
39+
40+
DockerRegistryUserAuthentication(String username, String password, String url, String email) {
41+
this.username = username;
42+
this.password = password;
43+
this.url = url;
44+
this.email = email;
45+
}
46+
47+
String getUsername() {
48+
return this.username;
49+
}
50+
51+
String getPassword() {
52+
return this.password;
53+
}
54+
55+
String getUrl() {
56+
return this.url;
57+
}
58+
59+
String getEmail() {
60+
return this.email;
61+
}
62+
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Docker configuration options.
19+
*/
20+
package org.springframework.boot.buildpack.platform.docker.configuration;

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@
3636
import org.apache.http.entity.AbstractHttpEntity;
3737
import org.apache.http.impl.client.CloseableHttpClient;
3838

39+
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
3940
import org.springframework.boot.buildpack.platform.io.Content;
4041
import org.springframework.boot.buildpack.platform.io.IOConsumer;
4142
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
4243
import org.springframework.util.Assert;
44+
import org.springframework.util.StringUtils;
4345

4446
/**
4547
* Abstract base class for {@link HttpTransport} implementations backed by a
@@ -55,11 +57,14 @@ abstract class HttpClientTransport implements HttpTransport {
5557

5658
private final HttpHost host;
5759

58-
protected HttpClientTransport(CloseableHttpClient client, HttpHost host) {
60+
private final String registryAuthHeader;
61+
62+
protected HttpClientTransport(CloseableHttpClient client, HttpHost host, DockerConfiguration dockerConfiguration) {
5963
Assert.notNull(client, "Client must not be null");
6064
Assert.notNull(host, "Host must not be null");
6165
this.client = client;
6266
this.host = host;
67+
this.registryAuthHeader = buildRegistryAuthHeader(dockerConfiguration);
6368
}
6469

6570
/**
@@ -116,6 +121,15 @@ public Response delete(URI uri) {
116121
return execute(new HttpDelete(uri));
117122
}
118123

124+
private String buildRegistryAuthHeader(DockerConfiguration dockerConfiguration) {
125+
if (dockerConfiguration == null || dockerConfiguration.getRegistryAuthentication() == null) {
126+
return null;
127+
}
128+
129+
String authHeader = dockerConfiguration.getRegistryAuthentication().createAuthHeader();
130+
return (StringUtils.hasText(authHeader)) ? authHeader : null;
131+
}
132+
119133
private Response execute(HttpEntityEnclosingRequestBase request, String contentType,
120134
IOConsumer<OutputStream> writer) {
121135
request.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
@@ -125,6 +139,9 @@ private Response execute(HttpEntityEnclosingRequestBase request, String contentT
125139

126140
private Response execute(HttpUriRequest request) {
127141
try {
142+
if (this.registryAuthHeader != null) {
143+
request.addHeader("X-Registry-Auth", this.registryAuthHeader);
144+
}
128145
CloseableHttpResponse response = this.client.execute(this.host, request);
129146
StatusLine statusLine = response.getStatusLine();
130147
int statusCode = statusLine.getStatusCode();

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.OutputStream;
2323
import java.net.URI;
2424

25+
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfiguration;
2526
import org.springframework.boot.buildpack.platform.io.IOConsumer;
2627
import org.springframework.boot.buildpack.platform.system.Environment;
2728

@@ -84,7 +85,17 @@ public interface HttpTransport {
8485
* @return a {@link HttpTransport} instance
8586
*/
8687
static HttpTransport create() {
87-
return create(Environment.SYSTEM);
88+
return create(DockerConfiguration.withDefaults());
89+
}
90+
91+
/**
92+
* Create the most suitable {@link HttpTransport} based on the
93+
* {@link Environment#SYSTEM system environment}.
94+
* @param dockerConfiguration the Docker engine configuration
95+
* @return a {@link HttpTransport} instance
96+
*/
97+
static HttpTransport create(DockerConfiguration dockerConfiguration) {
98+
return create(Environment.SYSTEM, dockerConfiguration);
8899
}
89100

90101
/**
@@ -94,8 +105,19 @@ static HttpTransport create() {
94105
* @return a {@link HttpTransport} instance
95106
*/
96107
static HttpTransport create(Environment environment) {
97-
HttpTransport remote = RemoteHttpClientTransport.createIfPossible(environment);
98-
return (remote != null) ? remote : LocalHttpClientTransport.create(environment);
108+
return create(environment, DockerConfiguration.withDefaults());
109+
}
110+
111+
/**
112+
* Create the most suitable {@link HttpTransport} based on the given
113+
* {@link Environment} and {@link DockerConfiguration}.
114+
* @param environment the source environment
115+
* @param dockerConfiguration the Docker engine configuration
116+
* @return a {@link HttpTransport} instance
117+
*/
118+
static HttpTransport create(Environment environment, DockerConfiguration dockerConfiguration) {
119+
HttpTransport remote = RemoteHttpClientTransport.createIfPossible(environment, dockerConfiguration);
120+
return (remote != null) ? remote : LocalHttpClientTransport.create(environment, dockerConfiguration);
99121
}
100122

101123
/**

0 commit comments

Comments
 (0)