-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Container.java
506 lines (435 loc) · 17.5 KB
/
Container.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
package org.testcontainers.containers;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Info;
import lombok.NonNull;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.output.OutputFrame;
import org.testcontainers.containers.startupcheck.StartupCheckStrategy;
import org.testcontainers.containers.traits.LinkableContainer;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.utility.LogUtils;
import org.testcontainers.utility.MountableFile;
import java.io.IOException;
import java.nio.charset.Charset;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Function;
public interface Container<SELF extends Container<SELF>> extends LinkableContainer, ContainerState {
/**
* @return a reference to this container instance, cast to the expected generic type.
*/
@SuppressWarnings("unchecked")
default SELF self() {
return (SELF) this;
}
/**
* Class to hold results from a "docker exec" command. Note that, due to the limitations of the
* docker API, there's no easy way to get the result code from the process we ran.
*/
class ExecResult {
private final String stdout;
private final String stderr;
public ExecResult(String stdout, String stderr) {
this.stdout = stdout;
this.stderr = stderr;
}
public String getStdout() {
return stdout;
}
public String getStderr() {
return stderr;
}
}
/**
* Set the command that should be run in the container. Consider using {@link #withCommand(String)}
* for building a container in a fluent style.
*
* @param command a command in single string format (will automatically be split on spaces)
*/
void setCommand(@NonNull String command);
/**
* Set the command that should be run in the container. Consider using {@link #withCommand(String...)}
* for building a container in a fluent style.
*
* @param commandParts a command as an array of string parts
*/
void setCommand(@NonNull String... commandParts);
/**
* Add an environment variable to be passed to the container. Consider using {@link #withEnv(String, String)}
* for building a container in a fluent style.
*
* @param key environment variable key
* @param value environment variable value
*/
void addEnv(String key, String value);
/**
* Adds a file system binding. Consider using {@link #withFileSystemBind(String, String, BindMode)}
* for building a container in a fluent style.
*
* @param hostPath the file system path on the host
* @param containerPath the file system path inside the container
* @param mode the bind mode
*/
default void addFileSystemBind(final String hostPath, final String containerPath, final BindMode mode) {
addFileSystemBind(hostPath, containerPath, mode, SelinuxContext.NONE);
}
/**
* Adds a file system binding. Consider using {@link #withFileSystemBind(String, String, BindMode)}
* for building a container in a fluent style.
*
* @param hostPath the file system path on the host
* @param containerPath the file system path inside the container
* @param mode the bind mode
* @param selinuxContext selinux context argument to use for this file
*/
void addFileSystemBind(String hostPath, String containerPath, BindMode mode, SelinuxContext selinuxContext);
/**
* Add a link to another container.
*
* @param otherContainer the other container object to link to
* @param alias the alias (for the other container) that this container should be able to use
* @deprecated Links are deprecated (see <a href="https://github.com/testcontainers/testcontainers-java/issues/465">#465</a>). Please use {@link Network} features instead.
*/
@Deprecated
void addLink(LinkableContainer otherContainer, String alias);
/**
* Add an exposed port. Consider using {@link #withExposedPorts(Integer...)}
* for building a container in a fluent style.
*
* @param port a TCP port
*/
void addExposedPort(Integer port);
/**
* Add exposed ports. Consider using {@link #withExposedPorts(Integer...)}
* for building a container in a fluent style.
*
* @param ports an array of TCP ports
*/
void addExposedPorts(int... ports);
/**
* Specify the {@link WaitStrategy} to use to determine if the container is ready.
*
* @see org.testcontainers.containers.wait.strategy.Wait#defaultWaitStrategy()
* @param waitStrategy the WaitStrategy to use
* @return this
*/
SELF waitingFor(@NonNull WaitStrategy waitStrategy);
/**
* Adds a file system binding.
*
* @param hostPath the file system path on the host
* @param containerPath the file system path inside the container
* @return this
*/
default SELF withFileSystemBind(String hostPath, String containerPath) {
return withFileSystemBind(hostPath, containerPath, BindMode.READ_WRITE);
}
/**
* Adds a file system binding.
*
* @param hostPath the file system path on the host
* @param containerPath the file system path inside the container
* @param mode the bind mode
* @return this
*/
SELF withFileSystemBind(String hostPath, String containerPath, BindMode mode);
/**
* Adds container volumes.
*
* @param container the container to add volumes from
* @param mode the bind mode
* @return this
*/
SELF withVolumesFrom(Container container, BindMode mode);
/**
* Set the ports that this container listens on
*
* @param ports an array of TCP ports
* @return this
*/
SELF withExposedPorts(Integer... ports);
/**
* Set the file to be copied before starting a created container
*
* @param mountableFile a Mountable file with path of source file / folder on host machine
* @param containerPath a destination path on conatiner to which the files / folders to be copied
* @return this
*/
SELF withCopyFileToContainer(MountableFile mountableFile, String containerPath);
/**
* Add an environment variable to be passed to the container.
*
* @param key environment variable key
* @param value environment variable value
* @return this
*/
SELF withEnv(String key, String value);
/**
* Add an environment variable to be passed to the container.
*
* @param key environment variable key
* @param mapper environment variable value mapper, accepts old value as an argument
* @return this
*/
default SELF withEnv(String key, Function<Optional<String>, String> mapper) {
Optional<String> oldValue = Optional.ofNullable(getEnvMap().get(key));
return withEnv(key, mapper.apply(oldValue));
}
/**
* Add environment variables to be passed to the container.
*
* @param env map of environment variables
* @return this
*/
SELF withEnv(Map<String, String> env);
/**
* Add a label to the container.
*
* @param key label key
* @param value label value
* @return this
*/
SELF withLabel(String key, String value);
/**
* Add labels to the container.
* @param labels map of labels
* @return this
*/
SELF withLabels(Map<String, String> labels);
/**
* Set the command that should be run in the container
*
* @param cmd a command in single string format (will automatically be split on spaces)
* @return this
*/
SELF withCommand(String cmd);
/**
* Set the command that should be run in the container
*
* @param commandParts a command as an array of string parts
* @return this
*/
SELF withCommand(String... commandParts);
/**
* Add an extra host entry to be passed to the container
* @param hostname hostname to use for this hosts file entry
* @param ipAddress IP address to use for this hosts file entry
* @return this
*/
SELF withExtraHost(String hostname, String ipAddress);
/**
* Set the network mode for this container, similar to the <code>--net <name></code>
* option on the docker CLI.
*
* @param networkMode network mode, e.g. including 'host', 'bridge', 'none' or the name of an existing named network.
* @return this
*/
SELF withNetworkMode(String networkMode);
/**
* Set the network for this container, similar to the <code>--network <name></code>
* option on the docker CLI.
*
* @param network the instance of {@link Network}
* @return this
*/
SELF withNetwork(Network network);
/**
* Set the network aliases for this container, similar to the <code>--network-alias <my-service></code>
* option on the docker CLI.
*
* @param aliases the list of aliases
* @return this
*/
SELF withNetworkAliases(String... aliases);
/**
* Map a resource (file or directory) on the classpath to a path inside the container.
* This will only work if you are running your tests outside a Docker container.
*
* @param resourcePath path to the resource on the classpath (relative to the classpath root; should not start with a leading slash)
* @param containerPath path this should be mapped to inside the container
* @param mode access mode for the file
* @return this
*/
default SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode) {
withClasspathResourceMapping(resourcePath, containerPath, mode, SelinuxContext.NONE);
return self();
}
/**
* Map a resource (file or directory) on the classpath to a path inside the container.
* This will only work if you are running your tests outside a Docker container.
*
* @param resourcePath path to the resource on the classpath (relative to the classpath root; should not start with a leading slash)
* @param containerPath path this should be mapped to inside the container
* @param mode access mode for the file
* @param selinuxContext selinux context argument to use for this file
* @return this
*/
SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode, SelinuxContext selinuxContext);
/**
* Set the duration of waiting time until container treated as started.
* @see WaitStrategy#waitUntilReady(org.testcontainers.containers.wait.strategy.WaitStrategyTarget)
*
* @param startupTimeout timeout
* @return this
*/
SELF withStartupTimeout(Duration startupTimeout);
/**
* Set the privilegedMode mode for the container
* @param mode boolean
* @return this
*/
SELF withPrivilegedMode(boolean mode);
/**
* Only consider a container to have successfully started if it has been running for this duration. The default
* value is null; if that's the value, ignore this check.
*
* @param minimumRunningDuration duration this container should run for if started successfully
* @return this
*/
SELF withMinimumRunningDuration(Duration minimumRunningDuration);
/**
* Set the startup check strategy used for checking whether the container has started.
*
* @param strategy startup check strategy
*/
SELF withStartupCheckStrategy(StartupCheckStrategy strategy);
/**
* Set the working directory that the container should use on startup.
*
* @param workDir path to the working directory inside the container
*/
SELF withWorkingDirectory(String workDir);
/**
* <b>Resolve</b> Docker image and set it.
*
* @param dockerImageName image name
*/
void setDockerImageName(@NonNull String dockerImageName);
/**
* Get image name.
*
* @return image name
*/
@NonNull
String getDockerImageName();
/**
* Get the IP address that containers (e.g. browsers) can use to reference a service running on the local machine,
* i.e. the machine on which this test is running.
* <p>
* For example, if a web server is running on port 8080 on this local machine, the containerized web driver needs
* to be pointed at "http://" + getTestHostIpAddress() + ":8080" in order to access it. Trying to hit localhost
* from inside the container is not going to work, since the container has its own IP address.
*
* @return the IP address of the host machine
*/
String getTestHostIpAddress();
/**
* Follow container output, sending each frame (usually, line) to a consumer. Stdout and stderr will be followed.
*
* @param consumer consumer that the frames should be sent to
*/
default void followOutput(Consumer<OutputFrame> consumer) {
LogUtils.followOutput(DockerClientFactory.instance().client(), getContainerId(), consumer);
}
/**
* Follow container output, sending each frame (usually, line) to a consumer. This method allows Stdout and/or stderr
* to be selected.
*
* @param consumer consumer that the frames should be sent to
* @param types types that should be followed (one or both of STDOUT, STDERR)
*/
default void followOutput(Consumer<OutputFrame> consumer, OutputFrame.OutputType... types) {
LogUtils.followOutput(DockerClientFactory.instance().client(), getContainerId(), consumer, types);
}
/**
* Attach an output consumer at container startup, enabling stdout and stderr to be followed, waited on, etc.
* <p>
* More than one consumer may be registered.
*
* @param consumer consumer that output frames should be sent to
* @return this
*/
SELF withLogConsumer(Consumer<OutputFrame> consumer);
/**
*
* @deprecated please use {@code org.testcontainers.DockerClientFactory.instance().client().infoCmd().exec()}
*/
@Deprecated
Info fetchDockerDaemonInfo() throws IOException;
/**
* Run a command inside a running container, as though using "docker exec", and interpreting
* the output as UTF8.
* <p>
* @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, String...)
*/
ExecResult execInContainer(String... command)
throws UnsupportedOperationException, IOException, InterruptedException;
/**
* Run a command inside a running container, as though using "docker exec".
* <p>
* @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, Charset, String...)
*/
ExecResult execInContainer(Charset outputCharset, String... command)
throws UnsupportedOperationException, IOException, InterruptedException;
/**
*
* Copies a file which resides inside the classpath to the container.
*
* @param mountableLocalFile file which is copied into the container
* @param containerPath destination path inside the container
* @throws IOException if there's an issue communicating with Docker
* @throws InterruptedException if the thread waiting for the response is interrupted
*/
void copyFileToContainer(MountableFile mountableLocalFile, String containerPath) throws IOException, InterruptedException;
/**
* Copies a file which resides inside the container to user defined directory
*
* @param containerPath path to file which is copied from container
* @param destinationPath destination path to which file is copied with file name
* @throws IOException if there's an issue communicating with Docker or receiving entry from TarArchiveInputStream
* @throws InterruptedException if the thread waiting for the response is interrupted
*/
void copyFileFromContainer(String containerPath, String destinationPath) throws IOException, InterruptedException;
List<String> getPortBindings();
List<String> getExtraHosts();
Future<String> getImage();
/**
*
* @deprecated use getEnvMap
*/
@Deprecated
List<String> getEnv();
Map<String, String> getEnvMap();
String[] getCommandParts();
List<Bind> getBinds();
/**
* @deprecated Links are deprecated (see <a href="https://github.com/testcontainers/testcontainers-java/issues/465">#465</a>). Please use {@link Network} features instead.
*/
@Deprecated
Map<String, LinkableContainer> getLinkedContainers();
DockerClient getDockerClient();
/**
*
* @deprecated please use {@code org.testcontainers.DockerClientFactory.instance().client().infoCmd().exec()}
*/
@Deprecated
Info getDockerDaemonInfo();
void setExposedPorts(List<Integer> exposedPorts);
void setPortBindings(List<String> portBindings);
void setExtraHosts(List<String> extraHosts);
void setImage(Future<String> image);
void setEnv(List<String> env);
void setCommandParts(String[] commandParts);
void setBinds(List<Bind> binds);
/**
* @deprecated Links are deprecated (see <a href="https://github.com/testcontainers/testcontainers-java/issues/465">#465</a>). Please use {@link Network} features instead.
*/
@Deprecated
void setLinkedContainers(Map<String, LinkableContainer> linkedContainers);
void setWaitStrategy(WaitStrategy waitStrategy);
}