Skip to content

Commit f75c73e

Browse files
committed
Use Testcontainers in the launch script integration tests
Closes gh-19366
1 parent 3e48e36 commit f75c73e

File tree

7 files changed

+47
-204
lines changed

7 files changed

+47
-204
lines changed

spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/pom.xml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,13 @@
3030
<artifactId>spring-boot-starter-undertow</artifactId>
3131
</dependency>
3232
<dependency>
33-
<groupId>com.github.docker-java</groupId>
34-
<artifactId>docker-java</artifactId>
35-
<version>3.1.2</version>
33+
<groupId>org.springframework.boot</groupId>
34+
<artifactId>spring-boot-starter-test</artifactId>
3635
<scope>test</scope>
3736
</dependency>
3837
<dependency>
39-
<groupId>org.springframework.boot</groupId>
40-
<artifactId>spring-boot-starter-test</artifactId>
38+
<groupId>org.testcontainers</groupId>
39+
<artifactId>testcontainers</artifactId>
4140
<scope>test</scope>
4241
</dependency>
4342
</dependencies>

spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/main/java/org/springframework/boot/launchscript/LaunchVerificationController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class LaunchVerificationController {
2424

2525
@RequestMapping("/")
2626
public String verifyLaunch() {
27-
return "Launched";
27+
return "Launched\n";
2828
}
2929

3030
}

spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/java/org/springframework/boot/launchscript/SysVinitLaunchScriptIT.java

Lines changed: 32 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -17,39 +17,20 @@
1717
package org.springframework.boot.launchscript;
1818

1919
import java.io.File;
20-
import java.io.FileInputStream;
21-
import java.io.InputStream;
20+
import java.time.Duration;
2221
import java.util.ArrayList;
23-
import java.util.Arrays;
24-
import java.util.Collections;
25-
import java.util.HashSet;
2622
import java.util.List;
27-
import java.util.Locale;
28-
import java.util.concurrent.TimeUnit;
2923
import java.util.regex.Pattern;
3024

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;
4825
import org.assertj.core.api.Condition;
4926
import org.junit.Test;
5027
import org.junit.runner.RunWith;
5128
import org.junit.runners.Parameterized;
5229
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;
5334

5435
import org.springframework.boot.ansi.AnsiColor;
5536

@@ -68,8 +49,6 @@
6849
@RunWith(Parameterized.class)
6950
public class SysVinitLaunchScriptIT {
7051

71-
private final SpringBootDockerCmdExecFactory commandExecFactory = new SpringBootDockerCmdExecFactory();
72-
7352
private static final char ESC = 27;
7453

7554
private final String os;
@@ -270,126 +249,15 @@ private void doLaunch(String script) throws Exception {
270249
}
271250

272251
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);
389258
}
390259
}
391-
throw new IllegalStateException(
392-
"Could not find test application in target directory. Have you built it (mvn package)?");
260+
return consumer.toUtf8String();
393261
}
394262

395263
private Condition<String> coloredString(AnsiColor color, String string) {
@@ -417,60 +285,30 @@ private String extract(String label, String output) {
417285
throw new IllegalArgumentException("Failed to extract " + label + " from output: " + output);
418286
}
419287

420-
private static final class CopyToContainerCmdExec extends AbstrSyncDockerCmdExec<CopyToContainerCmd, Void> {
288+
private static final class LaunchScriptTestContainer extends GenericContainer<LaunchScriptTestContainer> {
421289

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));
424300
}
425301

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+
}
439309
}
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)?");
474312
}
475313

476314
}

spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/CentOS/6.9-a23bced6/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ RUN yum install -y wget && \
77
https://cdn.azul.com/zulu/bin/zulu8.21.0.1-jdk8.0.131-linux.x86_64.rpm && \
88
yum --nogpg localinstall -y jdk.rpm && \
99
rm -f jdk.rpm
10+
ADD spring-boot-launch-script-tests.jar /spring-boot-launch-script-tests.jar
11+
ADD test-functions.sh /test-functions.sh

spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/Ubuntu/trusty-20160914/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ RUN apt-get update && \
66
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
77
ENV JAVA_HOME /opt/openjdk
88
ENV PATH $JAVA_HOME/bin:$PATH
9+
ADD spring-boot-launch-script-tests.jar /spring-boot-launch-script-tests.jar
10+
ADD test-functions.sh /test-functions.sh

spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/conf/Ubuntu/xenial-20160914/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ RUN apt-get update && \
66
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
77
ENV JAVA_HOME /opt/openjdk
88
ENV PATH $JAVA_HOME/bin:$PATH
9+
ADD spring-boot-launch-script-tests.jar /spring-boot-launch-script-tests.jar
10+
ADD test-functions.sh /test-functions.sh

spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/resources/scripts/test-functions.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
install_service() {
22
mkdir /test-service
3-
mv /spring-boot-launch-script-tests-*.jar /test-service/spring-boot-app.jar
3+
mv /spring-boot-launch-script-tests.jar /test-service/spring-boot-app.jar
44
chmod +x /test-service/spring-boot-app.jar
55
ln -s /test-service/spring-boot-app.jar /etc/init.d/spring-boot-app
66
}
77

88
install_double_link_service() {
99
mkdir /test-service
10-
mv /spring-boot-launch-script-tests-*.jar /test-service/
11-
chmod +x /test-service/spring-boot-launch-script-tests-*.jar
12-
ln -s /test-service/spring-boot-launch-script-tests-*.jar /test-service/spring-boot-app.jar
10+
mv /spring-boot-launch-script-tests.jar /test-service/
11+
chmod +x /test-service/spring-boot-launch-script-tests.jar
12+
ln -s /test-service/spring-boot-launch-script-tests.jar /test-service/spring-boot-app.jar
1313
ln -s /test-service/spring-boot-app.jar /etc/init.d/spring-boot-app
1414
}
1515

0 commit comments

Comments
 (0)