Skip to content

Commit

Permalink
Add property to control Docker Compose start command execution
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mhalbritter committed Apr 5, 2024
1 parent 2ab0e02 commit 6d192e6
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 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.
Expand Down Expand Up @@ -32,6 +32,7 @@
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.Start.Skip;
import org.springframework.boot.docker.compose.lifecycle.DockerComposeProperties.Stop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
Expand Down Expand Up @@ -119,7 +120,11 @@ void start() {
Wait wait = this.properties.getReadiness().getWait();
List<RunningService> runningServices = dockerCompose.getRunningServices();
if (lifecycleManagement.shouldStart()) {
if (runningServices.isEmpty()) {
Skip skip = this.properties.getStart().getSkip();
if (skip.shouldSkip(runningServices)) {
logger.info(skip.getLogMessage());
}
else {
start.getCommand().applyTo(dockerCompose, start.getLogLevel());
runningServices = dockerCompose.getRunningServices();
if (wait == Wait.ONLY_IF_STARTED) {
Expand All @@ -129,9 +134,6 @@ void start() {
this.shutdownHandlers.add(() -> stop.getCommand().applyTo(dockerCompose, stop.getTimeout()));
}
}
else {
logger.info("There are already Docker Compose services running, skipping startup");
}
}
List<RunningService> relevantServices = new ArrayList<>(runningServices);
relevantServices.removeIf(this::isIgnored);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 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.
Expand All @@ -19,10 +19,12 @@
import java.io.File;
import java.time.Duration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.docker.compose.core.RunningService;
import org.springframework.boot.logging.LogLevel;

/**
Expand Down Expand Up @@ -148,6 +150,11 @@ public static class Start {
*/
private LogLevel logLevel = LogLevel.INFO;

/**
* Whether to skip executing the start command.
*/
private Skip skip = Skip.IF_RUNNING;

public StartCommand getCommand() {
return this.command;
}
Expand All @@ -164,6 +171,51 @@ public void setLogLevel(LogLevel logLevel) {
this.logLevel = logLevel;
}

public Skip getSkip() {
return this.skip;
}

public void setSkip(Skip skip) {
this.skip = skip;
}

/**
* Start command skip mode.
*/
public enum Skip {

/**
* Never skip start.
*/
NEVER {
@Override
boolean shouldSkip(List<RunningService> runningServices) {
return false;
}
},
/**
* Skip start if there are already services running.
*/
IF_RUNNING {
@Override
boolean shouldSkip(List<RunningService> runningServices) {
return !runningServices.isEmpty();
}

@Override
String getLogMessage() {
return "There are already Docker Compose services running, skipping startup";
}
};

abstract boolean shouldSkip(List<RunningService> runningServices);

String getLogMessage() {
return "";
}

}

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"name": "spring.docker.compose.start.log-level",
"defaultValue": "info"
},
{
"name": "spring.docker.compose.start.skip",
"defaultValue": "if-running"
},
{
"name": "spring.docker.compose.stop.command",
"defaultValue": "stop"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 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.
Expand Down Expand Up @@ -39,6 +39,7 @@
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.Skip;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.context.ApplicationContext;
Expand Down Expand Up @@ -384,6 +385,38 @@ void shouldNotLogIfThereAreNoServicesRunning(CapturedOutput output) {
assertThat(output).doesNotContain("There are already Docker Compose services running, skipping startup");
}

@Test
void shouldStartIfSkipModeIsIfRunningAndNoServicesAreRunning() {
given(this.dockerCompose.hasDefinedServices()).willReturn(true);
this.properties.getStart().setSkip(Skip.IF_RUNNING);
this.lifecycleManager.start();
then(this.dockerCompose).should().up(any());
}

@Test
void shouldNotStartIfSkipModeIsIfRunningAndServicesAreAlreadyRunning() {
setUpRunningServices();
this.properties.getStart().setSkip(Skip.IF_RUNNING);
this.lifecycleManager.start();
then(this.dockerCompose).should(never()).up(any());
}

@Test
void shouldStartIfSkipModeIsNeverAndNoServicesAreRunning() {
given(this.dockerCompose.hasDefinedServices()).willReturn(true);
this.properties.getStart().setSkip(Skip.NEVER);
this.lifecycleManager.start();
then(this.dockerCompose).should().up(any());
}

@Test
void shouldStartIfSkipModeIsNeverAndServicesAreAlreadyRunning() {
setUpRunningServices();
this.properties.getStart().setSkip(Skip.NEVER);
this.lifecycleManager.start();
then(this.dockerCompose).should().up(any());
}

private void setUpRunningServices() {
setUpRunningServices(true);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 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.
Expand All @@ -18,16 +18,21 @@

import java.io.File;
import java.time.Duration;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
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 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.Skip;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

/**
* Tests for {@link DockerComposeProperties}.
Expand Down Expand Up @@ -84,4 +89,16 @@ void getWhenPropertiesReturnsBound() {
assertThat(properties.getReadiness().getTcp().getReadTimeout()).isEqualTo(Duration.ofMillis(500));
}

@Test
void skipModeNeverShouldNeverSkip() {
assertThat(Skip.NEVER.shouldSkip(Collections.emptyList())).isFalse();
assertThat(Skip.NEVER.shouldSkip(List.of(mock(RunningService.class)))).isFalse();
}

@Test
void skipModeIfRunningShouldSkipWhenServicesAreRunning() {
assertThat(Skip.IF_RUNNING.shouldSkip(Collections.emptyList())).isFalse();
assertThat(Skip.IF_RUNNING.shouldSkip(List.of(mock(RunningService.class)))).isTrue();
}

}

0 comments on commit 6d192e6

Please sign in to comment.