From f75c73eb7aa370cc9bd023009718fc829aa54453 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 20 Dec 2019 13:14:23 +0000 Subject: [PATCH] Use Testcontainers in the launch script integration tests Closes gh-19366 --- .../spring-boot-launch-script-tests/pom.xml | 9 +- .../LaunchVerificationController.java | 2 +- .../launchscript/SysVinitLaunchScriptIT.java | 226 +++--------------- .../conf/CentOS/6.9-a23bced6/Dockerfile | 2 + .../conf/Ubuntu/trusty-20160914/Dockerfile | 2 + .../conf/Ubuntu/xenial-20160914/Dockerfile | 2 + .../test/resources/scripts/test-functions.sh | 8 +- 7 files changed, 47 insertions(+), 204 deletions(-) diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/pom.xml b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/pom.xml index 97302e35e89..c005f3dadc5 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/pom.xml +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/pom.xml @@ -30,14 +30,13 @@ spring-boot-starter-undertow - com.github.docker-java - docker-java - 3.1.2 + org.springframework.boot + spring-boot-starter-test test - org.springframework.boot - spring-boot-starter-test + org.testcontainers + testcontainers test diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/main/java/org/springframework/boot/launchscript/LaunchVerificationController.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/main/java/org/springframework/boot/launchscript/LaunchVerificationController.java index b3c2fa1dedc..f27016c2ef9 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/main/java/org/springframework/boot/launchscript/LaunchVerificationController.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/main/java/org/springframework/boot/launchscript/LaunchVerificationController.java @@ -24,7 +24,7 @@ public class LaunchVerificationController { @RequestMapping("/") public String verifyLaunch() { - return "Launched"; + return "Launched\n"; } } diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/java/org/springframework/boot/launchscript/SysVinitLaunchScriptIT.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/java/org/springframework/boot/launchscript/SysVinitLaunchScriptIT.java index 43200e19556..71f7bb96d9b 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/java/org/springframework/boot/launchscript/SysVinitLaunchScriptIT.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/java/org/springframework/boot/launchscript/SysVinitLaunchScriptIT.java @@ -17,39 +17,20 @@ package org.springframework.boot.launchscript; import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; +import java.time.Duration; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Locale; -import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.WebTarget; - -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.command.DockerCmd; -import com.github.dockerjava.api.exception.DockerClientException; -import com.github.dockerjava.api.model.BuildResponseItem; -import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.core.DefaultDockerClientConfig; -import com.github.dockerjava.core.DockerClientBuilder; -import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.core.command.AttachContainerResultCallback; -import com.github.dockerjava.core.command.BuildImageResultCallback; -import com.github.dockerjava.core.command.WaitContainerResultCallback; -import com.github.dockerjava.core.util.CompressArchiveUtil; -import com.github.dockerjava.jaxrs.AbstrSyncDockerCmdExec; -import com.github.dockerjava.jaxrs.JerseyDockerCmdExecFactory; import org.assertj.core.api.Condition; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.ToStringConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.utility.MountableFile; import org.springframework.boot.ansi.AnsiColor; @@ -68,8 +49,6 @@ import static org.junit.Assume.assumeThat; @RunWith(Parameterized.class) public class SysVinitLaunchScriptIT { - private final SpringBootDockerCmdExecFactory commandExecFactory = new SpringBootDockerCmdExecFactory(); - private static final char ESC = 27; private final String os; @@ -270,126 +249,15 @@ public class SysVinitLaunchScriptIT { } private String doTest(String script) throws Exception { - DockerClient docker = createClient(); - String imageId = buildImage(docker); - String container = createContainer(docker, imageId, script); - try { - copyFilesToContainer(docker, container, script); - docker.startContainerCmd(container).exec(); - StringBuilder output = new StringBuilder(); - AttachContainerResultCallback resultCallback = docker.attachContainerCmd(container).withStdOut(true) - .withStdErr(true).withFollowStream(true).withLogs(true).exec(new AttachContainerResultCallback() { - - @Override - public void onNext(Frame item) { - output.append(new String(item.getPayload())); - super.onNext(item); - } - - }); - resultCallback.awaitCompletion(60, TimeUnit.SECONDS); - WaitContainerResultCallback waitContainerCallback = new WaitContainerResultCallback(); - docker.waitContainerCmd(container).exec(waitContainerCallback); - waitContainerCallback.awaitCompletion(60, TimeUnit.SECONDS); - return output.toString(); - } - finally { - try { - docker.removeContainerCmd(container).exec(); - } - catch (Exception ex) { - // Continue + ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); + try (LaunchScriptTestContainer container = new LaunchScriptTestContainer(this.os, this.version, script)) { + container.withLogConsumer(consumer); + container.start(); + while (container.isRunning()) { + Thread.sleep(100); } } - } - - private DockerClient createClient() { - DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().withApiVersion("1.19") - .build(); - return DockerClientBuilder.getInstance(config).withDockerCmdExecFactory(this.commandExecFactory).build(); - } - - private String buildImage(DockerClient docker) { - String dockerfile = "src/test/resources/conf/" + this.os + "/" + this.version + "/Dockerfile"; - String tag = "spring-boot-it/" + this.os.toLowerCase(Locale.ENGLISH) + ":" + this.version; - BuildImageResultCallback resultCallback = new BuildImageResultCallback() { - - private List items = new ArrayList<>(); - - @Override - public void onNext(BuildResponseItem item) { - super.onNext(item); - this.items.add(item); - } - - @Override - public String awaitImageId() { - try { - awaitCompletion(); - } - catch (InterruptedException ex) { - throw new DockerClientException("Interrupted while waiting for image id", ex); - } - return getImageId(); - } - - @SuppressWarnings("deprecation") - private String getImageId() { - if (this.items.isEmpty()) { - throw new DockerClientException("Could not build image"); - } - String imageId = extractImageId(); - if (imageId == null) { - throw new DockerClientException( - "Could not build image: " + this.items.get(this.items.size() - 1).getError()); - } - return imageId; - } - - private String extractImageId() { - Collections.reverse(this.items); - for (BuildResponseItem item : this.items) { - if (item.isErrorIndicated() || item.getStream() == null) { - return null; - } - if (item.getStream().contains("Successfully built")) { - return item.getStream().replace("Successfully built", "").trim(); - } - } - return null; - } - - }; - docker.buildImageCmd(new File(dockerfile)).withTags(new HashSet<>(Arrays.asList(tag))).exec(resultCallback); - String imageId = resultCallback.awaitImageId(); - return imageId; - } - - private String createContainer(DockerClient docker, String imageId, String testScript) { - return docker.createContainerCmd(imageId).withTty(false) - .withCmd("/bin/bash", "-c", "chmod +x " + testScript + " && ./" + testScript).exec().getId(); - } - - private void copyFilesToContainer(DockerClient docker, final String container, String script) { - copyToContainer(docker, container, findApplication()); - copyToContainer(docker, container, new File("src/test/resources/scripts/test-functions.sh")); - copyToContainer(docker, container, new File("src/test/resources/scripts/" + script)); - } - - private void copyToContainer(DockerClient docker, final String container, final File file) { - this.commandExecFactory.createCopyToContainerCmdExec().exec(new CopyToContainerCmd(container, file)); - } - - private File findApplication() { - File targetDir = new File("target"); - for (File file : targetDir.listFiles()) { - if (file.getName().startsWith("spring-boot-launch-script-tests") && file.getName().endsWith(".jar") - && !file.getName().endsWith("-sources.jar")) { - return file; - } - } - throw new IllegalStateException( - "Could not find test application in target directory. Have you built it (mvn package)?"); + return consumer.toUtf8String(); } private Condition coloredString(AnsiColor color, String string) { @@ -417,60 +285,30 @@ public class SysVinitLaunchScriptIT { throw new IllegalArgumentException("Failed to extract " + label + " from output: " + output); } - private static final class CopyToContainerCmdExec extends AbstrSyncDockerCmdExec { + private static final class LaunchScriptTestContainer extends GenericContainer { - private CopyToContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { - super(baseResource, dockerClientConfig); + private LaunchScriptTestContainer(String os, String version, String testScript) { + super(new ImageFromDockerfile("spring-boot-launch-script/" + os.toLowerCase() + "-" + version) + .withFileFromFile("Dockerfile", + new File("src/test/resources/conf/" + os + "/" + version + "/Dockerfile")) + .withFileFromFile("spring-boot-launch-script-tests.jar", findApplication()) + .withFileFromFile("test-functions.sh", new File("src/test/resources/scripts/test-functions.sh"))); + withCopyFileToContainer(MountableFile.forHostPath("src/test/resources/scripts/" + testScript), + "/" + testScript); + withCommand("/bin/bash", "-c", "chmod +x " + testScript + " && ./" + testScript); + withStartupTimeout(Duration.ofMinutes(5)); } - @Override - protected Void execute(CopyToContainerCmd command) { - try (InputStream streamToUpload = new FileInputStream( - CompressArchiveUtil.archiveTARFiles(command.getFile().getParentFile(), - Arrays.asList(command.getFile()), command.getFile().getName()))) { - WebTarget webResource = getBaseResource().path("/containers/{id}/archive").resolveTemplate("id", - command.getContainer()); - webResource.queryParam("path", ".").queryParam("noOverwriteDirNonDir", false).request() - .put(Entity.entity(streamToUpload, "application/x-tar")).close(); - return null; + private static File findApplication() { + File targetDir = new File("target"); + for (File file : targetDir.listFiles()) { + if (file.getName().startsWith("spring-boot-launch-script-tests") && file.getName().endsWith(".jar") + && !file.getName().endsWith("-sources.jar")) { + return file; + } } - catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - } - - private static final class CopyToContainerCmd implements DockerCmd { - - private final String container; - - private final File file; - - private CopyToContainerCmd(String container, File file) { - this.container = container; - this.file = file; - } - - public String getContainer() { - return this.container; - } - - public File getFile() { - return this.file; - } - - @Override - public void close() { - - } - - } - - private static final class SpringBootDockerCmdExecFactory extends JerseyDockerCmdExecFactory { - - private CopyToContainerCmdExec createCopyToContainerCmdExec() { - return new CopyToContainerCmdExec(getBaseResource(), getDockerClientConfig()); + throw new IllegalStateException( + "Could not find test application in target directory. Have you built it (mvn package)?"); } } diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/CentOS/6.9-a23bced6/Dockerfile b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/CentOS/6.9-a23bced6/Dockerfile index f4856452b61..70513764d0f 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/CentOS/6.9-a23bced6/Dockerfile +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/CentOS/6.9-a23bced6/Dockerfile @@ -7,3 +7,5 @@ RUN yum install -y wget && \ https://cdn.azul.com/zulu/bin/zulu8.21.0.1-jdk8.0.131-linux.x86_64.rpm && \ yum --nogpg localinstall -y jdk.rpm && \ rm -f jdk.rpm +ADD spring-boot-launch-script-tests.jar /spring-boot-launch-script-tests.jar +ADD test-functions.sh /test-functions.sh \ No newline at end of file diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/Ubuntu/trusty-20160914/Dockerfile b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/Ubuntu/trusty-20160914/Dockerfile index 0e4cc372d52..cf95d7cacfa 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/Ubuntu/trusty-20160914/Dockerfile +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/Ubuntu/trusty-20160914/Dockerfile @@ -6,3 +6,5 @@ RUN apt-get update && \ curl -L https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u202-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u202b08.tar.gz | tar zx --strip-components=1 ENV JAVA_HOME /opt/openjdk ENV PATH $JAVA_HOME/bin:$PATH +ADD spring-boot-launch-script-tests.jar /spring-boot-launch-script-tests.jar +ADD test-functions.sh /test-functions.sh \ No newline at end of file diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/Ubuntu/xenial-20160914/Dockerfile b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/Ubuntu/xenial-20160914/Dockerfile index e8e84e0c478..d1a37468b5a 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/Ubuntu/xenial-20160914/Dockerfile +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/Ubuntu/xenial-20160914/Dockerfile @@ -6,3 +6,5 @@ RUN apt-get update && \ curl -L https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u202-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u202b08.tar.gz | tar zx --strip-components=1 ENV JAVA_HOME /opt/openjdk ENV PATH $JAVA_HOME/bin:$PATH +ADD spring-boot-launch-script-tests.jar /spring-boot-launch-script-tests.jar +ADD test-functions.sh /test-functions.sh \ No newline at end of file diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/scripts/test-functions.sh b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/scripts/test-functions.sh index 549031b229c..903713b888e 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/scripts/test-functions.sh +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/scripts/test-functions.sh @@ -1,15 +1,15 @@ install_service() { mkdir /test-service - mv /spring-boot-launch-script-tests-*.jar /test-service/spring-boot-app.jar + mv /spring-boot-launch-script-tests.jar /test-service/spring-boot-app.jar chmod +x /test-service/spring-boot-app.jar ln -s /test-service/spring-boot-app.jar /etc/init.d/spring-boot-app } install_double_link_service() { mkdir /test-service - mv /spring-boot-launch-script-tests-*.jar /test-service/ - chmod +x /test-service/spring-boot-launch-script-tests-*.jar - ln -s /test-service/spring-boot-launch-script-tests-*.jar /test-service/spring-boot-app.jar + mv /spring-boot-launch-script-tests.jar /test-service/ + chmod +x /test-service/spring-boot-launch-script-tests.jar + ln -s /test-service/spring-boot-launch-script-tests.jar /test-service/spring-boot-app.jar ln -s /test-service/spring-boot-app.jar /etc/init.d/spring-boot-app }