From c1e2cb41617f258c28e915435f4deb2ad2075fa5 Mon Sep 17 00:00:00 2001 From: Otto Urpelainen Date: Jul 26 2022 11:01:21 +0000 Subject: [PATCH 1/4] Add unit test for buildLabel --- diff --git a/tests/test_lib/test_build_label.py b/tests/test_lib/test_build_label.py new file mode 100644 index 0000000..f835af3 --- /dev/null +++ b/tests/test_lib/test_build_label.py @@ -0,0 +1,72 @@ +from __future__ import absolute_import +import mock +import os +import rpm +import unittest + +import koji + +class TestBuildLabel(unittest.TestCase): + def test_buildLabel(self): + """Test the buildLabel method""" + + self.assertRaises(AttributeError, koji.buildLabel, None) + self.assertRaises(AttributeError, koji.buildLabel, 1) + self.assertRaises(AttributeError, koji.buildLabel, []) + + input = {} + ret = koji.buildLabel(input) + self.assertEqual(ret, "None-None-None") + + input = {"name": "foo"} + ret = koji.buildLabel(input) + self.assertEqual(ret, "foo-None-None") + + input = {"version": "1.0.2"} + ret = koji.buildLabel(input) + self.assertEqual(ret, "None-1.0.2-None") + + input = {"release": "2"} + ret = koji.buildLabel(input) + self.assertEqual(ret, "None-None-2") + + input = {"name": "foo", "version": "1.0.2"} + ret = koji.buildLabel(input) + self.assertEqual(ret, "foo-1.0.2-None") + + input = {"name": "foo", "release": "2"} + ret = koji.buildLabel(input) + self.assertEqual(ret, "foo-None-2") + + input = {"version": "1.0.2", "release": "2"} + ret = koji.buildLabel(input) + self.assertEqual(ret, "None-1.0.2-2") + + input = {"name": "foo", "version": "1.0.2", "release": "2"} + ret = koji.buildLabel(input) + self.assertEqual(ret, "foo-1.0.2-2") + + input = {"package_name": "bar", "version": "1.0.2", "release": "2"} + ret = koji.buildLabel(input) + self.assertEqual(ret, "bar-1.0.2-2") + + input = { + "package_name": "bar", + "name": "foo", + "version": "1.0.2", + "release": "2" + } + ret = koji.buildLabel(input) + self.assertEqual(ret, "bar-1.0.2-2") + + input = {"epoch": 7, "name": "foo", "version": "1.0.2", "release": "2"} + ret = koji.buildLabel(input) + self.assertEqual(ret, "foo-1.0.2-2") + + input = {"epoch": 7, "name": "foo", "version": "1.0.2", "release": "2"} + ret = koji.buildLabel(input, True) + self.assertEqual(ret, "7:foo-1.0.2-2") + + input = {"name": "foo", "version": "1.0.2", "release": "2"} + ret = koji.buildLabel(input, True) + self.assertEqual(ret, "foo-1.0.2-2") From de372962708476d9497373393f02410ff5b44896 Mon Sep 17 00:00:00 2001 From: Otto Urpelainen Date: Jul 26 2022 11:01:21 +0000 Subject: [PATCH 2/4] Add utility function to watch builds External tools that interact with Koji need to wait for builds to appear in a repo. For example, Fedora cli tools 'bodhi' and 'fedpkg' can create buildroot overrides, which only become useful after the override's build appears in the correct repo. At the moment, 'bodhi' waits for a repo by invoking the 'koji wait-repo' cli tool while 'fedpkg' does not wait at all. In order to make it easier for such tools to wait for a repo, the wait implementation from 'koji wait-repo' is moved to koji_cli.lib namespace where it is available for use through Python import. --- diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py index 15c7788..94c3468 100644 --- a/cli/koji_cli/commands.py +++ b/cli/koji_cli/commands.py @@ -45,6 +45,7 @@ from koji_cli.lib import ( print_task_recurse, unique_path, warn, + watch_builds, watch_logs, watch_tasks, truncate_string @@ -7044,8 +7045,6 @@ def anon_handle_wait_repo(options, session, args): "value only") (suboptions, args) = parser.parse_args(args) - start = time.time() - builds = [koji.parse_NVR(build) for build in suboptions.builds] if len(args) < 1: parser.error("Please specify a tag name") @@ -7087,38 +7086,12 @@ def anon_handle_wait_repo(options, session, args): warn("nvr %s is not current in tag %s\n latest build in %s is %s" % (expected_nvr, tag, tag, present_nvr)) - last_repo = None - repo = session.getRepo(tag_id) - - while True: - if builds and repo and repo != last_repo: - if koji.util.checkForBuilds(session, tag_id, builds, repo['create_event'], - latest=True): - if not suboptions.quiet: - print("Successfully waited %s for %s to appear in the %s repo" % - (koji.util.duration(start), koji.util.printList(suboptions.builds), tag)) - return - - if (time.time() - start) >= (suboptions.timeout * 60.0): - if not suboptions.quiet: - if builds: - error("Unsuccessfully waited %s for %s to appear in the %s repo" % - (koji.util.duration(start), koji.util.printList(suboptions.builds), tag)) - else: - error("Unsuccessfully waited %s for a new %s repo" % - (koji.util.duration(start), tag)) - error() - - time.sleep(options.poll_interval) - last_repo = repo - repo = session.getRepo(tag_id) - - if not builds: - if repo != last_repo: - if not suboptions.quiet: - print("Successfully waited %s for a new %s repo" % - (koji.util.duration(start), tag)) - return + try: + watch_builds( + session, tag_id, builds, quiet=suboptions.quiet, + poll_interval=options.poll_interval, timeout=suboptions.timeout) + except koji.GenericError as e: + error(str(e)) def handle_regen_repo(options, session, args): diff --git a/cli/koji_cli/lib.py b/cli/koji_cli/lib.py index e5821d8..c533064 100644 --- a/cli/koji_cli/lib.py +++ b/cli/koji_cli/lib.py @@ -831,6 +831,63 @@ def _list_tasks(options, session): return tasklist +def watch_builds(session, tag_id, builds, quiet=False, poll_interval=5, timeout=120): + """Watch for given builds to appear in given tag. If no build are given, + watch for new repo for given tag. + + :param session: Koji session object + :param tag_id: Tag id + :param builds: List of builds as NVR dicts + :param quiet: no/verbose + :param poll_interval: Poll interval in seconds + :param timeout: Watch timeout in minutes""" + last_repo = None + repo = session.getRepo(tag_id) + + # String representations for logs and exceptions + builds_str = koji.util.printList( + [koji.buildLabel(build) for build in builds]) + tag_info = session.getTag(tag_id) + if not tag_info: + raise koji.GenericError("No tag with id: %s" % tag_id) + tag_name = tag_info['name'] + + start = time.time() + while True: + if builds and repo and repo != last_repo: + if koji.util.checkForBuilds(session, tag_id, builds, + repo['create_event'], latest=True): + if not quiet: + print("Successfully waited %s for %s " + "to appear in the %s repo" % + (koji.util.duration(start), builds_str, tag_name)) + return + + if (time.time() - start >= timeout * 60.0): + if not quiet: + if builds: + raise koji.GenericError( + "Unsuccessfully waited %s for %s " + "to appear in the %s repo" % + (koji.util.duration(start), builds_str, tag_name)) + else: + raise koji.GenericError( + "Unsuccessfully waited %s for a new %s repo" % + (koji.util.duration(start), tag_name)) + raise koji.GenericError() + + time.sleep(poll_interval) + last_repo = repo + repo = session.getRepo(tag_id) + + if not builds: + if repo != last_repo: + if not quiet: + print("Successfully waited %s for a new %s repo" % + (koji.util.duration(start), tag_name)) + return + + def format_inheritance_flags(parent): """Return a human readable string of inheritance flags""" flags = '' From d410609b728c6131ca2145e2832e6fe6153c6ee4 Mon Sep 17 00:00:00 2001 From: Tomas Kopecek Date: Jul 26 2022 11:01:21 +0000 Subject: [PATCH 3/4] exception-free watch_builds Related: https://pagure.io/koji/issue/2981 --- diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py index 94c3468..6417c5f 100644 --- a/cli/koji_cli/commands.py +++ b/cli/koji_cli/commands.py @@ -7086,12 +7086,13 @@ def anon_handle_wait_repo(options, session, args): warn("nvr %s is not current in tag %s\n latest build in %s is %s" % (expected_nvr, tag, tag, present_nvr)) - try: - watch_builds( - session, tag_id, builds, quiet=suboptions.quiet, - poll_interval=options.poll_interval, timeout=suboptions.timeout) - except koji.GenericError as e: - error(str(e)) + success, msg = watch_builds(session, tag_id, builds, + poll_interval=options.poll_interval, timeout=suboptions.timeout) + if success: + if not suboptions.quiet: + print(msg) + else: + error('' if suboptions.quiet else msg) def handle_regen_repo(options, session, args): diff --git a/cli/koji_cli/lib.py b/cli/koji_cli/lib.py index c533064..aef51fd 100644 --- a/cli/koji_cli/lib.py +++ b/cli/koji_cli/lib.py @@ -831,25 +831,23 @@ def _list_tasks(options, session): return tasklist -def watch_builds(session, tag_id, builds, quiet=False, poll_interval=5, timeout=120): - """Watch for given builds to appear in given tag. If no build are given, +def watch_builds(session, tag_id, builds, poll_interval=5, timeout=120): + """Watch for given builds to appear in given tag. If no builds are given, watch for new repo for given tag. :param session: Koji session object - :param tag_id: Tag id - :param builds: List of builds as NVR dicts - :param quiet: no/verbose - :param poll_interval: Poll interval in seconds - :param timeout: Watch timeout in minutes""" + :param int tag_id: Tag id + :param [dict] builds: List of builds as NVR dicts + :param int poll_interval: Poll interval in seconds + :param int timeout: Watch timeout in minutes + :returns bool, msg: False if timeouted + """ last_repo = None repo = session.getRepo(tag_id) # String representations for logs and exceptions - builds_str = koji.util.printList( - [koji.buildLabel(build) for build in builds]) - tag_info = session.getTag(tag_id) - if not tag_info: - raise koji.GenericError("No tag with id: %s" % tag_id) + builds_str = koji.util.printList([koji.buildLabel(build) for build in builds]) + tag_info = session.getTag(tag_id, strict=True) tag_name = tag_info['name'] start = time.time() @@ -857,24 +855,16 @@ def watch_builds(session, tag_id, builds, quiet=False, poll_interval=5, timeout= if builds and repo and repo != last_repo: if koji.util.checkForBuilds(session, tag_id, builds, repo['create_event'], latest=True): - if not quiet: - print("Successfully waited %s for %s " - "to appear in the %s repo" % - (koji.util.duration(start), builds_str, tag_name)) - return + return (True, "Successfully waited %s for %s to appear in the %s repo" % + (koji.util.duration(start), builds_str, tag_name)) if (time.time() - start >= timeout * 60.0): - if not quiet: - if builds: - raise koji.GenericError( - "Unsuccessfully waited %s for %s " - "to appear in the %s repo" % + if builds: + return (False, "Unsuccessfully waited %s for %s to appear in the %s repo" % (koji.util.duration(start), builds_str, tag_name)) - else: - raise koji.GenericError( - "Unsuccessfully waited %s for a new %s repo" % + else: + return (False, "Unsuccessfully waited %s for a new %s repo" % (koji.util.duration(start), tag_name)) - raise koji.GenericError() time.sleep(poll_interval) last_repo = repo @@ -882,10 +872,8 @@ def watch_builds(session, tag_id, builds, quiet=False, poll_interval=5, timeout= if not builds: if repo != last_repo: - if not quiet: - print("Successfully waited %s for a new %s repo" % - (koji.util.duration(start), tag_name)) - return + return (True, "Successfully waited %s for a new %s repo" % + (koji.util.duration(start), tag_name)) def format_inheritance_flags(parent): diff --git a/tests/test_cli/test_wait_repo.py b/tests/test_cli/test_wait_repo.py index 25fe82b..a4da3a2 100644 --- a/tests/test_cli/test_wait_repo.py +++ b/tests/test_cli/test_wait_repo.py @@ -36,7 +36,7 @@ class TestWaitRepo(utils.CliTestCase): self.options = mock.MagicMock() self.options.quiet = True - self.options.poll_interval = 1 # second + self.options.poll_interval = 0.0001 # keep it fast self.options.weburl = 'https://localhost.local' self.session = mock.MagicMock() From 8e3b7a9ea0775d1f5ba61baa0032e7591e435dc9 Mon Sep 17 00:00:00 2001 From: Tomas Kopecek Date: Jul 26 2022 11:03:45 +0000 Subject: [PATCH 4/4] rename function --- diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py index 6417c5f..36f6afc 100644 --- a/cli/koji_cli/commands.py +++ b/cli/koji_cli/commands.py @@ -45,7 +45,7 @@ from koji_cli.lib import ( print_task_recurse, unique_path, warn, - watch_builds, + wait_repo, watch_logs, watch_tasks, truncate_string @@ -7086,8 +7086,8 @@ def anon_handle_wait_repo(options, session, args): warn("nvr %s is not current in tag %s\n latest build in %s is %s" % (expected_nvr, tag, tag, present_nvr)) - success, msg = watch_builds(session, tag_id, builds, - poll_interval=options.poll_interval, timeout=suboptions.timeout) + success, msg = wait_repo(session, tag_id, builds, + poll_interval=options.poll_interval, timeout=suboptions.timeout) if success: if not suboptions.quiet: print(msg) diff --git a/cli/koji_cli/lib.py b/cli/koji_cli/lib.py index aef51fd..2c69ff6 100644 --- a/cli/koji_cli/lib.py +++ b/cli/koji_cli/lib.py @@ -831,7 +831,7 @@ def _list_tasks(options, session): return tasklist -def watch_builds(session, tag_id, builds, poll_interval=5, timeout=120): +def wait_repo(session, tag_id, builds, poll_interval=5, timeout=120): """Watch for given builds to appear in given tag. If no builds are given, watch for new repo for given tag.