Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES/7213.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Pinned uvloop (optional dependency) to prevent a known bug in a newer releases.
6 changes: 3 additions & 3 deletions pulpcore/tests/unit/conftest.py
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By lazy importing this we avoid triggering django machinery, which makes it easier to run unit tests directly from the host (which is what I did for this test).

Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import pytest
from uuid import uuid4

from pulpcore.app.models import Domain
from pulpcore.app.util import set_domain


@pytest.fixture
def fake_domain():
"""A fixture to prevent `get_domain` to call out to the database."""
from pulpcore.app.models import Domain
from pulpcore.app.util import set_domain

set_domain(Domain(pk=uuid4(), name=uuid4()))
105 changes: 105 additions & 0 deletions pulpcore/tests/unit/test_startup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""App startup scenarios which doesn't require a full instance."""

import base64
import os
import secrets
import subprocess
from textwrap import dedent
from pathlib import Path

import pytest


@pytest.fixture
def pulpcore_source_dir() -> Path:
"""The pulpcore source directory (repository root)."""
source_dir = Path(__file__).parents[3]
lookup_dirs = (
Path(__file__).parents[3],
Path("/pulpcore"),
Path("/src/pulpcore"),
)
source_dir = None
for dir in lookup_dirs:
if (dir / "pyproject.toml").exists():
source_dir = dir
break
if not source_dir:
raise RuntimeError("Couldn't find pulpcore's source")
return source_dir


@pytest.fixture
def pulpcore_dist(pulpcore_source_dir) -> Path | None:
pulpcore_dist_glob = list((pulpcore_source_dir / "dist").glob("*.whl"))
pulpcore_dist = pulpcore_dist_glob[0] if len(pulpcore_dist_glob) else None
return pulpcore_dist


@pytest.fixture
def encryption_key_file(tmp_path: Path) -> Path:
"""A file with a symmetric encryption key."""
key_file = tmp_path / "symmetric.key"
key = base64.urlsafe_b64encode(secrets.token_bytes(32))
key_file.write_bytes(key)
return key_file


class TestUvloop:
def test_uvloop_startup(
self, pulpcore_source_dir: Path, pulpcore_dist: Path | None, pulp_env: dict[str, str]
):
"""
Test that pulpcore-content can start successfully with uvloop enabled.
"""
TIMEOUT_PROGRAM_EXIT_CODE = 124 # means 'timeout N' exited after N seconds
ci_constraints = pulpcore_source_dir / ".ci" / "assets" / "ci_constraints.txt"
install_source = pulpcore_dist or pulpcore_source_dir

cmd = [
"uv",
"run",
"--isolated",
"--no-project",
"--with",
f"{str(install_source.resolve())}[uvloop]",
"--with-requirements",
str(ci_constraints.resolve()),
"timeout",
"5",
"pulpcore-content",
"--bind",
"127.0.0.1:0", # Use random available port
]

result = subprocess.run(cmd, env=pulp_env, capture_output=True)

stderr = result.stderr.decode()
assert result.returncode == TIMEOUT_PROGRAM_EXIT_CODE, (
f"pulpcore-content exited prematurely with exit code {result.returncode}.\n"
f"Output: {stderr}"
)
assert "Using uvloop as the asyncio event loop" in stderr
assert (
"Connection in use:" not in stderr
), "Some other program is using the port shown in stderr"

@pytest.fixture
def settings_file(self, tmp_path: Path, encryption_key_file: Path) -> Path:
settings_file = tmp_path / "settings.py"
settings_content = f"""
UVLOOP_ENABLED = True
FILE_UPLOAD_TEMP_DIR = "{tmp_path.resolve()}"
WORKING_DIRECTORY = "{tmp_path.resolve()}"
DB_ENCRYPTION_KEY = "{encryption_key_file.resolve()}"
"""
settings_file.write_text(dedent(settings_content))
return settings_file

@pytest.fixture
def pulp_env(self, settings_file: Path) -> dict[str, str]:
"""Configured environment."""
env = {}
env["PATH"] = os.environ["PATH"] # enable subprocess to find system binaries
env["PULP_SETTINGS"] = str(settings_file)
return env
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ kafka = [
"confluent-kafka>=2.4.0,<2.14.0",
]
diagnostics = ["pyinstrument~=5.0", "memray~=1.17"]
uvloop = ["uvloop>=0.20,<0.23"]
uvloop = ["uvloop>=0.20,<0.22"]

[project.urls]
Homepage = "https://pulpproject.org"
Expand Down
1 change: 1 addition & 0 deletions unittest_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ pytest-django
pytest-asyncio
pytest-aiohttp
pytest-redis<4.1.0 # https://github.com/ClearcodeHQ/pytest-redis/issues/679
uv
Loading