Skip to content

Commit 896655d

Browse files
committed
Add DockerEnvironmentNotAvailableFailureAnalyzer
Introduce a FailureAnalyzer to provide actionable guidance when Testcontainers fails to detect a running Docker-compatible environment. Signed-off-by: Dmytro Nosan <[email protected]>
1 parent 08a1d66 commit 896655d

File tree

4 files changed

+137
-0
lines changed

4 files changed

+137
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2012-present 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+
* https://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.testcontainers.diagnostics;
18+
19+
import org.jspecify.annotations.Nullable;
20+
21+
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
22+
import org.springframework.boot.diagnostics.FailureAnalysis;
23+
import org.springframework.util.StringUtils;
24+
25+
/**
26+
* Failure analyzer for Docker environment not available.
27+
*
28+
* @author Dmytro Nosan
29+
*/
30+
class DockerEnvironmentNotAvailableFailureAnalyzer extends AbstractFailureAnalyzer<IllegalStateException> {
31+
32+
private static final String EXPECTED_MESSAGE = "Could not find a valid Docker environment";
33+
34+
@Override
35+
protected @Nullable FailureAnalysis analyze(Throwable rootFailure, IllegalStateException cause) {
36+
if (!StringUtils.hasText(cause.getMessage()) || !cause.getMessage().contains(EXPECTED_MESSAGE)) {
37+
return null;
38+
}
39+
String action = """
40+
- Ensure a Docker-compatible container engine is installed and running. This includes:
41+
* Docker: (Desktop/Engine) Ensure the service is active. Verify accessibility by running `docker info`.
42+
* Podman: Ensure the Podman socket is active and export the `DOCKER_HOST` environment variable to point to it.
43+
* Colima: Ensure the Colima VM is started and the Docker context is active.
44+
- If using a non-default setup (e.g., remote host, WSL), confirm the `DOCKER_HOST` environment variable is correctly set.
45+
- If running Testcontainers in CI, ensure the runner has access to the daemon.
46+
- Review the Testcontainers documentation for troubleshooting and advanced configuration options.
47+
""";
48+
return new FailureAnalysis("Could not find a valid Docker environment for Testcontainers.", action, cause);
49+
}
50+
51+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2012-present 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+
* https://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+
/**
18+
* Classes for diagnosing issues with Testcontainers.
19+
*/
20+
@NullMarked
21+
package org.springframework.boot.testcontainers.diagnostics;
22+
23+
import org.jspecify.annotations.NullMarked;

core/spring-boot-testcontainers/src/main/resources/META-INF/spring.factories

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ org.springframework.boot.testcontainers.lifecycle.TestcontainersLifecycleApplica
66
org.springframework.test.context.ContextCustomizerFactory=\
77
org.springframework.boot.testcontainers.service.connection.ServiceConnectionContextCustomizerFactory
88

9+
# Failure Analyzers
10+
org.springframework.boot.diagnostics.FailureAnalyzer=\
11+
org.springframework.boot.testcontainers.diagnostics.DockerEnvironmentNotAvailableFailureAnalyzer
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2012-present 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+
* https://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.testcontainers.diagnostics;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.boot.diagnostics.FailureAnalysis;
22+
23+
import static org.assertj.core.api.Assertions.assertThat;
24+
25+
/**
26+
* Tests for {@link DockerEnvironmentNotAvailableFailureAnalyzer}.
27+
*
28+
* @author Dmytro Nosan
29+
*/
30+
class DockerEnvironmentNotAvailableFailureAnalyzerTests {
31+
32+
private final DockerEnvironmentNotAvailableFailureAnalyzer analyzer = new DockerEnvironmentNotAvailableFailureAnalyzer();
33+
34+
@Test
35+
void shouldReturnFailureAnalysisWhenMessageMatches() {
36+
IllegalStateException cause = new IllegalStateException(
37+
"Could not find a valid Docker environment. Please see logs and check configuration");
38+
FailureAnalysis analysis = this.analyzer
39+
.analyze(new RuntimeException("Root", new RuntimeException("Intermediate", cause)));
40+
assertThat(analysis).isNotNull();
41+
assertThat(analysis.getDescription())
42+
.isEqualTo("Could not find a valid Docker environment for Testcontainers.");
43+
assertThat(analysis.getAction())
44+
.contains("Ensure a Docker-compatible container engine is installed and running");
45+
assertThat(analysis.getCause()).isSameAs(cause);
46+
}
47+
48+
@Test
49+
void shouldReturnNullWhenMessageDoesNotMatch() {
50+
FailureAnalysis analysis = this.analyzer.analyze(new IllegalStateException("Some message"));
51+
assertThat(analysis).isNull();
52+
}
53+
54+
@Test
55+
void shouldReturnNullWhenMessageIsNull() {
56+
FailureAnalysis analysis = this.analyzer.analyze(new IllegalStateException());
57+
assertThat(analysis).isNull();
58+
}
59+
60+
}

0 commit comments

Comments
 (0)