Respect profiles when listing running Docker Compose containers

Closes gh-40139
This commit is contained in:
Moritz Halbritter 2024-12-10 14:22:01 +01:00
parent 68b5b956d1
commit e2cf9e105c
4 changed files with 138 additions and 4 deletions

View File

@ -0,0 +1,116 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.docker.compose.core;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.List;
import java.util.Set;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.testsupport.container.DisabledIfDockerUnavailable;
import org.springframework.boot.testsupport.container.TestImage;
import org.springframework.boot.testsupport.process.DisabledIfProcessUnavailable;
import org.springframework.core.io.ClassPathResource;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DefaultDockerCompose}.
*
* @author Moritz Halbritter
*/
@DisabledIfDockerUnavailable
@DisabledIfProcessUnavailable({ "docker", "compose" })
class DefaultDockerComposeIntegrationTests {
@Test
void shouldWorkWithProfiles(@TempDir Path tempDir) throws IOException {
// Profile 1 contains redis1 and redis3
// Profile 2 contains redis2 and redis3
File composeFile = createComposeFile(tempDir, "profiles.yaml").toFile();
DefaultDockerCompose dockerComposeWithProfile1 = new DefaultDockerCompose(
new DockerCli(tempDir.toFile(), DockerComposeFile.of(composeFile), Set.of("1")), null);
DefaultDockerCompose dockerComposeWithProfile2 = new DefaultDockerCompose(
new DockerCli(tempDir.toFile(), DockerComposeFile.of(composeFile), Set.of("2")), null);
DefaultDockerCompose dockerComposeWithAllProfiles = new DefaultDockerCompose(
new DockerCli(tempDir.toFile(), DockerComposeFile.of(composeFile), Set.of("1", "2")), null);
dockerComposeWithAllProfiles.up(LogLevel.DEBUG);
try {
List<RunningService> runningServicesProfile1 = dockerComposeWithProfile1.getRunningServices();
assertThatContainsService(runningServicesProfile1, "redis1");
assertThatDoesNotContainService(runningServicesProfile1, "redis2");
assertThatContainsService(runningServicesProfile1, "redis3");
List<RunningService> runningServicesProfile2 = dockerComposeWithProfile2.getRunningServices();
assertThatDoesNotContainService(runningServicesProfile2, "redis1");
assertThatContainsService(runningServicesProfile2, "redis2");
assertThatContainsService(runningServicesProfile2, "redis3");
// Assert that redis3 is started only once and is shared between profile 1 and
// profile 2
assertThat(dockerComposeWithAllProfiles.getRunningServices()).hasSize(3);
RunningService redis3Profile1 = findService(runningServicesProfile1, "redis3");
RunningService redis3Profile2 = findService(runningServicesProfile2, "redis3");
assertThat(redis3Profile1).isNotNull();
assertThat(redis3Profile2).isNotNull();
assertThat(redis3Profile1.name()).isEqualTo(redis3Profile2.name());
}
finally {
dockerComposeWithAllProfiles.down(Duration.ofSeconds(10));
}
}
private RunningService findService(List<RunningService> runningServices, String serviceName) {
for (RunningService runningService : runningServices) {
if (runningService.name().contains(serviceName)) {
return runningService;
}
}
return null;
}
private void assertThatDoesNotContainService(List<RunningService> runningServices, String service) {
if (findService(runningServices, service) != null) {
Assertions.fail("Did not expect service '%s', but found it in [%s]", service, runningServices);
}
}
private void assertThatContainsService(List<RunningService> runningServices, String service) {
if (findService(runningServices, service) == null) {
Assertions.fail("Expected service '%s', but hasn't been found in [%s]", service, runningServices);
}
}
private static Path createComposeFile(Path tempDir, String resource) throws IOException {
String composeFileTemplate = new ClassPathResource(resource, DockerCliIntegrationTests.class)
.getContentAsString(StandardCharsets.UTF_8);
String content = composeFileTemplate.replace("{imageName}", TestImage.REDIS.toString());
Path composeFile = tempDir.resolve(resource);
Files.writeString(composeFile, content);
return composeFile;
}
}

View File

@ -0,0 +1,17 @@
services:
redis1:
profiles:
- '1'
image: '{imageName}'
ports:
- '6379'
redis2:
profiles:
- '2'
image: '{imageName}'
ports:
- '6379'
redis3:
image: '{imageName}'
ports:
- '6379'

View File

@ -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.
@ -151,7 +151,8 @@ abstract sealed class DockerCliCommand<R> {
static final class ComposePs extends DockerCliCommand<List<DockerCliComposePsResponse>> {
ComposePs() {
super(Type.DOCKER_COMPOSE, DockerCliComposePsResponse.class, true, "ps", "--format=json");
super(Type.DOCKER_COMPOSE, DockerCliComposePsResponse.class, true, "ps", "--orphans=false",
"--format=json");
}
}

View File

@ -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.
@ -62,7 +62,7 @@ class DockerCliCommandTests {
void composePs() {
DockerCliCommand<?> command = new DockerCliCommand.ComposePs();
assertThat(command.getType()).isEqualTo(DockerCliCommand.Type.DOCKER_COMPOSE);
assertThat(command.getCommand()).containsExactly("ps", "--format=json");
assertThat(command.getCommand()).containsExactly("ps", "--orphans=false", "--format=json");
assertThat(command.deserialize("[]")).isInstanceOf(List.class);
}