Skip to content

Commit 8e6d03b

Browse files
Meijuhscottfrederick
authored andcommitted
Add network option for image building
This commit adds configuration to the Maven and Gradle plugins to allow specifying the network mode to be provided to the image building goal and task. See gh-27486
1 parent e737388 commit 8e6d03b

File tree

17 files changed

+195
-15
lines changed

17 files changed

+195
-15
lines changed

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

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* @author Phillip Webb
3737
* @author Scott Frederick
3838
* @author Andrey Shlykov
39+
* @author Jeroen Meijer
3940
* @since 2.3.0
4041
*/
4142
public class BuildRequest {
@@ -68,6 +69,8 @@ public class BuildRequest {
6869

6970
private final List<Binding> bindings;
7071

72+
private final String network;
73+
7174
BuildRequest(ImageReference name, Function<Owner, TarArchive> applicationContent) {
7275
Assert.notNull(name, "Name must not be null");
7376
Assert.notNull(applicationContent, "ApplicationContent must not be null");
@@ -83,12 +86,13 @@ public class BuildRequest {
8386
this.creator = Creator.withVersion("");
8487
this.buildpacks = Collections.emptyList();
8588
this.bindings = Collections.emptyList();
89+
this.network = null;
8690
}
8791

8892
BuildRequest(ImageReference name, Function<Owner, TarArchive> applicationContent, ImageReference builder,
8993
ImageReference runImage, Creator creator, Map<String, String> env, boolean cleanCache,
9094
boolean verboseLogging, PullPolicy pullPolicy, boolean publish, List<BuildpackReference> buildpacks,
91-
List<Binding> bindings) {
95+
List<Binding> bindings, String network) {
9296
this.name = name;
9397
this.applicationContent = applicationContent;
9498
this.builder = builder;
@@ -101,6 +105,7 @@ public class BuildRequest {
101105
this.publish = publish;
102106
this.buildpacks = buildpacks;
103107
this.bindings = bindings;
108+
this.network = network;
104109
}
105110

106111
/**
@@ -112,7 +117,7 @@ public BuildRequest withBuilder(ImageReference builder) {
112117
Assert.notNull(builder, "Builder must not be null");
113118
return new BuildRequest(this.name, this.applicationContent, builder.inTaggedOrDigestForm(), this.runImage,
114119
this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish,
115-
this.buildpacks, this.bindings);
120+
this.buildpacks, this.bindings, this.network);
116121
}
117122

118123
/**
@@ -123,7 +128,7 @@ public BuildRequest withBuilder(ImageReference builder) {
123128
public BuildRequest withRunImage(ImageReference runImageName) {
124129
return new BuildRequest(this.name, this.applicationContent, this.builder, runImageName.inTaggedOrDigestForm(),
125130
this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish,
126-
this.buildpacks, this.bindings);
131+
this.buildpacks, this.bindings, this.network);
127132
}
128133

129134
/**
@@ -134,7 +139,8 @@ public BuildRequest withRunImage(ImageReference runImageName) {
134139
public BuildRequest withCreator(Creator creator) {
135140
Assert.notNull(creator, "Creator must not be null");
136141
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, creator, this.env,
137-
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings);
142+
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
143+
this.network);
138144
}
139145

140146
/**
@@ -150,7 +156,7 @@ public BuildRequest withEnv(String name, String value) {
150156
env.put(name, value);
151157
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator,
152158
Collections.unmodifiableMap(env), this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish,
153-
this.buildpacks, this.bindings);
159+
this.buildpacks, this.bindings, this.network);
154160
}
155161

156162
/**
@@ -164,7 +170,7 @@ public BuildRequest withEnv(Map<String, String> env) {
164170
updatedEnv.putAll(env);
165171
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator,
166172
Collections.unmodifiableMap(updatedEnv), this.cleanCache, this.verboseLogging, this.pullPolicy,
167-
this.publish, this.buildpacks, this.bindings);
173+
this.publish, this.buildpacks, this.bindings, this.network);
168174
}
169175

170176
/**
@@ -174,7 +180,8 @@ public BuildRequest withEnv(Map<String, String> env) {
174180
*/
175181
public BuildRequest withCleanCache(boolean cleanCache) {
176182
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
177-
cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings);
183+
cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
184+
this.network);
178185
}
179186

180187
/**
@@ -184,7 +191,8 @@ public BuildRequest withCleanCache(boolean cleanCache) {
184191
*/
185192
public BuildRequest withVerboseLogging(boolean verboseLogging) {
186193
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
187-
this.cleanCache, verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings);
194+
this.cleanCache, verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
195+
this.network);
188196
}
189197

