Skip to content

Commit 472b2c2

Browse files
authored
fix(ryuk): Enable Ryuk test suite. Ryuk image 0.5.1 -> 0.7.0. Add RYUK_RECONNECTION_TIMEOUT env variable (#509)
- Re-enables the Ryuk test suite - Bumps Ryuk container image from 0.5.1 - > 0.7.0 - Add env variable `RYUK_RECONNECTION_TIMEOUT` (As documented in the [official Ryuk repo](https://github.com/testcontainers/moby-ryuk?tab=readme-ov-file#ryuk-configuration))
1 parent 90762e8 commit 472b2c2

File tree

5 files changed

+64
-30
lines changed

5 files changed

+64
-30
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ The snippet above will spin up a postgres database in a container. The `get_conn
2525

2626
## Configuration
2727

28-
| Env Variable | Example | Description |
29-
| ----------------------------------------- | ----------------------------- | ---------------------------------------- |
30-
| `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE` | `/var/run/docker.sock` | Path to Docker's socket used by ryuk |
31-
| `TESTCONTAINERS_RYUK_PRIVILEGED` | `false` | Run ryuk as a privileged container |
32-
| `TESTCONTAINERS_RYUK_DISABLED` | `false` | Disable ryuk |
33-
| `RYUK_CONTAINER_IMAGE` | `testcontainers/ryuk:0.5.1` | Custom image for ryuk |
28+
| Env Variable | Example | Description |
29+
| --------------------------------------- | --------------------------- | ---------------------------------------------------------------------------------- |
30+
| `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE` | `/var/run/docker.sock` | Path to Docker's socket used by ryuk |
31+
| `TESTCONTAINERS_RYUK_PRIVILEGED` | `false` | Run ryuk as a privileged container |
32+
| `TESTCONTAINERS_RYUK_DISABLED` | `false` | Disable ryuk |
33+
| `RYUK_CONTAINER_IMAGE` | `testcontainers/ryuk:0.7.0` | Custom image for ryuk |
34+
| `RYUK_RECONNECTION_TIMEOUT` | `10s` | Reconnection timeout for Ryuk TCP socket before Ryuk reaps all dangling containers |

core/testcontainers/core/config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
SLEEP_TIME = int(environ.get("TC_POOLING_INTERVAL", 1))
55
TIMEOUT = MAX_TRIES * SLEEP_TIME
66

7-
RYUK_IMAGE: str = environ.get("RYUK_CONTAINER_IMAGE", "testcontainers/ryuk:0.5.1")
7+
RYUK_IMAGE: str = environ.get("RYUK_CONTAINER_IMAGE", "testcontainers/ryuk:0.7.0")
88
RYUK_PRIVILEGED: bool = environ.get("TESTCONTAINERS_RYUK_PRIVILEGED", "false") == "true"
99
RYUK_DISABLED: bool = environ.get("TESTCONTAINERS_RYUK_DISABLED", "false") == "true"
1010
RYUK_DOCKER_SOCKET: str = environ.get("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", "/var/run/docker.sock")
11+
RYUK_RECONNECTION_TIMEOUT: str = environ.get("RYUK_RECONNECTION_TIMEOUT", "10s")

core/testcontainers/core/container.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1+
import contextlib
12
from platform import system
23
from socket import socket
34
from typing import TYPE_CHECKING, Optional
45

5-
from testcontainers.core.config import RYUK_DISABLED, RYUK_DOCKER_SOCKET, RYUK_IMAGE, RYUK_PRIVILEGED
6+
import docker.errors
7+
8+
from testcontainers.core.config import (
9+
RYUK_DISABLED,
10+
RYUK_DOCKER_SOCKET,
11+
RYUK_IMAGE,
12+
RYUK_PRIVILEGED,
13+
RYUK_RECONNECTION_TIMEOUT,
14+
)
615
from testcontainers.core.docker_client import DockerClient
716
from testcontainers.core.exceptions import ContainerStartException
817
from testcontainers.core.labels import LABEL_SESSION_ID, SESSION_ID
@@ -177,8 +186,9 @@ def delete_instance(cls) -> None:
177186
Reaper._socket.close()
178187
Reaper._socket = None
179188

180-
if Reaper._container is not None:
181-
Reaper._container.stop()
189+
if Reaper._container is not None and Reaper._container._container is not None:
190+
with contextlib.suppress(docker.errors.NotFound):
191+
Reaper._container.stop()
182192
Reaper._container = None
183193

184194
if Reaper._instance is not None:
@@ -194,6 +204,7 @@ def _create_instance(cls) -> "Reaper":
194204
.with_exposed_ports(8080)
195205
.with_volume_mapping(RYUK_DOCKER_SOCKET, "/var/run/docker.sock", "rw")
196206
.with_kwargs(privileged=RYUK_PRIVILEGED, auto_remove=True)
207+
.with_env("RYUK_RECONNECTION_TIMEOUT", RYUK_RECONNECTION_TIMEOUT)
197208
.start()
198209
)
199210
wait_for_logs(Reaper._container, r".* Started!")

core/tests/test_ryuk.py

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,58 @@
1-
from contextlib import contextmanager
2-
1+
from time import sleep
32
import pytest
3+
from pytest import MonkeyPatch
4+
5+
from docker import DockerClient
6+
from docker.errors import NotFound
47

5-
from testcontainers.core import container
8+
from testcontainers.core import container as container_module
69
from testcontainers.core.container import Reaper
710
from testcontainers.core.container import DockerContainer
811
from testcontainers.core.waiting_utils import wait_for_logs
912

1013

11-
@pytest.mark.skip("invalid test - ryuk logs 'Removed' right before exiting")
12-
def test_wait_for_reaper():
14+
def test_wait_for_reaper(monkeypatch: MonkeyPatch):
15+
Reaper.delete_instance()
16+
monkeypatch.setattr(container_module, "RYUK_RECONNECTION_TIMEOUT", "0.1s")
17+
docker_client = DockerClient()
1318
container = DockerContainer("hello-world").start()
19+
20+
container_id = container.get_wrapped_container().short_id
21+
reaper_id = Reaper._container.get_wrapped_container().short_id
22+
23+
assert docker_client.containers.get(container_id) is not None
24+
assert docker_client.containers.get(reaper_id) is not None
25+
1426
wait_for_logs(container, "Hello from Docker!")
1527

16-
assert Reaper._socket is not None
1728
Reaper._socket.close()
1829

19-
assert Reaper._container is not None
20-
wait_for_logs(Reaper._container, r".* Removed \d .*", timeout=30)
30+
sleep(0.6) # Sleep until Ryuk reaps all dangling containers. 0.5 extra seconds for good measure.
2131

32+
with pytest.raises(NotFound):
33+
docker_client.containers.get(container_id)
34+
with pytest.raises(NotFound):
35+
docker_client.containers.get(reaper_id)
36+
37+
# Cleanup Ryuk class fields after manual Ryuk shutdown
38+
Reaper.delete_instance()
39+
40+
41+
def test_container_without_ryuk(monkeypatch: MonkeyPatch):
2242
Reaper.delete_instance()
43+
monkeypatch.setattr(container_module, "RYUK_DISABLED", True)
44+
with DockerContainer("hello-world") as container:
45+
wait_for_logs(container, "Hello from Docker!")
46+
assert Reaper._instance is None
2347

2448

25-
@contextmanager
26-
def reset_reaper_instance():
27-
old_value = Reaper._instance
28-
Reaper._instance = None
29-
yield
30-
Reaper._instance = old_value
49+
def test_ryuk_is_reused_in_same_process():
50+
with DockerContainer("hello-world") as container:
51+
wait_for_logs(container, "Hello from Docker!")
52+
reaper_instance = Reaper._instance
3153

54+
assert reaper_instance is not None
3255

33-
def test_container_without_ryuk(monkeypatch):
34-
monkeypatch.setattr(container, "RYUK_DISABLED", True)
35-
with reset_reaper_instance(), DockerContainer("hello-world") as cont:
36-
wait_for_logs(cont, "Hello from Docker!")
37-
assert Reaper._instance is None
56+
with DockerContainer("hello-world") as container:
57+
wait_for_logs(container, "Hello from Docker!")
58+
assert reaper_instance is Reaper._instance

index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ Configuration
106106
+-------------------------------------------+-------------------------------+------------------------------------------+
107107
| ``TESTCONTAINERS_RYUK_DISABLED`` | ``false`` | Disable ryuk |
108108
+-------------------------------------------+-------------------------------+------------------------------------------+
109-
| ``RYUK_CONTAINER_IMAGE`` | ``testcontainers/ryuk:0.5.1`` | Custom image for ryuk |
109+
| ``RYUK_CONTAINER_IMAGE`` | ``testcontainers/ryuk:0.7.0`` | Custom image for ryuk |
110110
+-------------------------------------------+-------------------------------+------------------------------------------+
111111

112112
Development and Contributing

0 commit comments

Comments
 (0)