diff --git a/src/pushsource/_impl/backend/errata_source/errata_source.py b/src/pushsource/_impl/backend/errata_source/errata_source.py index 9c3efb86..9c9faeb3 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..fda4e051 100644 --- a/src/pushsource/_impl/backend/koji_source.py +++ b/src/pushsource/_impl/backend/koji_source.py @@ -11,6 +11,16 @@ 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 ( KojiBuildInfo, @@ -183,6 +193,8 @@ def __init__( using one of the provided keys. Include ``None`` if unsigned should also be permitted. + Supports also key alias in the format ``name1+name2+...`` + Keys should be listed in the order of preference. basedir (str) @@ -216,7 +228,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 +263,13 @@ 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: + # adjust alias to comma separated names of keys + out.append(key.replace("+", ",") if key else 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 +328,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 +339,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..5f2ed3cc 100644 --- a/tests/errata/test_errata_missing_modules.py +++ b/tests/errata/test_errata_missing_modules.py @@ -1,9 +1,20 @@ 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..6472b1b5 100644 --- a/tests/errata/test_errata_module_sources.py +++ b/tests/errata/test_errata_module_sources.py @@ -3,6 +3,7 @@ import pytest from pushsource import Source, ModuleMdSourcePushItem +from mock import patch @pytest.fixture @@ -162,7 +163,14 @@ 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") @@ -194,7 +202,14 @@ 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.""" @@ -208,7 +223,14 @@ def test_errata_module_sources_no_ftp_paths(source_factory): 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.""" @@ -229,7 +251,14 @@ def test_errata_module_sources_no_cdn_list(source_factory, caplog): ) -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..359234b9 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, @@ -11,7 +11,18 @@ ) -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..da6cea12 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,32 @@ 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 +99,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 +233,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 +248,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 +263,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 +278,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 +293,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 +308,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 +326,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..e7b05548 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,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_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 00000000..1e833d9e Binary files /dev/null and b/tests/koji/data/rpms/walrus-5.21-1.noarch.rpm differ diff --git a/tests/koji/test_koji.py b/tests/koji/test_koji.py index ee6fc976..fa284713 100644 --- a/tests/koji/test_koji.py +++ b/tests/koji/test_koji.py @@ -5,6 +5,8 @@ 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 +234,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 +256,25 @@ 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 +312,52 @@ 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", + ) diff --git a/tests/koji/test_koji_polling.py b/tests/koji/test_koji_polling.py index d34bb5c4..7675d1c4 100644 --- a/tests/koji/test_koji_polling.py +++ b/tests/koji/test_koji_polling.py @@ -1,8 +1,13 @@ +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 +15,16 @@ ) @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 +56,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 +79,16 @@ 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 +120,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",