WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 738038c

Browse files
committed
🧼 Cleanup network name after test run
1 parent d127130 commit 738038c

File tree

10 files changed

+119
-35
lines changed

10 files changed

+119
-35
lines changed

‎.github/workflows/tag.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222

2323
- name: 🔢 Extract version from __init__.py
2424
id: extract_version
25-
run: |
25+
run: |-
2626
VERSION=$(grep -oP '__version__ = "\K[^"]+' src/kloudkit/testshed/__init__.py)
2727
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
2828

‎README.md‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,11 @@ from kloudkit.testshed.docker import InlineVolume, RemoteVolume
115115

116116
@shed_env(MY_ENV_VAR="hello")
117117
@shed_volumes(
118-
("/path/to/host/data", "/app/data:ro"),
118+
("/path/to/host/data", "/app/data"),
119119
InlineVolume("/app/config.txt", "any content you want", mode=0o644),
120120
RemoteVolume("/app/remote-config.json", "https://api.example.com/config.json", mode=0o644),
121121
)
122-
def test_configured_docker_app(docker_sidecar):
123-
app = docker_sidecar("my-custom-app:latest")
122+
def test_configured_docker_app(shed):
124123
# ... test logic ...
125124
```
126125

@@ -145,7 +144,6 @@ TestShed extends `pytest` with options to control the Docker environment:
145144
- **`--shed-tag TAG|SHA`:** Image tag or digest *(default: `tests`)*.
146145
- **`--shed-build-context DIR`:** Docker build context *(default: `pytest.ini` directory)*.
147146
- **`--shed-image-policy POLICY`:** Image acquisition policy for building or pulling *(default: `pull`)*.
148-
- **`--shed-network NAME`:** Docker network *(default: `testshed-network`)*.
149147
- **`--shed-skip-bootstrap`:** Skip Docker bootstrapping *(useful for unit tests)*.
150148

151149
> [!NOTE]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.0.1"
1+
__version__ = "0.0.2"

‎src/kloudkit/testshed/_internal/state.py‎

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
1+
import os
2+
import random
3+
import threading
14
from dataclasses import dataclass, field
25
from pathlib import Path
36

47

8+
def _dynamic_network_name(project_name: str | None = None) -> str:
9+
"""Generate a dynamic network name."""
10+
11+
xdist_worker = os.getenv("PYTEST_XDIST_WORKER")
12+
random_suffix = f"{random.randint(1000, 9999)}"
13+
14+
network_parts = filter(
15+
None,
16+
["testshed", project_name or Path.cwd().name, random_suffix, xdist_worker],
17+
)
18+
19+
return "-".join(network_parts)
20+
21+
522
@dataclass(slots=True)
623
class Options:
724
labels: dict[str, str] = field(
825
default_factory=lambda: {"com.kloudkit.testshed": "testing-container"}
926
)
10-
network: str = "testshed-network"
27+
network: str | None = None
1128
image: str | None = None
1229
tag: str = "tests"
1330
src_path: Path | None = None
@@ -25,11 +42,42 @@ def image_and_tag(self) -> str:
2542

2643
return f"{self.image}{sep}{self.tag}"
2744

45+
@classmethod
46+
def create(
47+
cls,
48+
project_name: str | None = None,
49+
image: str | None = None,
50+
tag: str = "tests",
51+
src_path: Path | None = None,
52+
tests_path: Path | None = None,
53+
stubs_path: Path | None = None,
54+
) -> "Options":
55+
"""Create an Options instance with a dynamic network name."""
2856

29-
_state: Options = Options()
57+
return cls(
58+
network=_dynamic_network_name(project_name),
59+
image=image,
60+
tag=tag,
61+
src_path=src_path,
62+
tests_path=tests_path,
63+
stubs_path=stubs_path,
64+
)
65+
66+
67+
_state: Options | None = None
68+
_state_lock = threading.RLock()
3069

3170

3271
def get_state() -> Options:
33-
"""Get the current state."""
72+
"""Get the current state in a thread-safe manner."""
73+
74+
with _state_lock:
75+
return Options() if _state is None else _state
76+
77+
78+
def set_state(options: Options) -> None:
79+
"""Set the global state in a thread-safe manner."""
3480

35-
return _state
81+
global _state
82+
with _state_lock:
83+
_state = options

‎src/kloudkit/testshed/docker/runtime/cleanup.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def run(
1313
cls,
1414
containers: list[Container] | None = None,
1515
labels: dict | None = None,
16+
network: bool = False,
1617
) -> None:
1718
"""Force-remove all provided containers or labeled."""
1819

@@ -28,3 +29,7 @@ def run(
2829
for container in containers:
2930
with contextlib.suppress(DockerException):
3031
container.remove(force=True, volumes=True)
32+
33+
if network:
34+
with contextlib.suppress(DockerException):
35+
docker.network.remove(get_state().network)
Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
1-
from kloudkit.testshed.docker.runtime.cleanup import Cleanup
21
from kloudkit.testshed.plugin.addoptions import pytest_addoption # noqa: F401
32
from kloudkit.testshed.plugin.configure import pytest_configure # noqa: F401
43
from kloudkit.testshed.plugin.presenter import (
54
pytest_report_header, # noqa: F401
65
)
7-
8-
import pytest
9-
10-
11-
def pytest_keyboard_interrupt(excinfo: pytest.ExceptionInfo) -> None:
12-
"""Cleanup any dangling containers before exiting."""
13-
14-
Cleanup.run()
6+
from kloudkit.testshed.plugin.session import pytest_sessionfinish # noqa: F401

‎src/kloudkit/testshed/plugin/addoptions.py‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,6 @@ def pytest_addoption(parser: pytest.Parser) -> None:
4444
help="Image acquisition policy for building or pulling.",
4545
)
4646

47-
parser.addoption(
48-
"--shed-network",
49-
action="store",
50-
default="testshed-network",
51-
metavar="NAME",
52-
help="Docker network to use when running containers.",
53-
)
5447

5548
parser.addoption(
5649
"--shed-src-dir",

‎src/kloudkit/testshed/plugin/configure.py‎

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from pathlib import Path
22

3-
from kloudkit.testshed._internal.state import get_state
3+
from kloudkit.testshed._internal.state import (
4+
Options,
5+
set_state,
6+
)
47
from kloudkit.testshed.core.bootstrap import init_shed_image, init_shed_network
58
from kloudkit.testshed.plugin.validation import validate_config
69

@@ -41,15 +44,16 @@ def pytest_configure(config: pytest.Config) -> None:
4144

4245
validate_config(config)
4346

44-
state = get_state()
45-
46-
state.network = config.getoption("shed_network")
47-
state.image = config.getoption("shed_image")
48-
state.tag = config.getoption("shed_tag")
47+
state = Options.create(
48+
project_name=config.inipath.parent.name,
49+
image=config.getoption("shed_image"),
50+
tag=config.getoption("shed_tag"),
51+
src_path=_resolve_path("src_dir", config),
52+
stubs_path=_resolve_path("stubs_dir", config),
53+
tests_path=_resolve_path("tests_dir", config),
54+
)
4955

50-
state.src_path = _resolve_path("src_dir", config)
51-
state.stubs_path = _resolve_path("stubs_dir", config)
52-
state.tests_path = _resolve_path("tests_dir", config)
56+
set_state(state)
5357

5458
if config.getoption("shed_skip_bootstrap"):
5559
return
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from kloudkit.testshed.docker.runtime.cleanup import Cleanup
2+
3+
import pytest
4+
5+
6+
def pytest_sessionfinish(session: pytest.Session):
7+
"""Cleanup all Docker resources at the end of the test session."""
8+
9+
Cleanup.run(network=True)

‎tests/unit/test_options.py‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import os
2+
from unittest.mock import patch
3+
4+
from kloudkit.testshed._internal.state import Options
5+
6+
7+
def test_generate_name_with_project():
8+
options = Options.create(project_name="myproject")
9+
10+
parts = options.network.split("-")
11+
12+
assert parts[0] == "testshed"
13+
assert parts[1] == "myproject"
14+
assert len(parts[2]) == 4
15+
assert len(parts) == 3
16+
17+
18+
def test_generate_name_with_xdist():
19+
with patch.dict(os.environ, {"PYTEST_XDIST_WORKER": "gw0"}):
20+
options = Options.create(project_name="myproject")
21+
22+
parts = options.network.split("-")
23+
24+
assert parts[0] == "testshed"
25+
assert parts[1] == "myproject"
26+
assert len(parts[2]) == 4
27+
assert parts[3] == "gw0"
28+
assert len(parts) == 4
29+
30+
31+
def test_generate_name_random_suffix():
32+
options1 = Options.create(project_name="test")
33+
options2 = Options.create(project_name="test")
34+
35+
assert options1.network != options2.network

0 commit comments

Comments
 (0)