Skip to content

Commit da61d63

Browse files
nosanphilwebb
andcommitted
Add Docker configuration authentication to Maven and Gradle plugins
Update the Maven and Gradle plugins to make use of the new Docker configuration authentication support. See gh-45269 Signed-off-by: Dmytro Nosan <[email protected]> Co-authored-by: Phillip Webb <[email protected]>
1 parent e6fbcce commit da61d63

File tree

7 files changed

+66
-29
lines changed

7 files changed

+66
-29
lines changed

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/antora/modules/gradle-plugin/pages/packaging-oci-image.adoc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ The following table summarizes the available properties for `docker.builderRegis
100100

101101
For more details, see also xref:packaging-oci-image.adoc#build-image.examples.docker[examples].
102102

103+
[NOTE]
104+
====
105+
If credentials are not provided, the plugin reads the user's existing Docker configuration file (typically located at `$HOME/.docker/config.json`) to determine authentication methods.
106+
Using these methods, the plugin attempts to provide authentication credentials for the requested image.
107+
108+
The plugin supports the following authentication methods:
109+
110+
- *Credential Helpers*: External tools configured in the Docker configuration file to provide credentials for specific registries. For example, tools like `osxkeychain` or `ecr-login` handle authentication for certain registries.
111+
- *Credential Store*: A default fallback mechanism that securely stores and retrieves credentials (e.g., `desktop` for Docker Desktop).
112+
- *Static Credentials*: Credentials that are stored directly in the Docker configuration file under the `auths` section.
113+
====
114+
103115

104116

105117
[[build-image.customization]]

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/DockerSpec.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,14 @@ private BuilderDockerConfiguration customizeHost(BuilderDockerConfiguration dock
145145
}
146146

147147
private BuilderDockerConfiguration customizeBuilderAuthentication(BuilderDockerConfiguration dockerConfiguration) {
148-
return dockerConfiguration
149-
.withBuilderRegistryAuthentication(getRegistryAuthentication("builder", this.builderRegistry, null));
148+
return dockerConfiguration.withBuilderRegistryAuthentication(getRegistryAuthentication("builder",
149+
this.builderRegistry, DockerRegistryAuthentication.configuration(null)));
150150
}
151151

152152
private BuilderDockerConfiguration customizePublishAuthentication(BuilderDockerConfiguration dockerConfiguration) {
153-
return dockerConfiguration.withPublishRegistryAuthentication(
154-
getRegistryAuthentication("publish", this.publishRegistry, DockerRegistryAuthentication.EMPTY_USER));
153+
return dockerConfiguration
154+
.withPublishRegistryAuthentication(getRegistryAuthentication("publish", this.publishRegistry,
155+
DockerRegistryAuthentication.configuration(DockerRegistryAuthentication.EMPTY_USER)));
155156
}
156157

157158
private DockerRegistryAuthentication getRegistryAuthentication(String type, DockerRegistrySpec registry,

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/DockerSpecTests.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ void prepareDockerSpec(@TempDir File temp) {
5454
void asDockerConfigurationWithDefaults() {
5555
BuilderDockerConfiguration dockerConfiguration = this.dockerSpec.asDockerConfiguration();
5656
assertThat(dockerConfiguration.connection()).isNull();
57-
assertThat(dockerConfiguration.builderRegistryAuthentication()).isNull();
57+
assertThat(dockerConfiguration.builderRegistryAuthentication().getAuthHeader()).isNull();
5858
assertThat(decoded(dockerConfiguration.publishRegistryAuthentication().getAuthHeader()))
5959
.contains("\"username\" : \"\"")
6060
.contains("\"password\" : \"\"")
@@ -73,7 +73,7 @@ void asDockerConfigurationWithHostConfiguration() {
7373
assertThat(host.secure()).isTrue();
7474
assertThat(host.certificatePath()).isEqualTo("/tmp/ca-cert");
7575
assertThat(dockerConfiguration.bindHostToBuilder()).isFalse();
76-
assertThat(dockerConfiguration.builderRegistryAuthentication()).isNull();
76+
assertThat(dockerConfiguration.builderRegistryAuthentication().getAuthHeader()).isNull();
7777
assertThat(decoded(dockerConfiguration.publishRegistryAuthentication().getAuthHeader()))
7878
.contains("\"username\" : \"\"")
7979
.contains("\"password\" : \"\"")
@@ -90,7 +90,7 @@ void asDockerConfigurationWithHostConfigurationNoTlsVerify() {
9090
assertThat(host.secure()).isFalse();
9191
assertThat(host.certificatePath()).isNull();
9292
assertThat(dockerConfiguration.bindHostToBuilder()).isFalse();
93-
assertThat(dockerConfiguration.builderRegistryAuthentication()).isNull();
93+
assertThat(dockerConfiguration.builderRegistryAuthentication().getAuthHeader()).isNull();
9494
assertThat(decoded(dockerConfiguration.publishRegistryAuthentication().getAuthHeader()))
9595
.contains("\"username\" : \"\"")
9696
.contains("\"password\" : \"\"")
@@ -106,7 +106,7 @@ void asDockerConfigurationWithContextConfiguration() {
106106
.connection();
107107
assertThat(host.context()).isEqualTo("test-context");
108108
assertThat(dockerConfiguration.bindHostToBuilder()).isFalse();
109-
assertThat(dockerConfiguration.builderRegistryAuthentication()).isNull();
109+
assertThat(dockerConfiguration.builderRegistryAuthentication().getAuthHeader()).isNull();
110110
assertThat(decoded(dockerConfiguration.publishRegistryAuthentication().getAuthHeader()))
111111
.contains("\"username\" : \"\"")
112112
.contains("\"password\" : \"\"")
@@ -132,7 +132,7 @@ void asDockerConfigurationWithBindHostToBuilder() {
132132
assertThat(host.secure()).isFalse();
133133
assertThat(host.certificatePath()).isNull();
134134
assertThat(dockerConfiguration.bindHostToBuilder()).isTrue();
135-
assertThat(dockerConfiguration.builderRegistryAuthentication()).isNull();
135+
assertThat(dockerConfiguration.builderRegistryAuthentication().getAuthHeader()).isNull();
136136
assertThat(decoded(dockerConfiguration.publishRegistryAuthentication().getAuthHeader()))
137137
.contains("\"username\" : \"\"")
138138
.contains("\"password\" : \"\"")
@@ -213,7 +213,7 @@ void asDockerConfigurationWithUserAndTokenAuthFails() {
213213
}
214214

215215
String decoded(String value) {
216-
return new String(Base64.getDecoder().decode(value));
216+
return (value != null) ? new String(Base64.getDecoder().decode(value)) : value;
217217
}
218218

219219
}

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/antora/modules/maven-plugin/pages/build-image.adoc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,18 @@ The following table summarizes the available parameters for `docker.builderRegis
115115

