Skip to content

Commit fd8678c

Browse files
authored
Merge branch 'main' into add_timeplus_container
2 parents ece9945 + 7989b20 commit fd8678c

File tree

29 files changed

+462
-85
lines changed

29 files changed

+462
-85
lines changed

.github/workflows/moby-latest.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Tests against recent Docker engine releases
2+
3+
on:
4+
workflow_dispatch:
5+
schedule:
6+
# nightly build, at 23:59 CEST
7+
- cron: '59 23 * * *'
8+
9+
jobs:
10+
test_docker:
11+
strategy:
12+
matrix:
13+
install-docker-type: ["STABLE", "ROOTLESS", "ROOTFUL"]
14+
name: "Core tests using Docker ${{ matrix.install-docker-type }}"
15+
runs-on: ubuntu-22.04
16+
continue-on-error: true
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: ./.github/actions/setup-build
20+
21+
- name: Install Stable Docker
22+
if: ${{ matrix.install-docker-type == 'STABLE' }}
23+
run: curl https://get.docker.com
24+
25+
- name: Install Docker from the TEST channel
26+
if: ${{ matrix.install-docker-type == 'ROOTFUL' }}
27+
run: curl https://get.docker.com | CHANNEL=test sh
28+
29+
- name: Setup rootless Docker
30+
if: ${{ matrix.install-docker-type == 'ROOTLESS' }}
31+
uses: ScribeMD/rootless-docker@6bd157a512c2fafa4e0243a8aa87d964eb890886 # v0.2.2
32+
33+
- name: Remove Docker root socket
34+
if: ${{ matrix.install-docker-type == 'ROOTLESS' }}
35+
run: sudo rm -rf /var/run/docker.sock
36+
37+
- name: Check Docker version
38+
run: docker version
39+
40+
- name: Build with Gradle
41+
run: ./gradlew cleanTest --no-daemon --continue --scan -Dscan.tag.DOCKER_${{ matrix.install-docker-type }} testcontainers:test -Dorg.gradle.caching=false
42+
- uses: ./.github/actions/setup-junit-report
43+
44+
- name: Notify to Slack on failures
45+
if: failure()
46+
id: slack
47+
uses: slackapi/[email protected]
48+
with:
49+
payload: |
50+
{
51+
"tc_project": "testcontainers-java",
52+
"tc_docker_install_type": "${{ matrix.install-docker-type }}",
53+
"tc_github_action_url": "https://github.com/testcontainers/testcontainers-java/actions/runs/${{ env.GITHUB_RUN_ID }}/job/${{ env.GITHUB_RUN_NUMBER }}",
54+
"tc_github_action_status": "FAILED",
55+
"tc_slack_channel_id": "${{ secrets.SLACK_DOCKER_LATEST_CHANNEL_ID }}"
56+
}
57+
env:
58+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_DOCKER_LATEST_WEBHOOK }}

