Skip to content

Commit 6d192e6

Browse files
committed
Add property to control Docker Compose start command execution
If the property 'spring.docker.compose.start.skip' is set to 'never', the start command is always executed. The default value of 'if-running' only executes the start command if there are no services running already, which is the old behavior. Closes gh-39749
1 parent 2ab0e02 commit 6d192e6

File tree

5 files changed

+116
-8
lines changed

5 files changed

+116
-8
lines changed

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

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@
3232
import org.springframework.boot.docker.compose.core.RunningService;
3333
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Readiness.Wait;
3434
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Start;
35+
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Start.Skip;
3536
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Stop;
3637
import org.springframework.context.ApplicationContext;
3738
import org.springframework.context.ApplicationListener;
@@ -119,7 +120,11 @@ void start() {
119120
Wait wait = this.properties.getReadiness().getWait();
120121
List<RunningService> runningServices = dockerCompose.getRunningServices();
121122
if (lifecycleManagement.shouldStart()) {
122-
if (runningServices.isEmpty()) {
123+
Skip skip = this.properties.getStart().getSkip();
124+
if (skip.shouldSkip(runningServices)) {
125+
logger.info(skip.getLogMessage());
126+
}
127+
else {
123128
start.getCommand().applyTo(dockerCompose, start.getLogLevel());
124129
runningServices = dockerCompose.getRunningServices();
125130
if (wait == Wait.ONLY_IF_STARTED) {
@@ -129,9 +134,6 @@ void start() {
129134
this.shutdownHandlers.add(() -> stop.getCommand().applyTo(dockerCompose, stop.getTimeout()));
130135
}
131136
}
132-
else {
133-
logger.info("There are already Docker Compose services running, skipping startup");
134-
}
135137
}
136138
List<RunningService> relevantServices = new ArrayList<>(runningServices);
137139
relevantServices.removeIf(this::isIgnored);

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

+53-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,10 +19,12 @@
1919
import java.io.File;
2020
import java.time.Duration;
2121
import java.util.LinkedHashSet;
22+
import java.util.List;
2223
import java.util.Set;
2324

2425
import org.springframework.boot.context.properties.ConfigurationProperties;
2526
import org.springframework.boot.context.properties.bind.Binder;
27+
import org.springframework.boot.docker.compose.core.RunningService;
2628
import org.springframework.boot.logging.LogLevel;
2729

2830
/**
@@ -148,6 +150,11 @@ public static class Start {
148150
*/
149151
private LogLevel logLevel = LogLevel.INFO;
150152

153+
/**
154+
* Whether to skip executing the start command.
155+
*/
156+
private Skip skip = Skip.IF_RUNNING;
157+
151158
public StartCommand getCommand() {
152159
return this.command;
153160
}
@@ -164,6 +171,51 @@ public void setLogLevel(LogLevel logLevel) {
164171
this.logLevel = logLevel;
165172
}
166173

174+
public Skip getSkip() {
175+
return this.skip;
176+
}
177+
178+
public void setSkip(Skip skip) {
179+
this.skip = skip;
180+
}
181+
182+
/**
183+
* Start command skip mode.
184+
*/
185+
public enum Skip {
186+
187+
/**
188+
* Never skip start.
189+
*/
190+
NEVER {
191+
@Override
192+
boolean shouldSkip(List<RunningService> runningServices) {
193+
return false;
194+
}
195+
},
196+
/**
197+
* Skip start if there are already services running.
198+
*/
199+
IF_RUNNING {
200+
@Override
201+
boolean shouldSkip(List<RunningService> runningServices) {
202+
return !runningServices.isEmpty();
203+
}
204+
205+
@Override
206+
String getLogMessage() {
207+
return "There are already Docker Compose services running, skipping startup";
208+
}
209+
};
210+
211+
abstract boolean shouldSkip(List<RunningService> runningServices);
212+
213+
String getLogMessage() {
214+
return "";
215+
}
216+
217+
}
218+
167219
}
168220

169221
/**

spring-boot-project/spring-boot-docker-compose/src/main/resources/META-INF/additional-spring-configuration-metadata.json

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
"name": "spring.docker.compose.start.log-level",
1919
"defaultValue": "info"
2020
},
21+
{
22+
"name": "spring.docker.compose.start.skip",
23+
"defaultValue": "if-running"
24+
},
2125
{
2226
"name": "spring.docker.compose.stop.command",
2327
"defaultValue": "stop"

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

+34-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@
3939
import org.springframework.boot.docker.compose.core.DockerComposeFile;
4040
import org.springframework.boot.docker.compose.core.RunningService;
4141
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Readiness.Wait;
42+
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Start.Skip;
4243
import org.springframework.boot.test.system.CapturedOutput;
4344
import org.springframework.boot.test.system.OutputCaptureExtension;
4445
import org.springframework.context.ApplicationContext;
@@ -384,6 +385,38 @@ void shouldNotLogIfThereAreNoServicesRunning(CapturedOutput output) {
384385
assertThat(output).doesNotContain("There are already Docker Compose services running, skipping startup");
385386
}
386387

388+
@Test
389+
void shouldStartIfSkipModeIsIfRunningAndNoServicesAreRunning() {
390+
given(this.dockerCompose.hasDefinedServices()).willReturn(true);
391+
this.properties.getStart().setSkip(Skip.IF_RUNNING);
392+
this.lifecycleManager.start();
393+
then(this.dockerCompose).should().up(any());
394+
}
395+
396+
@Test
397+
void shouldNotStartIfSkipModeIsIfRunningAndServicesAreAlreadyRunning() {
398+
setUpRunningServices();
399+
this.properties.getStart().setSkip(Skip.IF_RUNNING);
400+
this.lifecycleManager.start();
401+
then(this.dockerCompose).should(never()).up(any());
402+
}
403+
404+
@Test
405+
void shouldStartIfSkipModeIsNeverAndNoServicesAreRunning() {
406+
given(this.dockerCompose.hasDefinedServices()).willReturn(true);
407+
this.properties.getStart().setSkip(Skip.NEVER);
408+
this.lifecycleManager.start();
409+
then(this.dockerCompose).should().up(any());
410+
}
411+
412+
@Test
413+
void shouldStartIfSkipModeIsNeverAndServicesAreAlreadyRunning() {
414+
setUpRunningServices();
415+
this.properties.getStart().setSkip(Skip.NEVER);
416+
this.lifecycleManager.start();
417+
then(this.dockerCompose).should().up(any());
418+
}
419+
387420
private void setUpRunningServices() {
388421
setUpRunningServices(true);
389422
}

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

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,16 +18,21 @@
1818

1919
import java.io.File;
2020
import java.time.Duration;
21+
import java.util.Collections;
2122
import java.util.LinkedHashMap;
23+
import java.util.List;
2224
import java.util.Map;
2325

2426
import org.junit.jupiter.api.Test;
2527

2628
import org.springframework.boot.context.properties.bind.Binder;
2729
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
30+
import org.springframework.boot.docker.compose.core.RunningService;
2831
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Readiness.Wait;
32+
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Start.Skip;
2933

3034
import static org.assertj.core.api.Assertions.assertThat;
35+
import static org.mockito.Mockito.mock;
3136

3237
/**
3338
* Tests for {@link DockerComposeProperties}.
@@ -84,4 +89,16 @@ void getWhenPropertiesReturnsBound() {
8489
assertThat(properties.getReadiness().getTcp().getReadTimeout()).isEqualTo(Duration.ofMillis(500));
8590
}
8691

92+
@Test
93+
void skipModeNeverShouldNeverSkip() {
94+
assertThat(Skip.NEVER.shouldSkip(Collections.emptyList())).isFalse();
95+
assertThat(Skip.NEVER.shouldSkip(List.of(mock(RunningService.class)))).isFalse();
96+
}
97+
98+
@Test
99+
void skipModeIfRunningShouldSkipWhenServicesAreRunning() {
100+
assertThat(Skip.IF_RUNNING.shouldSkip(Collections.emptyList())).isFalse();
101+
assertThat(Skip.IF_RUNNING.shouldSkip(List.of(mock(RunningService.class)))).isTrue();
102+
}
103+
87104
}

0 commit comments

Comments
 (0)