|
| 1 | +From 73b4f34bea6613e9bcc9acfe46761073cceaff5a Mon Sep 17 00:00:00 2001 |
| 2 | +From: Adam Williamson <awilliam@redhat.com> |
| 3 | +Date: Fri, 14 Mar 2025 16:04:21 -0700 |
| 4 | +Subject: [PATCH 1/2] download-build: allow fallback to unsigned with --key |
| 5 | + |
| 6 | +If you pass --key to download-build and signed packages aren't |
| 7 | +available, Koji will skip the unsigned package, or error out. |
| 8 | +This adds a modified behavior controlled by the new |
| 9 | +--fallback-unsigned arg. If this is passed with --key, unsigned |
| 10 | +copies will be downloaded for packages for which no signed copy |
| 11 | +can be found. |
| 12 | + |
| 13 | +This is primarily intended to work with a proposed Bodhi feature: |
| 14 | +https://github.com/fedora-infra/bodhi/pull/5859 . That would |
| 15 | +make Bodhi's `bodhi updates download` command automatically try |
| 16 | +to download signed copies, but I think it would be best if it |
| 17 | +falls back to getting unsigned copies if that doesn't work. Just |
| 18 | +failing out entirely seems wrong for that case. Implementing the |
| 19 | +fallback in Bodhi itself is more awkward and messy than adding it |
| 20 | +in Koji, and it may be useful for others in Koji I guess. |
| 21 | + |
| 22 | +Note there are two distinct 'no signed copies' cases. In the |
| 23 | +simple one, queryRPMSigs tells us Koji has no record of the |
| 24 | +package ever being signed with the key in question. In this case |
| 25 | +we don't bother trying to download a signed copy. In the other |
| 26 | +case, queryRPMSigs tells us the package *has* been signed with |
| 27 | +the key, but it turns out that signed copy has been garbage- |
| 28 | +collected and we can no longer download it. In this case we have |
| 29 | +to catch the failure on the download attempt and retry the |
| 30 | +download with sigkey set to None. |
| 31 | + |
| 32 | +Signed-off-by: Adam Williamson <awilliam@redhat.com> |
| 33 | +--- |
| 34 | + cli/koji_cli/commands.py | 26 ++++++++++++++++++++++++-- |
| 35 | + 1 file changed, 24 insertions(+), 2 deletions(-) |
| 36 | + |
| 37 | +diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py |
| 38 | +index 29b6e0a6..73c586fc 100644 |
| 39 | +--- a/cli/koji_cli/commands.py |
| 40 | ++++ b/cli/koji_cli/commands.py |
| 41 | +@@ -19,6 +19,7 @@ from datetime import datetime |
| 42 | + from dateutil.tz import tzutc |
| 43 | + from optparse import SUPPRESS_HELP, OptionParser |
| 44 | + |
| 45 | ++from requests.exceptions import HTTPError |
| 46 | + import six |
| 47 | + import six.moves.xmlrpc_client |
| 48 | + from six.moves import filter, map, range, zip |
| 49 | +@@ -6830,6 +6831,8 @@ def anon_handle_download_build(options, session, args): |
| 50 | + parser.add_option("--task-id", action="store_true", help="Interperet id as a task id") |
| 51 | + parser.add_option("--rpm", action="store_true", help="Download the given rpm") |
| 52 | + parser.add_option("--key", help="Download rpms signed with the given key") |
| 53 | ++ parser.add_option("--fallback-unsigned", action="store_true", |
| 54 | ++ help="When used with --key: download unsigned if signed packages not found") |
| 55 | + parser.add_option("--topurl", metavar="URL", default=options.topurl, |
| 56 | + help="URL under which Koji files are accessible") |
| 57 | + parser.add_option("--noprogress", action="store_true", help="Do not display progress meter") |
| 58 | +@@ -6912,6 +6915,7 @@ def anon_handle_download_build(options, session, args): |
| 59 | + continue |
| 60 | + rpms.append(rpm) |
| 61 | + |
| 62 | ++ unsigned = [] |
| 63 | + if suboptions.key: |
| 64 | + with session.multicall() as m: |
| 65 | + results = [m.queryRPMSigs(rpm_id=r['id'], sigkey=suboptions.key) for r in rpms] |
| 66 | +@@ -6921,14 +6925,32 @@ def anon_handle_download_build(options, session, args): |
| 67 | + nvra = "%(nvr)s-%(arch)s.rpm" % rpm |
| 68 | + warn("No such sigkey %s for rpm %s" % (suboptions.key, nvra)) |
| 69 | + rpms.remove(rpm) |
| 70 | ++ if suboptions.fallback_unsigned: |
| 71 | ++ unsigned.append(rpm) |
| 72 | + |
| 73 | +- size = len(rpms) + len(archives) |
| 74 | ++ size = len(rpms) + len(unsigned) + len(archives) |
| 75 | + number = 0 |
| 76 | + |
| 77 | + # run the download |
| 78 | + for rpm in rpms: |
| 79 | + number += 1 |
| 80 | +- download_rpm(info, rpm, suboptions.topurl, sigkey=suboptions.key, quiet=suboptions.quiet, |
| 81 | ++ try: |
| 82 | ++ download_rpm(info, rpm, suboptions.topurl, sigkey=suboptions.key, quiet=suboptions.quiet, |
| 83 | ++ noprogress=suboptions.noprogress, num=number, size=size) |
| 84 | ++ except HTTPError as err: |
| 85 | ++ # this is necessary even with the 'unsigned' handling above |
| 86 | ++ # because sometimes queryRPMSigs will still tell us a |
| 87 | ++ # package was signed with a given key, but the signed copy |
| 88 | ++ # has been garbage-collected |
| 89 | ++ if suboptions.key and suboptions.fallback_unsigned and err.response.status_code == 404: |
| 90 | ++ warn("Signed copy not present, will download unsigned copy") |
| 91 | ++ download_rpm(info, rpm, suboptions.topurl, sigkey=None, quiet=suboptions.quiet, |
| 92 | ++ noprogress=suboptions.noprogress, num=number, size=size) |
| 93 | ++ else: |
| 94 | ++ raise |
| 95 | ++ for rpm in unsigned: |
| 96 | ++ number += 1 |
| 97 | ++ download_rpm(info, rpm, suboptions.topurl, sigkey=None, quiet=suboptions.quiet, |
| 98 | + noprogress=suboptions.noprogress, num=number, size=size) |
| 99 | + for archive in archives: |
| 100 | + number += 1 |
| 101 | +-- |
| 102 | +2.52.0 |
| 103 | + |
0 commit comments