Skip to content

Commit a8602a1

Browse files
committed
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
1 parent d018aa8 commit a8602a1

File tree

4 files changed

+95
-5
lines changed

4 files changed

+95
-5
lines changed

Diff for: spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManager.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.boot.docker.compose.core.DockerCompose;
3030
import org.springframework.boot.docker.compose.core.DockerComposeFile;
3131
import org.springframework.boot.docker.compose.core.RunningService;
32+
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Readiness.Wait;
3233
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Start;
3334
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Stop;
3435
import org.springframework.context.ApplicationContext;
@@ -110,15 +111,19 @@ void start() {
110111
LifecycleManagement lifecycleManagement = this.properties.getLifecycleManagement();
111112
Start start = this.properties.getStart();
112113
Stop stop = this.properties.getStop();
114+
Wait wait = this.properties.getReadiness().getWait();
113115
if (lifecycleManagement.shouldStart() && !dockerCompose.hasRunningServices()) {
114116
start.getCommand().applyTo(dockerCompose, start.getLogLevel());
117+
wait = (wait != Wait.ONLY_IF_STARTED) ? wait : Wait.ALWAYS;
115118
if (lifecycleManagement.shouldStop()) {
116119
this.shutdownHandlers.add(() -> stop.getCommand().applyTo(dockerCompose, stop.getTimeout()));
117120
}
118121
}
119122
List<RunningService> runningServices = new ArrayList<>(dockerCompose.getRunningServices());
120123
runningServices.removeIf(this::isIgnored);
121-
this.serviceReadinessChecks.waitUntilReady(runningServices);
124+
if (wait == Wait.ALWAYS || wait == null) {
125+
this.serviceReadinessChecks.waitUntilReady(runningServices);
126+
}
122127
publishEvent(new DockerComposeServicesReadyEvent(this.applicationContext, runningServices));
123128
}
124129

Diff for: spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeProperties.java

