From 3e00e71da4d2b5e7fd30315468d4e54c86ba6150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Bartha?= <39852431+totallyzen@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:21:09 +0100 Subject: [PATCH] feat(compose): support for setting profiles (#738) # Change Adds `profiles` support for docker compose. # Context I've had to use a workaround in a company project for compose profiles leading to multiple compose profile files. This should help by supporting profiles in compose which is a very handy feature when your service has some variety of ways to run (contextual, environmental, etc) Without this, it's still possible to run profiles buy setting the `COMPOSE_PROFILES` env variable, but this is certainly cleaner and easier on test writing. # Docker Docs https://docs.docker.com/compose/how-tos/profiles/#assigning-profiles-to-services --- core/testcontainers/compose/compose.py | 3 +++ .../profile_support/compose.yaml | 16 ++++++++++++ core/tests/test_compose.py | 26 ++++++++++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 core/tests/compose_fixtures/profile_support/compose.yaml diff --git a/core/testcontainers/compose/compose.py b/core/testcontainers/compose/compose.py index 564eda8f..e8ce3745 100644 --- a/core/testcontainers/compose/compose.py +++ b/core/testcontainers/compose/compose.py @@ -171,6 +171,7 @@ class DockerCompose: env_file: Optional[str] = None services: Optional[list[str]] = None docker_command_path: Optional[str] = None + profiles: Optional[list[str]] = None def __post_init__(self): if isinstance(self.compose_file_name, str): @@ -198,6 +199,8 @@ def compose_command_property(self) -> list[str]: if self.compose_file_name: for file in self.compose_file_name: docker_compose_cmd += ["-f", file] + if self.profiles: + docker_compose_cmd += [item for profile in self.profiles for item in ["--profile", profile]] if self.env_file: docker_compose_cmd += ["--env-file", self.env_file] return docker_compose_cmd diff --git a/core/tests/compose_fixtures/profile_support/compose.yaml b/core/tests/compose_fixtures/profile_support/compose.yaml new file mode 100644 index 00000000..c7bec7cc --- /dev/null +++ b/core/tests/compose_fixtures/profile_support/compose.yaml @@ -0,0 +1,16 @@ +services: + runs-always: &simple-service + image: alpine:latest + init: true + command: + - sh + - -c + - 'while true; do sleep 0.1 ; date -Ins; done' + runs-profile-a: + <<: *simple-service + profiles: + - profile-a + runs-profile-b: + <<: *simple-service + profiles: + - profile-b diff --git a/core/tests/test_compose.py b/core/tests/test_compose.py index b43da28c..9279ce3f 100644 --- a/core/tests/test_compose.py +++ b/core/tests/test_compose.py @@ -2,7 +2,7 @@ from pathlib import Path from re import split from time import sleep -from typing import Union +from typing import Union, Optional from urllib.request import urlopen, Request import pytest @@ -352,3 +352,27 @@ def fetch(req: Union[Request, str]): if 200 < res.getcode() >= 400: raise Exception(f"HTTP Error: {res.getcode()} - {res.reason}: {body}") return res.getcode(), body + + +@pytest.mark.parametrize( + argnames=["profiles", "running", "not_running"], + argvalues=[ + pytest.param(None, ["runs-always"], ["runs-profile-a", "runs-profile-b"], id="default"), + pytest.param( + ["profile-a"], ["runs-always", "runs-profile-a"], ["runs-profile-b"], id="one-additional-profile-via-str" + ), + pytest.param( + ["profile-a", "profile-b"], + ["runs-always", "runs-profile-a", "runs-profile-b"], + [], + id="all-profiles-explicitly", + ), + ], +) +def test_compose_profile_support(profiles: Optional[list[str]], running: list[str], not_running: list[str]): + with DockerCompose(context=FIXTURES / "profile_support", profiles=profiles) as compose: + for service in running: + assert compose.get_container(service) is not None + for service in not_running: + with pytest.raises(ContainerIsNotRunning): + compose.get_container(service)