Skip to content

Commit 7589a8f

Browse files
authored
Update some files from upstream docker-stacks project (#108)
1 parent 42dc1c9 commit 7589a8f

File tree

3 files changed

+47
-22
lines changed

3 files changed

+47
-22
lines changed

pytest.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[pytest]
22
addopts = -ra --color=yes
33
log_cli = 1
4-
log_cli_level = DEBUG
4+
log_cli_level = INFO
55
log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
66
log_cli_date_format=%Y-%m-%d %H:%M:%S
77
markers =

{{cookiecutter.stack_name}}/tests/conftest.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
3+
import logging
34
import os
45
from collections.abc import Generator
56

@@ -10,6 +11,8 @@
1011
from tests.utils.tracked_container import TrackedContainer
1112
from urllib3.util.retry import Retry
1213

14+
LOGGER = logging.getLogger(__name__)
15+
1316

1417
@pytest.fixture(scope="session")
1518
def http_client() -> requests.Session:
@@ -24,7 +27,9 @@ def http_client() -> requests.Session:
2427
@pytest.fixture(scope="session")
2528
def docker_client() -> docker.DockerClient:
2629
"""Docker client configured based on the host environment"""
27-
return docker.from_env()
30+
client = docker.from_env()
31+
LOGGER.debug(f"Docker client created: {client.version()}")
32+
return client
2833

2934

3035
@pytest.fixture(scope="session")
@@ -45,7 +50,6 @@ def container(
4550
container = TrackedContainer(
4651
docker_client,
4752
image_name,
48-
detach=True,
4953
)
5054
yield container
5155
container.remove()

{{cookiecutter.stack_name}}/tests/utils/tracked_container.py

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,18 @@ class TrackedContainer:
1919
Docker client instance
2020
image_name: str
2121
Name of the docker image to launch
22-
**kwargs: dict, optional
23-
Default keyword arguments to pass to docker.DockerClient.containers.run
2422
"""
2523

2624
def __init__(
2725
self,
2826
docker_client: docker.DockerClient,
2927
image_name: str,
30-
**kwargs: Any,
3128
):
3229
self.container: Container | None = None
3330
self.docker_client: docker.DockerClient = docker_client
3431
self.image_name: str = image_name
35-
self.kwargs: Any = kwargs
3632

37-
def run_detached(self, **kwargs: Any) -> Container:
33+
def run_detached(self, **kwargs: Any) -> None:
3834
"""Runs a docker container using the pre-configured image name
3935
and a mix of the pre-configured container options and those passed
4036
to this method.
@@ -47,18 +43,41 @@ def run_detached(self, **kwargs: Any) -> Container:
4743
**kwargs: dict, optional
4844
Keyword arguments to pass to docker.DockerClient.containers.run
4945
extending and/or overriding key/value pairs passed to the constructor
50-
51-
Returns
52-
-------
53-
docker.Container
5446
"""
55-
all_kwargs = self.kwargs | kwargs
56-
LOGGER.info(f"Running {self.image_name} with args {all_kwargs} ...")
47+
LOGGER.info(
48+
f"Creating a container for the image: {self.image_name} with args: {kwargs} ..."
49+
)
50+
default_kwargs = {"detach": True, "tty": True}
51+
final_kwargs = default_kwargs | kwargs
5752
self.container = self.docker_client.containers.run(
58-
self.image_name,
59-
**all_kwargs,
53+
self.image_name, **final_kwargs
6054
)
61-
return self.container
55+
LOGGER.info(f"Container {self.container.name} created")
56+
57+
def get_logs(self) -> str:
58+
assert self.container is not None
59+
logs = self.container.logs().decode()
60+
assert isinstance(logs, str)
61+
return logs
62+
63+
def get_health(self) -> str:
64+
assert self.container is not None
65+
self.container.reload()
66+
return self.container.health # type: ignore
67+
68+
def exec_cmd(self, cmd: str, **kwargs: Any) -> str:
69+
assert self.container is not None
70+
container = self.container
71+
72+
LOGGER.info(f"Running cmd: `{cmd}` on container: {container.name}")
73+
default_kwargs = {"tty": True}
74+
final_kwargs = default_kwargs | kwargs
75+
exec_result = container.exec_run(cmd, **final_kwargs)
76+
output = exec_result.output.decode().rstrip()
77+
assert isinstance(output, str)
78+
LOGGER.debug(f"Command output: {output}")
79+
assert exec_result.exit_code == 0, f"Command: `{cmd}` failed"
80+
return output
6281

6382
def run_and_wait(
6483
self,
@@ -68,14 +87,15 @@ def run_and_wait(
6887
no_failure: bool = True,
6988
**kwargs: Any,
7089
) -> str:
71-
running_container = self.run_detached(**kwargs)
72-
rv = running_container.wait(timeout=timeout)
73-
logs = running_container.logs().decode("utf-8")
74-
assert isinstance(logs, str)
90+
self.run_detached(**kwargs)
91+
assert self.container is not None
92+
rv = self.container.wait(timeout=timeout)
93+
logs = self.get_logs()
7594
LOGGER.debug(logs)
7695
assert no_warnings == (not self.get_warnings(logs))
7796
assert no_errors == (not self.get_errors(logs))
7897
assert no_failure == (rv["StatusCode"] == 0)
98+
self.remove()
7999
return logs
80100

81101
@staticmethod
@@ -93,8 +113,9 @@ def _lines_starting_with(logs: str, pattern: str) -> list[str]:
93113
def remove(self) -> None:
94114
"""Kills and removes the tracked docker container."""
95115
if self.container is None:
96-
LOGGER.info("No container to remove")
116+
LOGGER.debug("No container to remove")
97117
else:
98118
LOGGER.info(f"Removing container {self.container.name} ...")
99119
self.container.remove(force=True)
100120
LOGGER.info(f"Container {self.container.name} removed")
121+
self.container = None

0 commit comments

Comments
 (0)