From 0386b68f25592bd0395bf19a859d27a77cae03d9 Mon Sep 17 00:00:00 2001 From: Robert Bikar Date: Mon, 8 Dec 2025 16:08:16 +0100 Subject: [PATCH 1/3] Key alias support [RHELDST-35396] For PQC signed RPMs (not only), Errata tool API will provide a string called "key alias" on `sig_key` field in format "name1,name2,...". In order support this following changes are now implemented: For Errata source: * replace `,` with `+` in string provided from `sig_key` field, this is later provided to Koji source as a signing key argument. For Koji source: * keep backwards compatible behavior for parsing `signing_key` argument * replace `+` with `,` for each value in `signing_key` (later used for path to RPM file) * extract signing key ID from RPM headers every time as we don't know whether caller passed alias or actual key ID, as the alias can be formed as one name only ("sig_key":"alias_name") --- .../backend/errata_source/errata_source.py | 5 + src/pushsource/_impl/backend/koji_source.py | 30 ++- .../data/RHSA-2020:0509-sig-key-alias.yaml | 185 ++++++++++++++++++ tests/errata/test_errata_missing_modules.py | 6 +- tests/errata/test_errata_module_sources.py | 22 ++- tests/errata/test_errata_modules.py | 7 +- tests/errata/test_errata_rpms.py | 35 ++-- tests/errata/test_errata_rpms_filter.py | 5 +- tests/koji/data/rpms/walrus-5.21-1.noarch.rpm | Bin 0 -> 2445 bytes tests/koji/test_koji.py | 60 +++++- tests/koji/test_koji_polling.py | 17 +- 11 files changed, 328 insertions(+), 44 deletions(-) create mode 100644 tests/errata/data/RHSA-2020:0509-sig-key-alias.yaml create mode 100644 tests/koji/data/rpms/walrus-5.21-1.noarch.rpm diff --git a/src/pushsource/_impl/backend/errata_source/errata_source.py b/src/pushsource/_impl/backend/errata_source/errata_source.py index 9c3efb86..f21d5346 100644 --- a/src/pushsource/_impl/backend/errata_source/errata_source.py +++ b/src/pushsource/_impl/backend/errata_source/errata_source.py @@ -415,6 +415,11 @@ def _filter_rpms_by_arch(self, erratum, rpm_filenames): def _rpm_push_items_from_build(self, erratum, build_nvr, build_info): rpms = build_info.get("rpms") or {} signing_key = build_info.get("sig_key") or None + if signing_key: + # Errata Tool API may return key alias in sig_key field in format "name1,name2,...", but + # we need to convert "," into "+" otherwise it would be used as multiple keys for Koji + # source which would result in incorrect processing. + signing_key = signing_key.replace(",", "+") sha256sums = (build_info.get("checksums") or {}).get("sha256") or {} md5sums = (build_info.get("checksums") or {}).get("md5") or {} diff --git a/src/pushsource/_impl/backend/koji_source.py b/src/pushsource/_impl/backend/koji_source.py index c158b847..12138c57 100644 --- a/src/pushsource/_impl/backend/koji_source.py +++ b/src/pushsource/_impl/backend/koji_source.py @@ -10,6 +10,15 @@ import koji from more_executors import Executors from more_executors.futures import f_map +try: + from kobo import rpmlib +except Exception as ex: # pragma: no cover, pylint: disable=broad-except + # If kobo.rpmlib is unavailable, let's not immediately crash. + # We will hold this exception and re-raise it only if there's an + # attempt to use the related functionality. + from .. import broken_rpmlib as rpmlib + + rpmlib.CAUSE = ex from ..source import Source from ..model import ( @@ -181,7 +190,9 @@ def __init__( signing_key (list[str]) GPG signing key ID(s). If provided, content must be signed using one of the provided keys. Include ``None`` if unsigned - should also be permitted. + should also be permitted. + + Supports also key alias in the format ``name1+name2+...`` Keys should be listed in the order of preference. @@ -216,7 +227,7 @@ def __init__( ) self._container_build = [try_int(x) for x in list_argument(container_build)] self._vmi_build = [try_int(x) for x in list_argument(vmi_build)] - self._signing_key = list_argument(signing_key) + self._signing_key = self._parse_signing_key(list_argument(signing_key)) self._dest = list_argument(dest) self._timeout = timeout self._pathinfo = koji.PathInfo(basedir) @@ -251,6 +262,15 @@ def _koji_session(self): tls.koji_session = koji.ClientSession(self._url, {"anon_retry": True}) return tls.koji_session + def _parse_signing_key(self, keys): + out = [] + for key in keys: + if key: + out.append(key.replace("+", ",")) # adjust alias to comma separated names of keys + else: + out.append(key) + return out + def _koji_check(self): # Do a basic connection check with koji. # If this succeeds, we can be reasonably sure that the koji connection is @@ -309,7 +329,6 @@ def _push_items_from_rpm_meta(self, rpm, meta): candidate = unsigned_path wait_exist(candidate, timeout, poll_rate) - # If signing keys requested, try them in order of preference # Some key should be present at this stage, let's try them all for key in self._signing_key: @@ -321,7 +340,10 @@ def _push_items_from_rpm_meta(self, rpm, meta): candidate_paths.append(candidate) if os.path.exists(candidate): rpm_path = candidate - rpm_signing_key = key + # we may only get key alias as input, let's extract actual key ID from RPM header in all cases + # as we don't know if the provided data are alias or actual key ID + header = rpmlib.get_rpm_header(candidate) + rpm_signing_key = rpmlib.get_keys_from_header(header) break if self._signing_key: diff --git a/tests/errata/data/RHSA-2020:0509-sig-key-alias.yaml b/tests/errata/data/RHSA-2020:0509-sig-key-alias.yaml new file mode 100644 index 00000000..c7045206 --- /dev/null +++ b/tests/errata/data/RHSA-2020:0509-sig-key-alias.yaml @@ -0,0 +1,185 @@ +advisory_id: RHSA-2020:0509-sig-key-alias +cdn_file_list: + sudo-1.8.25p1-4.el8_0.3: + checksums: + md5: + sudo-1.8.25p1-4.el8_0.3.ppc64le.rpm: 0d56f302617696d3511e71e1669e62c0 + sudo-1.8.25p1-4.el8_0.3.src.rpm: f94ab3724b498e3faeab643fe2a67c9c + sudo-1.8.25p1-4.el8_0.3.x86_64.rpm: 25e9470c4fe96034fe1d7525c04b5d8e + sudo-debuginfo-1.8.25p1-4.el8_0.3.ppc64le.rpm: e242826fb38f487502cdc1f1a06991d2 + sudo-debuginfo-1.8.25p1-4.el8_0.3.x86_64.rpm: 91126f02975c06015880d6ea99cb2760 + sudo-debugsource-1.8.25p1-4.el8_0.3.ppc64le.rpm: d6da7e2e3d9efe050fef2e8d047682be + sudo-debugsource-1.8.25p1-4.el8_0.3.x86_64.rpm: 6b0967941c0caf626c073dc7da0272b6 + sha256: + sudo-1.8.25p1-4.el8_0.3.ppc64le.rpm: 31c4f73af90c6d267cc5281c59e4a93ae3557b2253d9a8e3fef55f3cafca6e54 + sudo-1.8.25p1-4.el8_0.3.src.rpm: 10d7724302a60d0d2ca890fc7834b8143df55ba1ce0176469ea634ac4ab7aa28 + sudo-1.8.25p1-4.el8_0.3.x86_64.rpm: 593f872c1869f7beb963c8df2945fc691a1d999945c8c45c6bc7e02731fa016f + sudo-debuginfo-1.8.25p1-4.el8_0.3.ppc64le.rpm: 04db0c39efb31518ff79bf98d1c27256d46cdc72b967a5b2094a6efec3166df2 + sudo-debuginfo-1.8.25p1-4.el8_0.3.x86_64.rpm: 1b7d3a7613236ffea7c4553eb9dea69fc19557005ac3a059d7e83efc08c5b754 + sudo-debugsource-1.8.25p1-4.el8_0.3.ppc64le.rpm: 355cbb9dc348b17782cff57120391685d6a1f6884facc54fac4b7fb54abeffba + sudo-debugsource-1.8.25p1-4.el8_0.3.x86_64.rpm: 43e318fa49e4df685ea0d5f0925a00a336236b2e20f27f9365c39a48102c2cf6 + rpms: + sudo-1.8.25p1-4.el8_0.3.ppc64le.rpm: + - rhel-8-for-ppc64le-baseos-e4s-rpms__8_DOT_0 + sudo-1.8.25p1-4.el8_0.3.src.rpm: + - rhel-8-for-ppc64le-baseos-e4s-source-rpms__8_DOT_0 + - rhel-8-for-x86_64-baseos-e4s-source-rpms__8_DOT_0 + sudo-1.8.25p1-4.el8_0.3.x86_64.rpm: + - rhel-8-for-x86_64-baseos-e4s-rpms__8_DOT_0 + sudo-debuginfo-1.8.25p1-4.el8_0.3.ppc64le.rpm: + - rhel-8-for-ppc64le-baseos-e4s-debug-rpms__8_DOT_0 + sudo-debuginfo-1.8.25p1-4.el8_0.3.x86_64.rpm: + - rhel-8-for-x86_64-baseos-e4s-debug-rpms__8_DOT_0 + sudo-debugsource-1.8.25p1-4.el8_0.3.ppc64le.rpm: + - rhel-8-for-ppc64le-baseos-e4s-debug-rpms__8_DOT_0 + sudo-debugsource-1.8.25p1-4.el8_0.3.x86_64.rpm: + - rhel-8-for-x86_64-baseos-e4s-debug-rpms__8_DOT_0 + sig_key: foo,bar,baz +cdn_metadata: + description: 'The sudo packages contain the sudo utility which allows system administrators + to provide certain users with the permission to execute privileged commands, which + are used for system management purposes, without having to log in as root. + + + Security Fix(es): + + + * sudo: Stack based buffer overflow when pwfeedback is enabled (CVE-2019-18634) + + + For more details about the security issue(s), including the impact, a CVSS score, + acknowledgments, and other related information, refer to the CVE page(s) listed + in the References section.' + from: release-engineering@redhat.com + id: RHSA-2020:0509 + issued: 2020-02-13 19:00:11 UTC + pkglist: + - packages: + - arch: ppc64le + epoch: '0' + filename: sudo-1.8.25p1-4.el8_0.3.ppc64le.rpm + name: sudo + release: 4.el8_0.3 + src: sudo-1.8.25p1-4.el8_0.3.src.rpm + sum: + - md5 + - 0d56f302617696d3511e71e1669e62c0 + - sha256 + - 31c4f73af90c6d267cc5281c59e4a93ae3557b2253d9a8e3fef55f3cafca6e54 + version: 1.8.25p1 + - arch: SRPMS + epoch: '0' + filename: sudo-1.8.25p1-4.el8_0.3.src.rpm + name: sudo + release: 4.el8_0.3 + src: sudo-1.8.25p1-4.el8_0.3.src.rpm + sum: + - md5 + - f94ab3724b498e3faeab643fe2a67c9c + - sha256 + - 10d7724302a60d0d2ca890fc7834b8143df55ba1ce0176469ea634ac4ab7aa28 + version: 1.8.25p1 + - arch: x86_64 + epoch: '0' + filename: sudo-1.8.25p1-4.el8_0.3.x86_64.rpm + name: sudo + release: 4.el8_0.3 + src: sudo-1.8.25p1-4.el8_0.3.src.rpm + sum: + - md5 + - 25e9470c4fe96034fe1d7525c04b5d8e + - sha256 + - 593f872c1869f7beb963c8df2945fc691a1d999945c8c45c6bc7e02731fa016f + version: 1.8.25p1 + - arch: ppc64le + epoch: '0' + filename: sudo-debuginfo-1.8.25p1-4.el8_0.3.ppc64le.rpm + name: sudo-debuginfo + release: 4.el8_0.3 + src: sudo-1.8.25p1-4.el8_0.3.src.rpm + sum: + - md5 + - e242826fb38f487502cdc1f1a06991d2 + - sha256 + - 04db0c39efb31518ff79bf98d1c27256d46cdc72b967a5b2094a6efec3166df2 + version: 1.8.25p1 + - arch: x86_64 + epoch: '0' + filename: sudo-debuginfo-1.8.25p1-4.el8_0.3.x86_64.rpm + name: sudo-debuginfo + release: 4.el8_0.3 + src: sudo-1.8.25p1-4.el8_0.3.src.rpm + sum: + - md5 + - 91126f02975c06015880d6ea99cb2760 + - sha256 + - 1b7d3a7613236ffea7c4553eb9dea69fc19557005ac3a059d7e83efc08c5b754 + version: 1.8.25p1 + - arch: ppc64le + epoch: '0' + filename: sudo-debugsource-1.8.25p1-4.el8_0.3.ppc64le.rpm + name: sudo-debugsource + release: 4.el8_0.3 + src: sudo-1.8.25p1-4.el8_0.3.src.rpm + reboot_suggested: true + sum: + - md5 + - d6da7e2e3d9efe050fef2e8d047682be + - sha256 + - 355cbb9dc348b17782cff57120391685d6a1f6884facc54fac4b7fb54abeffba + version: 1.8.25p1 + - arch: x86_64 + epoch: '0' + filename: sudo-debugsource-1.8.25p1-4.el8_0.3.x86_64.rpm + name: sudo-debugsource + release: 4.el8_0.3 + src: sudo-1.8.25p1-4.el8_0.3.src.rpm + sum: + - md5 + - 6b0967941c0caf626c073dc7da0272b6 + - sha256 + - 43e318fa49e4df685ea0d5f0925a00a336236b2e20f27f9365c39a48102c2cf6 + version: 1.8.25p1 + pulp_user_metadata: + content_types: + - rpm + pushcount: '3' + reboot_suggested: false + references: + - href: https://access.redhat.com/errata/RHSA-2020:0509 + id: null + title: RHSA-2020:0509 + type: self + - href: https://bugzilla.redhat.com/show_bug.cgi?id=1796944 + id: '1796944' + title: 'CVE-2019-18634 sudo: Stack based buffer overflow when pwfeedback is enabled' + type: bugzilla + - href: https://www.redhat.com/security/data/cve/CVE-2019-18634.html + id: CVE-2019-18634 + title: CVE-2019-18634 + type: cve + - href: https://access.redhat.com/security/updates/classification/#important + id: classification + title: important + type: other + release: '0' + rights: Copyright 2020 Red Hat Inc + severity: Important + solution: 'For details on how to apply this update, which includes the changes described + in this advisory, refer to: + + + https://access.redhat.com/articles/11258' + status: final + summary: 'An update for sudo is now available for Red Hat Enterprise Linux 8.0 Update + Services for SAP Solutions. + + + Red Hat Product Security has rated this update as having a security impact of + Important. A Common Vulnerability Scoring System (CVSS) base score, which gives + a detailed severity rating, is available for each vulnerability from the CVE link(s) + in the References section.' + title: 'Important: sudo security update' + type: security + updated: 2020-02-13 19:00:11 UTC + version: '3' diff --git a/tests/errata/test_errata_missing_modules.py b/tests/errata/test_errata_missing_modules.py index 243fe7c2..03becc17 100644 --- a/tests/errata/test_errata_missing_modules.py +++ b/tests/errata/test_errata_missing_modules.py @@ -1,9 +1,11 @@ from pytest import raises - +from mock import patch from pushsource import Source -def test_errata_modules_via_koji(fake_errata_tool, fake_koji, koji_dir): +@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") +@patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") +def test_errata_modules_via_koji(mock_get_rpm_header, mock_get_keys_from_headers, fake_errata_tool, fake_koji, koji_dir): """Errata source gives an error if ET requested modules which don't exist in koji""" source = Source.get( diff --git a/tests/errata/test_errata_module_sources.py b/tests/errata/test_errata_module_sources.py index aceba658..72fedb84 100644 --- a/tests/errata/test_errata_module_sources.py +++ b/tests/errata/test_errata_module_sources.py @@ -3,7 +3,7 @@ import pytest from pushsource import Source, ModuleMdSourcePushItem - +from mock import patch @pytest.fixture def source_factory(fake_errata_tool, fake_koji, koji_dir): @@ -161,8 +161,9 @@ def source_factory(fake_errata_tool, fake_koji, koji_dir): yield ctor - -def test_errata_module_sources(source_factory, koji_dir): +@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") +@patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") +def test_errata_module_sources(mock_get_rpm_header, mock_get_keys_from_headers, source_factory, koji_dir): """Errata source can provide ModuleMdSourcePushItems, typical scenario.""" source = source_factory(errata="RHEA-2020:0346") @@ -193,8 +194,9 @@ def test_errata_module_sources(source_factory, koji_dir): ) ] - -def test_errata_module_sources_no_ftp_paths(source_factory): +@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") +@patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") +def test_errata_module_sources_no_ftp_paths(mock_get_rpm_header, mock_get_keys_from_headers, source_factory): """Errata source skips ModuleMdSourcePushItems if ET does not request any FTP paths for modules.""" @@ -207,8 +209,9 @@ def test_errata_module_sources_no_ftp_paths(source_factory): # Should not have found anything since ET reported no dests for modules in FTP paths assert src_items == [] - -def test_errata_module_sources_no_cdn_list(source_factory, caplog): +@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") +@patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") +def test_errata_module_sources_no_cdn_list(mock_get_rpm_header, mock_get_keys_from_headers, source_factory, caplog): """Errata source skips ModuleMdSourcePushItems if ET does not present those modules in get_advisory_cdn_file_list.""" @@ -228,8 +231,9 @@ def test_errata_module_sources_no_cdn_list(source_factory, caplog): in caplog.text ) - -def test_errata_module_missing_sources(source_factory, fake_koji): +@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") +@patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") +def test_errata_module_missing_sources(mock_get_rpm_header, mock_get_keys_from_headers, source_factory, fake_koji): """Errata source gives fatal error if ET requests some FTP paths for modules, yet no module sources exist on koji build.""" source = source_factory(errata="RHEA-2020:0346") diff --git a/tests/errata/test_errata_modules.py b/tests/errata/test_errata_modules.py index 02838599..756e5d3e 100644 --- a/tests/errata/test_errata_modules.py +++ b/tests/errata/test_errata_modules.py @@ -1,5 +1,5 @@ import os - +from mock import patch from pushsource import ( Source, ErratumPushItem, @@ -10,8 +10,9 @@ ModuleMdPushItem, ) - -def test_errata_modules_via_koji(fake_errata_tool, fake_koji, koji_dir): +@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") +@patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") +def test_errata_modules_via_koji(mock_get_rpm_header, mock_get_keys_from_headers, fake_errata_tool, fake_koji, koji_dir): """Errata source containing a module yields modules & RPMs taken from koji source""" diff --git a/tests/errata/test_errata_rpms.py b/tests/errata/test_errata_rpms.py index 5ee7bb35..4fab1c9d 100644 --- a/tests/errata/test_errata_rpms.py +++ b/tests/errata/test_errata_rpms.py @@ -1,4 +1,6 @@ import os +import pytest +from mock import patch from pushsource import ( Source, @@ -9,13 +11,20 @@ RpmPushItem, ) +TEST_DATA = ( + ("RHSA-2020:0509", "fd431d51"), + ("RHSA-2020:0509-sig-key-alias", "foo,bar,baz"), +) -def test_errata_rpms_via_koji(fake_errata_tool, fake_koji, koji_dir): +@pytest.mark.parametrize("erratum, expected_sig_key_path", TEST_DATA) +@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") +@patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") +def test_errata_rpms_via_koji(mock_get_rpm_header, mock_get_keys_from_headers, fake_errata_tool, erratum, expected_sig_key_path, fake_koji, koji_dir): """Errata source yields RPMs taken from koji source""" source = Source.get( "errata:https://errata.example.com", - errata="RHSA-2020:0509", + errata=erratum, koji_source="koji:https://koji.example.com?basedir=%s" % koji_dir, ) @@ -78,18 +87,12 @@ def test_errata_rpms_via_koji(fake_errata_tool, fake_koji, koji_dir): "nvr": "sudo-1.8.25p1-4.el8_0.3", } - signed_rpm_path = os.path.join( - koji_dir, - "vol/somevol/packages/foobuild/1.0/1.el8", - "data/signed/def456/x86_64/foo-1.0-1.x86_64.rpm", - ) - # Make signed RPMs exist (contents not relevant here) for filename, rpm in fake_koji.rpm_data.items(): signed_rpm_path = os.path.join( koji_dir, "packages/sudo/1.8.25p1/4.el8_0.3/", - "data/signed/fd431d51/%s/%s" % (rpm["arch"], filename), + "data/signed/%s/%s/%s" % (expected_sig_key_path, rpm["arch"], filename), ) signed_dir = os.path.dirname(signed_rpm_path) if not os.path.exists(signed_dir): @@ -218,7 +221,7 @@ def test_errata_rpms_via_koji(fake_errata_tool, fake_koji, koji_dir): src=os.path.join( koji_dir, "packages/sudo/1.8.25p1/4.el8_0.3/data/signed", - "fd431d51/ppc64le/sudo-1.8.25p1-4.el8_0.3.ppc64le.rpm", + f"{expected_sig_key_path}/ppc64le/sudo-1.8.25p1-4.el8_0.3.ppc64le.rpm", ), dest=["rhel-8-for-ppc64le-baseos-e4s-rpms__8_DOT_0"], md5sum="0d56f302617696d3511e71e1669e62c0", @@ -233,7 +236,7 @@ def test_errata_rpms_via_koji(fake_errata_tool, fake_koji, koji_dir): src=os.path.join( koji_dir, "packages/sudo/1.8.25p1/4.el8_0.3/data/signed", - "fd431d51/ppc64le/sudo-debuginfo-1.8.25p1-4.el8_0.3.ppc64le.rpm", + f"{expected_sig_key_path}/ppc64le/sudo-debuginfo-1.8.25p1-4.el8_0.3.ppc64le.rpm", ), dest=["rhel-8-for-ppc64le-baseos-e4s-debug-rpms__8_DOT_0"], md5sum="e242826fb38f487502cdc1f1a06991d2", @@ -248,7 +251,7 @@ def test_errata_rpms_via_koji(fake_errata_tool, fake_koji, koji_dir): src=os.path.join( koji_dir, "packages/sudo/1.8.25p1/4.el8_0.3/data/signed", - "fd431d51/ppc64le/sudo-debuginfo-1.8.25p1-4.el8_0.3.ppc64le.rpm", + f"{expected_sig_key_path}/ppc64le/sudo-debuginfo-1.8.25p1-4.el8_0.3.ppc64le.rpm", ), dest=["rhel-8-for-ppc64le-baseos-e4s-debug-rpms__8_DOT_0"], md5sum="e242826fb38f487502cdc1f1a06991d2", @@ -263,7 +266,7 @@ def test_errata_rpms_via_koji(fake_errata_tool, fake_koji, koji_dir): src=os.path.join( koji_dir, "packages/sudo/1.8.25p1/4.el8_0.3/data/signed", - "fd431d51/ppc64le/sudo-debugsource-1.8.25p1-4.el8_0.3.ppc64le.rpm", + f"{expected_sig_key_path}/ppc64le/sudo-debugsource-1.8.25p1-4.el8_0.3.ppc64le.rpm", ), dest=["rhel-8-for-ppc64le-baseos-e4s-debug-rpms__8_DOT_0"], md5sum="d6da7e2e3d9efe050fef2e8d047682be", @@ -278,7 +281,7 @@ def test_errata_rpms_via_koji(fake_errata_tool, fake_koji, koji_dir): src=os.path.join( koji_dir, "packages/sudo/1.8.25p1/4.el8_0.3/data/signed", - "fd431d51/ppc64le/sudo-debugsource-1.8.25p1-4.el8_0.3.ppc64le.rpm", + f"{expected_sig_key_path}/ppc64le/sudo-debugsource-1.8.25p1-4.el8_0.3.ppc64le.rpm", ), dest=["rhel-8-for-ppc64le-baseos-e4s-debug-rpms__8_DOT_0"], md5sum="d6da7e2e3d9efe050fef2e8d047682be", @@ -293,7 +296,7 @@ def test_errata_rpms_via_koji(fake_errata_tool, fake_koji, koji_dir): src=os.path.join( koji_dir, "packages/sudo/1.8.25p1/4.el8_0.3/data/signed", - "fd431d51/src/sudo-1.8.25p1-4.el8_0.3.src.rpm", + f"{expected_sig_key_path}/src/sudo-1.8.25p1-4.el8_0.3.src.rpm", ), dest=[ "rhel-8-for-ppc64le-baseos-e4s-source-rpms__8_DOT_0", @@ -311,7 +314,7 @@ def test_errata_rpms_via_koji(fake_errata_tool, fake_koji, koji_dir): src=os.path.join( koji_dir, "packages/sudo/1.8.25p1/4.el8_0.3/data/signed", - "fd431d51/x86_64/sudo-1.8.25p1-4.el8_0.3.x86_64.rpm", + f"{expected_sig_key_path}/x86_64/sudo-1.8.25p1-4.el8_0.3.x86_64.rpm", ), dest=["rhel-8-for-x86_64-baseos-e4s-rpms__8_DOT_0"], md5sum="25e9470c4fe96034fe1d7525c04b5d8e", diff --git a/tests/errata/test_errata_rpms_filter.py b/tests/errata/test_errata_rpms_filter.py index 6e3201e5..d327030c 100644 --- a/tests/errata/test_errata_rpms_filter.py +++ b/tests/errata/test_errata_rpms_filter.py @@ -1,6 +1,7 @@ import os import pytest +from mock import patch from pushsource import Source, RpmPushItem @@ -137,8 +138,10 @@ def source_factory(fake_errata_tool, fake_koji, koji_dir): ), ], ) +@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") +@patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") def test_errata_rpms_filtered_by_arch( - source_factory, rpm_filter_arch, expected_filenames + mock_get_rpm_header, mock_get_keys_from_headers, source_factory, rpm_filter_arch, expected_filenames ): """Errata source can filter produced RPMs by arch""" diff --git a/tests/koji/data/rpms/walrus-5.21-1.noarch.rpm b/tests/koji/data/rpms/walrus-5.21-1.noarch.rpm new file mode 100644 index 0000000000000000000000000000000000000000..1e833d9e2bbe7c02397f08aeba3c709001d71a39 GIT binary patch literal 2445 zcmb7`e^69a6vyu_EMewvO{CNXHSxzT@9i(xQVf?A5ET*?49vH?Z((C$x827Ne~gL) zCE^%pfT7}#s5Z^iB4r9i5{Zx$(1da@C(Ho@$;P1^vF)7QYhz9Q(cQW4dq3}-d*6HK z-gEEv9l3MOi$D=&a~6B1L#CE1SQ(3V{(lh>$v->oINB|u_nsg`oCobNsMr=jN5X@j z@-SS9$Pel;X!v;A1p0b)1A7BUpabx2Q0Om;^!Rx7dRV*$6vp%F2L*qz4OCPh8s{Y; zOZ9=p{rSyZzrA&$L?o_A^CkPsAGBATH-~<(sV1T+cU#Ye^+^@Be(C5=AK8}Tdr91g zc|Ex=T&^4H@;b77{<~i!ANee~{YacwXT^7Zhgm^lel0X@_pI3sFMSg@+&1dhmG=@0 zS0(ip?v5Whx~%2?s!bOq+wT4l_U;#F&eTjAzkBm=U?%sn%x-=#sO)GCS~cbb6T4=Z`qah?HU3YQ zL^hP&cQlQty_j%vqHw)_Wo=P%Y{B?f4w31n4?T>i?b2zwrfi<5T@9?VbP7N z(LarePLjHEs^SvIl>FSrPM&hn+M=&d>o6VAYKNt74RzP=kuot3&jBh)Tt% zRjeVD4K*?$Y7@i|iqEuvi=(N?mz;|IHaT@k)}89CU0btE9hY|mTKpq?nvZ{-*6oZ@ z#GKpsdG_qvzLN*6Ve7}mUyrT%w7YUwneM>sfVeuf-)@~NC;Qy`?^hpR@xFpYN0z?V zGV-hTd2tGFs@)!j+p{P#?mUfumZS31-g=ejZ3`Fio`zDvvYe;=VP zsp2M$6&hw}n=46*KOoiPg`qeoga4_LlmR-kaN|Pr3GsL@D~2 z9G@0{EwS^&^@ZEWt1it=5yS6-d>%&2{$$^})=wJaIv320byOCP-$mRt}7%)Q24 zE>{t>VNDY9SRSZhZOY)*JgArl6G73(2-O!VmPhLz@_>jb_kbb~p^SNeJe)G-0orgZ z?B7RuCgnSzkQd}p9$}tff5;=Z|6NeD$5S2vMSB9}-$8Nw)szQ8(T;hH&wEI@jPf6p z_j%YG$HrPo8T;ba+yD4XZ;`{W1!n}KDd+cK9abFT#>=sWCG>eXZ2S8Pd2EUTQT-sl$I; z;FO4Tq`%I~vK=fWDVCWFV`H)uBq~GT?HRlf9B#8&%mz*{TQeLaRS;~?2L}rd!Oj^{ ztpX=E@kXniv+-7&g_m3HDa2yV$jl)bR?conb&r=@N$@m^RBH?-g@y~!>lI9>R;vvO zF&H?G)2KBXwTjVVtlh2Ef`xQjFp(`)NM$KfS-M6A7a=o)pPRyG z@OHC-7;I)MNm*yM5$&T;c_JpW^Tt$8ka?o$c?zzD@MXHFD5=21;6}5ZHwaey+DBGU zW&h6lh;_$i9nG)LYGfxk(d6E2U9LggJVs6~#5D!QOUZ=5PsK3INrq8L+I*gVqu@E8 zj@*M$YigWdcQ&EDCBD7!Y|GZd+&Xe=g`S+a-4Z@}`>4@3 WyL-C)E1k|pSmQ%PLG3{>bAJMMz);!% literal 0 HcmV?d00001 diff --git a/tests/koji/test_koji.py b/tests/koji/test_koji.py index ee6fc976..d89cb308 100644 --- a/tests/koji/test_koji.py +++ b/tests/koji/test_koji.py @@ -5,6 +5,7 @@ from pushsource import Source, RpmPushItem +DATADIR = os.path.join(os.path.dirname(__file__), "data") def test_koji_needs_url(): """Can't obtain source without giving URL""" @@ -232,7 +233,7 @@ def test_koji_uses_signing_key(fake_koji, koji_dir, caplog): source = Source.get( "koji:https://koji.example.com/?rpm=foo-1.0-1.x86_64.rpm", basedir=koji_dir, - signing_key=["ABC123", None, "DEF456"], + signing_key=["ABC123", None, "F78FB195"], ) fake_koji.rpm_data["foo-1.0-1.x86_64.rpm"] = { @@ -254,23 +255,23 @@ def test_koji_uses_signing_key(fake_koji, koji_dir, caplog): signed_rpm_path = os.path.join( koji_dir, "vol/somevol/packages/foobuild/1.0/1.el8", - "data/signed/def456/x86_64/foo-1.0-1.x86_64.rpm", + "data/signed/f78fb195/x86_64/foo-1.0-1.x86_64.rpm", ) - # Make the signed RPM exist (contents not relevant here) + # Make the signed RPM exist, make symlink to the test RPM file os.makedirs(os.path.dirname(signed_rpm_path)) - open(signed_rpm_path, "wb") + os.symlink(os.path.join(DATADIR, "rpms", "walrus-5.21-1.noarch.rpm"), signed_rpm_path) # Eagerly fetch items = list(source) - # It should have found the RPM using the signing key we created within testdata + # It should have found the RPM using the signing key extracted from RPM header assert items[0] == RpmPushItem( name="foo-1.0-1.x86_64.rpm", state="PENDING", src=signed_rpm_path, build="foobuild-1.0-1.el8", - signing_key="def456", + signing_key="f78fb195", ) @@ -308,3 +309,50 @@ def test_koji_cache(fake_koji, koji_dir): # It should have stored something in the cache assert cache + + +def test_koji_uses_signing_key_alias(fake_koji, koji_dir, caplog): + """RPM uses first existing of specified signing keys including multi-key alias.""" + + source = Source.get( + "koji:https://koji.example.com/?rpm=foo-1.0-1.x86_64.rpm", + basedir=koji_dir, + signing_key=["foo+bar+baz", None, "F78FB195"], + ) + + fake_koji.rpm_data["foo-1.0-1.x86_64.rpm"] = { + "arch": "x86_64", + "name": "foo", + "version": "1.0", + "release": "1", + "build_id": 1234, + } + fake_koji.build_data[1234] = { + "id": 1234, + "name": "foobuild", + "version": "1.0", + "release": "1.el8", + "nvr": "foobuild-1.0-1.el8", + "volume_name": "somevol", + } + # alias is converted to comma separate list ok key names + signed_rpm_path = os.path.join( + koji_dir, + "vol/somevol/packages/foobuild/1.0/1.el8", + "data/signed/foo,bar,baz/x86_64/foo-1.0-1.x86_64.rpm", + ) + + # Make the signed RPM exist, make symlink to the test RPM file + os.makedirs(os.path.dirname(signed_rpm_path)) + os.symlink(os.path.join(DATADIR, "rpms", "walrus-5.21-1.noarch.rpm"), signed_rpm_path) + # Eagerly fetch + items = list(source) + + # It should have found the RPM using the signing key extracted from RPM header + assert items[0] == RpmPushItem( + name="foo-1.0-1.x86_64.rpm", + state="PENDING", + src=signed_rpm_path, + build="foobuild-1.0-1.el8", + signing_key="f78fb195", + ) \ No newline at end of file diff --git a/tests/koji/test_koji_polling.py b/tests/koji/test_koji_polling.py index d34bb5c4..b7897391 100644 --- a/tests/koji/test_koji_polling.py +++ b/tests/koji/test_koji_polling.py @@ -1,8 +1,12 @@ +from typing import Any + + import os from mock import patch from pushsource import Source, RpmPushItem +DATADIR = os.path.join(os.path.dirname(__file__), "data") @patch.dict( "os.environ", @@ -10,8 +14,10 @@ ) @patch("pushsource._impl.helpers.os.path.exists") @patch("pushsource._impl.helpers.time.sleep") +@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header") +@patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") def test_koji_poll_for_signed_rpm_highest_priority_key_present( - mock_sleep, mock_path_exists, fake_koji, koji_dir, caplog + mock_get_rpm_header, mock_get_keys_from_headers, mock_sleep, mock_path_exists, fake_koji, koji_dir, caplog ): """Highest priority key becomes present after some time.""" @@ -43,6 +49,8 @@ def test_koji_poll_for_signed_rpm_highest_priority_key_present( "data/signed/abc123/x86_64/foo-1.0-1.x86_64.rpm", ) mock_path_exists.side_effect = [False, True, True, True] + mock_get_keys_from_headers.return_value = "abc123" + # Eagerly fetch items = list(source) @@ -64,8 +72,10 @@ def test_koji_poll_for_signed_rpm_highest_priority_key_present( ) @patch("pushsource._impl.helpers.os.path.exists") @patch("pushsource._impl.helpers.time.sleep") +@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header") +@patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") def test_koji_poll_for_signed_rpm_highest_priority_key_absent( - mock_sleep, mock_path_exists, fake_koji, koji_dir, caplog + mock_get_rpm_header, mock_get_keys_from_headers, mock_sleep, mock_path_exists, fake_koji, koji_dir, caplog ): """Highest priority key is always absent and a lower priority key is found.""" @@ -97,10 +107,11 @@ def test_koji_poll_for_signed_rpm_highest_priority_key_absent( "data/signed/def456/x86_64/foo-1.0-1.x86_64.rpm", ) mock_path_exists.side_effect = [False, False, False, False, False, True, True, True] + mock_get_keys_from_headers.return_value = "def456" # Eagerly fetch items = list(source) - # It should have found the RPM using the signing key we created within testdata + # It should have found the RPM using the signing key we return from RPM headers assert items[0] == RpmPushItem( name="foo-1.0-1.x86_64.rpm", state="PENDING", From 6db71f7d2fb57c37a956de1cf794b29f5c969df6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 15:53:25 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../backend/errata_source/errata_source.py | 4 +- src/pushsource/_impl/backend/koji_source.py | 9 ++-- tests/errata/test_errata_missing_modules.py | 13 +++++- tests/errata/test_errata_module_sources.py | 41 +++++++++++++++---- tests/errata/test_errata_modules.py | 14 ++++++- tests/errata/test_errata_rpms.py | 16 +++++++- tests/errata/test_errata_rpms_filter.py | 11 ++++- tests/koji/test_koji.py | 13 ++++-- tests/koji/test_koji_polling.py | 17 +++++++- 9 files changed, 111 insertions(+), 27 deletions(-) diff --git a/src/pushsource/_impl/backend/errata_source/errata_source.py b/src/pushsource/_impl/backend/errata_source/errata_source.py index f21d5346..9c9faeb3 100644 --- a/src/pushsource/_impl/backend/errata_source/errata_source.py +++ b/src/pushsource/_impl/backend/errata_source/errata_source.py @@ -416,8 +416,8 @@ def _rpm_push_items_from_build(self, erratum, build_nvr, build_info): rpms = build_info.get("rpms") or {} signing_key = build_info.get("sig_key") or None if signing_key: - # Errata Tool API may return key alias in sig_key field in format "name1,name2,...", but - # we need to convert "," into "+" otherwise it would be used as multiple keys for Koji + # Errata Tool API may return key alias in sig_key field in format "name1,name2,...", but + # we need to convert "," into "+" otherwise it would be used as multiple keys for Koji # source which would result in incorrect processing. signing_key = signing_key.replace(",", "+") sha256sums = (build_info.get("checksums") or {}).get("sha256") or {} diff --git a/src/pushsource/_impl/backend/koji_source.py b/src/pushsource/_impl/backend/koji_source.py index 12138c57..71e5dfca 100644 --- a/src/pushsource/_impl/backend/koji_source.py +++ b/src/pushsource/_impl/backend/koji_source.py @@ -10,6 +10,7 @@ import koji from more_executors import Executors from more_executors.futures import f_map + try: from kobo import rpmlib except Exception as ex: # pragma: no cover, pylint: disable=broad-except @@ -190,8 +191,8 @@ def __init__( signing_key (list[str]) GPG signing key ID(s). If provided, content must be signed using one of the provided keys. Include ``None`` if unsigned - should also be permitted. - + should also be permitted. + Supports also key alias in the format ``name1+name2+...`` Keys should be listed in the order of preference. @@ -266,7 +267,9 @@ def _parse_signing_key(self, keys): out = [] for key in keys: if key: - out.append(key.replace("+", ",")) # adjust alias to comma separated names of keys + out.append( + key.replace("+", ",") + ) # adjust alias to comma separated names of keys else: out.append(key) return out diff --git a/tests/errata/test_errata_missing_modules.py b/tests/errata/test_errata_missing_modules.py index 03becc17..5f2ed3cc 100644 --- a/tests/errata/test_errata_missing_modules.py +++ b/tests/errata/test_errata_missing_modules.py @@ -3,9 +3,18 @@ from pushsource import Source -@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") +@patch( + "pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", + return_value="fd431d51", +) @patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") -def test_errata_modules_via_koji(mock_get_rpm_header, mock_get_keys_from_headers, fake_errata_tool, fake_koji, koji_dir): +def test_errata_modules_via_koji( + mock_get_rpm_header, + mock_get_keys_from_headers, + fake_errata_tool, + fake_koji, + koji_dir, +): """Errata source gives an error if ET requested modules which don't exist in koji""" source = Source.get( diff --git a/tests/errata/test_errata_module_sources.py b/tests/errata/test_errata_module_sources.py index 72fedb84..6472b1b5 100644 --- a/tests/errata/test_errata_module_sources.py +++ b/tests/errata/test_errata_module_sources.py @@ -5,6 +5,7 @@ from pushsource import Source, ModuleMdSourcePushItem from mock import patch + @pytest.fixture def source_factory(fake_errata_tool, fake_koji, koji_dir): ctor = Source.get_partial( @@ -161,9 +162,15 @@ def source_factory(fake_errata_tool, fake_koji, koji_dir): yield ctor -@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") + +@patch( + "pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", + return_value="fd431d51", +) @patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") -def test_errata_module_sources(mock_get_rpm_header, mock_get_keys_from_headers, source_factory, koji_dir): +def test_errata_module_sources( + mock_get_rpm_header, mock_get_keys_from_headers, source_factory, koji_dir +): """Errata source can provide ModuleMdSourcePushItems, typical scenario.""" source = source_factory(errata="RHEA-2020:0346") @@ -194,9 +201,15 @@ def test_errata_module_sources(mock_get_rpm_header, mock_get_keys_from_headers, ) ] -@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") + +@patch( + "pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", + return_value="fd431d51", +) @patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") -def test_errata_module_sources_no_ftp_paths(mock_get_rpm_header, mock_get_keys_from_headers, source_factory): +def test_errata_module_sources_no_ftp_paths( + mock_get_rpm_header, mock_get_keys_from_headers, source_factory +): """Errata source skips ModuleMdSourcePushItems if ET does not request any FTP paths for modules.""" @@ -209,9 +222,15 @@ def test_errata_module_sources_no_ftp_paths(mock_get_rpm_header, mock_get_keys_f # Should not have found anything since ET reported no dests for modules in FTP paths assert src_items == [] -@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") + +@patch( + "pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", + return_value="fd431d51", +) @patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") -def test_errata_module_sources_no_cdn_list(mock_get_rpm_header, mock_get_keys_from_headers, source_factory, caplog): +def test_errata_module_sources_no_cdn_list( + mock_get_rpm_header, mock_get_keys_from_headers, source_factory, caplog +): """Errata source skips ModuleMdSourcePushItems if ET does not present those modules in get_advisory_cdn_file_list.""" @@ -231,9 +250,15 @@ def test_errata_module_sources_no_cdn_list(mock_get_rpm_header, mock_get_keys_fr in caplog.text ) -@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") + +@patch( + "pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", + return_value="fd431d51", +) @patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") -def test_errata_module_missing_sources(mock_get_rpm_header, mock_get_keys_from_headers, source_factory, fake_koji): +def test_errata_module_missing_sources( + mock_get_rpm_header, mock_get_keys_from_headers, source_factory, fake_koji +): """Errata source gives fatal error if ET requests some FTP paths for modules, yet no module sources exist on koji build.""" source = source_factory(errata="RHEA-2020:0346") diff --git a/tests/errata/test_errata_modules.py b/tests/errata/test_errata_modules.py index 756e5d3e..359234b9 100644 --- a/tests/errata/test_errata_modules.py +++ b/tests/errata/test_errata_modules.py @@ -10,9 +10,19 @@ ModuleMdPushItem, ) -@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") + +@patch( + "pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", + return_value="fd431d51", +) @patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") -def test_errata_modules_via_koji(mock_get_rpm_header, mock_get_keys_from_headers, fake_errata_tool, fake_koji, koji_dir): +def test_errata_modules_via_koji( + mock_get_rpm_header, + mock_get_keys_from_headers, + fake_errata_tool, + fake_koji, + koji_dir, +): """Errata source containing a module yields modules & RPMs taken from koji source""" diff --git a/tests/errata/test_errata_rpms.py b/tests/errata/test_errata_rpms.py index 4fab1c9d..da6cea12 100644 --- a/tests/errata/test_errata_rpms.py +++ b/tests/errata/test_errata_rpms.py @@ -16,10 +16,22 @@ ("RHSA-2020:0509-sig-key-alias", "foo,bar,baz"), ) + @pytest.mark.parametrize("erratum, expected_sig_key_path", TEST_DATA) -@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") +@patch( + "pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", + return_value="fd431d51", +) @patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") -def test_errata_rpms_via_koji(mock_get_rpm_header, mock_get_keys_from_headers, fake_errata_tool, erratum, expected_sig_key_path, fake_koji, koji_dir): +def test_errata_rpms_via_koji( + mock_get_rpm_header, + mock_get_keys_from_headers, + fake_errata_tool, + erratum, + expected_sig_key_path, + fake_koji, + koji_dir, +): """Errata source yields RPMs taken from koji source""" source = Source.get( diff --git a/tests/errata/test_errata_rpms_filter.py b/tests/errata/test_errata_rpms_filter.py index d327030c..e7b05548 100644 --- a/tests/errata/test_errata_rpms_filter.py +++ b/tests/errata/test_errata_rpms_filter.py @@ -138,10 +138,17 @@ def source_factory(fake_errata_tool, fake_koji, koji_dir): ), ], ) -@patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", return_value="fd431d51") +@patch( + "pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header", + return_value="fd431d51", +) @patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") def test_errata_rpms_filtered_by_arch( - mock_get_rpm_header, mock_get_keys_from_headers, source_factory, rpm_filter_arch, expected_filenames + mock_get_rpm_header, + mock_get_keys_from_headers, + source_factory, + rpm_filter_arch, + expected_filenames, ): """Errata source can filter produced RPMs by arch""" diff --git a/tests/koji/test_koji.py b/tests/koji/test_koji.py index d89cb308..fa284713 100644 --- a/tests/koji/test_koji.py +++ b/tests/koji/test_koji.py @@ -7,6 +7,7 @@ DATADIR = os.path.join(os.path.dirname(__file__), "data") + def test_koji_needs_url(): """Can't obtain source without giving URL""" @@ -260,7 +261,9 @@ def test_koji_uses_signing_key(fake_koji, koji_dir, caplog): # Make the signed RPM exist, make symlink to the test RPM file os.makedirs(os.path.dirname(signed_rpm_path)) - os.symlink(os.path.join(DATADIR, "rpms", "walrus-5.21-1.noarch.rpm"), signed_rpm_path) + os.symlink( + os.path.join(DATADIR, "rpms", "walrus-5.21-1.noarch.rpm"), signed_rpm_path + ) # Eagerly fetch items = list(source) @@ -344,10 +347,12 @@ def test_koji_uses_signing_key_alias(fake_koji, koji_dir, caplog): # Make the signed RPM exist, make symlink to the test RPM file os.makedirs(os.path.dirname(signed_rpm_path)) - os.symlink(os.path.join(DATADIR, "rpms", "walrus-5.21-1.noarch.rpm"), signed_rpm_path) + os.symlink( + os.path.join(DATADIR, "rpms", "walrus-5.21-1.noarch.rpm"), signed_rpm_path + ) # Eagerly fetch items = list(source) - + # It should have found the RPM using the signing key extracted from RPM header assert items[0] == RpmPushItem( name="foo-1.0-1.x86_64.rpm", @@ -355,4 +360,4 @@ def test_koji_uses_signing_key_alias(fake_koji, koji_dir, caplog): src=signed_rpm_path, build="foobuild-1.0-1.el8", signing_key="f78fb195", - ) \ No newline at end of file + ) diff --git a/tests/koji/test_koji_polling.py b/tests/koji/test_koji_polling.py index b7897391..7675d1c4 100644 --- a/tests/koji/test_koji_polling.py +++ b/tests/koji/test_koji_polling.py @@ -8,6 +8,7 @@ DATADIR = os.path.join(os.path.dirname(__file__), "data") + @patch.dict( "os.environ", {"PUSHSOURCE_SRC_POLL_TIMEOUT": "60"}, @@ -17,7 +18,13 @@ @patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header") @patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") def test_koji_poll_for_signed_rpm_highest_priority_key_present( - mock_get_rpm_header, mock_get_keys_from_headers, mock_sleep, mock_path_exists, fake_koji, koji_dir, caplog + mock_get_rpm_header, + mock_get_keys_from_headers, + mock_sleep, + mock_path_exists, + fake_koji, + koji_dir, + caplog, ): """Highest priority key becomes present after some time.""" @@ -75,7 +82,13 @@ def test_koji_poll_for_signed_rpm_highest_priority_key_present( @patch("pushsource._impl.backend.koji_source.rpmlib.get_keys_from_header") @patch("pushsource._impl.backend.koji_source.rpmlib.get_rpm_header") def test_koji_poll_for_signed_rpm_highest_priority_key_absent( - mock_get_rpm_header, mock_get_keys_from_headers, mock_sleep, mock_path_exists, fake_koji, koji_dir, caplog + mock_get_rpm_header, + mock_get_keys_from_headers, + mock_sleep, + mock_path_exists, + fake_koji, + koji_dir, + caplog, ): """Highest priority key is always absent and a lower priority key is found.""" From d7dd589359e6d4ebd9b8a55a1848b21234159acd Mon Sep 17 00:00:00 2001 From: Robert Bikar Date: Tue, 9 Dec 2025 18:15:37 +0100 Subject: [PATCH 3/3] Review minor fix --- src/pushsource/_impl/backend/koji_source.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pushsource/_impl/backend/koji_source.py b/src/pushsource/_impl/backend/koji_source.py index 71e5dfca..fda4e051 100644 --- a/src/pushsource/_impl/backend/koji_source.py +++ b/src/pushsource/_impl/backend/koji_source.py @@ -266,12 +266,8 @@ def _koji_session(self): def _parse_signing_key(self, keys): out = [] for key in keys: - if key: - out.append( - key.replace("+", ",") - ) # adjust alias to comma separated names of keys - else: - out.append(key) + # adjust alias to comma separated names of keys + out.append(key.replace("+", ",") if key else key) return out def _koji_check(self):