Skip to content

Commit e28915b

Browse files
committed
Make integration tests self-contained
Fixes spring-projectsgh-10516
1 parent 3955c28 commit e28915b

File tree

21 files changed

+409
-399
lines changed

21 files changed

+409
-399
lines changed

Diff for: ci/images/docker-lib.sh

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
sanitize_cgroups() {
2+
mkdir -p /sys/fs/cgroup
3+
mountpoint -q /sys/fs/cgroup || \
4+
mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
5+
6+
mount -o remount,rw /sys/fs/cgroup
7+
8+
sed -e 1d /proc/cgroups | while read sys hierarchy num enabled; do
9+
if [ "$enabled" != "1" ]; then
10+
# subsystem disabled; skip
11+
continue
12+
fi
13+
14+
grouping="$(cat /proc/self/cgroup | cut -d: -f2 | grep "\\<$sys\\>")"
15+
if [ -z "$grouping" ]; then
16+
# subsystem not mounted anywhere; mount it on its own
17+
grouping="$sys"
18+
fi
19+
20+
mountpoint="/sys/fs/cgroup/$grouping"
21+
22+
mkdir -p "$mountpoint"
23+
24+
# clear out existing mount to make sure new one is read-write
25+
if mountpoint -q "$mountpoint"; then
26+
umount "$mountpoint"
27+
fi
28+
29+
mount -n -t cgroup -o "$grouping" cgroup "$mountpoint"
30+
31+
if [ "$grouping" != "$sys" ]; then
32+
if [ -L "/sys/fs/cgroup/$sys" ]; then
33+
rm "/sys/fs/cgroup/$sys"
34+
fi
35+
36+
ln -s "$mountpoint" "/sys/fs/cgroup/$sys"
37+
fi
38+
done
39+
}
40+
41+
start_docker() {
42+
mkdir -p /var/log
43+
mkdir -p /var/run
44+
45+
sanitize_cgroups
46+
47+
# check for /proc/sys being mounted readonly, as systemd does
48+
if grep '/proc/sys\s\+\w\+\s\+ro,' /proc/mounts >/dev/null; then
49+
mount -o remount,rw /proc/sys
50+
fi
51+
52+
local server_args=""
53+
54+
for registry in $1; do
55+
server_args="${server_args} --insecure-registry ${registry}"
56+
done
57+
58+
if [ -n "$2" ]; then
59+
server_args="${server_args} --registry-mirror=$2"
60+
fi
61+
62+
if [ -n "$3" ]; then
63+
server_args="${server_args} -g=$3"
64+
fi
65+
66+
docker daemon --data-root /scratch/docker ${server_args} >/tmp/docker.log 2>&1 &
67+
echo $! > /tmp/docker.pid
68+
69+
trap stop_docker EXIT
70+
71+
sleep 1
72+
73+
until docker info >/dev/null 2>&1; do
74+
echo waiting for docker to come up...
75+
sleep 1
76+
done
77+
}
78+
79+
stop_docker() {
80+
local pid=$(cat /tmp/docker.pid)
81+
if [ -z "$pid" ]; then
82+
return 0
83+
fi
84+
85+
kill -TERM $pid
86+
wait $pid
87+
}

Diff for: ci/images/spring-boot-ci-image/Dockerfile

+26-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,29 @@ RUN apt-get update && \
55
apt-get install -y libxml2-utils && \
66
apt-get install -y jq
77