+36
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,11 @@ public void setInTests(boolean inTests) {
244244
*/
245245
public static class Readiness {
246246

247+
/**
248+
* Wait strategy to use.
249+
*/
250+
private Wait wait = Wait.ALWAYS;
251+
247252
/**
248253
* Timeout of the readiness checks.
249254
*/
@@ -254,6 +259,14 @@ public static class Readiness {
254259
*/
255260
private final Tcp tcp = new Tcp();
256261

262+
public Wait getWait() {
263+
return this.wait;
264+
}
265+
266+
public void setWait(Wait wait) {
267+
this.wait = wait;
268+
}
269+
257270
public Duration getTimeout() {
258271
return this.timeout;
259272
}
@@ -266,6 +279,29 @@ public Tcp getTcp() {
266279
return this.tcp;
267280
}
268281

282+
/**
283+
* Readiness wait strategies.
284+
*/
285+
public enum Wait {
286+
287+
/**
288+
* Always perform readiness checks.
289+
*/
290+
ALWAYS,
291+
292+
/**
293+
* Always perform readiness checks.
294+
*/
295+
NEVER,
296+
297+
/**
298+
* Only perform readiness checks if docker was started with lifecycle
299+
* management.
300+
*/
301+
ONLY_IF_STARTED
302+
303+
}
304+
269305
/**
270306
* TCP properties.
271307
*/

Diff for: spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposeLifecycleManagerTests.java

+49-4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.boot.docker.compose.core.DockerCompose;
3737
import org.springframework.boot.docker.compose.core.DockerComposeFile;
3838
import org.springframework.boot.docker.compose.core.RunningService;
39+
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Readiness.Wait;
3940
import org.springframework.context.ApplicationContext;
4041
import org.springframework.context.ApplicationListener;
4142
import org.springframework.context.support.GenericApplicationContext;
@@ -268,7 +269,7 @@ void startWhenHasStopTimeoutUsesDuration() {
268269
void startWhenHasIgnoreLabelIgnoresService() {
269270
EventCapturingListener listener = new EventCapturingListener();
270271
this.eventListeners.add(listener);
271-
setUpRunningServices(Map.of("org.springframework.boot.ignore", "true"));
272+
setUpRunningServices(true, Map.of("org.springframework.boot.ignore", "true"));
272273
this.lifecycleManager.start();
273274
this.shutdownHandlers.run();
274275
assertThat(listener.getEvent()).isNotNull();
@@ -285,6 +286,40 @@ void startWaitsUntilReady() {
285286
then(this.serviceReadinessChecks).should().waitUntilReady(this.runningServices);
286287
}
287288

289+
@Test
290+
void startWhenWaitNeverDoesNotWaitUntilReady() {
291+
this.properties.getReadiness().setWait(Wait.NEVER);
292+
EventCapturingListener listener = new EventCapturingListener();
293+
this.eventListeners.add(listener);
294+
setUpRunningServices();
295+
this.lifecycleManager.start();
296+
this.shutdownHandlers.run();
297+
then(this.serviceReadinessChecks).should(never()).waitUntilReady(this.runningServices);
298+
}
299+
300+
@Test
301+
void startWhenWaitOnlyIfStartedAndNotStartedDoesNotWaitUntilReady() {
302+
this.properties.getReadiness().setWait(Wait.ONLY_IF_STARTED);
303+
this.properties.setLifecycleManagement(LifecycleManagement.NONE);
304+
EventCapturingListener listener = new EventCapturingListener();
305+
this.eventListeners.add(listener);
306+
setUpRunningServices();
307+
this.lifecycleManager.start();
308+
this.shutdownHandlers.run();
309+
then(this.serviceReadinessChecks).should(never()).waitUntilReady(this.runningServices);
310+
}
311+
312+
@Test
313+
void startWhenWaitOnlyIfStartedAndStartedWaitsUntilReady() {
314+
this.properties.getReadiness().setWait(Wait.ONLY_IF_STARTED);
315+
EventCapturingListener listener = new EventCapturingListener();
316+
this.eventListeners.add(listener);
317+
setUpRunningServices(false);
318+
this.lifecycleManager.start();
319+
this.shutdownHandlers.run();
320+
then(this.serviceReadinessChecks).should().waitUntilReady(this.runningServices);
321+
}
322+
288323
@Test
289324
void startGetsDockerComposeWithActiveProfiles() {
290325
this.properties.getProfiles().setActive(Set.of("my-profile"));
@@ -306,16 +341,26 @@ void startPublishesEvent() {
306341
}
307342

308343
private void setUpRunningServices() {
309-
setUpRunningServices(Collections.emptyMap());
344+
setUpRunningServices(true);
310345
}
311346

312-
private void setUpRunningServices(Map<String, String> labels) {
347+
private void setUpRunningServices(boolean started) {
348+
setUpRunningServices(started, Collections.emptyMap());
349+
}
350+
351+
@SuppressWarnings("unchecked")
352+
private void setUpRunningServices(boolean started, Map<String, String> labels) {
313353
given(this.dockerCompose.hasDefinedServices()).willReturn(true);
314354
given(this.dockerCompose.hasRunningServices()).willReturn(true);
315355
RunningService runningService = mock(RunningService.class);
316356
given(runningService.labels()).willReturn(labels);
317357
this.runningServices = List.of(runningService);
318-
given(this.dockerCompose.getRunningServices()).willReturn(this.runningServices);
358+
if (started) {
359+
given(this.dockerCompose.getRunningServices()).willReturn(this.runningServices);
360+
}
361+
else {
362+
given(this.dockerCompose.getRunningServices()).willReturn(Collections.emptyList(), this.runningServices);
363+
}
319364
}
320365

321366
/**

Diff for: spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/lifecycle/DockerComposePropertiesTests.java

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import org.springframework.boot.context.properties.bind.Binder;
2727
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
28+
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Readiness.Wait;
2829

2930
import static org.assertj.core.api.Assertions.assertThat;
3031

@@ -48,6 +49,7 @@ void getWhenNoPropertiesReturnsNew() {
4849
assertThat(properties.getStop().getCommand()).isEqualTo(StopCommand.STOP);
4950
assertThat(properties.getStop().getTimeout()).isEqualTo(Duration.ofSeconds(10));
5051
assertThat(properties.getProfiles().getActive()).isEmpty();
52+
assertThat(properties.getReadiness().getWait()).isEqualTo(Wait.ALWAYS);
5153
assertThat(properties.getReadiness().getTimeout()).isEqualTo(Duration.ofMinutes(2));
5254
assertThat(properties.getReadiness().getTcp().getConnectTimeout()).isEqualTo(Duration.ofMillis(200));
5355
assertThat(properties.getReadiness().getTcp().getReadTimeout()).isEqualTo(Duration.ofMillis(200));
@@ -63,6 +65,7 @@ void getWhenPropertiesReturnsBound() {
6365
source.put("spring.docker.compose.stop.command", "down");
6466
source.put("spring.docker.compose.stop.timeout", "5s");
6567
source.put("spring.docker.compose.profiles.active", "myprofile");
68+
source.put("spring.docker.compose.readiness.wait", "only-if-started");
6669
source.put("spring.docker.compose.readiness.timeout", "10s");
6770
source.put("spring.docker.compose.readiness.tcp.connect-timeout", "400ms");
6871
source.put("spring.docker.compose.readiness.tcp.read-timeout", "500ms");
@@ -75,6 +78,7 @@ void getWhenPropertiesReturnsBound() {
7578
assertThat(properties.getStop().getCommand()).isEqualTo(StopCommand.DOWN);
7679
assertThat(properties.getStop().getTimeout()).isEqualTo(Duration.ofSeconds(5));
7780
assertThat(properties.getProfiles().getActive()).containsExactly("myprofile");
81+
assertThat(properties.getReadiness().getWait()).isEqualTo(Wait.ONLY_IF_STARTED);
7882
assertThat(properties.getReadiness().getTimeout()).isEqualTo(Duration.ofSeconds(10));
7983
assertThat(properties.getReadiness().getTcp().getConnectTimeout()).isEqualTo(Duration.ofMillis(400));
8084
assertThat(properties.getReadiness().getTcp().getReadTimeout()).isEqualTo(Duration.ofMillis(500));

0 commit comments

Comments
 (0)