Skip to content

Commit 59a4018

Browse files
committed
Merge branch 'gh-35435'
Closes gh-35435
2 parents a8602a1 + 9bacf42 commit 59a4018

File tree

7 files changed

+84
-91
lines changed

7 files changed

+84
-91
lines changed

spring-boot-project/spring-boot-devtools/src/main/resources/org/springframework/boot/devtools/env/devtools-property-defaults.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ spring.template.provider.cache=false
1414
spring.thymeleaf.cache=false
1515
spring.web.resources.cache.period=0
1616
spring.web.resources.chain.cache=false
17+
spring.docker.compose.readiness.wait=only-if-started

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/DefaultDockerCompose.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,6 @@ public boolean hasDefinedServices() {
6969
return !this.cli.run(new DockerCliCommand.ComposeConfig()).services().isEmpty();
7070
}
7171

72-
@Override
73-
public boolean hasRunningServices() {
74-
return runComposePs().stream().anyMatch(this::isRunning);
75-
}
76-
7772
@Override
7873
public List<RunningService> getRunningServices() {
7974
List<DockerCliComposePsResponse> runningPsResponses = runComposePs().stream().filter(this::isRunning).toList();

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/DockerCli.java

Lines changed: 76 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import java.io.File;
2020
import java.util.ArrayList;
2121
import java.util.Collections;
22+
import java.util.HashMap;
2223
import java.util.List;
24+
import java.util.Map;
2325
import java.util.Set;
2426
import java.util.function.Consumer;
2527

@@ -39,13 +41,13 @@
3941
*/
4042
class DockerCli {
4143

44+
private static final Map<File, DockerCommands> dockerCommandsCache = new HashMap<>();
45+
4246
private static final Log logger = LogFactory.getLog(DockerCli.class);
4347

4448
private final ProcessRunner processRunner;
4549

46-
private final List<String> dockerCommand;
47-
48-
private final List<String> dockerComposeCommand;
50+
private final DockerCommands dockerCommands;
4951

5052
private final DockerComposeFile composeFile;
5153

@@ -59,56 +61,12 @@ class DockerCli {
5961
*/
6062
DockerCli(File workingDirectory, DockerComposeFile composeFile, Set<String> activeProfiles) {
6163
this.processRunner = new ProcessRunner(workingDirectory);
62-
this.dockerCommand = getDockerCommand(this.processRunner);
63-
this.dockerComposeCommand = getDockerComposeCommand(this.processRunner);
64+
this.dockerCommands = dockerCommandsCache.computeIfAbsent(workingDirectory,
65+
(key) -> new DockerCommands(this.processRunner));
6466
this.composeFile = composeFile;
6567
this.activeProfiles = (activeProfiles != null) ? activeProfiles : Collections.emptySet();
6668
}
6769

68-
private List<String> getDockerCommand(ProcessRunner processRunner) {
69-
try {
70-
String version = processRunner.run("docker", "version", "--format", "{{.Client.Version}}");
71-
logger.trace(LogMessage.format("Using docker %s", version));
72-
return List.of("docker");
73-
}
74-
catch (ProcessStartException ex) {
75-
throw new DockerProcessStartException("Unable to start docker process. Is docker correctly installed?", ex);
76-
}
77-
catch (ProcessExitException ex) {
78-
if (ex.getStdErr().contains("docker daemon is not running")
79-
|| ex.getStdErr().contains("Cannot connect to the Docker daemon")) {
80-
throw new DockerNotRunningException(ex.getStdErr(), ex);
81-
}
82-
throw ex;
83-
84-
}
85-
}
86-
87-
private List<String> getDockerComposeCommand(ProcessRunner processRunner) {
88-
try {
89-
DockerCliComposeVersionResponse response = DockerJson.deserialize(
90-
processRunner.run("docker", "compose", "version", "--format", "json"),
91-
DockerCliComposeVersionResponse.class);
92-
logger.trace(LogMessage.format("Using docker compose %s", response.version()));
93-
return List.of("docker", "compose");
94-
}
95-
catch (ProcessExitException ex) {
96-
// Ignore and try docker-compose
97-
}
98-
try {
99-
DockerCliComposeVersionResponse response = DockerJson.deserialize(
100-
processRunner.run("docker-compose", "version", "--format", "json"),
101-
DockerCliComposeVersionResponse.class);
102-
logger.trace(LogMessage.format("Using docker-compose %s", response.version()));
103-
return List.of("docker-compose");
104-
}
105-
catch (ProcessStartException ex) {
106-
throw new DockerProcessStartException(
107-
"Unable to start 'docker-compose' process or use 'docker compose'. Is docker correctly installed?",
108-
ex);
109-
}
110-
}
111-
11270
/**
11371
* Run the given {@link DockerCli} command and return the response.
11472
* @param <R> the response type
@@ -132,9 +90,9 @@ private Consumer<String> createOutputConsumer(LogLevel logLevel) {
13290

13391
private List<String> createCommand(Type type) {
13492
return switch (type) {
135-
case DOCKER -> new ArrayList<>(this.dockerCommand);
93+
case DOCKER -> new ArrayList<>(this.dockerCommands.get(type));
13694
case DOCKER_COMPOSE -> {
137-
List<String> result = new ArrayList<>(this.dockerComposeCommand);
95+
List<String> result = new ArrayList<>(this.dockerCommands.get(type));
13896
if (this.composeFile != null) {
13997
result.add("--file");
14098
result.add(this.composeFile.toString());
@@ -158,4 +116,71 @@ DockerComposeFile getDockerComposeFile() {
158116
return this.composeFile;
159117
}
160118

119+
/**
120+
* Holds details of the actual CLI commands to invoke.
121+
*/
122+
private static class DockerCommands {
123+
124+
private final List<String> dockerCommand;
125+
126+
private final List<String> dockerComposeCommand;
127+
128+
DockerCommands(ProcessRunner processRunner) {
129+
this.dockerCommand = getDockerCommand(processRunner);
130+
this.dockerComposeCommand = getDockerComposeCommand(processRunner);
131+
}
132+
133+
private List<String> getDockerCommand(ProcessRunner processRunner) {
134+
try {
135+
String version = processRunner.run("docker", "version", "--format", "{{.Client.Version}}");
136+
logger.trace(LogMessage.format("Using docker %s", version));
137+
return List.of("docker");
138+
}
139+
catch (ProcessStartException ex) {
140+
throw new DockerProcessStartException("Unable to start docker process. Is docker correctly installed?",
141+
ex);
142+
}
143+
catch (ProcessExitException ex) {
144+
if (ex.getStdErr().contains("docker daemon is not running")
145+
|| ex.getStdErr().contains("Cannot connect to the Docker daemon")) {
146+
throw new DockerNotRunningException(ex.getStdErr(), ex);
147+
}
148+
throw ex;
149+
}
150+
}
151+
152+
private List<String> getDockerComposeCommand(ProcessRunner processRunner) {
153+
try {
154+
DockerCliComposeVersionResponse response = DockerJson.deserialize(
155+
processRunner.run("docker", "compose", "version", "--format", "json"),
156+
DockerCliComposeVersionResponse.class);
157+
logger.trace(LogMessage.format("Using docker compose %s", response.version()));
158+
return List.of("docker", "compose");
159+
}
160+
catch (ProcessExitException ex) {
161+
// Ignore and try docker-compose
162+
}
163+
try {
164+
DockerCliComposeVersionResponse response = DockerJson.deserialize(
165+
processRunner.run("docker-compose", "version", "--format", "json"),
166+
DockerCliComposeVersionResponse.class);
167+
logger.trace(LogMessage.format("Using docker-compose %s", response.version()));
168+
return List.of("docker-compose");
169+
}
170+
catch (ProcessStartException ex) {
171+
throw new DockerProcessStartException(
172+
"Unable to start 'docker-compose' process or use 'docker compose'. Is docker correctly installed?",
173+
ex);
174+
}
175+
}
176+
177+
List<String> get(Type type) {
178+
return switch (type) {
179+
case DOCKER -> this.dockerCommand;
180+
case DOCKER_COMPOSE -> this.dockerComposeCommand;
181+
};
182+
}
183+
184+
}
185+
161186
}

spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/DockerCompose.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,6 @@ public interface DockerCompose {
7373
*/
7474
boolean hasDefinedServices();
7575

76-
/**
77-
* Return if services defined in the {@link DockerComposeFile} for the active profile
78-
* are running.
79-
* @return {@code true} if services are running
80-
* @see #hasDefinedServices()
81-
* @see #getRunningServices()
82-
*/
83-
boolean hasRunningServices();
84-
8576
/**
8677
* Return the running services for the active profile, or an empty list if no services
8778
* are running.

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,19 +112,21 @@ void start() {
112112
Start start = this.properties.getStart();
113113
Stop stop = this.properties.getStop();
114114
Wait wait = this.properties.getReadiness().getWait();
115-
if (lifecycleManagement.shouldStart() && !dockerCompose.hasRunningServices()) {
115+
List<RunningService> runningServices = dockerCompose.getRunningServices();
116+
if (lifecycleManagement.shouldStart() && runningServices.isEmpty()) {
116117
start.getCommand().applyTo(dockerCompose, start.getLogLevel());
118+
runningServices = dockerCompose.getRunningServices();
117119
wait = (wait != Wait.ONLY_IF_STARTED) ? wait : Wait.ALWAYS;
118120
if (lifecycleManagement.shouldStop()) {
119121
this.shutdownHandlers.add(() -> stop.getCommand().applyTo(dockerCompose, stop.getTimeout()));
120122
}
121123
}
122-
List<RunningService> runningServices = new ArrayList<>(dockerCompose.getRunningServices());
123-
runningServices.removeIf(this::isIgnored);
124+
List<RunningService> relevantServices = new ArrayList<>(runningServices);
125+
relevantServices.removeIf(this::isIgnored);
124126
if (wait == Wait.ALWAYS || wait == null) {
125-
this.serviceReadinessChecks.waitUntilReady(runningServices);
127+
this.serviceReadinessChecks.waitUntilReady(relevantServices);
126128
}
127-
publishEvent(new DockerComposeServicesReadyEvent(this.applicationContext, runningServices));
129+
publishEvent(new DockerComposeServicesReadyEvent(this.applicationContext, relevantServices));
128130
}
129131

130132
protected DockerComposeFile getComposeFile() {

spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/core/DefaultDockerComposeTests.java

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -96,26 +96,6 @@ void hasDefinedServicesWhenComposeConfigServicesIsNotEmptyReturnsTrue() {
9696
assertThat(compose.hasDefinedServices()).isTrue();
9797
}
9898

99-
@Test
100-
void hasRunningServicesWhenPsListsRunningServiceReturnsTrue() {
101-
willReturn(List.of(new DockerCliComposePsResponse("id", "name", "image", "exited"),
102-
new DockerCliComposePsResponse("id", "name", "image", "running")))
103-
.given(this.cli)
104-
.run(new DockerCliCommand.ComposePs());
105-
DefaultDockerCompose compose = new DefaultDockerCompose(this.cli, HOST);
106-
assertThat(compose.hasRunningServices()).isTrue();
107-
}
108-
109-
@Test
110-
void hasRunningServicesWhenPsListReturnsAllExitedReturnsFalse() {
111-
willReturn(List.of(new DockerCliComposePsResponse("id", "name", "image", "exited"),
112-
new DockerCliComposePsResponse("id", "name", "image", "running")))
113-
.given(this.cli)
114-
.run(new DockerCliCommand.ComposePs());
115-
DefaultDockerCompose compose = new DefaultDockerCompose(this.cli, HOST);
116-
assertThat(compose.hasRunningServices()).isTrue();
117-
}
118-
11999
@Test
120100
void getRunningServicesReturnsServices() {
121101
String id = "123";

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,6 @@ private void setUpRunningServices(boolean started) {
351351
@SuppressWarnings("unchecked")
352352
private void setUpRunningServices(boolean started, Map<String, String> labels) {
353353
given(this.dockerCompose.hasDefinedServices()).willReturn(true);
354-
given(this.dockerCompose.hasRunningServices()).willReturn(true);
355354
RunningService runningService = mock(RunningService.class);
356355
given(runningService.labels()).willReturn(labels);
357356
this.runningServices = List.of(runningService);

0 commit comments

Comments
 (0)