diff --git a/CHANGES/7213.bugfix b/CHANGES/7213.bugfix new file mode 100644 index 0000000000..da0ddd31dc --- /dev/null +++ b/CHANGES/7213.bugfix @@ -0,0 +1 @@ +Pinned uvloop (optional dependency) to prevent a known bug in a newer releases. diff --git a/pulpcore/tests/unit/conftest.py b/pulpcore/tests/unit/conftest.py index 03b9536618..dc98722949 100644 --- a/pulpcore/tests/unit/conftest.py +++ b/pulpcore/tests/unit/conftest.py @@ -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())) diff --git a/pulpcore/tests/unit/test_startup.py b/pulpcore/tests/unit/test_startup.py new file mode 100644 index 0000000000..7797d1be61 --- /dev/null +++ b/pulpcore/tests/unit/test_startup.py @@ -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 diff --git a/pyproject.toml b/pyproject.toml index 34de4905a4..654829a1b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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" diff --git a/unittest_requirements.txt b/unittest_requirements.txt index 251cad542a..2468c66d09 100644 --- a/unittest_requirements.txt +++ b/unittest_requirements.txt @@ -6,3 +6,4 @@ pytest-django pytest-asyncio pytest-aiohttp pytest-redis<4.1.0 # https://github.com/ClearcodeHQ/pytest-redis/issues/679 +uv