8-
ADD https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.1/concourse-java.sh /opt/
8+
ADD https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.1/concourse-java.sh /opt/
9+
10+
ENV DOCKER_VERSION=17.05.0-ce \
11+
ENTRYKIT_VERSION=0.4.0
12+
13+
RUN apt-get update && \
14+
apt-get install -y curl && \
15+
apt-get install -y libudev1 && \
16+
apt-get install -y iptables && \
17+
curl https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_VERSION}.tgz | tar zx && \
18+
mv /docker/* /bin/ && chmod +x /bin/docker*
19+
20+
# Install entrykit
21+
RUN curl -L https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz | tar zx && \
22+
chmod +x entrykit && \
23+
mv entrykit /bin/entrykit && \
24+
entrykit --symlink
25+
26+
COPY ../docker-lib.sh /docker-lib.sh
27+
28+
ENTRYPOINT [ \
29+
"switch", \
30+
"shell=/bin/sh", "--", \
31+
"codep", \
32+
"/bin/docker daemon" \
33+
]

Diff for: ci/images/spring-boot-jdk9-ci-image/Dockerfile

+26-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,29 @@ RUN apt-get update && \
55
apt-get install -y libxml2-utils && \
66
apt-get install -y jq
77

8-
ADD https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.1/concourse-java.sh /opt/
8+
ADD https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.1/concourse-java.sh /opt/
9+
10+
ENV DOCKER_VERSION=17.05.0-ce \
11+
ENTRYKIT_VERSION=0.4.0
12+
13+
RUN apt-get update && \
14+
apt-get install -y curl && \
15+
apt-get install -y libudev1 && \
16+
apt-get install -y iptables && \
17+
curl https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_VERSION}.tgz | tar zx && \
18+
mv /docker/* /bin/ && chmod +x /bin/docker*
19+
20+
# Install entrykit
21+
RUN curl -L https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz | tar zx && \
22+
chmod +x entrykit && \
23+
mv entrykit /bin/entrykit && \
24+
entrykit --symlink
25+
26+
COPY ../docker-lib.sh /docker-lib.sh
27+
28+
ENTRYPOINT [ \
29+
"switch", \
30+
"shell=/bin/sh", "--", \
31+
"codep", \
32+
"/bin/docker daemon" \
33+
]

Diff for: ci/pipeline.yml

+2
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ jobs:
8585
trigger: true
8686
- do:
8787
- task: build-project
88+
privileged: true
8889
timeout: 1h30m
8990
image: spring-boot-ci-image
9091
file: git-repo/ci/tasks/build-project.yml
@@ -170,6 +171,7 @@ jobs:
170171
trigger: true
171172
- do:
172173
- task: build-project
174+
privileged: true
173175
timeout: 1h30m
174176
image: spring-boot-jdk9-ci-image
175177
file: git-repo/ci/tasks/build-project.yml

Diff for: ci/tasks/build-project.yml

+8-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,11 @@ caches:
88
- path: maven
99
- path: gradle
1010
run:
11-
path: git-repo/ci/scripts/build-project.sh
11+
path: bash
12+
args:
13+
- -exc
14+
- |
15+
source /docker-lib.sh
16+
start_docker
17+
${PWD}/git-repo/ci/scripts/build-project.sh
18+

Diff for: spring-boot-project/spring-boot-autoconfigure/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -760,5 +760,10 @@
760760
<artifactId>tomcat-embed-jasper</artifactId>
761761
<scope>test</scope>
762762
</dependency>
763+
<dependency>
764+
<groupId>org.testcontainers</groupId>
765+
<artifactId>testcontainers</artifactId>
766+
<scope>test</scope>
767+
</dependency>
763768
</dependencies>
764769
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure;
18+
19+
import java.util.function.Supplier;
20+
21+
import org.junit.AssumptionViolatedException;
22+
import org.junit.rules.TestRule;
23+
import org.junit.runner.Description;
24+
import org.junit.runners.model.Statement;
25+
import org.testcontainers.DockerClientFactory;
26+
import org.testcontainers.containers.GenericContainer;
27+
28+
/**
29+
* {@link TestRule} for working with an optional Docker environment. Spins up a {@link GenericContainer}
30+
* if a valid docker environment is found.
31+
*
32+
* @author Madhura Bhave
33+
*/
34+
public class DockerTestContainer<T extends GenericContainer> implements TestRule {
35+
36+
private Supplier<T> containerSupplier;
37+
38+
public DockerTestContainer(Supplier<T> containerSupplier) {
39+
this.containerSupplier = containerSupplier;
40+
}
41+
42+
@Override
43+
public Statement apply(Statement base, Description description) {
44+
try {
45+
DockerClientFactory.instance().client();
46+
return this.containerSupplier.get().apply(base, description);
47+
}
48+
catch (Throwable t) {
49+
return new SkipStatement();
50+
}
51+
}
52+
53+
private static class SkipStatement extends Statement {
54+
55+
@Override
56+
public void evaluate() {
57+
throw new AssumptionViolatedException(
58+
"Could not find a valid Docker environment.");
59+
}
60+
61+
}
62+
}
63+

