From c95cfeb05c070e93c0d5f4ddce09aafd4b06e66f Mon Sep 17 00:00:00 2001 From: Daniil Anfimov Date: Mon, 24 Aug 2026 16:10:17 +0300 Subject: [PATCH] Include platform info in sign task payload packages Add platform_id and platform_name to each package entry returned by get_available_sign_task() so sign nodes can apply per-platform rules (e.g. requiring file/IMA signatures). Both fields are optional on SignRpmInfo, so older sign nodes are unaffected. Part 1 of AlmaLinux/build-system#546 --- alws/crud/sign_task.py | 30 ++++-- alws/schemas/sign_schema.py | 2 + tests/test_crud/test_sign_task.py | 171 ++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 10 deletions(-) create mode 100644 tests/test_crud/test_sign_task.py diff --git a/alws/crud/sign_task.py b/alws/crud/sign_task.py index cc28f7d5..261a8301 100644 --- a/alws/crud/sign_task.py +++ b/alws/crud/sign_task.py @@ -263,9 +263,9 @@ async def get_available_sign_task( select(models.SourceRpm) .where(models.SourceRpm.build_id == sign_task.build_id) .options( - selectinload(models.SourceRpm.artifact).selectinload( - models.BuildTaskArtifact.build_task, - ) + selectinload(models.SourceRpm.artifact) + .selectinload(models.BuildTaskArtifact.build_task) + .selectinload(models.BuildTask.platform) ) ) build_src_rpms = build_src_rpms.scalars().all() @@ -275,9 +275,9 @@ async def get_available_sign_task( select(models.BinaryRpm) .where(models.BinaryRpm.build_id == sign_task.build_id) .options( - selectinload(models.BinaryRpm.artifact).selectinload( - models.BuildTaskArtifact.build_task, - ) + selectinload(models.BinaryRpm.artifact) + .selectinload(models.BuildTaskArtifact.build_task) + .selectinload(models.BuildTask.platform) ) ) build_binary_rpms = build_binary_rpms.scalars().all() @@ -293,10 +293,11 @@ async def get_available_sign_task( repo_mapping = await __get_build_repos(db, sign_task.build_id) for src_rpm in build_src_rpms: + build_task = src_rpm.artifact.build_task repo_unique_key = RepoUniqueKey( arch='src', debug=False, - platform_id=src_rpm.artifact.build_task.platform_id, + platform_id=build_task.platform_id, ) repo = repo_mapping[repo_unique_key] packages.append({ @@ -306,25 +307,34 @@ async def get_available_sign_task( "arch": "src", "type": "rpm", "download_url": __get_package_url(repo.url, src_rpm.artifact.name), + "platform_id": build_task.platform_id, + "platform_name": ( + build_task.platform.name if build_task.platform else None + ), }) for binary_rpm in build_binary_rpms: + build_task = binary_rpm.artifact.build_task debug = is_debuginfo_rpm(binary_rpm.artifact.name) repo_unique_key = RepoUniqueKey( - arch=binary_rpm.artifact.build_task.arch, + arch=build_task.arch, debug=debug, - platform_id=binary_rpm.artifact.build_task.platform_id, + platform_id=build_task.platform_id, ) repo = repo_mapping[repo_unique_key] packages.append({ "id": binary_rpm.artifact.id, "name": binary_rpm.artifact.name, "cas_hash": binary_rpm.artifact.cas_hash, - "arch": binary_rpm.artifact.build_task.arch, + "arch": build_task.arch, "type": "rpm", "download_url": __get_package_url( repo.url, binary_rpm.artifact.name ), + "platform_id": build_task.platform_id, + "platform_name": ( + build_task.platform.name if build_task.platform else None + ), }) sign_task_payload["packages"] = packages await db.flush() diff --git a/alws/schemas/sign_schema.py b/alws/schemas/sign_schema.py index ddd01d26..9eb06167 100644 --- a/alws/schemas/sign_schema.py +++ b/alws/schemas/sign_schema.py @@ -73,6 +73,8 @@ class SignRpmInfo(BaseModel): type: str download_url: str cas_hash: typing.Optional[str] = None + platform_id: typing.Optional[int] = None + platform_name: typing.Optional[str] = None class SignedRpmInfo(BaseModel): diff --git a/tests/test_crud/test_sign_task.py b/tests/test_crud/test_sign_task.py new file mode 100644 index 00000000..51e7311a --- /dev/null +++ b/tests/test_crud/test_sign_task.py @@ -0,0 +1,171 @@ +import pytest +from sqlalchemy import delete +from sqlalchemy.ext.asyncio import AsyncSession + +from alws import models +from alws.constants import SignStatus +from alws.crud.sign_task import get_available_sign_task +from tests.constants import ADMIN_USER_ID + + +@pytest.mark.anyio +class TestGetAvailableSignTask: + + @pytest.fixture + async def multi_platform_sign_task( + self, + async_session: AsyncSession, + base_platform: models.Platform, + sign_key: models.SignKey, + ): + second_platform = models.Platform( + name="Test-Sign-Platform", + type="rpm", + distr_type="rhel", + distr_version="10", + test_dist_name="test", + arch_list=["x86_64"], + data={}, + ) + async_session.add(second_platform) + await async_session.flush() + + repos = [ + models.Repository( + name=f"test-sign-repo-{platform.id}-{arch}", + arch=arch, + url=f"http://example.com/{platform.id}-{arch}/", + type="rpm", + debug=False, + production=False, + platform_id=platform.id, + ) + for platform in (base_platform, second_platform) + for arch in ("src", "x86_64") + ] + build = models.Build( + owner_id=ADMIN_USER_ID, + mock_options={}, + repos=repos, + ) + build_tasks = {} + for platform in (base_platform, second_platform): + ref = models.BuildTaskRef(url="http://example.com/test.git") + async_session.add(ref) + await async_session.flush() + build_task = models.BuildTask( + build=build, + platform_id=platform.id, + ref_id=ref.id, + status=0, + index=0, + arch="x86_64", + mock_options={}, + ) + build_tasks[platform.id] = build_task + async_session.add(build_task) + async_session.add(build) + await async_session.flush() + + source_rpms = [] + binary_rpms = [] + for platform in (base_platform, second_platform): + build_task = build_tasks[platform.id] + src_artifact = models.BuildTaskArtifact( + build_task_id=build_task.id, + name=f"test-package-{platform.id}-1.0-1.src.rpm", + type="rpm", + href=f"test-src-href-{platform.id}", + ) + binary_artifact = models.BuildTaskArtifact( + build_task_id=build_task.id, + name=f"test-package-{platform.id}-1.0-1.x86_64.rpm", + type="rpm", + href=f"test-binary-href-{platform.id}", + ) + async_session.add_all([src_artifact, binary_artifact]) + await async_session.flush() + source_rpm = models.SourceRpm( + build_id=build.id, + artifact_id=src_artifact.id, + ) + async_session.add(source_rpm) + await async_session.flush() + binary_rpm = models.BinaryRpm( + build_id=build.id, + artifact_id=binary_artifact.id, + source_rpm_id=source_rpm.id, + ) + async_session.add(binary_rpm) + source_rpms.append(source_rpm) + binary_rpms.append(binary_rpm) + + sign_task = models.SignTask( + build_id=build.id, + sign_key_id=sign_key.id, + status=SignStatus.IDLE, + ) + async_session.add(sign_task) + await async_session.commit() + + yield { + "sign_key": sign_key, + "platforms": (base_platform, second_platform), + "build": build, + } + + repo_ids = [repo.id for repo in repos] + await async_session.execute(delete(models.BinaryRpm)) + await async_session.execute(delete(models.SourceRpm)) + await async_session.execute(delete(models.BuildTaskArtifact)) + await async_session.execute(delete(models.SignTask)) + await async_session.execute(delete(models.BuildTask)) + await async_session.execute(delete(models.BuildTaskRef)) + await async_session.execute(delete(models.BuildRepo)) + await async_session.execute( + delete(models.Repository).where( + models.Repository.id.in_(repo_ids) + ) + ) + await async_session.execute( + delete(models.Build).where(models.Build.id == build.id) + ) + await async_session.execute( + delete(models.Platform).where( + models.Platform.id == second_platform.id + ) + ) + await async_session.commit() + + async def test_payload_contains_platform_info( + self, + async_session: AsyncSession, + multi_platform_sign_task, + ): + sign_key = multi_platform_sign_task["sign_key"] + platforms = multi_platform_sign_task["platforms"] + platform_names = {p.id: p.name for p in platforms} + + payload = await get_available_sign_task( + async_session, [sign_key.keyid] + ) + + assert payload, "No sign task payload returned" + packages = payload["packages"] + # one source and one binary RPM per platform + assert len(packages) == 4 + src_platforms = set() + binary_platforms = set() + for package in packages: + assert package["platform_id"] in platform_names + assert ( + package["platform_name"] + == platform_names[package["platform_id"]] + ) + if package["arch"] == "src": + src_platforms.add(package["platform_id"]) + else: + binary_platforms.add(package["platform_id"]) + expected_platform_ids = set(platform_names) + assert src_platforms == expected_platform_ids + assert binary_platforms == expected_platform_ids