190198
/**
@@ -194,7 +202,8 @@ public BuildRequest withVerboseLogging(boolean verboseLogging) {
194202
*/
195203
public BuildRequest withPullPolicy(PullPolicy pullPolicy) {
196204
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
197-
this.cleanCache, this.verboseLogging, pullPolicy, this.publish, this.buildpacks, this.bindings);
205+
this.cleanCache, this.verboseLogging, pullPolicy, this.publish, this.buildpacks, this.bindings,
206+
this.network);
198207
}
199208

200209
/**
@@ -204,7 +213,8 @@ public BuildRequest withPullPolicy(PullPolicy pullPolicy) {
204213
*/
205214
public BuildRequest withPublish(boolean publish) {
206215
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
207-
this.cleanCache, this.verboseLogging, this.pullPolicy, publish, this.buildpacks, this.bindings);
216+
this.cleanCache, this.verboseLogging, this.pullPolicy, publish, this.buildpacks, this.bindings,
217+
this.network);
208218
}
209219

210220
/**
@@ -227,7 +237,8 @@ public BuildRequest withBuildpacks(BuildpackReference... buildpacks) {
227237
public BuildRequest withBuildpacks(List<BuildpackReference> buildpacks) {
228238
Assert.notNull(buildpacks, "Buildpacks must not be null");
229239
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
230-
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, buildpacks, this.bindings);
240+
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, buildpacks, this.bindings,
241+
this.network);
231242
}
232243

233244
/**
@@ -250,7 +261,14 @@ public BuildRequest withBindings(Binding... bindings) {
250261
public BuildRequest withBindings(List<Binding> bindings) {
251262
Assert.notNull(bindings, "Bindings must not be null");
252263
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
253-
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, bindings);
264+
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, bindings,
265+
this.network);
266+
}
267+
268+
public BuildRequest withNetwork(String network) {
269+
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
270+
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
271+
network);
254272
}
255273

256274
/**
@@ -353,6 +371,10 @@ public List<Binding> getBindings() {
353371
return this.bindings;
354372
}
355373

374+
public String getNetwork() {
375+
return this.network;
376+
}
377+
356378
/**
357379
* Factory method to create a new {@link BuildRequest} from a JAR file.
358380
* @param jarFile the source jar file

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
*
3939
* @author Phillip Webb
4040
* @author Scott Frederick
41+
* @author Jeroen Meijer
4142
*/
4243
class Lifecycle implements Closeable {
4344

@@ -147,6 +148,7 @@ private Phase createPhase() {
147148
this.request.getBindings().forEach(phase::withBinding);
148149
}
149150
phase.withEnv(PLATFORM_API_VERSION_KEY, this.platformVersion.toString());
151+
phase.withNetworkMode(this.request.getNetwork());
150152
return phase;
151153
}
152154

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
*
3232
* @author Phillip Webb
3333
* @author Scott Frederick
34+
* @author Jeroen Meijer
3435
*/
3536
class Phase {
3637

@@ -48,6 +49,8 @@ class Phase {
4849

4950
private final Map<String, String> env = new LinkedHashMap<>();
5051

52+
private String networkMode;
53+
5154
/**
5255
* Create a new {@link Phase} instance.
5356
* @param name the name of the phase
@@ -101,6 +104,10 @@ void withEnv(String name, String value) {
101104
this.env.put(name, value);
102105
}
103106

107+
void withNetworkMode(String networkMode) {
108+
this.networkMode = networkMode;
109+
}
110+
104111
/**
105112
* Return the name of the phase.
106113
* @return the phase name
@@ -127,6 +134,7 @@ void apply(ContainerConfig.Update update) {
127134
update.withLabel("author", "spring-boot");
128135
this.bindings.forEach(update::withBinding);
129136
this.env.forEach(update::withEnv);
137+
update.withNetworkMode(this.networkMode);
130138
}
131139

132140
}

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,15 @@
4040
*
4141
* @author Phillip Webb
4242
* @author Scott Frederick
43+
* @author Jeroen Meijer
4344
* @since 2.3.0
4445
*/
4546
public class ContainerConfig {
4647

4748
private final String json;
4849

4950
ContainerConfig(String user, ImageReference image, String command, List<String> args, Map<String, String> labels,
50-
List<Binding> bindings, Map<String, String> env) throws IOException {
51+
List<Binding> bindings, Map<String, String> env, String networkMode) throws IOException {
5152
Assert.notNull(image, "Image must not be null");
5253
Assert.hasText(command, "Command must not be empty");
5354
ObjectMapper objectMapper = SharedObjectMapper.get();
@@ -64,6 +65,9 @@ public class ContainerConfig {
6465
ObjectNode labelsNode = node.putObject("Labels");
6566
labels.forEach(labelsNode::put);
6667
ObjectNode hostConfigNode = node.putObject("HostConfig");
68+
if (networkMode != null) {
69+
hostConfigNode.put("NetworkMode", networkMode);
70+
}
6771
ArrayNode bindsNode = hostConfigNode.putArray("Binds");
6872
bindings.forEach((binding) -> bindsNode.add(binding.toString()));
6973
this.json = objectMapper.writeValueAsString(node);
@@ -114,6 +118,8 @@ public static class Update {
114118

115119
private final Map<String, String> env = new LinkedHashMap<>();
116120

121+
private String networkMode;
122+
117123
Update(ImageReference image) {
118124
this.image = image;
119125
}
@@ -122,7 +128,7 @@ private ContainerConfig run(Consumer<Update> update) {
122128
update.accept(this);
123129
try {
124130
return new ContainerConfig(this.user, this.image, this.command, this.args, this.labels, this.bindings,
125-
this.env);
131+
this.env, this.networkMode);
126132
}
127133
catch (IOException ex) {
128134
throw new IllegalStateException(ex);
@@ -182,6 +188,10 @@ public void withEnv(String name, String value) {
182188
this.env.put(name, value);
183189
}
184190

191+
public void withNetworkMode(String networkMode) {
192+
this.networkMode = networkMode;
193+
}
194+
185195
}
186196

187197
}

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/BuildRequestTests.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
*
4646
* @author Phillip Webb
4747
* @author Scott Frederick
48+
* @author Jeroen Meijer
4849
*/
4950
public class BuildRequestTests {
5051

@@ -199,6 +200,12 @@ void withBindingsWhenBindingsIsNullThrowsException() throws IOException {
199200
.withMessage("Bindings must not be null");
200201
}
201202

203+
@Test
204+
void withNetworkUpdatesNetwork() throws IOException {
205+
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar")).withNetwork("test");
206+
assertThat(request.getNetwork()).isEqualTo("test");
207+
}
208+
202209
private void hasExpectedJarContent(TarArchive archive) {
203210
try {
204211
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/LifecycleTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
*
6363
* @author Phillip Webb
6464
* @author Scott Frederick
65+
* @author Jeroen Meijer
6566
*/
6667
class LifecycleTests {
6768

@@ -188,6 +189,17 @@ void closeClearsVolumes() throws Exception {
188189
verify(this.docker.volume()).delete(VolumeName.of("pack-app-aaaaaaaaaa"), true);
189190
}
190191

192+
@Test
193+
void executeWithNetworkExecutesPhases() throws Exception {
194+
given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId());
195+
given(this.docker.container().create(any(), any())).willAnswer(answerWithGeneratedContainerId());
196+
given(this.docker.container().wait(any())).willReturn(ContainerStatus.of(0, null));
197+
BuildRequest request = getTestRequest().withNetwork("test");
198+
createLifecycle(request).execute();
199+
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-network.json"));
200+
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
201+
}
202+
191203
private DockerApi mockDockerApi() {
192204
DockerApi docker = mock(DockerApi.class);
193205
ImageApi imageApi = mock(ImageApi.class);

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/PhaseTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
*
3333
* @author Phillip Webb
3434
* @author Scott Frederick
35+
* @author Jeroen Meijer
3536
*/
3637
class PhaseTests {
3738

@@ -56,6 +57,7 @@ void applyUpdatesConfiguration() {
5657
phase.apply(update);
5758
verify(update).withCommand("/cnb/lifecycle/test", NO_ARGS);
5859
verify(update).withLabel("author", "spring-boot");
60+
verify(update).withNetworkMode(null);
5961
verifyNoMoreInteractions(update);
6062
}
6163

@@ -69,6 +71,7 @@ void applyWhenWithDaemonAccessUpdatesConfigurationWithRootUserAndDomainSocketBin
6971
verify(update).withBinding(Binding.from("/var/run/docker.sock", "/var/run/docker.sock"));
7072
verify(update).withCommand("/cnb/lifecycle/test", NO_ARGS);
7173
verify(update).withLabel("author", "spring-boot");
74+
verify(update).withNetworkMode(null);
7275
verifyNoMoreInteractions(update);
7376
}
7477

@@ -80,6 +83,7 @@ void applyWhenWithLogLevelArgAndVerboseLoggingUpdatesConfigurationWithLogLevel()
8083
phase.apply(update);
8184
verify(update).withCommand("/cnb/lifecycle/test", "-log-level", "debug");
8285
verify(update).withLabel("author", "spring-boot");
86+
verify(update).withNetworkMode(null);
8387
verifyNoMoreInteractions(update);
8488
}
8589

@@ -91,6 +95,7 @@ void applyWhenWithLogLevelArgAndNonVerboseLoggingDoesNotUpdateLogLevel() {
9195
phase.apply(update);
9296
verify(update).withCommand("/cnb/lifecycle/test");
9397
verify(update).withLabel("author", "spring-boot");
98+
verify(update).withNetworkMode(null);
9499
verifyNoMoreInteractions(update);
95100
}
96101

@@ -102,6 +107,7 @@ void applyWhenWithArgsUpdatesConfigurationWithArguments() {
102107
phase.apply(update);
103108
verify(update).withCommand("/cnb/lifecycle/test", "a", "b", "c");
104109
verify(update).withLabel("author", "spring-boot");
110+
verify(update).withNetworkMode(null);
105111
verifyNoMoreInteractions(update);
106112
}
107113

@@ -115,6 +121,7 @@ void applyWhenWithBindsUpdatesConfigurationWithBinds() {
115121
verify(update).withCommand("/cnb/lifecycle/test");
116122
verify(update).withLabel("author", "spring-boot");
117123
verify(update).withBinding(Binding.from(volumeName, "/test"));
124+
verify(update).withNetworkMode(null);
118125
verifyNoMoreInteractions(update);
119126
}
120127

@@ -129,6 +136,19 @@ void applyWhenWithEnvUpdatesConfigurationWithEnv() {
129136
verify(update).withLabel("author", "spring-boot");
130137
verify(update).withEnv("name1", "value1");
131138
verify(update).withEnv("name2", "value2");
139+
verify(update).withNetworkMode(null);
140+
verifyNoMoreInteractions(update);
141+
}
142+
143+
@Test
144+
void applyWhenWithNetworkModeUpdatesConfigurationWithNetworkMode() {
145+
Phase phase = new Phase("test", true);
146+
phase.withNetworkMode("test");
147+
Update update = mock(Update.class);
148+
phase.apply(update);
149+
verify(update).withCommand("/cnb/lifecycle/test");
150+
verify(update).withNetworkMode("test");
151+
verify(update).withLabel("author", "spring-boot");
132152
verifyNoMoreInteractions(update);
133153
}
134154

0 commit comments

Comments
 (0)