Diff for: spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java

+48-4
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,24 @@
1616

1717
package org.springframework.boot.autoconfigure.data.cassandra;
1818

19+
import java.util.concurrent.Callable;
20+
import java.util.concurrent.TimeUnit;
21+
import java.util.function.Supplier;
22+
23+
import com.datastax.driver.core.Cluster;
1924
import com.datastax.driver.core.Session;
25+
import com.datastax.driver.core.exceptions.NoHostAvailableException;
2026
import org.junit.After;
21-
import org.junit.Rule;
27+
import org.junit.ClassRule;
2228
import org.junit.Test;
29+
import org.rnorth.ducttape.TimeoutException;
30+
import org.rnorth.ducttape.unreliables.Unreliables;
31+
import org.testcontainers.containers.FixedHostPortGenericContainer;
32+
import org.testcontainers.containers.GenericContainer;
33+
import org.testcontainers.containers.wait.HostPortWaitStrategy;
2334

2435
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
36+
import org.springframework.boot.autoconfigure.DockerTestContainer;
2537
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
2638
import org.springframework.boot.autoconfigure.data.cassandra.city.City;
2739
import org.springframework.boot.test.util.TestPropertyValues;
@@ -39,8 +51,11 @@
3951
*/
4052
public class CassandraDataAutoConfigurationIntegrationTests {
4153

42-
@Rule
43-
public final CassandraTestServer cassandra = new CassandraTestServer();
54+
@ClassRule
55+
public static DockerTestContainer<GenericContainer> genericContainer = new DockerTestContainer<>((Supplier<GenericContainer>) () -> new FixedHostPortGenericContainer("cassandra:latest")
56+
.withFixedExposedPort(9042, 9042)
57+
.waitingFor(new ConnectionVerifyingWaitStrategy()));
58+
4459

4560
private AnnotationConfigApplicationContext context;
4661

@@ -84,10 +99,39 @@ public void hasRecreateSchemaActionSet() {
8499
}
85100

86101
private void createTestKeyspaceIfNotExists() {
87-
try (Session session = this.cassandra.getCluster().connect()) {
102+
Cluster cluster = Cluster.builder().addContactPoint("localhost").build();
103+
try (Session session = cluster.connect()) {
88104
session.execute("CREATE KEYSPACE IF NOT EXISTS boot_test"
89105
+ " WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };");
90106
}
91107
}
92108

109+
static class ConnectionVerifyingWaitStrategy extends HostPortWaitStrategy {
110+
111+
@Override
112+
protected void waitUntilReady() {
113+
super.waitUntilReady();
114+
Cluster cluster = Cluster.builder().addContactPoint("localhost").build();
115+
try {
116+
Unreliables.retryUntilTrue((int) startupTimeout.getSeconds(), TimeUnit.SECONDS,
117+
checkConnection(cluster));
118+
}
119+
catch (TimeoutException e) {
120+
throw new IllegalStateException();
121+
}
122+
}
123+
124+
private Callable<Boolean> checkConnection(Cluster cluster) {
125+
return () -> {
126+
try {
127+
cluster.connect();
128+
return true;
129+
}
130+
catch (NoHostAvailableException ex) {
131+
return false;
132+
}
133+
};
134+
}
135+
}
136+
93137
}

0 commit comments

Comments
 (0)