Skip to content

Commit cba5eb0

Browse files
authored
[grid] Improve browser container labels and naming in Dynamic Grid (#16599)
Signed-off-by: Viet Nguyen Duc <[email protected]>
1 parent fbb2a9c commit cba5eb0

File tree

6 files changed

+262
-22
lines changed

6 files changed

+262
-22
lines changed

java/src/org/openqa/selenium/docker/ContainerConfig.java

Lines changed: 163 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public class ContainerConfig {
4444
private final boolean autoRemove;
4545
private final long shmSize;
4646
private final Map<String, Object> hostConfig;
47+
private final Map<String, String> labels;
48+
private final String name;
4749

4850
public ContainerConfig(
4951
Image image,
@@ -61,6 +63,7 @@ public ContainerConfig(
6163
devices,
6264
networkName,
6365
shmSize,
66+
ImmutableMap.of(),
6467
ImmutableMap.of());
6568
}
6669

@@ -73,6 +76,52 @@ public ContainerConfig(
7376
String networkName,
7477
long shmSize,
7578
Map<String, Object> hostConfig) {
79+
this(
80+
image,
81+
portBindings,
82+
envVars,
83+
volumeBinds,
84+
devices,
85+
networkName,
86+
shmSize,
87+
hostConfig,
88+
ImmutableMap.of());
89+
}
90+
91+
public ContainerConfig(
92+
Image image,
93+
Multimap<String, Map<String, Object>> portBindings,
94+
Map<String, String> envVars,
95+
Map<String, String> volumeBinds,
96+
List<Device> devices,
97+
String networkName,
98+
long shmSize,
99+
Map<String, Object> hostConfig,
100+
Map<String, String> labels) {
101+
this(
102+
image,
103+
portBindings,
104+
envVars,
105+
volumeBinds,
106+
devices,
107+
networkName,
108+
shmSize,
109+
hostConfig,
110+
labels,
111+
null);
112+
}
113+
114+
public ContainerConfig(
115+
Image image,
116+
Multimap<String, Map<String, Object>> portBindings,
117+
Map<String, String> envVars,
118+
Map<String, String> volumeBinds,
119+
List<Device> devices,
120+
String networkName,
121+
long shmSize,
122+
Map<String, Object> hostConfig,
123+
Map<String, String> labels,
124+
String name) {
76125
this.image = image;
77126
this.portBindings = portBindings;
78127
this.envVars = envVars;
@@ -82,6 +131,12 @@ public ContainerConfig(
82131
this.autoRemove = true;
83132
this.shmSize = shmSize;
84133
this.hostConfig = hostConfig;
134+
this.labels = labels;
135+
this.name = name;
136+
}
137+
138+
public String getName() {
139+
return this.name;
85140
}
86141

87142
public Image getImage() {
@@ -114,40 +169,94 @@ public ContainerConfig map(Port containerPort, Port hostPort) {
114169
ImmutableMap.of("HostPort", String.valueOf(hostPort.getPort()), "HostIp", ""));
115170

116171
return new ContainerConfig(
117-
image, updatedBindings, envVars, volumeBinds, devices, networkName, shmSize);
172+
image,
173+
updatedBindings,
174+
envVars,
175+
volumeBinds,
176+
devices,
177+
networkName,
178+
shmSize,
179+
hostConfig,
180+
labels,
181+
name);
118182
}
119183

120184
public ContainerConfig env(Map<String, String> envVars) {
121185
Require.nonNull("Container env vars", envVars);
122186

123187
return new ContainerConfig(
124-
image, portBindings, envVars, volumeBinds, devices, networkName, shmSize);
188+
image,
189+
portBindings,
190+
envVars,
191+
volumeBinds,
192+
devices,
193+
networkName,
194+
shmSize,
195+
hostConfig,
196+
labels,
197+
name);
125198
}
126199

127200
public ContainerConfig bind(Map<String, String> volumeBinds) {
128201
Require.nonNull("Container volume binds", volumeBinds);
129202

130203
return new ContainerConfig(
131-
image, portBindings, envVars, volumeBinds, devices, networkName, shmSize);
204+
image,
205+
portBindings,
206+
envVars,
207+
volumeBinds,
208+
devices,
209+
networkName,
210+
shmSize,
211+
hostConfig,
212+
labels,
213+
name);
132214
}
133215

134216
public ContainerConfig network(String networkName) {
135217
Require.nonNull("Container network name", networkName);
136218

137219
return new ContainerConfig(
138-
image, portBindings, envVars, volumeBinds, devices, networkName, shmSize);
220+
image,
221+
portBindings,
222+
envVars,
223+
volumeBinds,
224+
devices,
225+
networkName,
226+
shmSize,
227+
hostConfig,
228+
labels,
229+
name);
139230
}
140231

