Merge pull request #47797 from nosan

* pr/47797:
  Polish "Add failure analysis for invalid Docker environments"
  Add failure analysis for invalid Docker environments

Closes gh-47797
This commit is contained in:
Stéphane Nicoll
2025-12-19 11:46:26 +01:00
3 changed files with 115 additions and 0 deletions
@@ -0,0 +1,52 @@
/*
* Copyright 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.testcontainers.lifecycle;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.util.StringUtils;
/**
* A {@code FailureAnalyzer} that performs analysis of failures caused by Testcontainers
* not finding a valid Docker environment.
*
* @author Dmytro Nosan
* @author Stephane Nicoll
*/
class DockerEnvironmentNotFoundFailureAnalyzer extends AbstractFailureAnalyzer<IllegalStateException> {
private static final String EXPECTED_MESSAGE = "Could not find a valid Docker environment";
private static final String DESCRIPTION = "Could not find a valid Docker environment for Testcontainers.";
private static final String ACTION = """
- Ensure a Docker-compatible container engine is installed and running.
- If running Testcontainers in CI, ensure the runner has access to the daemon, typically by using a mounted socket or a Docker-in-Docker setup.
- Review the Testcontainers documentation for troubleshooting and advanced configuration options.
""";
@Override
protected @Nullable FailureAnalysis analyze(Throwable rootFailure, IllegalStateException cause) {
if (StringUtils.hasText(cause.getMessage()) && cause.getMessage().contains(EXPECTED_MESSAGE)) {
return new FailureAnalysis(DESCRIPTION, ACTION, cause);
}
return null;
}
}
@@ -6,3 +6,6 @@ org.springframework.boot.testcontainers.lifecycle.TestcontainersLifecycleApplica
org.springframework.test.context.ContextCustomizerFactory=\
org.springframework.boot.testcontainers.service.connection.ServiceConnectionContextCustomizerFactory
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.testcontainers.lifecycle.DockerEnvironmentNotFoundFailureAnalyzer
@@ -0,0 +1,60 @@
/*
* Copyright 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.testcontainers.lifecycle;
import org.junit.jupiter.api.Test;
import org.springframework.boot.diagnostics.FailureAnalysis;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DockerEnvironmentNotFoundFailureAnalyzer}.
*
* @author Dmytro Nosan
*/
class DockerEnvironmentNotFoundFailureAnalyzerTests {
private final DockerEnvironmentNotFoundFailureAnalyzer analyzer = new DockerEnvironmentNotFoundFailureAnalyzer();
@Test
void shouldReturnFailureAnalysisWhenMessageMatches() {
IllegalStateException cause = new IllegalStateException(
"Could not find a valid Docker environment. Please see logs and check configuration");
FailureAnalysis analysis = this.analyzer
.analyze(new RuntimeException("Root", new RuntimeException("Intermediate", cause)));
assertThat(analysis).isNotNull();
assertThat(analysis.getDescription())
.isEqualTo("Could not find a valid Docker environment for Testcontainers.");
assertThat(analysis.getAction())
.contains("Ensure a Docker-compatible container engine is installed and running");
assertThat(analysis.getCause()).isSameAs(cause);
}
@Test
void shouldReturnNullWhenMessageDoesNotMatch() {
FailureAnalysis analysis = this.analyzer.analyze(new IllegalStateException("Some message"));
assertThat(analysis).isNull();
}
@Test
void shouldReturnNullWhenMessageIsNull() {
FailureAnalysis analysis = this.analyzer.analyze(new IllegalStateException());
assertThat(analysis).isNull();
}
}