Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compose): allow running specific services in compose #337

Merged
merged 7 commits into from
May 9, 2023
14 changes: 11 additions & 3 deletions compose/testcontainers/compose/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import requests
import subprocess
from typing import Iterable, List, Optional, Tuple, Union

from testcontainers.core.waiting_utils import wait_container_is_ready
import requests

from testcontainers.core.exceptions import NoSuchPortExposed
from testcontainers.core.waiting_utils import wait_container_is_ready
balint-backmaker marked this conversation as resolved.
Show resolved Hide resolved


class DockerCompose:
Expand All @@ -16,6 +17,7 @@ class DockerCompose:
pull: Pull images before launching environment.
build: Build images referenced in the configuration file.
env_file: Path to an env file containing environment variables to pass to docker compose.
services: The list of services to be run when starting this DockerCompose.
balint-backmaker marked this conversation as resolved.
Show resolved Hide resolved

Example:

Expand All @@ -38,19 +40,23 @@ class DockerCompose:
hello-world:
image: "hello-world"
"""

def __init__(
self,
filepath: str,
compose_file_name: Union[str, Iterable] = "docker-compose.yml",
pull: bool = False,
build: bool = False,
env_file: Optional[str] = None) -> None:
env_file: Optional[str] = None,
services: Optional[List[str]] = None
) -> None:
self.filepath = filepath
self.compose_file_names = [compose_file_name] if isinstance(compose_file_name, str) else \
list(compose_file_name)
self.pull = pull
self.build = build
self.env_file = env_file
self.services = services

def __enter__(self) -> "DockerCompose":
self.start()
Expand Down Expand Up @@ -84,6 +90,8 @@ def start(self) -> None:
up_cmd = self.docker_compose_command() + ['up', '-d']
if self.build:
up_cmd.append('--build')
if self.services:
up_cmd.extend(self.services)

self._call_command(cmd=up_cmd)

Expand Down
33 changes: 32 additions & 1 deletion compose/tests/test_docker_compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from testcontainers.core.exceptions import NoSuchPortExposed
from testcontainers.core.waiting_utils import wait_for_logs


ROOT = os.path.dirname(__file__)


Expand Down Expand Up @@ -40,6 +39,38 @@ def test_can_build_images_before_spawning_service_via_compose():
assert "--build" in docker_compose_cmd


def test_can_specify_services():
with patch.object(DockerCompose, "_call_command") as call_mock:
with DockerCompose(ROOT, services=["hub", "firefox"]) as compose:
...

assert compose.services
docker_compose_cmd = call_mock.call_args_list[0][1]["cmd"]
services_at_the_end = docker_compose_cmd[-2:]
assert "firefox" in services_at_the_end
assert "hub" in services_at_the_end
assert "chrome" not in docker_compose_cmd


@pytest.mark.parametrize("should_run_hub", [
[True],
[False],
])
def test_can_run_specific_services(should_run_hub: bool):
# compose V2 will improve this test by being able to assert that "firefox" also started/exited
services = ["firefox"]
if should_run_hub:
services.append("hub")

with DockerCompose(ROOT, services=services) as compose:
if should_run_hub:
assert compose.get_service_host("hub", 4444)
assert compose.get_service_port("hub", 4444)
else:
with pytest.raises(NoSuchPortExposed):
assert compose.get_service_host("hub", 4444)


def test_can_throw_exception_if_no_port_exposed():
with DockerCompose(ROOT) as compose:
with pytest.raises(NoSuchPortExposed):
Expand Down