|
17 | 17 | package org.springframework.boot.launchscript;
|
18 | 18 |
|
19 | 19 | import java.io.File;
|
20 |
| -import java.io.FileInputStream; |
21 |
| -import java.io.InputStream; |
| 20 | +import java.time.Duration; |
22 | 21 | import java.util.ArrayList;
|
23 |
| -import java.util.Arrays; |
24 |
| -import java.util.Collections; |
25 |
| -import java.util.HashSet; |
26 | 22 | import java.util.List;
|
27 |
| -import java.util.Locale; |
28 |
| -import java.util.concurrent.TimeUnit; |
29 | 23 | import java.util.regex.Pattern;
|
30 | 24 |
|
31 |
| -import javax.ws.rs.client.Entity; |
32 |
| -import javax.ws.rs.client.WebTarget; |
33 |
| - |
34 |
| -import com.github.dockerjava.api.DockerClient; |
35 |
| -import com.github.dockerjava.api.command.DockerCmd; |
36 |
| -import com.github.dockerjava.api.exception.DockerClientException; |
37 |
| -import com.github.dockerjava.api.model.BuildResponseItem; |
38 |
| -import com.github.dockerjava.api.model.Frame; |
39 |
| -import com.github.dockerjava.core.DefaultDockerClientConfig; |
40 |
| -import com.github.dockerjava.core.DockerClientBuilder; |
41 |
| -import com.github.dockerjava.core.DockerClientConfig; |
42 |
| -import com.github.dockerjava.core.command.AttachContainerResultCallback; |
43 |
| -import com.github.dockerjava.core.command.BuildImageResultCallback; |
44 |
| -import com.github.dockerjava.core.command.WaitContainerResultCallback; |
45 |
| -import com.github.dockerjava.core.util.CompressArchiveUtil; |
46 |
| -import com.github.dockerjava.jaxrs.AbstrSyncDockerCmdExec; |
47 |
| -import com.github.dockerjava.jaxrs.JerseyDockerCmdExecFactory; |
48 | 25 | import org.assertj.core.api.Condition;
|
49 | 26 | import org.junit.jupiter.api.Assumptions;
|
50 | 27 | import org.junit.jupiter.params.ParameterizedTest;
|
51 | 28 | import org.junit.jupiter.params.provider.MethodSource;
|
| 29 | +import org.testcontainers.containers.GenericContainer; |
| 30 | +import org.testcontainers.containers.output.ToStringConsumer; |
| 31 | +import org.testcontainers.images.builder.ImageFromDockerfile; |
| 32 | +import org.testcontainers.utility.MountableFile; |
52 | 33 |
|
53 | 34 | import org.springframework.boot.ansi.AnsiColor;
|
54 | 35 |
|
|
63 | 44 | */
|
64 | 45 | class SysVinitLaunchScriptIT {
|
65 | 46 |
|
66 |
| - private final SpringBootDockerCmdExecFactory commandExecFactory = new SpringBootDockerCmdExecFactory(); |
67 |
| - |
68 | 47 | private static final char ESC = 27;
|
69 | 48 |
|
70 | 49 | @ParameterizedTest(name = "{0} {1}")
|
@@ -313,126 +292,15 @@ private void doLaunch(String os, String version, String script) throws Exception
|
313 | 292 | }
|
314 | 293 |
|
315 | 294 | private String doTest(String os, String version, String script) throws Exception {
|
316 |
| - DockerClient docker = createClient(); |
317 |
| - String imageId = buildImage(os, version, docker); |
318 |
| - String container = createContainer(docker, imageId, script); |
319 |
| - try { |
320 |
| - copyFilesToContainer(docker, container, script); |
321 |
| - docker.startContainerCmd(container).exec(); |
322 |
| - StringBuilder output = new StringBuilder(); |
323 |
| - AttachContainerResultCallback resultCallback = docker.attachContainerCmd(container).withStdOut(true) |
324 |
| - .withStdErr(true).withFollowStream(true).withLogs(true).exec(new AttachContainerResultCallback() { |
325 |
| - |
326 |
| - @Override |
327 |
| - public void onNext(Frame item) { |
328 |
| - output.append(new String(item.getPayload())); |
329 |
| - super.onNext(item); |
330 |
| - } |
331 |
| - |
332 |
| - }); |
333 |
| - resultCallback.awaitCompletion(60, TimeUnit.SECONDS); |
334 |
| - WaitContainerResultCallback waitContainerCallback = new WaitContainerResultCallback(); |
335 |
| - docker.waitContainerCmd(container).exec(waitContainerCallback); |
336 |
| - waitContainerCallback.awaitCompletion(60, TimeUnit.SECONDS); |
337 |
| - return output.toString(); |
338 |
| - } |
339 |
| - finally { |
340 |
| - try { |
341 |
| - docker.removeContainerCmd(container).exec(); |
342 |
| - } |
343 |
| - catch (Exception ex) { |
344 |
| - // Continue |
| 295 | + ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); |
| 296 | + try (LaunchScriptTestContainer container = new LaunchScriptTestContainer(os, version, script)) { |
| 297 | + container.withLogConsumer(consumer); |
| 298 | + container.start(); |
| 299 | + while (container.isRunning()) { |
| 300 | + Thread.sleep(100); |
345 | 301 | }
|
346 | 302 | }
|
347 |
| - } |
348 |
| - |
349 |
| - private DockerClient createClient() { |
350 |
| - DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().withApiVersion("1.19") |
351 |
| - .build(); |
352 |
| - return DockerClientBuilder.getInstance(config).withDockerCmdExecFactory(this.commandExecFactory).build(); |
353 |
| - } |
354 |
| - |
355 |
| - private String buildImage(String os, String version, DockerClient docker) { |
356 |
| - String dockerfile = "src/test/resources/conf/" + os + "/" + version + "/Dockerfile"; |
357 |
| - String tag = "spring-boot-it/" + os.toLowerCase(Locale.ENGLISH) + ":" + version; |
358 |
| - BuildImageResultCallback resultCallback = new BuildImageResultCallback() { |
359 |
| - |
360 |
| - private List<BuildResponseItem> items = new ArrayList<>(); |
361 |
| - |
362 |
| - @Override |
363 |
| - public void onNext(BuildResponseItem item) { |
364 |
| - super.onNext(item); |
365 |
| - this.items.add(item); |
366 |
| - } |
367 |
| - |
368 |
| - @Override |
369 |
| - public String awaitImageId() { |
370 |
| - try { |
371 |
| - awaitCompletion(); |
372 |
| - } |
373 |
| - catch (InterruptedException ex) { |
374 |
| - throw new DockerClientException("Interrupted while waiting for image id", ex); |
375 |
| - } |
376 |
| - return getImageId(); |
377 |
| - } |
378 |
| - |
379 |
| - @SuppressWarnings("deprecation") |
380 |
| - private String getImageId() { |
381 |
| - if (this.items.isEmpty()) { |
382 |
| - throw new DockerClientException("Could not build image"); |
383 |
| - } |
384 |
| - String imageId = extractImageId(); |
385 |
| - if (imageId == null) { |
386 |
| - throw new DockerClientException( |
387 |
| - "Could not build image: " + this.items.get(this.items.size() - 1).getError()); |
388 |
| - } |
389 |
| - return imageId; |
390 |
| - } |
391 |
| - |
392 |
| - private String extractImageId() { |
393 |
| - Collections.reverse(this.items); |
394 |
| - for (BuildResponseItem item : this.items) { |
395 |
| - if (item.isErrorIndicated() || item.getStream() == null) { |
396 |
| - return null; |
397 |
| - } |
398 |
| - if (item.getStream().contains("Successfully built")) { |
399 |
| - return item.getStream().replace("Successfully built", "").trim(); |
400 |
| - } |
401 |
| - } |
402 |
| - return null; |
403 |
| - } |
404 |
| - |
405 |
| - }; |
406 |
| - docker.buildImageCmd(new File(dockerfile)).withTags(new HashSet<>(Arrays.asList(tag))).exec(resultCallback); |
407 |
| - String imageId = resultCallback.awaitImageId(); |
408 |
| - return imageId; |
409 |
| - } |
410 |
| - |
411 |
| - private String createContainer(DockerClient docker, String imageId, String testScript) { |
412 |
| - return docker.createContainerCmd(imageId).withTty(false) |
413 |
| - .withCmd("/bin/bash", "-c", "chmod +x " + testScript + " && ./" + testScript).exec().getId(); |
414 |
| - } |
415 |
| - |
416 |
| - private void copyFilesToContainer(DockerClient docker, final String container, String script) { |
417 |
| - copyToContainer(docker, container, findApplication()); |
418 |
| - copyToContainer(docker, container, new File("src/test/resources/scripts/test-functions.sh")); |
419 |
| - copyToContainer(docker, container, new File("src/test/resources/scripts/" + script)); |
420 |
| - } |
421 |
| - |
422 |
| - private void copyToContainer(DockerClient docker, final String container, final File file) { |
423 |
| - this.commandExecFactory.createCopyToContainerCmdExec().exec(new CopyToContainerCmd(container, file)); |
424 |
| - } |
425 |
| - |
426 |
| - private File findApplication() { |
427 |
| - File targetDir = new File("target"); |
428 |
| - for (File file : targetDir.listFiles()) { |
429 |
| - if (file.getName().startsWith("spring-boot-launch-script-tests") && file.getName().endsWith(".jar") |
430 |
| - && !file.getName().endsWith("-sources.jar")) { |
431 |
| - return file; |
432 |
| - } |
433 |
| - } |
434 |
| - throw new IllegalStateException( |
435 |
| - "Could not find test application in target directory. Have you built it (mvn package)?"); |
| 303 | + return consumer.toUtf8String(); |
436 | 304 | }
|
437 | 305 |
|
438 | 306 | private Condition<String> coloredString(AnsiColor color, String string) {
|
@@ -460,59 +328,30 @@ private String extract(String label, String output) {
|
460 | 328 | throw new IllegalArgumentException("Failed to extract " + label + " from output: " + output);
|
461 | 329 | }
|
462 | 330 |
|
463 |
| - private static final class CopyToContainerCmdExec extends AbstrSyncDockerCmdExec<CopyToContainerCmd, Void> { |
| 331 | + private static final class LaunchScriptTestContainer extends GenericContainer<LaunchScriptTestContainer> { |
464 | 332 |
|
465 |
| - private CopyToContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { |
466 |
| - super(baseResource, dockerClientConfig); |
| 333 | + private LaunchScriptTestContainer(String os, String version, String testScript) { |
| 334 | + super(new ImageFromDockerfile("spring-boot-launch-script/" + os.toLowerCase() + "-" + version) |
| 335 | + .withFileFromFile("Dockerfile", |
| 336 | + new File("src/test/resources/conf/" + os + "/" + version + "/Dockerfile")) |
| 337 | + .withFileFromFile("spring-boot-launch-script-tests.jar", findApplication()) |
| 338 | + .withFileFromFile("test-functions.sh", new File("src/test/resources/scripts/test-functions.sh"))); |
| 339 | + withCopyFileToContainer(MountableFile.forHostPath("src/test/resources/scripts/" + testScript), |
| 340 | + "/" + testScript); |
| 341 | + withCommand("/bin/bash", "-c", "chmod +x " + testScript + " && ./" + testScript); |
| 342 | + withStartupTimeout(Duration.ofMinutes(5)); |
467 | 343 | }
|
468 | 344 |
|
469 |
| - @Override |
470 |
| - protected Void execute(CopyToContainerCmd command) { |
471 |
| - try (InputStream streamToUpload = new FileInputStream( |
472 |
| - CompressArchiveUtil.archiveTARFiles(command.getFile().getParentFile(), |
473 |
| - Arrays.asList(command.getFile()), command.getFile().getName()))) { |
474 |
| - WebTarget webResource = getBaseResource().path("/containers/{id}/archive").resolveTemplate("id", |
475 |
| - command.getContainer()); |
476 |
| - webResource.queryParam("path", ".").queryParam("noOverwriteDirNonDir", false).request() |
477 |
| - .put(Entity.entity(streamToUpload, "application/x-tar")).close(); |
478 |
| - return null; |
479 |
| - } |
480 |
| - catch (Exception ex) { |
481 |
| - throw new RuntimeException(ex); |
| 345 | + private static File findApplication() { |
| 346 | + File targetDir = new File("target"); |
| 347 | + for (File file : targetDir.listFiles()) { |
| 348 | + if (file.getName().startsWith("spring-boot-launch-script-tests") && file.getName().endsWith(".jar") |
| 349 | + && !file.getName().endsWith("-sources.jar")) { |
| 350 | + return file; |
| 351 | + } |
482 | 352 | }
|
483 |
| - } |
484 |
| - |
485 |
| - } |
486 |
| - |
487 |
| - private static final class CopyToContainerCmd implements DockerCmd<Void> { |
488 |
| - |
489 |
| - private final String container; |
490 |
| - |
491 |
| - private final File file; |
492 |
| - |
493 |
| - private CopyToContainerCmd(String container, File file) { |
494 |
| - this.container = container; |
495 |
| - this.file = file; |
496 |
| - } |
497 |
| - |
498 |
| - String getContainer() { |
499 |
| - return this.container; |
500 |
| - } |
501 |
| - |
502 |
| - File getFile() { |
503 |
| - return this.file; |
504 |
| - } |
505 |
| - |
506 |
| - @Override |
507 |
| - public void close() { |
508 |
| - } |
509 |
| - |
510 |
| - } |
511 |
| - |
512 |
| - private static final class SpringBootDockerCmdExecFactory extends JerseyDockerCmdExecFactory { |
513 |
| - |
514 |
| - private CopyToContainerCmdExec createCopyToContainerCmdExec() { |
515 |
| - return new CopyToContainerCmdExec(getBaseResource(), getDockerClientConfig()); |
| 353 | + throw new IllegalStateException( |
| 354 | + "Could not find test application in target directory. Have you built it (mvn package)?"); |
516 | 355 | }
|
517 | 356 |
|
518 | 357 | }
|
|
0 commit comments