|
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.Test;
|
50 | 27 | import org.junit.runner.RunWith;
|
51 | 28 | import org.junit.runners.Parameterized;
|
52 | 29 | import org.junit.runners.Parameterized.Parameters;
|
| 30 | +import org.testcontainers.containers.GenericContainer; |
| 31 | +import org.testcontainers.containers.output.ToStringConsumer; |
| 32 | +import org.testcontainers.images.builder.ImageFromDockerfile; |
| 33 | +import org.testcontainers.utility.MountableFile; |
53 | 34 |
|
54 | 35 | import org.springframework.boot.ansi.AnsiColor;
|
55 | 36 |
|
|
68 | 49 | @RunWith(Parameterized.class)
|
69 | 50 | public class SysVinitLaunchScriptIT {
|
70 | 51 |
|
71 |
| - private final SpringBootDockerCmdExecFactory commandExecFactory = new SpringBootDockerCmdExecFactory(); |
72 |
| - |
73 | 52 | private static final char ESC = 27;
|
74 | 53 |
|
75 | 54 | private final String os;
|
@@ -270,126 +249,15 @@ private void doLaunch(String script) throws Exception {
|
270 | 249 | }
|
271 | 250 |
|
272 | 251 | private String doTest(String script) throws Exception {
|
273 |
| - DockerClient docker = createClient(); |
274 |
| - String imageId = buildImage(docker); |
275 |
| - String container = createContainer(docker, imageId, script); |
276 |
| - try { |
277 |
| - copyFilesToContainer(docker, container, script); |
278 |
| - docker.startContainerCmd(container).exec(); |
279 |
| - StringBuilder output = new StringBuilder(); |
280 |
| - AttachContainerResultCallback resultCallback = docker.attachContainerCmd(container).withStdOut(true) |
281 |
| - .withStdErr(true).withFollowStream(true).withLogs(true).exec(new AttachContainerResultCallback() { |
282 |
| - |
283 |
| - @Override |
284 |
| - public void onNext(Frame item) { |
285 |
| - output.append(new String(item.getPayload())); |
286 |
| - super.onNext(item); |
287 |
| - } |
288 |
| - |
289 |
| - }); |
290 |
| - resultCallback.awaitCompletion(60, TimeUnit.SECONDS); |
291 |
| - WaitContainerResultCallback waitContainerCallback = new WaitContainerResultCallback(); |
292 |
| - docker.waitContainerCmd(container).exec(waitContainerCallback); |
293 |
| - waitContainerCallback.awaitCompletion(60, TimeUnit.SECONDS); |
294 |
| - return output.toString(); |
295 |
| - } |
296 |
| - finally { |
297 |
| - try { |
298 |
| - docker.removeContainerCmd(container).exec(); |
299 |
| - } |
300 |
| - catch (Exception ex) { |
301 |
| - // Continue |
302 |
| - } |
303 |
| - } |
304 |
| - } |
305 |
| - |
306 |
| - private DockerClient createClient() { |
307 |
| - DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().withApiVersion("1.19") |
308 |
| - .build(); |
309 |
| - return DockerClientBuilder.getInstance(config).withDockerCmdExecFactory(this.commandExecFactory).build(); |
310 |
| - } |
311 |
| - |
312 |
| - private String buildImage(DockerClient docker) { |
313 |
| - String dockerfile = "src/test/resources/conf/" + this.os + "/" + this.version + "/Dockerfile"; |
314 |
| - String tag = "spring-boot-it/" + this.os.toLowerCase(Locale.ENGLISH) + ":" + this.version; |
315 |
| - BuildImageResultCallback resultCallback = new BuildImageResultCallback() { |
316 |
| - |
317 |
| - private List<BuildResponseItem> items = new ArrayList<>(); |
318 |
| - |
319 |
| - @Override |
320 |
| - public void onNext(BuildResponseItem item) { |
321 |
| - super.onNext(item); |
322 |
| - this.items.add(item); |
323 |
| - } |
324 |
| - |
325 |
| - @Override |
326 |
| - public String awaitImageId() { |
327 |
| - try { |
328 |
| - awaitCompletion(); |
329 |
| - } |
330 |
| - catch (InterruptedException ex) { |
331 |
| - throw new DockerClientException("Interrupted while waiting for image id", ex); |
332 |
| - } |
333 |
| - return getImageId(); |
334 |
| - } |
335 |
| - |
336 |
| - @SuppressWarnings("deprecation") |
337 |
| - private String getImageId() { |
338 |
| - if (this.items.isEmpty()) { |
339 |
| - throw new DockerClientException("Could not build image"); |
340 |
| - } |
341 |
| - String imageId = extractImageId(); |
342 |
| - if (imageId == null) { |
343 |
| - throw new DockerClientException( |
344 |
| - "Could not build image: " + this.items.get(this.items.size() - 1).getError()); |
345 |
| - } |
346 |
| - return imageId; |
347 |
| - } |
348 |
| - |
349 |
| - private String extractImageId() { |
350 |
| - Collections.reverse(this.items); |
351 |
| - for (BuildResponseItem item : this.items) { |
352 |
| - if (item.isErrorIndicated() || item.getStream() == null) { |
353 |
| - return null; |
354 |
| - } |
355 |
| - if (item.getStream().contains("Successfully built")) { |
356 |
| - return item.getStream().replace("Successfully built", "").trim(); |
357 |
| - } |
358 |
| - } |
359 |
| - return null; |
360 |
| - } |
361 |
| - |
362 |
| - }; |
363 |
| - docker.buildImageCmd(new File(dockerfile)).withTags(new HashSet<>(Arrays.asList(tag))).exec(resultCallback); |
364 |
| - String imageId = resultCallback.awaitImageId(); |
365 |
| - return imageId; |
366 |
| - } |
367 |
| - |
368 |
| - private String createContainer(DockerClient docker, String imageId, String testScript) { |
369 |
| - return docker.createContainerCmd(imageId).withTty(false) |
370 |
| - .withCmd("/bin/bash", "-c", "chmod +x " + testScript + " && ./" + testScript).exec().getId(); |
371 |
| - } |
372 |
| - |
373 |
| - private void copyFilesToContainer(DockerClient docker, final String container, String script) { |
374 |
| - copyToContainer(docker, container, findApplication()); |
375 |
| - copyToContainer(docker, container, new File("src/test/resources/scripts/test-functions.sh")); |
376 |
| - copyToContainer(docker, container, new File("src/test/resources/scripts/" + script)); |
377 |
| - } |
378 |
| - |
379 |
| - private void copyToContainer(DockerClient docker, final String container, final File file) { |
380 |
| - this.commandExecFactory.createCopyToContainerCmdExec().exec(new CopyToContainerCmd(container, file)); |
381 |
| - } |
382 |
| - |
383 |
| - private File findApplication() { |
384 |
| - File targetDir = new File("target"); |
385 |
| - for (File file : targetDir.listFiles()) { |
386 |
| - if (file.getName().startsWith("spring-boot-launch-script-tests") && file.getName().endsWith(".jar") |
387 |
| - && !file.getName().endsWith("-sources.jar")) { |
388 |
| - return file; |
| 252 | + ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); |
| 253 | + try (LaunchScriptTestContainer container = new LaunchScriptTestContainer(this.os, this.version, script)) { |
| 254 | + container.withLogConsumer(consumer); |
| 255 | + container.start(); |
| 256 | + while (container.isRunning()) { |
| 257 | + Thread.sleep(100); |
389 | 258 | }
|
390 | 259 | }
|
391 |
| - throw new IllegalStateException( |
392 |
| - "Could not find test application in target directory. Have you built it (mvn package)?"); |
| 260 | + return consumer.toUtf8String(); |
393 | 261 | }
|
394 | 262 |
|
395 | 263 | private Condition<String> coloredString(AnsiColor color, String string) {
|
@@ -417,60 +285,30 @@ private String extract(String label, String output) {
|
417 | 285 | throw new IllegalArgumentException("Failed to extract " + label + " from output: " + output);
|
418 | 286 | }
|
419 | 287 |
|
420 |
| - private static final class CopyToContainerCmdExec extends AbstrSyncDockerCmdExec<CopyToContainerCmd, Void> { |
| 288 | + private static final class LaunchScriptTestContainer extends GenericContainer<LaunchScriptTestContainer> { |
421 | 289 |
|
422 |
| - private CopyToContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { |
423 |
| - super(baseResource, dockerClientConfig); |
| 290 | + private LaunchScriptTestContainer(String os, String version, String testScript) { |
| 291 | + super(new ImageFromDockerfile("spring-boot-launch-script/" + os.toLowerCase() + "-" + version) |
| 292 | + .withFileFromFile("Dockerfile", |
| 293 | + new File("src/test/resources/conf/" + os + "/" + version + "/Dockerfile")) |
| 294 | + .withFileFromFile("spring-boot-launch-script-tests.jar", findApplication()) |
| 295 | + .withFileFromFile("test-functions.sh", new File("src/test/resources/scripts/test-functions.sh"))); |
| 296 | + withCopyFileToContainer(MountableFile.forHostPath("src/test/resources/scripts/" + testScript), |
| 297 | + "/" + testScript); |
| 298 | + withCommand("/bin/bash", "-c", "chmod +x " + testScript + " && ./" + testScript); |
| 299 | + withStartupTimeout(Duration.ofMinutes(5)); |
424 | 300 | }
|
425 | 301 |
|
426 |
| - @Override |
427 |
| - protected Void execute(CopyToContainerCmd command) { |
428 |
| - try (InputStream streamToUpload = new FileInputStream( |
429 |
| - CompressArchiveUtil.archiveTARFiles(command.getFile().getParentFile(), |
430 |
| - Arrays.asList(command.getFile()), command.getFile().getName()))) { |
431 |
| - WebTarget webResource = getBaseResource().path("/containers/{id}/archive").resolveTemplate("id", |
432 |
| - command.getContainer()); |
433 |
| - webResource.queryParam("path", ".").queryParam("noOverwriteDirNonDir", false).request() |
434 |
| - .put(Entity.entity(streamToUpload, "application/x-tar")).close(); |
435 |
| - return null; |
436 |
| - } |
437 |
| - catch (Exception ex) { |
438 |
| - throw new RuntimeException(ex); |
| 302 | + private static File findApplication() { |
| 303 | + File targetDir = new File("target"); |
| 304 | + for (File file : targetDir.listFiles()) { |
| 305 | + if (file.getName().startsWith("spring-boot-launch-script-tests") && file.getName().endsWith(".jar") |
| 306 | + && !file.getName().endsWith("-sources.jar")) { |
| 307 | + return file; |
| 308 | + } |
439 | 309 | }
|
440 |
| - } |
441 |
| - |
442 |
| - } |
443 |
| - |
444 |
| - private static final class CopyToContainerCmd implements DockerCmd<Void> { |
445 |
| - |
446 |
| - private final String container; |
447 |
| - |
448 |
| - private final File file; |
449 |
| - |
450 |
| - private CopyToContainerCmd(String container, File file) { |
451 |
| - this.container = container; |
452 |
| - this.file = file; |
453 |
| - } |
454 |
| - |
455 |
| - public String getContainer() { |
456 |
| - return this.container; |
457 |
| - } |
458 |
| - |
459 |
| - public File getFile() { |
460 |
| - return this.file; |
461 |
| - } |
462 |
| - |
463 |
| - @Override |
464 |
| - public void close() { |
465 |
| - |
466 |
| - } |
467 |
| - |
468 |
| - } |
469 |
| - |
470 |
| - private static final class SpringBootDockerCmdExecFactory extends JerseyDockerCmdExecFactory { |
471 |
| - |
472 |
| - private CopyToContainerCmdExec createCopyToContainerCmdExec() { |
473 |
| - return new CopyToContainerCmdExec(getBaseResource(), getDockerClientConfig()); |
| 310 | + throw new IllegalStateException( |
| 311 | + "Could not find test application in target directory. Have you built it (mvn package)?"); |
474 | 312 | }
|
475 | 313 |
|
476 | 314 | }
|
|
0 commit comments