From e2cf9e105c69f35ac6e38c994ecd526dee1b7883 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Tue, 10 Dec 2024 14:22:01 +0100 Subject: [PATCH] Respect profiles when listing running Docker Compose containers Closes gh-40139 --- .../DefaultDockerComposeIntegrationTests.java | 116 ++++++++++++++++++ .../boot/docker/compose/core/profiles.yaml | 17 +++ .../docker/compose/core/DockerCliCommand.java | 5 +- .../compose/core/DockerCliCommandTests.java | 4 +- 4 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/core/DefaultDockerComposeIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/core/profiles.yaml diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/core/DefaultDockerComposeIntegrationTests.java b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/core/DefaultDockerComposeIntegrationTests.java new file mode 100644 index 000000000000..2021f0af6a5f --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/java/org/springframework/boot/docker/compose/core/DefaultDockerComposeIntegrationTests.java @@ -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 runningServicesProfile1 = dockerComposeWithProfile1.getRunningServices(); + assertThatContainsService(runningServicesProfile1, "redis1"); + assertThatDoesNotContainService(runningServicesProfile1, "redis2"); + assertThatContainsService(runningServicesProfile1, "redis3"); + + List 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 runningServices, String serviceName) { + for (RunningService runningService : runningServices) { + if (runningService.name().contains(serviceName)) { + return runningService; + } + } + return null; + } + + private void assertThatDoesNotContainService(List 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 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; + } + +} diff --git a/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/core/profiles.yaml b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/core/profiles.yaml new file mode 100644 index 000000000000..c32fb2b121e6 --- /dev/null +++ b/spring-boot-project/spring-boot-docker-compose/src/dockerTest/resources/org/springframework/boot/docker/compose/core/profiles.yaml @@ -0,0 +1,17 @@ +services: + redis1: + profiles: + - '1' + image: '{imageName}' + ports: + - '6379' + redis2: + profiles: + - '2' + image: '{imageName}' + ports: + - '6379' + redis3: + image: '{imageName}' + ports: + - '6379' diff --git a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/DockerCliCommand.java b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/DockerCliCommand.java index feb2d8dd6aa8..9c9f588ecfc2 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/DockerCliCommand.java +++ b/spring-boot-project/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/DockerCliCommand.java @@ -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 @@ static final class ComposeConfig extends DockerCliCommand> { ComposePs() { - super(Type.DOCKER_COMPOSE, DockerCliComposePsResponse.class, true, "ps", "--format=json"); + super(Type.DOCKER_COMPOSE, DockerCliComposePsResponse.class, true, "ps", "--orphans=false", + "--format=json"); } } diff --git a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/core/DockerCliCommandTests.java b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/core/DockerCliCommandTests.java index 4ed1d983bd17..c1aa4abf6793 100644 --- a/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/core/DockerCliCommandTests.java +++ b/spring-boot-project/spring-boot-docker-compose/src/test/java/org/springframework/boot/docker/compose/core/DockerCliCommandTests.java @@ -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 @@ void composeConfig() { 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); }