Skip to content

Commit ea2f656

Browse files
authored
Added psutil support to worker container (#114)
* Refactored src/pytest_celery/vendors/worker/Dockerfile: Split pip packages * Refactored WorkerInitialContent.generate(): Split logic into private methods * src/pytest_celery/vendors/worker/app.py -> src/pytest_celery/vendors/worker/content/app.py * Added 'utils.py' to /app in the worker's container * Added psutil package and CeleryTestWorker.get_running_processes_info() API * Fixed unit tests * Fixed error with type annotations in Python < 3.10 * Fixed mypy errors * Reduced reruns for parallel CI runs to default (a.k.a tox.ini) * Reduced reruns for integration & smoke CI runs from 10 -> 2 reruns
1 parent 1337916 commit ea2f656

File tree

15 files changed

+372
-256
lines changed

15 files changed

+372
-256
lines changed

.github/workflows/parallel-support.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ jobs:
6666
poetry install --with dev,test,ci
6767
6868
- name: Run tox for all environments in parallel
69-
timeout-minutes: 20
69+
timeout-minutes: 15
7070
run: |
71-
tox -e xdist -- --reruns 10
71+
tox -e xdist
7272
7373
parallel:
7474
runs-on: ${{ matrix.os }}
@@ -112,6 +112,6 @@ jobs:
112112
poetry install --with dev,test,ci
113113
114114
- name: Run tox for all environments in parallel
115-
timeout-minutes: 20
115+
timeout-minutes: 15
116116
run: |
117-
tox -e parallel -- --reruns 10
117+
tox -e parallel

.github/workflows/python-package.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ jobs:
123123
- name: Run tox for "${{ matrix.python-version }}-integration"
124124
timeout-minutes: 15
125125
run: |
126-
tox --verbose --verbose -e "${{ matrix.python-version }}-integration" -- -n auto --reruns 10 --rerun-except AssertionError
126+
tox --verbose --verbose -e "${{ matrix.python-version }}-integration" -- -n auto --reruns 2 --rerun-except AssertionError
127127
128128
Smoke:
129129
needs:
@@ -172,4 +172,4 @@ jobs:
172172
- name: Run tox for "${{ matrix.python-version }}-smoke"
173173
timeout-minutes: 15
174174
run: |
175-
tox --verbose --verbose -e "${{ matrix.python-version }}-smoke" -- -n auto --reruns 10 --rerun-except AssertionError
175+
tox --verbose --verbose -e "${{ matrix.python-version }}-smoke" -- -n auto --reruns 2 --rerun-except AssertionError

poetry.lock

Lines changed: 249 additions & 221 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ celery = { version = "^5", extras = ["redis", "memcache", "pymemcache"] }
7171
retry = "^0.9.2"
7272
pytest-docker-tools = "^3.1.3"
7373
docker = "^7.0.0"
74+
psutil = "^5.9.7"
7475

7576
[tool.poetry.group.dev]
7677

src/pytest_celery/api/worker.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
import json
4+
35
from celery import Celery
46

57
from pytest_celery.api.base import CeleryTestCluster
@@ -34,6 +36,26 @@ def worker_queue(self) -> str:
3436
def hostname(self) -> str:
3537
return f"{self.worker_name}@{super().hostname()}"
3638

39+
def get_running_processes_info(
40+
self,
41+
columns: list[str] | None = None,
42+
filters: dict[str, str] | None = None,
43+
) -> list[dict]:
44+
exit_code, output = self.container.exec_run(
45+
f'python -c "from utils import get_running_processes_info; print(get_running_processes_info({columns!r}))"'
46+
)
47+
48+
if exit_code != 0:
49+
raise RuntimeError(f"Failed to get processes info: {output}")
50+
51+
decoded_str = output.decode("utf-8")
52+
output = json.loads(decoded_str)
53+
54+
if filters:
55+
output = [item for item in output if all(item.get(key) == value for key, value in filters.items())]
56+
57+
return output
58+
3759

3860
class CeleryWorkerCluster(CeleryTestCluster):
3961
def __init__(self, *workers: tuple[CeleryTestWorker | CeleryTestContainer]) -> None:

src/pytest_celery/vendors/worker/Dockerfile

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ ENV PYTHONUNBUFFERED=1
1919
ENV PYTHONDONTWRITEBYTECODE=1
2020

2121
# Install Python dependencies
22-
RUN pip install --no-cache-dir --upgrade pip \
23-
&& pip install --no-cache-dir celery[redis,memcache,pymemcache]${CELERY_VERSION:+==$CELERY_VERSION}
22+
RUN pip install --no-cache-dir --upgrade \
23+
pip \
24+
celery[redis,memcache,pymemcache]${CELERY_VERSION:+==$CELERY_VERSION} \
25+
psutil
2426

2527
# The workdir must be /app
2628
WORKDIR /app
2729

28-
COPY app.py app.py
30+
COPY content/ .
2931

3032
# Switch to the test_user
3133
USER test_user

src/pytest_celery/vendors/worker/container.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,16 @@ def worker_queue(cls) -> str:
3939

4040
@classmethod
4141
def app_module(cls) -> ModuleType:
42-
from pytest_celery.vendors.worker import app
42+
from pytest_celery.vendors.worker.content import app
4343

4444
return app
4545

46+
@classmethod
47+
def utils_module(cls) -> ModuleType:
48+
from pytest_celery.vendors.worker.content import utils
49+
50+
return utils
51+
4652
@classmethod
4753
def tasks_modules(cls) -> set:
4854
from pytest_celery.vendors.worker import tasks
@@ -88,15 +94,20 @@ def initial_content(
8894
worker_signals: set | None = None,
8995
worker_app: Celery | None = None,
9096
app_module: ModuleType | None = None,
97+
utils_module: ModuleType | None = None,
9198
) -> dict:
9299
if app_module is None:
93100
app_module = cls.app_module()
94101

102+
if utils_module is None:
103+
utils_module = cls.utils_module()
104+
95105
if worker_tasks is None:
96106
worker_tasks = cls.tasks_modules()
97107

98108
content = WorkerInitialContent()
99109
content.set_app_module(app_module)
110+
content.set_utils_module(utils_module)
100111
content.add_modules("tasks", worker_tasks)
101112
if worker_signals:
102113
content.add_modules("signals", worker_signals)

src/pytest_celery/vendors/worker/content/__init__.py

Whitespace-only changes.

src/pytest_celery/vendors/worker/app.py renamed to src/pytest_celery/vendors/worker/content/app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
""" Template for Celery worker application. """
2+
from __future__ import annotations
23

34
import json
45
import logging
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from __future__ import annotations
2+
3+
import json
4+
5+
import psutil
6+
7+
8+
def get_running_processes_info(columns: list[str] | None = None) -> str:
9+
if not columns:
10+
columns = [
11+
"pid",
12+
"name",
13+
"username",
14+
"cmdline",
15+
"cpu_percent",
16+
"memory_percent",
17+
"create_time",
18+
]
19+
processes = [proc.info for proc in psutil.process_iter(columns)]
20+
return json.dumps(processes)

0 commit comments

Comments
 (0)