116116
For more details, see also xref:build-image.adoc#build-image.examples.docker[examples].
117117

118+
[NOTE]
119+
====
120+
If credentials are not provided, the plugin reads the user's existing Docker configuration file (typically located at `$HOME/.docker/config.json`) to determine authentication methods.
121+
Using these methods, the plugin attempts to provide authentication credentials for the requested image.
122+
123+
The plugin supports the following authentication methods:
124+
125+
- *Credential Helpers*: External tools configured in the Docker configuration file to provide credentials for specific registries. For example, tools like `osxkeychain` or `ecr-login` handle authentication for certain registries.
126+
- *Credential Store*: A default fallback mechanism that securely stores and retrieves credentials (e.g., `desktop` for Docker Desktop).
127+
- *Static Credentials*: Credentials that are stored directly in the Docker configuration file under the `auths` section.
128+
====
129+
118130

119131

120132
[[build-image.customization]]

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildImageMojo.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,9 @@ private void buildImage() throws MojoExecutionException {
262262
Libraries libraries = getLibraries(Collections.emptySet());
263263
try {
264264
BuildRequest request = getBuildRequest(libraries);
265-
BuilderDockerConfiguration dockerConfiguration = (this.docker != null)
266-
? this.docker.asDockerConfiguration(request.isPublish())
267-
: new Docker().asDockerConfiguration(request.isPublish());
265+
Docker docker = (this.docker != null) ? this.docker : new Docker();
266+
BuilderDockerConfiguration dockerConfiguration = docker.asDockerConfiguration(getLog(),
267+
request.isPublish());
268268
Builder builder = new Builder(new MojoBuildLog(this::getLog), dockerConfiguration);
269269
builder.build(request);
270270
}

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/Docker.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.maven;
1818

19+
import org.apache.maven.plugin.logging.Log;
20+
1921
import org.springframework.boot.buildpack.platform.build.BuilderDockerConfiguration;
2022
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryAuthentication;
2123

@@ -141,15 +143,16 @@ void setPublishRegistry(DockerRegistry builderRegistry) {
141143
* Returns this configuration as a {@link BuilderDockerConfiguration} instance. This
142144
* method should only be called when the configuration is complete and will no longer
143145
* be changed.
146+
* @param log the output log
144147
* @param publish whether the image should be published
145148
* @return the Docker configuration
146149
*/
147-
BuilderDockerConfiguration asDockerConfiguration(boolean publish) {
150+
BuilderDockerConfiguration asDockerConfiguration(Log log, boolean publish) {
148151
BuilderDockerConfiguration dockerConfiguration = new BuilderDockerConfiguration();
149152
dockerConfiguration = customizeHost(dockerConfiguration);
150153
dockerConfiguration = dockerConfiguration.withBindHostToBuilder(this.bindHostToBuilder);
151-
dockerConfiguration = customizeBuilderAuthentication(dockerConfiguration);
152-
dockerConfiguration = customizePublishAuthentication(dockerConfiguration, publish);
154+
dockerConfiguration = customizeBuilderAuthentication(log, dockerConfiguration);
155+
dockerConfiguration = customizePublishAuthentication(log, dockerConfiguration, publish);
153156
return dockerConfiguration;
154157
}
155158

@@ -167,18 +170,23 @@ private BuilderDockerConfiguration customizeHost(BuilderDockerConfiguration dock
167170
return dockerConfiguration;
168171
}
169172

170-
private BuilderDockerConfiguration customizeBuilderAuthentication(BuilderDockerConfiguration dockerConfiguration) {
171-
return dockerConfiguration
172-
.withBuilderRegistryAuthentication(getRegistryAuthentication("builder", this.builderRegistry, null));
173+
private BuilderDockerConfiguration customizeBuilderAuthentication(Log log,
174+
BuilderDockerConfiguration dockerConfiguration) {
175+
DockerRegistryAuthentication authentication = DockerRegistryAuthentication.configuration(null,
176+
(message, ex) -> log.warn(message));
177+
return dockerConfiguration.withBuilderRegistryAuthentication(
178+
getRegistryAuthentication("builder", this.builderRegistry, authentication));
173179
}
174180

175-
private BuilderDockerConfiguration customizePublishAuthentication(BuilderDockerConfiguration dockerConfiguration,
176-
boolean publish) {
181+
private BuilderDockerConfiguration customizePublishAuthentication(Log log,
182+
BuilderDockerConfiguration dockerConfiguration, boolean publish) {
177183
if (!publish) {
178184
return dockerConfiguration;
179185
}
186+
DockerRegistryAuthentication authentication = DockerRegistryAuthentication
187+
.configuration(DockerRegistryAuthentication.EMPTY_USER, (message, ex) -> log.warn(message));
180188
return dockerConfiguration.withPublishRegistryAuthentication(
181-
getRegistryAuthentication("publish", this.publishRegistry, DockerRegistryAuthentication.EMPTY_USER));
189+
getRegistryAuthentication("publish", this.publishRegistry, authentication));
182190
}
183191

184192
private DockerRegistryAuthentication getRegistryAuthentication(String type, DockerRegistry registry,

spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/test/java/org/springframework/boot/maven/DockerTests.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import java.util.Base64;
2020

21+
import org.apache.maven.plugin.logging.Log;
22+
import org.apache.maven.plugin.logging.SystemStreamLog;
2123
import org.junit.jupiter.api.Test;
2224

2325
import org.springframework.boot.buildpack.platform.build.BuilderDockerConfiguration;
@@ -34,12 +36,14 @@
3436
*/
3537
class DockerTests {
3638

39+
private final Log log = new SystemStreamLog();
40+
3741
@Test
3842
void asDockerConfigurationWithDefaults() {
3943
Docker docker = new Docker();
4044
BuilderDockerConfiguration dockerConfiguration = createDockerConfiguration(docker);
4145
assertThat(dockerConfiguration.connection()).isNull();
42-
assertThat(dockerConfiguration.builderRegistryAuthentication()).isNull();
46+
assertThat(dockerConfiguration.builderRegistryAuthentication().getAuthHeader()).isNull();
4347
assertThat(decoded(dockerConfiguration.publishRegistryAuthentication().getAuthHeader()))
4448
.contains("\"username\" : \"\"")
4549
.contains("\"password\" : \"\"")
@@ -59,7 +63,7 @@ void asDockerConfigurationWithHostConfiguration() {
5963
assertThat(host.secure()).isTrue();
6064
assertThat(host.certificatePath()).isEqualTo("/tmp/ca-cert");
6165
assertThat(dockerConfiguration.bindHostToBuilder()).isFalse();
62-
assertThat(createDockerConfiguration(docker).builderRegistryAuthentication()).isNull();
66+
assertThat(createDockerConfiguration(docker).builderRegistryAuthentication().getAuthHeader()).isNull();
6367
assertThat(decoded(dockerConfiguration.publishRegistryAuthentication().getAuthHeader()))
6468
.contains("\"username\" : \"\"")
6569
.contains("\"password\" : \"\"")
@@ -76,7 +80,7 @@ void asDockerConfigurationWithContextConfiguration() {
7680
.connection();
7781
assertThat(context.context()).isEqualTo("test-context");
7882
assertThat(dockerConfiguration.bindHostToBuilder()).isFalse();
79-
assertThat(createDockerConfiguration(docker).builderRegistryAuthentication()).isNull();
83+
assertThat(createDockerConfiguration(docker).builderRegistryAuthentication().getAuthHeader()).isNull();
8084
assertThat(decoded(dockerConfiguration.publishRegistryAuthentication().getAuthHeader()))
8185
.contains("\"username\" : \"\"")
8286
.contains("\"password\" : \"\"")
@@ -106,7 +110,7 @@ void asDockerConfigurationWithBindHostToBuilder() {
106110
assertThat(host.secure()).isTrue();
107111
assertThat(host.certificatePath()).isEqualTo("/tmp/ca-cert");
108112
assertThat(dockerConfiguration.bindHostToBuilder()).isTrue();
109-
assertThat(createDockerConfiguration(docker).builderRegistryAuthentication()).isNull();
113+
assertThat(createDockerConfiguration(docker).builderRegistryAuthentication().getAuthHeader()).isNull();
110114
assertThat(decoded(dockerConfiguration.publishRegistryAuthentication().getAuthHeader()))
111115
.contains("\"username\" : \"\"")
112116
.contains("\"password\" : \"\"")
@@ -157,7 +161,7 @@ void asDockerConfigurationWithIncompletePublishUserAuthDoesNotFailIfPublishIsDis
157161
Docker docker = new Docker();
158162
docker.setPublishRegistry(
159163
new Docker.DockerRegistry("user", null, "https://docker.example.com", "[email protected]"));
160-
BuilderDockerConfiguration dockerConfiguration = docker.asDockerConfiguration(false);
164+
BuilderDockerConfiguration dockerConfiguration = docker.asDockerConfiguration(this.log, false);
161165
assertThat(dockerConfiguration.publishRegistryAuthentication()).isNull();
162166
}
163167

@@ -193,12 +197,12 @@ void asDockerConfigurationWithUserAndTokenAuthDoesNotFailIfPublishingIsDisabled(
193197
dockerRegistry.setToken("token");
194198
Docker docker = new Docker();
195199
docker.setPublishRegistry(dockerRegistry);
196-
BuilderDockerConfiguration dockerConfiguration = docker.asDockerConfiguration(false);
200+
BuilderDockerConfiguration dockerConfiguration = docker.asDockerConfiguration(this.log, false);
197201
assertThat(dockerConfiguration.publishRegistryAuthentication()).isNull();
198202
}
199203

200204
private BuilderDockerConfiguration createDockerConfiguration(Docker docker) {
201-
return docker.asDockerConfiguration(true);
205+
return docker.asDockerConfiguration(this.log, true);
202206
}
203207

204208
String decoded(String value) {

0 commit comments

Comments
 (0)