Allow docker compose service readiness checks to be bypassed
Add `spring.docker.compose.readiness.wait` property that can be used to determine how Spring Boot should wait for docker compose services to become ready. Fixes gh-35545
This commit is contained in:
parent
d018aa8f6d
commit
a8602a1814
|
@ -29,6 +29,7 @@ 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.lifecycle.DockerComposeProperties.Readiness.Wait;
|
||||
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Start;
|
||||
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Stop;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -110,15 +111,19 @@ class DockerComposeLifecycleManager {
|
|||
LifecycleManagement lifecycleManagement = this.properties.getLifecycleManagement();
|
||||
Start start = this.properties.getStart();
|
||||
Stop stop = this.properties.getStop();
|
||||
Wait wait = this.properties.getReadiness().getWait();
|
||||
if (lifecycleManagement.shouldStart() && !dockerCompose.hasRunningServices()) {
|
||||
start.getCommand().applyTo(dockerCompose, start.getLogLevel());
|
||||
wait = (wait != Wait.ONLY_IF_STARTED) ? wait : Wait.ALWAYS;
|
||||
if (lifecycleManagement.shouldStop()) {
|
||||
this.shutdownHandlers.add(() -> stop.getCommand().applyTo(dockerCompose, stop.getTimeout()));
|
||||
}
|
||||
}
|
||||
List<RunningService> runningServices = new ArrayList<>(dockerCompose.getRunningServices());
|
||||
runningServices.removeIf(this::isIgnored);
|
||||
this.serviceReadinessChecks.waitUntilReady(runningServices);
|
||||
if (wait == Wait.ALWAYS || wait == null) {
|
||||
this.serviceReadinessChecks.waitUntilReady(runningServices);
|
||||
}
|
||||
publishEvent(new DockerComposeServicesReadyEvent(this.applicationContext, runningServices));
|
||||
}
|
||||
|
||||
|
|
|
@ -244,6 +244,11 @@ public class DockerComposeProperties {
|
|||
*/
|
||||
public static class Readiness {
|
||||
|
||||
/**
|
||||
* Wait strategy to use.
|
||||
*/
|
||||
private Wait wait = Wait.ALWAYS;
|
||||
|
||||
/**
|
||||
* Timeout of the readiness checks.
|
||||
*/
|
||||
|
@ -254,6 +259,14 @@ public class DockerComposeProperties {
|
|||
*/
|
||||
private final Tcp tcp = new Tcp();
|
||||
|
||||
public Wait getWait() {
|
||||
return this.wait;
|
||||
}
|
||||
|
||||
public void setWait(Wait wait) {
|
||||
this.wait = wait;
|
||||
}
|
||||
|
||||
public Duration getTimeout() {
|
||||
return this.timeout;
|
||||
}
|
||||
|
@ -266,6 +279,29 @@ public class DockerComposeProperties {
|
|||
return this.tcp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Readiness wait strategies.
|
||||
*/
|
||||
public enum Wait {
|
||||
|
||||
/**
|
||||
* Always perform readiness checks.
|
||||
*/
|
||||
ALWAYS,
|
||||
|
||||
/**
|
||||
* Always perform readiness checks.
|
||||
*/
|
||||
NEVER,
|
||||
|
||||
/**
|
||||
* Only perform readiness checks if docker was started with lifecycle
|
||||
* management.
|
||||
*/
|
||||
ONLY_IF_STARTED
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* TCP properties.
|
||||
*/
|
||||
|
|
|
@ -36,6 +36,7 @@ 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.lifecycle.DockerComposeProperties.Readiness.Wait;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
@ -268,7 +269,7 @@ class DockerComposeLifecycleManagerTests {
|
|||
void startWhenHasIgnoreLabelIgnoresService() {
|
||||
EventCapturingListener listener = new EventCapturingListener();
|
||||
this.eventListeners.add(listener);
|
||||
setUpRunningServices(Map.of("org.springframework.boot.ignore", "true"));
|
||||
setUpRunningServices(true, Map.of("org.springframework.boot.ignore", "true"));
|
||||
this.lifecycleManager.start();
|
||||
this.shutdownHandlers.run();
|
||||
assertThat(listener.getEvent()).isNotNull();
|
||||
|
@ -285,6 +286,40 @@ class DockerComposeLifecycleManagerTests {
|
|||
then(this.serviceReadinessChecks).should().waitUntilReady(this.runningServices);
|
||||
}
|
||||
|
||||
@Test
|
||||
void startWhenWaitNeverDoesNotWaitUntilReady() {
|
||||
this.properties.getReadiness().setWait(Wait.NEVER);
|
||||
EventCapturingListener listener = new EventCapturingListener();
|
||||
this.eventListeners.add(listener);
|
||||
setUpRunningServices();
|
||||
this.lifecycleManager.start();
|
||||
this.shutdownHandlers.run();
|
||||
then(this.serviceReadinessChecks).should(never()).waitUntilReady(this.runningServices);
|
||||
}
|
||||
|
||||
@Test
|
||||
void startWhenWaitOnlyIfStartedAndNotStartedDoesNotWaitUntilReady() {
|
||||
this.properties.getReadiness().setWait(Wait.ONLY_IF_STARTED);
|
||||
this.properties.setLifecycleManagement(LifecycleManagement.NONE);
|
||||
EventCapturingListener listener = new EventCapturingListener();
|
||||
this.eventListeners.add(listener);
|
||||
setUpRunningServices();
|
||||
this.lifecycleManager.start();
|
||||
this.shutdownHandlers.run();
|
||||
then(this.serviceReadinessChecks).should(never()).waitUntilReady(this.runningServices);
|
||||
}
|
||||
|
||||
@Test
|
||||
void startWhenWaitOnlyIfStartedAndStartedWaitsUntilReady() {
|
||||
this.properties.getReadiness().setWait(Wait.ONLY_IF_STARTED);
|
||||
EventCapturingListener listener = new EventCapturingListener();
|
||||
this.eventListeners.add(listener);
|
||||
setUpRunningServices(false);
|
||||
this.lifecycleManager.start();
|
||||
this.shutdownHandlers.run();
|
||||
then(this.serviceReadinessChecks).should().waitUntilReady(this.runningServices);
|
||||
}
|
||||
|
||||
@Test
|
||||
void startGetsDockerComposeWithActiveProfiles() {
|
||||
this.properties.getProfiles().setActive(Set.of("my-profile"));
|
||||
|
@ -306,16 +341,26 @@ class DockerComposeLifecycleManagerTests {
|
|||
}
|
||||
|
||||
private void setUpRunningServices() {
|
||||
setUpRunningServices(Collections.emptyMap());
|
||||
setUpRunningServices(true);
|
||||
}
|
||||
|
||||
private void setUpRunningServices(Map<String, String> labels) {
|
||||
private void setUpRunningServices(boolean started) {
|
||||
setUpRunningServices(started, Collections.emptyMap());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void setUpRunningServices(boolean started, Map<String, String> labels) {
|
||||
given(this.dockerCompose.hasDefinedServices()).willReturn(true);
|
||||
given(this.dockerCompose.hasRunningServices()).willReturn(true);
|
||||
RunningService runningService = mock(RunningService.class);
|
||||
given(runningService.labels()).willReturn(labels);
|
||||
this.runningServices = List.of(runningService);
|
||||
given(this.dockerCompose.getRunningServices()).willReturn(this.runningServices);
|
||||
if (started) {
|
||||
given(this.dockerCompose.getRunningServices()).willReturn(this.runningServices);
|
||||
}
|
||||
else {
|
||||
given(this.dockerCompose.getRunningServices()).willReturn(Collections.emptyList(), this.runningServices);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Readiness.Wait;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
@ -48,6 +49,7 @@ 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().getWait()).isEqualTo(Wait.ALWAYS);
|
||||
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));
|
||||
|
@ -63,6 +65,7 @@ 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.wait", "only-if-started");
|
||||
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");
|
||||
|
@ -75,6 +78,7 @@ 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().getWait()).isEqualTo(Wait.ONLY_IF_STARTED);
|
||||
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));
|
||||
|
|
Loading…
Reference in New Issue