Improve null-safety of core/spring-boot-docker-compose

See gh-46926
This commit is contained in:
Moritz Halbritter 2025-09-10 11:59:20 +02:00
parent 8c0018d739
commit a6b4400de7
6 changed files with 17 additions and 19 deletions

View File

@ -33,7 +33,7 @@ import org.jspecify.annotations.Nullable;
* @author Phillip Webb * @author Phillip Webb
*/ */
record DockerCliInspectResponse(String id, DockerCliInspectResponse.Config config, record DockerCliInspectResponse(String id, DockerCliInspectResponse.Config config,
DockerCliInspectResponse.NetworkSettings networkSettings, DockerCliInspectResponse.@Nullable NetworkSettings networkSettings,
DockerCliInspectResponse.@Nullable HostConfig hostConfig) { DockerCliInspectResponse.@Nullable HostConfig hostConfig) {
/** /**

View File

@ -40,11 +40,11 @@ class DockerEnv {
* Create a new {@link DockerEnv} instance. * Create a new {@link DockerEnv} instance.
* @param env a list of env entries in the form {@code name=value} or {@code name}. * @param env a list of env entries in the form {@code name=value} or {@code name}.
*/ */
DockerEnv(List<String> env) { DockerEnv(@Nullable List<String> env) {
this.map = parse(env); this.map = parse(env);
} }
private Map<String, @Nullable String> parse(List<String> env) { private Map<String, @Nullable String> parse(@Nullable List<String> env) {
if (CollectionUtils.isEmpty(env)) { if (CollectionUtils.isEmpty(env)) {
return Collections.emptyMap(); return Collections.emptyMap();
} }

View File

@ -83,7 +83,7 @@ final class DockerHost {
* {@link DockerCliContextResponse} * {@link DockerCliContextResponse}
* @return a new docker host instance * @return a new docker host instance
*/ */
static DockerHost get(@Nullable String host, Function<String, String> systemEnv, static DockerHost get(@Nullable String host, Function<String, @Nullable String> systemEnv,
Supplier<List<DockerCliContextResponse>> contextsSupplier) { Supplier<List<DockerCliContextResponse>> contextsSupplier) {
host = (StringUtils.hasText(host)) ? host : fromServicesHostEnv(systemEnv); host = (StringUtils.hasText(host)) ? host : fromServicesHostEnv(systemEnv);
host = (StringUtils.hasText(host)) ? host : fromDockerHostEnv(systemEnv); host = (StringUtils.hasText(host)) ? host : fromDockerHostEnv(systemEnv);
@ -92,11 +92,11 @@ final class DockerHost {
return new DockerHost(host); return new DockerHost(host);
} }
private static String fromServicesHostEnv(Function<String, String> systemEnv) { private static @Nullable String fromServicesHostEnv(Function<String, @Nullable String> systemEnv) {
return systemEnv.apply("SERVICES_HOST"); return systemEnv.apply("SERVICES_HOST");
} }
private static @Nullable String fromDockerHostEnv(Function<String, String> systemEnv) { private static @Nullable String fromDockerHostEnv(Function<String, @Nullable String> systemEnv) {
return fromEndpoint(systemEnv.apply("DOCKER_HOST")); return fromEndpoint(systemEnv.apply("DOCKER_HOST"));
} }

View File

@ -27,7 +27,6 @@ import org.jspecify.annotations.Nullable;
import org.springframework.aot.AotDetector; import org.springframework.aot.AotDetector;
import org.springframework.boot.SpringApplicationShutdownHandlers; import org.springframework.boot.SpringApplicationShutdownHandlers;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.docker.compose.core.DockerCompose; import org.springframework.boot.docker.compose.core.DockerCompose;
import org.springframework.boot.docker.compose.core.DockerComposeFile; import org.springframework.boot.docker.compose.core.DockerComposeFile;
import org.springframework.boot.docker.compose.core.RunningService; import org.springframework.boot.docker.compose.core.RunningService;
@ -74,14 +73,14 @@ class DockerComposeLifecycleManager {
private final ServiceReadinessChecks serviceReadinessChecks; private final ServiceReadinessChecks serviceReadinessChecks;
DockerComposeLifecycleManager(ApplicationContext applicationContext, Binder binder, DockerComposeLifecycleManager(ApplicationContext applicationContext,
SpringApplicationShutdownHandlers shutdownHandlers, DockerComposeProperties properties, SpringApplicationShutdownHandlers shutdownHandlers, DockerComposeProperties properties,
Set<ApplicationListener<?>> eventListeners) { Set<ApplicationListener<?>> eventListeners) {
this(null, applicationContext, binder, shutdownHandlers, properties, eventListeners, this(null, applicationContext, shutdownHandlers, properties, eventListeners, new DockerComposeSkipCheck(),
new DockerComposeSkipCheck(), null); null);
} }
DockerComposeLifecycleManager(@Nullable File workingDirectory, ApplicationContext applicationContext, Binder binder, DockerComposeLifecycleManager(@Nullable File workingDirectory, ApplicationContext applicationContext,
SpringApplicationShutdownHandlers shutdownHandlers, DockerComposeProperties properties, SpringApplicationShutdownHandlers shutdownHandlers, DockerComposeProperties properties,
Set<ApplicationListener<?>> eventListeners, DockerComposeSkipCheck skipCheck, Set<ApplicationListener<?>> eventListeners, DockerComposeSkipCheck skipCheck,
@Nullable ServiceReadinessChecks serviceReadinessChecks) { @Nullable ServiceReadinessChecks serviceReadinessChecks) {

View File

@ -56,8 +56,7 @@ class DockerComposeListener implements ApplicationListener<ApplicationPreparedEv
protected DockerComposeLifecycleManager createDockerComposeLifecycleManager( protected DockerComposeLifecycleManager createDockerComposeLifecycleManager(
ConfigurableApplicationContext applicationContext, Binder binder, DockerComposeProperties properties, ConfigurableApplicationContext applicationContext, Binder binder, DockerComposeProperties properties,
Set<ApplicationListener<?>> eventListeners) { Set<ApplicationListener<?>> eventListeners) {
return new DockerComposeLifecycleManager(applicationContext, binder, this.shutdownHandlers, properties, return new DockerComposeLifecycleManager(applicationContext, this.shutdownHandlers, properties, eventListeners);
eventListeners);
} }
} }

View File

@ -109,7 +109,7 @@ class DockerComposeLifecycleManagerTests {
this.eventListeners = new LinkedHashSet<>(); this.eventListeners = new LinkedHashSet<>();
this.skipCheck = mock(DockerComposeSkipCheck.class); this.skipCheck = mock(DockerComposeSkipCheck.class);
this.serviceReadinessChecks = mock(ServiceReadinessChecks.class); this.serviceReadinessChecks = mock(ServiceReadinessChecks.class);
this.lifecycleManager = new TestDockerComposeLifecycleManager(workingDirectory, this.applicationContext, binder, this.lifecycleManager = new TestDockerComposeLifecycleManager(workingDirectory, this.applicationContext,
this.shutdownHandlers, this.properties, this.eventListeners, this.skipCheck, this.shutdownHandlers, this.properties, this.eventListeners, this.skipCheck,
this.serviceReadinessChecks); this.serviceReadinessChecks);
} }
@ -152,15 +152,15 @@ class DockerComposeLifecycleManagerTests {
@Test @Test
void startWhenComposeFileNotFoundThrowsException() { void startWhenComposeFileNotFoundThrowsException() {
DockerComposeLifecycleManager manager = new DockerComposeLifecycleManager(new File("."), DockerComposeLifecycleManager manager = new DockerComposeLifecycleManager(new File("."),
this.applicationContext, null, this.shutdownHandlers, this.properties, this.eventListeners, this.applicationContext, this.shutdownHandlers, this.properties, this.eventListeners, this.skipCheck,
this.skipCheck, this.serviceReadinessChecks); this.serviceReadinessChecks);
assertThatIllegalStateException().isThrownBy(manager::start) assertThatIllegalStateException().isThrownBy(manager::start)
.withMessageContaining(Paths.get(".").toAbsolutePath().toString()); .withMessageContaining(Paths.get(".").toAbsolutePath().toString());
} }
@Test @Test
void startWhenComposeFileNotFoundAndWorkingDirectoryNullThrowsException() { void startWhenComposeFileNotFoundAndWorkingDirectoryNullThrowsException() {
DockerComposeLifecycleManager manager = new DockerComposeLifecycleManager(null, this.applicationContext, null, DockerComposeLifecycleManager manager = new DockerComposeLifecycleManager(null, this.applicationContext,
this.shutdownHandlers, this.properties, this.eventListeners, this.skipCheck, this.shutdownHandlers, this.properties, this.eventListeners, this.skipCheck,
this.serviceReadinessChecks); this.serviceReadinessChecks);
assertThatIllegalStateException().isThrownBy(manager::start) assertThatIllegalStateException().isThrownBy(manager::start)
@ -516,11 +516,11 @@ class DockerComposeLifecycleManagerTests {
*/ */
class TestDockerComposeLifecycleManager extends DockerComposeLifecycleManager { class TestDockerComposeLifecycleManager extends DockerComposeLifecycleManager {
TestDockerComposeLifecycleManager(File workingDirectory, ApplicationContext applicationContext, Binder binder, TestDockerComposeLifecycleManager(File workingDirectory, ApplicationContext applicationContext,
SpringApplicationShutdownHandlers shutdownHandlers, DockerComposeProperties properties, SpringApplicationShutdownHandlers shutdownHandlers, DockerComposeProperties properties,
Set<ApplicationListener<?>> eventListeners, DockerComposeSkipCheck skipCheck, Set<ApplicationListener<?>> eventListeners, DockerComposeSkipCheck skipCheck,
ServiceReadinessChecks serviceReadinessChecks) { ServiceReadinessChecks serviceReadinessChecks) {
super(workingDirectory, applicationContext, binder, shutdownHandlers, properties, eventListeners, skipCheck, super(workingDirectory, applicationContext, shutdownHandlers, properties, eventListeners, skipCheck,
serviceReadinessChecks); serviceReadinessChecks);
} }