22
33import com .github .dockerjava .api .DockerClient ;
44import com .github .dockerjava .api .command .CreateContainerCmd ;
5- import com .github .dockerjava .api .command .InspectContainerResponse ;
65import com .github .dockerjava .api .exception .InternalServerErrorException ;
76import com .github .dockerjava .api .exception .NotFoundException ;
87import com .github .dockerjava .api .model .*;
98import com .github .dockerjava .core .command .ExecStartResultCallback ;
109import com .github .dockerjava .core .command .PullImageResultCallback ;
1110import com .google .common .annotations .VisibleForTesting ;
11+ import com .google .common .collect .ImmutableMap ;
1212import lombok .Synchronized ;
1313import lombok .extern .slf4j .Slf4j ;
14- import org .apache .commons .io .IOUtils ;
1514import org .hamcrest .BaseMatcher ;
1615import org .hamcrest .Description ;
17- import org .rnorth .ducttape .unreliables .Unreliables ;
1816import org .rnorth .visibleassertions .VisibleAssertions ;
19- import org .testcontainers .dockerclient .*;
17+ import org .testcontainers .dockerclient .DockerClientProviderStrategy ;
18+ import org .testcontainers .dockerclient .DockerMachineClientProviderStrategy ;
2019import org .testcontainers .utility .ComparableVersion ;
21- import org .testcontainers .utility .MountableFile ;
20+ import org .testcontainers .utility .ResourceReaper ;
2221import org .testcontainers .utility .TestcontainersConfiguration ;
2322
2423import java .io .ByteArrayOutputStream ;
25- import java .io .IOException ;
2624import java .io .InputStream ;
27- import java .net .Socket ;
28- import java .nio .charset .Charset ;
2925import java .util .ArrayList ;
3026import java .util .List ;
27+ import java .util .Map ;
3128import java .util .Optional ;
3229import java .util .ServiceLoader ;
33- import java .util .concurrent . TimeUnit ;
30+ import java .util .UUID ;
3431import java .util .function .BiFunction ;
3532import java .util .function .Consumer ;
3633
4239@ Slf4j
4340public class DockerClientFactory {
4441
42+ public static final String TESTCONTAINERS_LABEL = DockerClientFactory .class .getPackage ().getName ();
43+ public static final String TESTCONTAINERS_SESSION_ID_LABEL = TESTCONTAINERS_LABEL + ".sessionId" ;
44+
45+ public static final String SESSION_ID = UUID .randomUUID ().toString ();
46+
47+ public static final Map <String , String > DEFAULT_LABELS = ImmutableMap .of (
48+ TESTCONTAINERS_LABEL , "true" ,
49+ TESTCONTAINERS_SESSION_ID_LABEL , SESSION_ID
50+ );
51+
4552 private static final String TINY_IMAGE = TestcontainersConfiguration .getInstance ().getTinyImage ();
4653 private static DockerClientFactory instance ;
4754
4855 // Cached client configuration
4956 private DockerClientProviderStrategy strategy ;
50- private boolean preconditionsChecked = false ;
57+ private boolean initialized = false ;
5158 private String activeApiVersion ;
5259 private String activeExecutionDriver ;
5360
@@ -95,7 +102,7 @@ public DockerClient client() {
95102 log .info ("Docker host IP address is {}" , hostIpAddress );
96103 DockerClient client = strategy .getClient ();
97104
98- if (!preconditionsChecked ) {
105+ if (!initialized ) {
99106 Info dockerInfo = client .infoCmd ().exec ();
100107 Version version = client .versionCmd ().exec ();
101108 activeApiVersion = version .getApiVersion ();
@@ -106,30 +113,19 @@ public DockerClient client() {
106113 " Operating System: " + dockerInfo .getOperatingSystem () + "\n " +
107114 " Total Memory: " + dockerInfo .getMemTotal () / (1024 * 1024 ) + " MB" );
108115
109- if (!TestcontainersConfiguration .getInstance ().isDisableChecks ()) {
110- VisibleAssertions .info ("Checking the system..." );
111-
112- checkDockerVersion (version .getVersion ());
113-
114- MountableFile mountableFile = MountableFile .forClasspathResource (this .getClass ().getName ().replace ("." , "/" ) + ".class" );
116+ String ryukContainerId = ResourceReaper .start (hostIpAddress , client );
117+ log .info ("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit" );
115118
116- runInsideDocker (
117- client ,
118- cmd -> cmd
119- .withCmd ("/bin/sh" , "-c" , "while true; do printf 'hello' | nc -l -p 80; done" )
120- .withBinds (new Bind (mountableFile .getResolvedPath (), new Volume ("/dummy" ), AccessMode .ro ))
121- .withExposedPorts (new ExposedPort (80 ))
122- .withPublishAllPorts (true ),
123- (dockerClient , id ) -> {
119+ VisibleAssertions .info ("Checking the system..." );
124120
125- checkDiskSpace (dockerClient , id );
126- checkMountableFile (dockerClient , id );
127- checkExposedPort (hostIpAddress , dockerClient , id );
121+ checkDockerVersion (version .getVersion ());
128122
129- return null ;
130- });
123+ if (!TestcontainersConfiguration .getInstance ().isDisableChecks ()) {
124+ checkDiskSpace (client , ryukContainerId );
125+ checkMountableFile (client , ryukContainerId );
131126 }
132- preconditionsChecked = true ;
127+
128+ initialized = true ;
133129 }
134130
135131 return client ;
@@ -178,26 +174,10 @@ private void checkMountableFile(DockerClient dockerClient, String id) {
178174 }
179175 }
180176
181- private void checkExposedPort (String hostIpAddress , DockerClient dockerClient , String id ) {
182- String response = Unreliables .retryUntilSuccess (3 , TimeUnit .SECONDS , () -> {
183- InspectContainerResponse inspectedContainer = dockerClient .inspectContainerCmd (id ).exec ();
184-
185- String portSpec = inspectedContainer .getNetworkSettings ().getPorts ().getBindings ().values ().iterator ().next ()[0 ].getHostPortSpec ();
186-
187- try (Socket socket = new Socket (hostIpAddress , Integer .parseInt (portSpec ))) {
188- return IOUtils .toString (socket .getInputStream (), Charset .defaultCharset ());
189- } catch (IOException e ) {
190- return e .getMessage ();
191- }
192- });
193-
194- VisibleAssertions .assertEquals ("A port exposed by a docker container should be accessible" , "hello" , response );
195- }
196-
197177 /**
198178 * Check whether the image is available locally and pull it otherwise
199179 */
200- private void checkAndPullImage (DockerClient client , String image ) {
180+ public void checkAndPullImage (DockerClient client , String image ) {
201181 List <Image > images = client .listImagesCmd ().withImageNameFilter (image ).exec ();
202182 if (images .isEmpty ()) {
203183 client .pullImageCmd (image ).exec (new PullImageResultCallback ()).awaitSuccess ();
@@ -221,7 +201,8 @@ public <T> T runInsideDocker(Consumer<CreateContainerCmd> createContainerCmdCons
221201
222202 private <T > T runInsideDocker (DockerClient client , Consumer <CreateContainerCmd > createContainerCmdConsumer , BiFunction <DockerClient , String , T > block ) {
223203 checkAndPullImage (client , TINY_IMAGE );
224- CreateContainerCmd createContainerCmd = client .createContainerCmd (TINY_IMAGE );
204+ CreateContainerCmd createContainerCmd = client .createContainerCmd (TINY_IMAGE )
205+ .withLabels (DEFAULT_LABELS );
225206 createContainerCmdConsumer .accept (createContainerCmd );
226207 String id = createContainerCmd .exec ().getId ();
227208
@@ -263,7 +244,7 @@ DiskSpaceUsage parseAvailableDiskSpace(String dfOutput) {
263244 * @return the docker API version of the daemon that we have connected to
264245 */
265246 public String getActiveApiVersion () {
266- if (!preconditionsChecked ) {
247+ if (!initialized ) {
267248 client ();
268249 }
269250 return activeApiVersion ;
@@ -273,7 +254,7 @@ public String getActiveApiVersion() {
273254 * @return the docker execution driver of the daemon that we have connected to
274255 */
275256 public String getActiveExecutionDriver () {
276- if (!preconditionsChecked ) {
257+ if (!initialized ) {
277258 client ();
278259 }
279260 return activeExecutionDriver ;
0 commit comments