core/src/main/java/org/testcontainers/DockerClientFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ private DockerClientProviderStrategy getOrInitializeStrategy() {
145145
if (strategy != null) {
146146
return strategy;
147147
}
148-
148+
log.info("Testcontainers version: {}", DEFAULT_LABELS.get(TESTCONTAINERS_VERSION_LABEL));
149149
List<DockerClientProviderStrategy> configurationStrategies = new ArrayList<>();
150150
ServiceLoader.load(DockerClientProviderStrategy.class).forEach(configurationStrategies::add);
151151

core/src/main/java/org/testcontainers/containers/GenericContainer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ protected WaitStrategy getWaitStrategy() {
893893

894894
@Override
895895
public void setWaitStrategy(WaitStrategy waitStrategy) {
896-
this.containerDef.setWaitStrategy(waitStrategy);
896+
this.waitStrategy = waitStrategy;
897897
}
898898

899899
/**

core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
import lombok.extern.slf4j.Slf4j;
88
import org.apache.commons.io.FileUtils;
99
import org.testcontainers.images.ParsedDockerfile;
10+
import org.yaml.snakeyaml.DumperOptions;
1011
import org.yaml.snakeyaml.LoaderOptions;
1112
import org.yaml.snakeyaml.Yaml;
1213
import org.yaml.snakeyaml.constructor.SafeConstructor;
14+
import org.yaml.snakeyaml.representer.Representer;
15+
import org.yaml.snakeyaml.resolver.Resolver;
1316

1417
import java.io.File;
1518
import java.io.FileInputStream;
@@ -34,10 +37,20 @@ class ParsedDockerComposeFile {
3437
private final File composeFile;
3538

3639
@Getter
37-
private Map<String, Set<String>> serviceNameToImageNames = new HashMap<>();
40+
private final Map<String, Set<String>> serviceNameToImageNames = new HashMap<>();
3841

3942
ParsedDockerComposeFile(File composeFile) {
40-
Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
43+
// The default is 50 and a big docker-compose.yml file can easily go above that number. 1,000 should give us some room
44+
LoaderOptions options = new LoaderOptions();
45+
options.setMaxAliasesForCollections(1_000);
46+
DumperOptions dumperOptions = new DumperOptions();
47+
Yaml yaml = new Yaml(
48+
new SafeConstructor(options),
49+
new Representer(dumperOptions),
50+
dumperOptions,
51+
options,
52+
new Resolver()
53+
);
4154
try (FileInputStream fileInputStream = FileUtils.openInputStream(composeFile)) {
4255
composeFileContent = yaml.load(fileInputStream);
4356
} catch (Exception e) {
@@ -82,7 +95,9 @@ private void parseAndValidate() {
8295
return;
8396
}
8497

85-
servicesMap = (Map<String, ?>) servicesElement;
98+
@SuppressWarnings("unchecked")
99+
Map<String, ?> temp = (Map<String, ?>) servicesElement;
100+
servicesMap = temp;
86101
} else {
87102
servicesMap = composeFileContent;
88103
}
@@ -99,15 +114,16 @@ private void parseAndValidate() {
99114
break;
100115
}
101116

102-
final Map serviceDefinitionMap = (Map) serviceDefinition;
117+
@SuppressWarnings("unchecked")
118+
final Map<String, ?> serviceDefinitionMap = (Map<String, ?>) serviceDefinition;
103119

104120
validateNoContainerNameSpecified(serviceName, serviceDefinitionMap);
105121
findServiceImageName(serviceName, serviceDefinitionMap);
106122
findImageNamesInDockerfile(serviceName, serviceDefinitionMap);
107123
}
108124
}
109125

110-
private void validateNoContainerNameSpecified(String serviceName, Map serviceDefinitionMap) {
126+
private void validateNoContainerNameSpecified(String serviceName, Map<String, ?> serviceDefinitionMap) {
111127
if (serviceDefinitionMap.containsKey("container_name")) {
112128
throw new IllegalStateException(
113129
String.format(
@@ -119,20 +135,21 @@ private void validateNoContainerNameSpecified(String serviceName, Map serviceDef
119135
}
120136
}
121137

122-
private void findServiceImageName(String serviceName, Map serviceDefinitionMap) {
123-
if (serviceDefinitionMap.containsKey("image") && serviceDefinitionMap.get("image") instanceof String) {
124-
final String imageName = (String) serviceDefinitionMap.get("image");
138+
private void findServiceImageName(String serviceName, Map<String, ?> serviceDefinitionMap) {
139+
Object result = serviceDefinitionMap.get("image");
140+
if (result instanceof String) {
141+
final String imageName = (String) result;
125142
log.debug("Resolved dependency image for Docker Compose in {}: {}", composeFileName, imageName);
126143
serviceNameToImageNames.put(serviceName, Sets.newHashSet(imageName));
127144
}
128145
}
129146

130-
private void findImageNamesInDockerfile(String serviceName, Map serviceDefinitionMap) {
147+
private void findImageNamesInDockerfile(String serviceName, Map<String, ?> serviceDefinitionMap) {
131148
final Object buildNode = serviceDefinitionMap.get("build");
132149
Path dockerfilePath = null;
133150

134151
if (buildNode instanceof Map) {
135-
final Map buildElement = (Map) buildNode;
152+
final Map<?, ?> buildElement = (Map<?, ?>) buildNode;
136153
final Object dockerfileRelativePath = buildElement.get("dockerfile");
137154
final Object contextRelativePath = buildElement.get("context");
138155
if (dockerfileRelativePath instanceof String && contextRelativePath instanceof String) {

core/src/test/java/org/testcontainers/containers/GenericContainerTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import org.testcontainers.TestImages;
2121
import org.testcontainers.containers.startupcheck.StartupCheckStrategy;
2222
import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy;
23+
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
24+
import org.testcontainers.containers.wait.strategy.Wait;
2325
import org.testcontainers.images.RemoteDockerImage;
2426
import org.testcontainers.images.builder.ImageFromDockerfile;
2527
import org.testcontainers.images.builder.Transferable;
@@ -256,6 +258,21 @@ public void shouldContainDefaultNetworkAliasWhenUsingContainerDef() {
256258
}
257259
}
258260

261+
@Test
262+
public void shouldRespectWaitStrategy() {
263+
try (
264+
HelloWorldLogStrategyContainer container = new HelloWorldLogStrategyContainer(
265+
"testcontainers/helloworld:1.1.0"
266+
)
267+
) {
268+
container.setWaitStrategy(Wait.forLogMessage(".*Starting server on port.*", 1));
269+
container.start();
270+
assertThat((LogMessageWaitStrategy) container.getWaitStrategy())
271+
.extracting("regEx", "times")
272+
.containsExactly(".*Starting server on port.*", 1);
273+
}
274+
}
275+
259276
static class NoopStartupCheckStrategy extends StartupCheckStrategy {
260277

261278
@Override
@@ -319,4 +336,13 @@ class HelloWorldContainerDef extends ContainerDef {
319336
}
320337
}
321338
}
339+
340+
static class HelloWorldLogStrategyContainer extends GenericContainer<HelloWorldContainer> {
341+
342+
public HelloWorldLogStrategyContainer(String image) {
343+
super(DockerImageName.parse(image));
344+
withExposedPorts(8080);
345+
waitingFor(Wait.forLogMessage(".*Starting server on port.*", 2));
346+
}
347+
}
322348
}

core/src/test/java/org/testcontainers/containers/JibTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.testcontainers.containers;
22

33
import com.github.dockerjava.api.command.InspectImageResponse;
4+
import org.junit.Ignore;
45
import org.junit.Test;
56
import org.testcontainers.DockerClientFactory;
67
import org.testcontainers.containers.output.OutputFrame.OutputType;
@@ -12,6 +13,7 @@
1213

1314
import static org.assertj.core.api.Assertions.assertThat;
1415

16+
@Ignore("Jib doesn't work with latest Docker version provided by GH Actions. Jib should be updated")
1517
public class JibTest {
1618

1719
@Test

core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,24 @@
33
import com.google.common.collect.ImmutableMap;
44
import com.google.common.collect.Sets;
55
import lombok.SneakyThrows;
6+
import org.junit.Rule;
67
import org.junit.Test;
8+
import org.junit.rules.TemporaryFolder;
79

810
import java.io.File;
11+
import java.io.PrintWriter;
912
import java.util.Collections;
1013

1114
import static org.assertj.core.api.Assertions.assertThat;
15+
import static org.assertj.core.api.Assertions.assertThatNoException;
1216
import static org.assertj.core.api.Assertions.assertThatThrownBy;
1317
import static org.assertj.core.api.Assertions.entry;
1418

1519
public class ParsedDockerComposeFileValidationTest {
1620

21+
@Rule
22+
public TemporaryFolder temporaryFolder = new TemporaryFolder();
23+
1724
@Test
1825
public void shouldValidate() {
1926
File file = new File("src/test/resources/docker-compose-container-name-v1.yml");
@@ -129,4 +136,22 @@ public void shouldObtainImageFromDockerfileBuildWithContext() {
129136
entry("custom", Sets.newHashSet("alpine:3.17"))
130137
); // redis, mysql from compose file, alpine:3.17 from Dockerfile build
131138
}
139+
140+
@Test
141+
public void shouldSupportALotOfAliases() throws Exception {
142+
File file = temporaryFolder.newFile();
143+
try (PrintWriter writer = new PrintWriter(file)) {
144+
writer.println("x-entry: &entry");
145+
writer.println(" key: value");
146+
writer.println();
147+
writer.println("services:");
148+
for (int i = 0; i < 1_000; i++) {
149+
writer.println(" service" + i + ":");
150+
writer.println(" image: busybox");
151+
writer.println(" environment:");
152+
writer.println(" <<: *entry");
153+
}
154+
}
155+
assertThatNoException().isThrownBy(() -> new ParsedDockerComposeFile(file));
156+
}
132157
}

core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ public static String[] parameters() {
1919
"alpine:3.17",
2020
"alpine", // omitting the tag should work and default to latest
2121
"alpine@sha256:1775bebec23e1f3ce486989bfc9ff3c4e951690df84aa9f926497d82f2ffca9d",
22-
"quay.io/testcontainers/ryuk:latest",
23-
"quay.io/testcontainers/ryuk:0.2.3",
24-
"quay.io/testcontainers/ryuk@sha256:bb5a635cac4bd96c93cc476969ce11dc56436238ec7cd028d0524462f4739dd9",
22+
"docker.io/testcontainers/ryuk:latest",
23+
"docker.io/testcontainers/ryuk:0.7.0",
24+
"docker.io/testcontainers/ryuk@sha256:bcbee39cd601396958ba1bd06ea14ad64ce0ea709de29a427d741d1f5262080a",
2525
// "ibmcom/db2express-c", // Big image for testing with slow networks
2626
};
2727
}

docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ public class DependsOnTest {
1313
public GenericContainer<?> redis = new GenericContainer<>("redis:6-alpine").withExposedPorts(6379);
1414

1515
@Rule
16-
public GenericContainer<?> nginx = new GenericContainer<>("nginx:1.9.4").dependsOn(redis).withExposedPorts(80);
16+
public GenericContainer<?> nginx = new GenericContainer<>("nginx:1.27.0-alpine3.19-slim")
17+
.dependsOn(redis)
18+
.withExposedPorts(80);
1719

1820
// }
1921

docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ public class WaitStrategiesTest {
1414

1515
@Rule
1616
// waitForNetworkListening {
17-
public GenericContainer nginx = new GenericContainer(DockerImageName.parse("nginx:1.9.4")) //
17+
public GenericContainer nginx = new GenericContainer(DockerImageName.parse("nginx:1.27.0-alpine3.19-slim")) //
1818
.withExposedPorts(80);
1919

2020
// }
2121

2222
@Rule
2323
// waitForSimpleHttp {
24-
public GenericContainer nginxWithHttpWait = new GenericContainer(DockerImageName.parse("nginx:1.9.4"))
24+
public GenericContainer nginxWithHttpWait = new GenericContainer(
25+
DockerImageName.parse("nginx:1.27.0-alpine3.19-slim")
26+
)
2527
.withExposedPorts(80)
2628
.waitingFor(Wait.forHttp("/"));
2729

0 commit comments

Comments
 (0)