141232
public ContainerConfig shmMemorySize(long shmSize) {
142233
return new ContainerConfig(
143-
image, portBindings, envVars, volumeBinds, devices, networkName, shmSize);
234+
image,
235+
portBindings,
236+
envVars,
237+
volumeBinds,
238+
devices,
239+
networkName,
240+
shmSize,
241+
hostConfig,
242+
labels,
243+
name);
144244
}
145245

146246
public ContainerConfig devices(List<Device> devices) {
147247
Require.nonNull("Container device files", devices);
148248

149249
return new ContainerConfig(
150-
image, portBindings, envVars, volumeBinds, devices, networkName, shmSize);
250+
image,
251+
portBindings,
252+
envVars,
253+
volumeBinds,
254+
devices,
255+
networkName,
256+
shmSize,
257+
hostConfig,
258+
labels,
259+
name);
151260
}
152261

153262
public ContainerConfig applyHostConfig(Map<String, Object> hostConfig, List<String> configKeys) {
@@ -158,7 +267,46 @@ public ContainerConfig applyHostConfig(Map<String, Object> hostConfig, List<Stri
158267
.collect(Collectors.toMap(key -> key, hostConfig::get));
159268

160269
return new ContainerConfig(
161-
image, portBindings, envVars, volumeBinds, devices, networkName, shmSize, setHostConfig);
270+
image,
271+
portBindings,
272+
envVars,
273+
volumeBinds,
274+
devices,
275+
networkName,
276+
shmSize,
277+
setHostConfig,
278+
labels,
279+
name);
280+
}
281+
282+
public ContainerConfig labels(Map<String, String> labels) {
283+
Require.nonNull("Container labels", labels);
284+
285+
return new ContainerConfig(
286+
image,
287+
portBindings,
288+
envVars,
289+
volumeBinds,
290+
devices,
291+
networkName,
292+
shmSize,
293+
hostConfig,
294+
labels,
295+
name);
296+
}
297+
298+
public ContainerConfig name(String name) {
299+
return new ContainerConfig(
300+
image,
301+
portBindings,
302+
envVars,
303+
volumeBinds,
304+
devices,
305+
networkName,
306+
shmSize,
307+
hostConfig,
308+
labels,
309+
name);
162310
}
163311

164312
@Override
@@ -221,9 +369,13 @@ private Map<String, Object> toJson() {
221369
hostConfig = ImmutableMap.copyOf(copyMap);
222370
}
223371

224-
return ImmutableMap.of(
225-
"Image", image.getId(),
226-
"Env", envVars,
227-
"HostConfig", hostConfig);
372+
Map<String, Object> config = new HashMap<>();
373+
config.put("Image", image.getId());
374+
config.put("Env", envVars);
375+
config.put("HostConfig", hostConfig);
376+
if (!labels.isEmpty()) {
377+
config.put("Labels", labels);
378+
}
379+
return config;
228380
}
229381
}

