From 060581d0784596bdbf14a89612c2cf12fb4de06e Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 17 May 2023 14:19:18 -0700 Subject: [PATCH 1/2] Remove pluggable docker compose ReadinessCheck Remove pluggable `ReadinessCheck` interface and only use the `TcpConnectServiceReadinessCheck` implementation for now. We may re-introduce pluggable checks in a future version. See gh-35544 --- .../readiness/ServiceNotReadyException.java | 10 +--- .../readiness/ServiceReadinessCheck.java | 47 ------------------- .../readiness/ServiceReadinessChecks.java | 32 +++++-------- .../TcpConnectServiceReadinessCheck.java | 8 ++-- .../ServiceReadinessChecksTests.java | 44 ++++------------- .../asciidoc/features/docker-compose.adoc | 2 - 6 files changed, 26 insertions(+), 117 deletions(-) delete mode 100644 spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessCheck.java diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceNotReadyException.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceNotReadyException.java index af7cc4f22b..9bfaca006d 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceNotReadyException.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceNotReadyException.java @@ -24,10 +24,8 @@ import org.springframework.boot.docker.compose.core.RunningService; * @author Moritz Halbritter * @author Andy Wilkinson * @author Phillip Webb - * @since 3.1.0 - * @see ServiceReadinessCheck */ -public class ServiceNotReadyException extends RuntimeException { +class ServiceNotReadyException extends RuntimeException { private final RunningService service; @@ -40,11 +38,7 @@ public class ServiceNotReadyException extends RuntimeException { this.service = service; } - /** - * Return the service that was not ready. - * @return the non-ready service - */ - public RunningService getService() { + RunningService getService() { return this.service; } diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessCheck.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessCheck.java deleted file mode 100644 index 0f4c9d2f62..0000000000 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessCheck.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2012-2023 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.docker.compose.readiness; - -import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.boot.docker.compose.core.RunningService; -import org.springframework.core.env.Environment; - -/** - * Strategy used to check if a {@link RunningService} is ready. Implementations may be - * registered in {@code spring.factories}. The following constructor arguments types are - * supported: - * - * - * @author Moritz Halbritter - * @author Andy Wilkinson - * @author Phillip Webb - * @since 3.1.0 - */ -public interface ServiceReadinessCheck { - - /** - * Checks whether the given {@code service} is ready. - * @param service service to check - * @throws ServiceNotReadyException if the service is not ready - */ - void check(RunningService service) throws ServiceNotReadyException; - -} diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecks.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecks.java index 4d1ea22526..c75605c1df 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecks.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecks.java @@ -32,12 +32,10 @@ import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.docker.compose.core.RunningService; import org.springframework.core.env.Environment; import org.springframework.core.io.support.SpringFactoriesLoader; -import org.springframework.core.io.support.SpringFactoriesLoader.ArgumentResolver; import org.springframework.core.log.LogMessage; /** - * A collection of {@link ServiceReadinessCheck} instances that can be used to - * {@link #wait() wait} for {@link RunningService services} to be ready. + * Utility used to {@link #wait() wait} for {@link RunningService services} to be ready. * * @author Moritz Halbritter * @author Andy Wilkinson @@ -58,7 +56,7 @@ public class ServiceReadinessChecks { private final ReadinessProperties properties; - private final List checks; + private final TcpConnectServiceReadinessCheck check; public ServiceReadinessChecks(ClassLoader classLoader, Environment environment, Binder binder) { this(Clock.systemUTC(), ServiceReadinessChecks::sleep, @@ -68,15 +66,11 @@ public class ServiceReadinessChecks { ServiceReadinessChecks(Clock clock, Consumer sleep, SpringFactoriesLoader loader, ClassLoader classLoader, Environment environment, Binder binder, - Function tcpCheckFactory) { - ArgumentResolver argumentResolver = ArgumentResolver.of(ClassLoader.class, classLoader) - .and(Environment.class, environment) - .and(Binder.class, binder); + Function tcpCheckFactory) { this.clock = clock; this.sleep = sleep; this.properties = ReadinessProperties.get(binder); - this.checks = new ArrayList<>(loader.load(ServiceReadinessCheck.class, argumentResolver)); - this.checks.add(tcpCheckFactory.apply(this.properties.getTcp())); + this.check = tcpCheckFactory.apply(this.properties.getTcp()); } /** @@ -106,16 +100,14 @@ public class ServiceReadinessChecks { continue; } logger.trace(LogMessage.format("Checking readiness of service '%s'", service)); - for (ServiceReadinessCheck check : this.checks) { - try { - check.check(service); - logger.trace(LogMessage.format("Service '%s' is ready", service)); - } - catch (ServiceNotReadyException ex) { - logger.trace(LogMessage.format("Service '%s' is not ready", service), ex); - exceptions = (exceptions != null) ? exceptions : new ArrayList<>(); - exceptions.add(ex); - } + try { + this.check.check(service); + logger.trace(LogMessage.format("Service '%s' is ready", service)); + } + catch (ServiceNotReadyException ex) { + logger.trace(LogMessage.format("Service '%s' is not ready", service), ex); + exceptions = (exceptions != null) ? exceptions : new ArrayList<>(); + exceptions.add(ex); } } return (exceptions != null) ? exceptions : Collections.emptyList(); diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/TcpConnectServiceReadinessCheck.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/TcpConnectServiceReadinessCheck.java index c68e088a2a..38da8e0608 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/TcpConnectServiceReadinessCheck.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/TcpConnectServiceReadinessCheck.java @@ -24,14 +24,13 @@ import java.net.SocketTimeoutException; import org.springframework.boot.docker.compose.core.RunningService; /** - * Default {@link ServiceReadinessCheck} that checks readiness by connecting to the - * exposed TCP ports. + * Checks readiness by connecting to the exposed TCP ports. * * @author Moritz Halbritter * @author Andy Wilkinson * @author Phillip Webb */ -class TcpConnectServiceReadinessCheck implements ServiceReadinessCheck { +class TcpConnectServiceReadinessCheck { private static final String DISABLE_LABEL = "org.springframework.boot.readiness-check.tcp.disable"; @@ -41,8 +40,7 @@ class TcpConnectServiceReadinessCheck implements ServiceReadinessCheck { this.properties = properties; } - @Override - public void check(RunningService service) { + void check(RunningService service) { if (service.labels().containsKey(DISABLE_LABEL)) { return; } diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecksTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecksTests.java index 237aefbf82..e3d04f42f2 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecksTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecksTests.java @@ -25,22 +25,16 @@ import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.ArgumentMatchers; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.docker.compose.core.RunningService; -import org.springframework.core.env.Environment; -import org.springframework.core.io.support.SpringFactoriesLoader.ArgumentResolver; import org.springframework.core.test.io.support.MockSpringFactoriesLoader; import org.springframework.mock.env.MockEnvironment; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; /** * Tests for {@link ServiceReadinessChecks}. @@ -67,8 +61,6 @@ class ServiceReadinessChecksTests { private List runningServices; - private final MockServiceReadinessCheck mockTcpCheck = new MockServiceReadinessCheck(); - @BeforeEach void setup() { this.clock = mock(Clock.class); @@ -81,42 +73,25 @@ class ServiceReadinessChecksTests { this.runningServices = List.of(this.runningService); } - @Test - void loadCanResolveArguments() { - this.loader = spy(MockSpringFactoriesLoader.class); - createChecks(); - then(this.loader).should() - .load(eq(ServiceReadinessCheck.class), ArgumentMatchers.assertArg((argumentResolver) -> { - assertThat(argumentResolver.resolve(ClassLoader.class)).isEqualTo(this.classLoader); - assertThat(argumentResolver.resolve(Environment.class)).isEqualTo(this.environment); - assertThat(argumentResolver.resolve(Binder.class)).isEqualTo(this.binder); - })); - } - @Test void waitUntilReadyWhenImmediatelyReady() { MockServiceReadinessCheck check = new MockServiceReadinessCheck(); - this.loader.addInstance(ServiceReadinessCheck.class, check); - createChecks().waitUntilReady(this.runningServices); + createChecks(check).waitUntilReady(this.runningServices); assertThat(check.getChecked()).contains(this.runningService); - assertThat(this.mockTcpCheck.getChecked()).contains(this.runningService); } @Test void waitUntilReadyWhenTakesTimeToBeReady() { MockServiceReadinessCheck check = new MockServiceReadinessCheck(2); - this.loader.addInstance(ServiceReadinessCheck.class, check); - createChecks().waitUntilReady(this.runningServices); + createChecks(check).waitUntilReady(this.runningServices); assertThat(check.getChecked()).hasSize(2).contains(this.runningService); - assertThat(this.mockTcpCheck.getChecked()).contains(this.runningService); } @Test void waitUntilReadyWhenTimeout() { MockServiceReadinessCheck check = new MockServiceReadinessCheck(Integer.MAX_VALUE); - this.loader.addInstance(ServiceReadinessCheck.class, check); assertThatExceptionOfType(ReadinessTimeoutException.class) - .isThrownBy(() -> createChecks().waitUntilReady(this.runningServices)) + .isThrownBy(() -> createChecks(check).waitUntilReady(this.runningServices)) .satisfies((ex) -> assertThat(ex.getSuppressed()).hasSize(1)); assertThat(check.getChecked()).hasSizeGreaterThan(10); } @@ -125,25 +100,23 @@ class ServiceReadinessChecksTests { void waitForWhenServiceHasDisableLabelDoesNotCheck() { given(this.runningService.labels()).willReturn(Map.of("org.springframework.boot.readiness-check.disable", "")); MockServiceReadinessCheck check = new MockServiceReadinessCheck(); - this.loader.addInstance(ServiceReadinessCheck.class, check); - createChecks().waitUntilReady(this.runningServices); + createChecks(check).waitUntilReady(this.runningServices); assertThat(check.getChecked()).isEmpty(); - assertThat(this.mockTcpCheck.getChecked()).isEmpty(); } void sleep(Duration duration) { this.now = this.now.plus(duration); } - private ServiceReadinessChecks createChecks() { + private ServiceReadinessChecks createChecks(TcpConnectServiceReadinessCheck check) { return new ServiceReadinessChecks(this.clock, this::sleep, this.loader, this.classLoader, this.environment, - this.binder, (properties) -> this.mockTcpCheck); + this.binder, (properties) -> check); } /** - * Mock {@link ServiceReadinessCheck}. + * Mock {@link TcpConnectServiceReadinessCheck}. */ - static class MockServiceReadinessCheck implements ServiceReadinessCheck { + static class MockServiceReadinessCheck extends TcpConnectServiceReadinessCheck { private final Integer failUntil; @@ -154,6 +127,7 @@ class ServiceReadinessChecksTests { } MockServiceReadinessCheck(Integer failUntil) { + super(null); this.failUntil = failUntil; } diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/docker-compose.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/docker-compose.adoc index db2b531899..6621b51576 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/docker-compose.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/docker-compose.adoc @@ -184,8 +184,6 @@ You can also change timeout values in your `application.properties` or `applicat The overall timeout can be configured using configprop:spring.docker.compose.readiness.timeout[]. -TIP: You can also provide your own `ServiceReadinessCheck` implementations and register them in the `spring.factories` file. - [[features.docker-compose.lifecycle]] From 6b0b6ccf498b4fb4624b416878933621cc60300a Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 17 May 2023 14:32:55 -0700 Subject: [PATCH 2/2] Move docker compose readiness code and make it package-private The `ReadinessCheck` interface has been removed making the dedicated package less necessary. By relocating the code we can make more of it pacakge-private. See gh-35544 --- .../DockerComposeLifecycleManager.java | 3 +- .../lifecycle/DockerComposeProperties.java | 68 ++++++++++++ .../ReadinessTimeoutException.java | 6 +- .../ServiceNotReadyException.java | 2 +- .../ServiceReadinessChecks.java | 29 ++--- .../TcpConnectServiceReadinessCheck.java | 6 +- .../readiness/ReadinessProperties.java | 101 ------------------ .../compose/readiness/package-info.java | 20 ---- .../DockerComposeLifecycleManagerTests.java | 1 - .../DockerComposePropertiesTests.java | 9 ++ .../ReadinessTimeoutExceptionTests.java | 2 +- .../ServiceNotReadyExceptionTests.java | 2 +- .../ServiceReadinessChecksTests.java | 21 +--- .../TcpConnectServiceReadinessCheckTests.java | 4 +- .../readiness/ReadinessPropertiesTests.java | 62 ----------- 15 files changed, 102 insertions(+), 234 deletions(-) rename spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/{readiness => lifecycle}/ReadinessTimeoutException.java (87%) rename spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/{readiness => lifecycle}/ServiceNotReadyException.java (95%) rename spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/{readiness => lifecycle}/ServiceReadinessChecks.java (74%) rename spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/{readiness => lifecycle}/TcpConnectServiceReadinessCheck.java (91%) delete mode 100644 spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ReadinessProperties.java delete mode 100644 spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/package-info.java rename spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/{readiness => lifecycle}/ReadinessTimeoutExceptionTests.java (97%) rename spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/{readiness => lifecycle}/ServiceNotReadyExceptionTests.java (95%) rename spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/{readiness => lifecycle}/ServiceReadinessChecksTests.java (83%) rename spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/{readiness => lifecycle}/TcpConnectServiceReadinessCheckTests.java (96%) delete mode 100644 spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ReadinessPropertiesTests.java diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManager.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManager.java index 4b5f0871ce..72498b045c 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManager.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManager.java @@ -31,7 +31,6 @@ import org.springframework.boot.docker.compose.core.DockerComposeFile; import org.springframework.boot.docker.compose.core.RunningService; import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Start; import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Stop; -import org.springframework.boot.docker.compose.readiness.ServiceReadinessChecks; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.event.SimpleApplicationEventMulticaster; @@ -88,7 +87,7 @@ class DockerComposeLifecycleManager { this.eventListeners = eventListeners; this.skipCheck = skipCheck; this.serviceReadinessChecks = (serviceReadinessChecks != null) ? serviceReadinessChecks - : new ServiceReadinessChecks(this.classLoader, applicationContext.getEnvironment(), binder); + : new ServiceReadinessChecks(properties.getReadiness()); } void start() { diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeProperties.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeProperties.java index 8d335bb198..26cea3ff04 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeProperties.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeProperties.java @@ -75,6 +75,8 @@ public class DockerComposeProperties { private final Skip skip = new Skip(); + private final Readiness readiness = new Readiness(); + public boolean isEnabled() { return this.enabled; } @@ -123,6 +125,10 @@ public class DockerComposeProperties { return this.skip; } + public Readiness getReadiness() { + return this.readiness; + } + static DockerComposeProperties get(Binder binder) { return binder.bind(NAME, DockerComposeProperties.class).orElseGet(DockerComposeProperties::new); } @@ -233,4 +239,66 @@ public class DockerComposeProperties { } + /** + * Readiness properties. + */ + public static class Readiness { + + /** + * Timeout of the readiness checks. + */ + private Duration timeout = Duration.ofMinutes(2); + + /** + * TCP properties. + */ + private final Tcp tcp = new Tcp(); + + public Duration getTimeout() { + return this.timeout; + } + + public void setTimeout(Duration timeout) { + this.timeout = timeout; + } + + public Tcp getTcp() { + return this.tcp; + } + + /** + * TCP properties. + */ + public static class Tcp { + + /** + * Timeout for connections. + */ + private Duration connectTimeout = Duration.ofMillis(200); + + /** + * Timeout for reads. + */ + private Duration readTimeout = Duration.ofMillis(200); + + public Duration getConnectTimeout() { + return this.connectTimeout; + } + + public void setConnectTimeout(Duration connectTimeout) { + this.connectTimeout = connectTimeout; + } + + public Duration getReadTimeout() { + return this.readTimeout; + } + + public void setReadTimeout(Duration readTimeout) { + this.readTimeout = readTimeout; + } + + } + + } + } diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ReadinessTimeoutException.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/ReadinessTimeoutException.java similarity index 87% rename from spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ReadinessTimeoutException.java rename to spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/ReadinessTimeoutException.java index 65da0515f6..7465730c10 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ReadinessTimeoutException.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/ReadinessTimeoutException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docker.compose.readiness; +package org.springframework.boot.docker.compose.lifecycle; import java.time.Duration; import java.util.List; @@ -23,9 +23,7 @@ import java.util.Objects; import org.springframework.boot.docker.compose.core.RunningService; /** - * Exception thrown if readiness checking has timed out. Related - * {@link ServiceNotReadyException ServiceNotReadyExceptions} are available from - * {@link #getSuppressed()}. + * Exception thrown if readiness checking has timed out. * * @author Moritz Halbritter * @author Andy Wilkinson diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceNotReadyException.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/ServiceNotReadyException.java similarity index 95% rename from spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceNotReadyException.java rename to spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/ServiceNotReadyException.java index 9bfaca006d..dea78a9e9f 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceNotReadyException.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/ServiceNotReadyException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docker.compose.readiness; +package org.springframework.boot.docker.compose.lifecycle; import org.springframework.boot.docker.compose.core.RunningService; diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecks.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/ServiceReadinessChecks.java similarity index 74% rename from spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecks.java rename to spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/ServiceReadinessChecks.java index c75605c1df..b08cb62d91 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecks.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/ServiceReadinessChecks.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docker.compose.readiness; +package org.springframework.boot.docker.compose.lifecycle; import java.time.Clock; import java.time.Duration; @@ -23,15 +23,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Consumer; -import java.util.function.Function; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.docker.compose.core.RunningService; -import org.springframework.core.env.Environment; -import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.log.LogMessage; /** @@ -40,9 +36,8 @@ import org.springframework.core.log.LogMessage; * @author Moritz Halbritter * @author Andy Wilkinson * @author Phillip Webb - * @since 3.1.0 */ -public class ServiceReadinessChecks { +class ServiceReadinessChecks { private static final Log logger = LogFactory.getLog(ServiceReadinessChecks.class); @@ -54,30 +49,28 @@ public class ServiceReadinessChecks { private final Consumer sleep; - private final ReadinessProperties properties; + private final DockerComposeProperties.Readiness properties; private final TcpConnectServiceReadinessCheck check; - public ServiceReadinessChecks(ClassLoader classLoader, Environment environment, Binder binder) { - this(Clock.systemUTC(), ServiceReadinessChecks::sleep, - SpringFactoriesLoader.forDefaultResourceLocation(classLoader), classLoader, environment, binder, - TcpConnectServiceReadinessCheck::new); + ServiceReadinessChecks(DockerComposeProperties.Readiness properties) { + this(properties, Clock.systemUTC(), ServiceReadinessChecks::sleep, + new TcpConnectServiceReadinessCheck(properties.getTcp())); } - ServiceReadinessChecks(Clock clock, Consumer sleep, SpringFactoriesLoader loader, ClassLoader classLoader, - Environment environment, Binder binder, - Function tcpCheckFactory) { + ServiceReadinessChecks(DockerComposeProperties.Readiness properties, Clock clock, Consumer sleep, + TcpConnectServiceReadinessCheck check) { this.clock = clock; this.sleep = sleep; - this.properties = ReadinessProperties.get(binder); - this.check = tcpCheckFactory.apply(this.properties.getTcp()); + this.properties = properties; + this.check = check; } /** * Wait for the given services to be ready. * @param runningServices the services to wait for */ - public void waitUntilReady(List runningServices) { + void waitUntilReady(List runningServices) { Duration timeout = this.properties.getTimeout(); Instant start = this.clock.instant(); while (true) { diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/TcpConnectServiceReadinessCheck.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/TcpConnectServiceReadinessCheck.java similarity index 91% rename from spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/TcpConnectServiceReadinessCheck.java rename to spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/TcpConnectServiceReadinessCheck.java index 38da8e0608..ee00bd9d1b 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/TcpConnectServiceReadinessCheck.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/TcpConnectServiceReadinessCheck.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docker.compose.readiness; +package org.springframework.boot.docker.compose.lifecycle; import java.io.IOException; import java.net.InetSocketAddress; @@ -34,9 +34,9 @@ class TcpConnectServiceReadinessCheck { private static final String DISABLE_LABEL = "org.springframework.boot.readiness-check.tcp.disable"; - private final ReadinessProperties.Tcp properties; + private final DockerComposeProperties.Readiness.Tcp properties; - TcpConnectServiceReadinessCheck(ReadinessProperties.Tcp properties) { + TcpConnectServiceReadinessCheck(DockerComposeProperties.Readiness.Tcp properties) { this.properties = properties; } diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ReadinessProperties.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ReadinessProperties.java deleted file mode 100644 index 8e4a561df8..0000000000 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/ReadinessProperties.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2012-2023 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.docker.compose.readiness; - -import java.time.Duration; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.bind.Binder; - -/** - * Readiness configuration properties. - * - * @author Moritz Halbritter - * @author Andy Wilkinson - * @author Phillip Webb - * @since 3.1.0 - */ -@ConfigurationProperties(ReadinessProperties.NAME) -public class ReadinessProperties { - - static final String NAME = "spring.docker.compose.readiness"; - - /** - * Timeout of the readiness checks. - */ - private Duration timeout = Duration.ofMinutes(2); - - /** - * TCP properties. - */ - private final Tcp tcp = new Tcp(); - - public Duration getTimeout() { - return this.timeout; - } - - public void setTimeout(Duration timeout) { - this.timeout = timeout; - } - - public Tcp getTcp() { - return this.tcp; - } - - /** - * Get the properties using the given binder. - * @param binder the binder used to get the properties - * @return a bound {@link ReadinessProperties} instance - */ - static ReadinessProperties get(Binder binder) { - return binder.bind(ReadinessProperties.NAME, ReadinessProperties.class).orElseGet(ReadinessProperties::new); - } - - /** - * TCP properties. - */ - public static class Tcp { - - /** - * Timeout for connections. - */ - private Duration connectTimeout = Duration.ofMillis(200); - - /** - * Timeout for reads. - */ - private Duration readTimeout = Duration.ofMillis(200); - - public Duration getConnectTimeout() { - return this.connectTimeout; - } - - public void setConnectTimeout(Duration connectTimeout) { - this.connectTimeout = connectTimeout; - } - - public Duration getReadTimeout() { - return this.readTimeout; - } - - public void setReadTimeout(Duration readTimeout) { - this.readTimeout = readTimeout; - } - - } - -} diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/package-info.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/package-info.java deleted file mode 100644 index 17542adf3a..0000000000 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/readiness/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2023 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. - */ - -/** - * Service readiness checks. - */ -package org.springframework.boot.docker.compose.readiness; diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManagerTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManagerTests.java index 81434a0fb6..89d67a19bd 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManagerTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManagerTests.java @@ -36,7 +36,6 @@ import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.docker.compose.core.DockerCompose; import org.springframework.boot.docker.compose.core.DockerComposeFile; import org.springframework.boot.docker.compose.core.RunningService; -import org.springframework.boot.docker.compose.readiness.ServiceReadinessChecks; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.support.GenericApplicationContext; diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposePropertiesTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposePropertiesTests.java index 23e6c39b89..03103c73fb 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposePropertiesTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposePropertiesTests.java @@ -48,6 +48,9 @@ class DockerComposePropertiesTests { assertThat(properties.getStop().getCommand()).isEqualTo(StopCommand.STOP); assertThat(properties.getStop().getTimeout()).isEqualTo(Duration.ofSeconds(10)); assertThat(properties.getProfiles().getActive()).isEmpty(); + assertThat(properties.getReadiness().getTimeout()).isEqualTo(Duration.ofMinutes(2)); + assertThat(properties.getReadiness().getTcp().getConnectTimeout()).isEqualTo(Duration.ofMillis(200)); + assertThat(properties.getReadiness().getTcp().getReadTimeout()).isEqualTo(Duration.ofMillis(200)); } @Test @@ -60,6 +63,9 @@ class DockerComposePropertiesTests { source.put("spring.docker.compose.stop.command", "down"); source.put("spring.docker.compose.stop.timeout", "5s"); source.put("spring.docker.compose.profiles.active", "myprofile"); + source.put("spring.docker.compose.readiness.timeout", "10s"); + source.put("spring.docker.compose.readiness.tcp.connect-timeout", "400ms"); + source.put("spring.docker.compose.readiness.tcp.read-timeout", "500ms"); Binder binder = new Binder(new MapConfigurationPropertySource(source)); DockerComposeProperties properties = DockerComposeProperties.get(binder); assertThat(properties.getFile()).isEqualTo(new File("my-compose.yml")); @@ -69,6 +75,9 @@ class DockerComposePropertiesTests { assertThat(properties.getStop().getCommand()).isEqualTo(StopCommand.DOWN); assertThat(properties.getStop().getTimeout()).isEqualTo(Duration.ofSeconds(5)); assertThat(properties.getProfiles().getActive()).containsExactly("myprofile"); + assertThat(properties.getReadiness().getTimeout()).isEqualTo(Duration.ofSeconds(10)); + assertThat(properties.getReadiness().getTcp().getConnectTimeout()).isEqualTo(Duration.ofMillis(400)); + assertThat(properties.getReadiness().getTcp().getReadTimeout()).isEqualTo(Duration.ofMillis(500)); } } diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ReadinessTimeoutExceptionTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/ReadinessTimeoutExceptionTests.java similarity index 97% rename from spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ReadinessTimeoutExceptionTests.java rename to spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/ReadinessTimeoutExceptionTests.java index 8e6bbc0c92..c229da365b 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ReadinessTimeoutExceptionTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/ReadinessTimeoutExceptionTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docker.compose.readiness; +package org.springframework.boot.docker.compose.lifecycle; import java.time.Duration; import java.util.List; diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ServiceNotReadyExceptionTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/ServiceNotReadyExceptionTests.java similarity index 95% rename from spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ServiceNotReadyExceptionTests.java rename to spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/ServiceNotReadyExceptionTests.java index a6aad17075..6db005a128 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ServiceNotReadyExceptionTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/ServiceNotReadyExceptionTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docker.compose.readiness; +package org.springframework.boot.docker.compose.lifecycle; import org.junit.jupiter.api.Test; diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecksTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/ServiceReadinessChecksTests.java similarity index 83% rename from spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecksTests.java rename to spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/ServiceReadinessChecksTests.java index e3d04f42f2..fa1f350ba3 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ServiceReadinessChecksTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/ServiceReadinessChecksTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docker.compose.readiness; +package org.springframework.boot.docker.compose.lifecycle; import java.time.Clock; import java.time.Duration; @@ -26,10 +26,7 @@ import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.docker.compose.core.RunningService; -import org.springframework.core.test.io.support.MockSpringFactoriesLoader; -import org.springframework.mock.env.MockEnvironment; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -49,14 +46,6 @@ class ServiceReadinessChecksTests { Instant now = Instant.now(); - private MockSpringFactoriesLoader loader; - - private ClassLoader classLoader; - - private MockEnvironment environment; - - private Binder binder; - private RunningService runningService; private List runningServices; @@ -65,10 +54,6 @@ class ServiceReadinessChecksTests { void setup() { this.clock = mock(Clock.class); given(this.clock.instant()).willAnswer((args) -> this.now); - this.loader = new MockSpringFactoriesLoader(); - this.classLoader = getClass().getClassLoader(); - this.environment = new MockEnvironment(); - this.binder = Binder.get(this.environment); this.runningService = mock(RunningService.class); this.runningServices = List.of(this.runningService); } @@ -109,8 +94,8 @@ class ServiceReadinessChecksTests { } private ServiceReadinessChecks createChecks(TcpConnectServiceReadinessCheck check) { - return new ServiceReadinessChecks(this.clock, this::sleep, this.loader, this.classLoader, this.environment, - this.binder, (properties) -> check); + DockerComposeProperties properties = new DockerComposeProperties(); + return new ServiceReadinessChecks(properties.getReadiness(), this.clock, this::sleep, check); } /** diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/TcpConnectServiceReadinessCheckTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/TcpConnectServiceReadinessCheckTests.java similarity index 96% rename from spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/TcpConnectServiceReadinessCheckTests.java rename to spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/TcpConnectServiceReadinessCheckTests.java index 619afa0a22..3e81a2cef3 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/TcpConnectServiceReadinessCheckTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/TcpConnectServiceReadinessCheckTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.docker.compose.readiness; +package org.springframework.boot.docker.compose.lifecycle; import java.io.IOException; import java.io.UncheckedIOException; @@ -50,7 +50,7 @@ class TcpConnectServiceReadinessCheckTests { @BeforeEach void setup() { - ReadinessProperties.Tcp tcpProperties = new ReadinessProperties.Tcp(); + DockerComposeProperties.Readiness.Tcp tcpProperties = new DockerComposeProperties.Readiness.Tcp(); tcpProperties.setConnectTimeout(Duration.ofMillis(100)); tcpProperties.setReadTimeout(Duration.ofMillis(100)); this.readinessCheck = new TcpConnectServiceReadinessCheck(tcpProperties); diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ReadinessPropertiesTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ReadinessPropertiesTests.java deleted file mode 100644 index 374733179f..0000000000 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/readiness/ReadinessPropertiesTests.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-2023 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.docker.compose.readiness; - -import java.time.Duration; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; - -import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link ReadinessProperties}. - * - * @author Moritz Halbritter - * @author Andy Wilkinson - * @author Phillip Webb - */ -class ReadinessPropertiesTests { - - @Test - void getWhenNoPropertiesReturnsNewInstance() { - Binder binder = new Binder(new MapConfigurationPropertySource()); - ReadinessProperties properties = ReadinessProperties.get(binder); - assertThat(properties.getTimeout()).isEqualTo(Duration.ofMinutes(2)); - assertThat(properties.getTcp().getConnectTimeout()).isEqualTo(Duration.ofMillis(200)); - assertThat(properties.getTcp().getReadTimeout()).isEqualTo(Duration.ofMillis(200)); - } - - @Test - void getWhenPropertiesReturnsBoundInstance() { - Map source = new LinkedHashMap<>(); - source.put("spring.docker.compose.readiness.timeout", "10s"); - source.put("spring.docker.compose.readiness.tcp.connect-timeout", "400ms"); - source.put("spring.docker.compose.readiness.tcp.read-timeout", "500ms"); - Binder binder = new Binder(new MapConfigurationPropertySource(source)); - ReadinessProperties properties = ReadinessProperties.get(binder); - assertThat(properties.getTimeout()).isEqualTo(Duration.ofSeconds(10)); - assertThat(properties.getTcp().getConnectTimeout()).isEqualTo(Duration.ofMillis(400)); - assertThat(properties.getTcp().getReadTimeout()).isEqualTo(Duration.ofMillis(500)); - - } - -}