java/src/org/openqa/selenium/docker/ContainerInfo.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,30 @@ public class ContainerInfo {
3030
private final List<Map<String, Object>> mountedVolumes;
3131
private final String networkName;
3232
private Map<String, Object> hostConfig;
33+
private Map<String, String> labels;
3334

3435
public ContainerInfo(
3536
ContainerId id,
3637
String ip,
3738
List<Map<String, Object>> mountedVolumes,
3839
String networkName,
3940
Map<String, Object> hostConfig) {
41+
this(id, ip, mountedVolumes, networkName, hostConfig, Map.of());
42+
}
43+
44+
public ContainerInfo(
45+
ContainerId id,
46+
String ip,
47+
List<Map<String, Object>> mountedVolumes,
48+
String networkName,
49+
Map<String, Object> hostConfig,
50+
Map<String, String> labels) {
4051
this.ip = Require.nonNull("Container ip address", ip);
4152
this.id = Require.nonNull("Container id", id);
4253
this.mountedVolumes = Require.nonNull("Mounted volumes", mountedVolumes);
4354
this.networkName = Require.nonNull("Network name", networkName);
4455
this.hostConfig = Require.nonNull("Host config", hostConfig);
56+
this.labels = Require.nonNull("Labels", labels);
4557
}
4658

4759
public String getIp() {
@@ -64,6 +76,10 @@ public Map<String, Object> getHostConfig() {
6476
return this.hostConfig;
6577
}
6678

79+
public Map<String, String> getLabels() {
80+
return this.labels;
81+
}
82+
6783
@Override
6884
public String toString() {
6985
return "ContainerInfo{"

java/src/org/openqa/selenium/docker/client/CreateContainer.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
import static org.openqa.selenium.remote.http.Contents.asJson;
2323
import static org.openqa.selenium.remote.http.HttpMethod.POST;
2424

25+
import java.io.UnsupportedEncodingException;
26+
import java.net.URLEncoder;
27+
import java.nio.charset.StandardCharsets;
2528
import java.util.Collection;
2629
import java.util.Map;
2730
import java.util.logging.Logger;
@@ -62,10 +65,22 @@ public Container apply(ContainerConfig info) {
6265
Map<String, Object> requestJson = JSON.toType(JSON.toJson(info), MAP_TYPE);
6366
Map<String, Object> adaptedRequest = adapter.adaptContainerCreateRequest(requestJson);
6467

68+
// Build the URL with optional name parameter
69+
String url = String.format("/v%s/containers/create", apiVersion);
70+
if (info.getName() != null && !info.getName().trim().isEmpty()) {
71+
String containerName = info.getName().trim();
72+
try {
73+
String encodedName = URLEncoder.encode(containerName, StandardCharsets.UTF_8.toString());
74+
url += "?name=" + encodedName;
75+
} catch (UnsupportedEncodingException e) {
76+
throw new DockerException("Failed to encode container name: " + containerName, e);
77+
}
78+
}
79+
6580
HttpResponse res =
6681
DockerMessages.throwIfNecessary(
6782
client.execute(
68-
new HttpRequest(POST, String.format("/v%s/containers/create", apiVersion))
83+
new HttpRequest(POST, url)
6984
.addHeader("Content-Type", JSON_UTF_8)
7085
.setContent(asJson(adaptedRequest))),
7186
"Unable to create container: ",

java/src/org/openqa/selenium/docker/client/InspectContainer.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ public ContainerInfo apply(ContainerId id) {
8080
mounts.stream().map(mount -> (Map<String, Object>) mount).collect(Collectors.toList());
8181
Map<String, Object> hostConfig =
8282
(Map<String, Object>) rawInspectInfo.getOrDefault("HostConfig", Collections.emptyMap());
83+
Map<String, Object> config =
84+
(Map<String, Object>) rawInspectInfo.getOrDefault("Config", Collections.emptyMap());
85+
Map<String, String> labels =
86+
(Map<String, String>) config.getOrDefault("Labels", Collections.emptyMap());
8387

84-
return new ContainerInfo(id, ip, mountedVolumes, networkName, hostConfig);
88+
return new ContainerInfo(id, ip, mountedVolumes, networkName, hostConfig, labels);
8589
}
8690
}

java/src/org/openqa/selenium/grid/node/docker/DockerOptions.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.util.logging.Logger;
3939
import java.util.regex.Matcher;
4040
import java.util.regex.Pattern;
41+
import java.util.stream.Collectors;
4142
import org.openqa.selenium.Capabilities;
4243
import org.openqa.selenium.Platform;
4344
import org.openqa.selenium.docker.ContainerId;
@@ -173,6 +174,7 @@ public Map<Capabilities, Collection<SessionFactory>> getDockerSessionFactories(
173174
DockerAssetsPath assetsPath = getAssetsPath(info);
174175
String networkName = getDockerNetworkName(info);
175176
Map<String, Object> hostConfig = getDockerHostConfig(info);
177+
Map<String, String> composeLabels = getComposeLabels(info);
176178

177179
loadImages(docker, kinds.keySet().toArray(new String[0]));
178180
Image videoImage = getVideoImage(docker);
@@ -208,7 +210,8 @@ public Map<Capabilities, Collection<SessionFactory>> getDockerSessionFactories(
208210
info.isPresent(),
209211
capabilities -> options.getSlotMatcher().matches(caps, capabilities),
210212
hostConfig,
211-
hostConfigKeys));
213+
hostConfigKeys,
214+
composeLabels));
212215
}
213216
LOG.info(
214217
String.format(
@@ -257,6 +260,19 @@ private Map<String, Object> getDockerHostConfig(Optional<ContainerInfo> info) {
257260
return info.map(ContainerInfo::getHostConfig).orElse(Collections.emptyMap());
258261
}
259262

263+
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
264+
private Map<String, String> getComposeLabels(Optional<ContainerInfo> info) {
265+
if (info.isEmpty()) {
266+
return Collections.emptyMap();
267+
}
268+
269+
Map<String, String> allLabels = info.get().getLabels();
270+
// Filter for Docker Compose labels (com.docker.compose.*)
271+
return allLabels.entrySet().stream()
272+
.filter(entry -> entry.getKey().startsWith("com.docker.compose."))
273+
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
274+
}
275+
260276
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
261277
private DockerAssetsPath getAssetsPath(Optional<ContainerInfo> info) {
262278
if (info.isPresent()) {

0 commit comments

Comments
 (0)