From bebb455c420d4ddbebac0594de6e669db45c9e85 Mon Sep 17 00:00:00 2001 From: Yuming Zhu Date: Jun 29 2018 08:49:09 +0000 Subject: [PATCH 1/4] check tag existence in list-tagged cmd and listTagged* APIs --- diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py index 77bc342..0826a06 100644 --- a/cli/koji_cli/commands.py +++ b/cli/koji_cli/commands.py @@ -2420,12 +2420,19 @@ def anon_handle_list_tagged(goptions, session, args): if options.type: opts['type'] = options.type event = koji.util.eventFromOpts(session, options) + event_id = None if event: opts['event'] = event['id'] + event_id = event['id'] event['timestr'] = time.asctime(time.localtime(event['ts'])) if not options.quiet: print("Querying at event %(id)i (%(timestr)s)" % event) + # check if tag exist(s|ed) + taginfo = session.getTag(tag, event=event_id) + if not taginfo: + parser.error(_("No such tag: %s" % tag)) + if options.rpms: rpms, builds = session.listTaggedRPMS(tag, **opts) data = rpms diff --git a/hub/kojihub.py b/hub/kojihub.py index 1e5158c..05a6535 100644 --- a/hub/kojihub.py +++ b/hub/kojihub.py @@ -9624,9 +9624,8 @@ class RootExports(object): def listTagged(self, tag, event=None, inherit=False, prefix=None, latest=False, package=None, owner=None, type=None): """List builds tagged with tag""" - if not isinstance(tag, (int, long)): - #lookup tag id - tag = get_tag_id(tag, strict=True) + #lookup tag id + tag = get_tag(tag, strict=True, event=event)['id'] results = readTaggedBuilds(tag, event, inherit=inherit, latest=latest, package=package, owner=owner, type=type) if prefix: prefix = prefix.lower() @@ -9635,15 +9634,14 @@ class RootExports(object): def listTaggedRPMS(self, tag, event=None, inherit=False, latest=False, package=None, arch=None, rpmsigs=False, owner=None, type=None): """List rpms and builds within tag""" - if not isinstance(tag, (int, long)): - #lookup tag id - tag = get_tag_id(tag, strict=True) + #lookup tag id + tag = get_tag(tag, strict=True, event=event)['id'] return readTaggedRPMS(tag, event=event, inherit=inherit, latest=latest, package=package, arch=arch, rpmsigs=rpmsigs, owner=owner, type=type) def listTaggedArchives(self, tag, event=None, inherit=False, latest=False, package=None, type=None): """List archives and builds within a tag""" - if not isinstance(tag, (int, long)): - tag = get_tag_id(tag, strict=True) + # lookup tag id + tag = get_tag(tag, strict=True, event=event)['id'] return readTaggedArchives(tag, event=event, inherit=inherit, latest=latest, package=package, type=type) def listBuilds(self, packageID=None, userID=None, taskID=None, prefix=None, state=None, From da7f971a045e0596120e6fca456e80289e732fdc Mon Sep 17 00:00:00 2001 From: Yuming Zhu Date: Jun 29 2018 08:49:09 +0000 Subject: [PATCH 2/4] unit test for cli list-tagged command --- diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py index 0826a06..10fc8df 100644 --- a/cli/koji_cli/commands.py +++ b/cli/koji_cli/commands.py @@ -2405,7 +2405,7 @@ def anon_handle_list_tagged(goptions, session, args): package = args[1] tag = args[0] opts = {} - for key in ('latest','inherit'): + for key in ('latest', 'inherit'): opts[key] = getattr(options, key) if options.latest_n is not None: opts['latest'] = options.latest_n diff --git a/tests/test_cli/test_list_tagged.py b/tests/test_cli/test_list_tagged.py new file mode 100644 index 0000000..3e178f7 --- /dev/null +++ b/tests/test_cli/test_list_tagged.py @@ -0,0 +1,240 @@ +import sys + +import mock + +from koji_cli.commands import anon_handle_list_tagged +from . import utils + + +class TestCliListTagged(utils.CliTestCase): + # Show long diffs in error output... + maxDiff = None + + def setUp(self): + self.error_format = """Usage: %s list-tagged [options] tag [package] +(Specify the --help global option for a list of other help options) + +%s: error: {message} +""" % (self.progname, self.progname) + self.session = mock.MagicMock() + self.options = mock.MagicMock(quiet=False) + self.session.getTag.return_value = {'id': 1} + self.session.listTaggedRPMS.return_value = [[{'id': 100, + 'build_id': 1, + 'name': 'rpmA', + 'version': '0.0.1', + 'release': '1.el6', + 'arch': 'noarch', + 'sigkey': 'sigkey'}, + {'id': 101, + 'build_id': 1, + 'name': 'rpmA', + 'version': '0.0.1', + 'release': '1.el6', + 'arch': 'x86_64', + 'sigkey': 'sigkey'} + ], [{'id': 1, + 'name': 'packagename', + 'version': 'version', + 'release': '1.el6', + 'nvr': 'n-v-r', + 'tag_name': 'tag', + 'owner_name': 'owner'}]] + self.session.listTagged.return_value = [{'id': 1, + 'name': 'packagename', + 'version': 'version', + 'release': '1.el6', + 'nvr': 'n-v-r', + 'tag_name': 'tag', + 'owner_name': 'owner'}] + + @mock.patch('koji.util.eventFromOpts', return_value={'id': 1000, + 'ts': 1000000.11}) + @mock.patch('koji_cli.commands.activate_session') + def test_list_tagged_builds(self, activate_session_mock, + event_from_opts_mock): + args = ['tag', 'pkg', '--latest', '--inherit', '--event=1000'] + + anon_handle_list_tagged(self.options, self.session, args) + activate_session_mock.assert_called_once_with(self.session, + self.options) + self.session.getTag.assert_called_once_with('tag', event=1000) + self.session.listTagged.assert_called_once_with('tag', + event=1000, + inherit=True, + latest=True, + package='pkg') + self.session.listTaggedRPMS.assert_not_called() + self.assert_console_message(sys.stdout, + 'Querying at event 1000 (Mon Jan 12 13:46:40 1970)\n' + 'Build Tag Built by\n' + '---------------------------------------- -------------------- ----------------\n' + 'n-v-r tag owner\n') + + @mock.patch('koji.util.eventFromOpts', return_value=None) + @mock.patch('koji_cli.commands.activate_session') + def test_list_tagged_builds_paths(self, activate_session_mock, + event_from_opts_mock): + args = ['tag', 'pkg', '--latest', '--inherit', '--paths'] + + anon_handle_list_tagged(self.options, self.session, args) + self.assert_console_message(sys.stdout, + 'Build Tag Built by\n' + '---------------------------------------- -------------------- ----------------\n' + '/mnt/koji/packages/packagename/version/1.el6 tag owner\n') + + @mock.patch('koji.util.eventFromOpts', return_value=None) + @mock.patch('koji_cli.commands.activate_session') + def test_list_tagged_rpms(self, activate_session_mock, + event_from_opts_mock): + args = ['tag', 'pkg', '--latest-n=3', '--rpms', '--sigs', + '--arch=x86_64'] + + anon_handle_list_tagged(self.options, self.session, args) + activate_session_mock.assert_called_once_with(self.session, + self.options) + self.session.getTag.assert_called_once_with('tag', event=None) + self.session.listTaggedRPMS.assert_called_once_with('tag', + package='pkg', + inherit=None, + latest=3, + rpmsigs=True, + arch='x86_64') + self.session.listTagged.assert_not_called() + self.assert_console_message(sys.stdout, + 'sigkey rpmA-0.0.1-1.el6.noarch\n' + 'sigkey rpmA-0.0.1-1.el6.x86_64\n') + + @mock.patch('koji.util.eventFromOpts', return_value=None) + @mock.patch('koji_cli.commands.activate_session') + def test_list_tagged_rpms_paths(self, activate_session_mock, + event_from_opts_mock): + args = ['tag', 'pkg', '--latest-n=3', '--rpms', + '--arch=x86_64', '--paths'] + + anon_handle_list_tagged(self.options, self.session, args) + self.assert_console_message(sys.stdout, + '/mnt/koji/packages/packagename/version/1.el6/noarch/rpmA-0.0.1-1.el6.noarch.rpm\n' + '/mnt/koji/packages/packagename/version/1.el6/x86_64/rpmA-0.0.1-1.el6.x86_64.rpm\n') + + + @mock.patch('koji.util.eventFromOpts', return_value=None) + @mock.patch('koji_cli.commands.activate_session') + def test_list_tagged_sigs_paths(self, activate_session_mock, + event_from_opts_mock): + args = ['tag', 'pkg', '--latest-n=3', '--rpms', '--sigs', + '--arch=x86_64', '--paths'] + + anon_handle_list_tagged(self.options, self.session, args) + self.assert_console_message(sys.stdout, '') + + @mock.patch('koji.util.eventFromOpts', return_value=None) + @mock.patch('koji_cli.commands.activate_session') + def test_list_tagged_type(self, activate_session_mock, + event_from_opts_mock): + args = ['tag', 'pkg', '--latest-n=3', '--type=maven'] + self.session.listTagged.return_value = [{'id': 1, + 'name': 'packagename', + 'version': 'version', + 'release': '1.el6', + 'nvr': 'n-v-r', + 'tag_name': 'tag', + 'owner_name': 'owner', + 'maven_group_id': 'group', + 'maven_artifact_id': 'artifact'}] + + anon_handle_list_tagged(self.options, self.session, args) + activate_session_mock.assert_called_once_with(self.session, + self.options) + self.session.getTag.assert_called_once_with('tag', event=None) + self.session.listTagged.assert_called_once_with('tag', + package='pkg', + inherit=None, + latest=3, + type='maven') + self.session.listTaggedRPMS.assert_not_called() + self.assert_console_message(sys.stdout, + 'Build Tag Group Id Artifact Id Built by\n' + '---------------------------------------- -------------------- -------------------- -------------------- ----------------\n' + 'n-v-r tag group artifact owner\n') + + + @mock.patch('koji.util.eventFromOpts', return_value=None) + @mock.patch('koji_cli.commands.activate_session') + def test_list_tagged_type_paths(self, activate_session_mock, + event_from_opts_mock): + args = ['tag', 'pkg', '--latest-n=3', '--type=maven', '--paths'] + self.session.listTagged.return_value = [{'id': 1, + 'name': 'packagename', + 'version': 'version', + 'release': '1.el6', + 'nvr': 'n-v-r', + 'tag_name': 'tag', + 'owner_name': 'owner', + 'maven_group_id': 'group', + 'maven_artifact_id': 'artifact'}] + + anon_handle_list_tagged(self.options, self.session, args) + self.assert_console_message(sys.stdout, + 'Build Tag Group Id Artifact Id Built by\n' + '---------------------------------------- -------------------- -------------------- -------------------- ----------------\n' + '/mnt/koji/packages/packagename/version/1.el6/maven tag group artifact owner\n') + + @mock.patch('koji.util.eventFromOpts', return_value={'id': 1000, + 'ts': 1000000.11}) + def test_list_tagged_args(self, event_from_opts_mock): + # Case 1, no argument + expected = self.format_error_message( + "A tag name must be specified") + self.assert_system_exit( + anon_handle_list_tagged, + self.options, + self.session, + [], + stderr=expected, + activate_session=None) + + # Case 2, arguments > 2 + expected = self.format_error_message( + "Only one package name may be specified") + self.assert_system_exit( + anon_handle_list_tagged, + self.options, + self.session, + ['tag', 'pkg1', 'pkg2'], + stderr=expected, + activate_session=None) + + # Case 3, no tag found + expected = self.format_error_message( + "No such tag: tag") + self.session.getTag.return_value = None + self.assert_system_exit( + anon_handle_list_tagged, + self.options, + self.session, + ['tag', 'pkg1'], + stderr=expected) + + def test_handle_list_tagged_help(self): + self.assert_help( + anon_handle_list_tagged, + """Usage: %s list-tagged [options] tag [package] +(Specify the --help global option for a list of other help options) + +Options: + -h, --help show this help message and exit + --arch=ARCH List rpms for this arch + --rpms Show rpms instead of builds + --inherit Follow inheritance + --latest Only show the latest builds/rpms + --latest-n=N Only show the latest N builds/rpms + --quiet Do not print the header information + --paths Show the file paths + --sigs Show signatures + --type=TYPE Show builds of the given type only. Currently supported + types: maven, win, image + --event=EVENT# query at event + --ts=TIMESTAMP query at timestamp + --repo=REPO# query at event for a repo +""" % self.progname) From e0e7af0ff1fc4b1329776164f3b22c5754279d4c Mon Sep 17 00:00:00 2001 From: Yuming Zhu Date: Jun 29 2018 08:49:09 +0000 Subject: [PATCH 3/4] cli: support multiple arches in list-tagged fixes: #933 --- diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py index 10fc8df..f034f8c 100644 --- a/cli/koji_cli/commands.py +++ b/cli/koji_cli/commands.py @@ -2378,7 +2378,7 @@ def anon_handle_list_tagged(goptions, session, args): usage = _("usage: %prog list-tagged [options] tag [package]") usage += _("\n(Specify the --help global option for a list of other help options)") parser = OptionParser(usage=usage) - parser.add_option("--arch", help=_("List rpms for this arch")) + parser.add_option("--arch", action="append", default=[], help=_("List rpms for this arch")) parser.add_option("--rpms", action="store_true", help=_("Show rpms instead of builds")) parser.add_option("--inherit", action="store_true", help=_("Follow inheritance")) parser.add_option("--latest", action="store_true", help=_("Only show the latest builds/rpms")) diff --git a/tests/test_cli/test_list_tagged.py b/tests/test_cli/test_list_tagged.py index 3e178f7..f8fc91e 100644 --- a/tests/test_cli/test_list_tagged.py +++ b/tests/test_cli/test_list_tagged.py @@ -88,7 +88,7 @@ class TestCliListTagged(utils.CliTestCase): def test_list_tagged_rpms(self, activate_session_mock, event_from_opts_mock): args = ['tag', 'pkg', '--latest-n=3', '--rpms', '--sigs', - '--arch=x86_64'] + '--arch=x86_64', '--arch=noarch'] anon_handle_list_tagged(self.options, self.session, args) activate_session_mock.assert_called_once_with(self.session, @@ -99,7 +99,8 @@ class TestCliListTagged(utils.CliTestCase): inherit=None, latest=3, rpmsigs=True, - arch='x86_64') + arch=['x86_64', + 'noarch']) self.session.listTagged.assert_not_called() self.assert_console_message(sys.stdout, 'sigkey rpmA-0.0.1-1.el6.noarch\n' From 4d0c62456e8e01d0391e992dbc2896da768549ad Mon Sep 17 00:00:00 2001 From: Yuming Zhu Date: Jun 29 2018 08:50:35 +0000 Subject: [PATCH 4/4] fix timezone in test_list_tagged --- diff --git a/tests/test_builder/test_build_notification.py b/tests/test_builder/test_build_notification.py index 6b3241f..7bff6c1 100644 --- a/tests/test_builder/test_build_notification.py +++ b/tests/test_builder/test_build_notification.py @@ -8,6 +8,8 @@ try: import unittest2 as unittest except ImportError: import unittest +import time + import koji import koji.util from .loadkojid import kojid @@ -55,6 +57,7 @@ class TestBuildNotification(unittest.TestCase): def setUp(self): self.original_timezone = os.environ.get('TZ') os.environ['TZ'] = 'US/Eastern' + time.tzset() self.tempdir = tempfile.mkdtemp() self.SMTP = mock.patch('smtplib.SMTP').start() self.session = mock.MagicMock() @@ -67,6 +70,7 @@ class TestBuildNotification(unittest.TestCase): del os.environ['TZ'] else: os.environ['TZ'] = self.original_timezone + time.tzset() mock.patch.stopall() def test_build_notification(self): diff --git a/tests/test_cli/test_list_tagged.py b/tests/test_cli/test_list_tagged.py index f8fc91e..8a9a956 100644 --- a/tests/test_cli/test_list_tagged.py +++ b/tests/test_cli/test_list_tagged.py @@ -1,4 +1,6 @@ import sys +import os +import time import mock @@ -11,6 +13,9 @@ class TestCliListTagged(utils.CliTestCase): maxDiff = None def setUp(self): + self.original_timezone = os.environ.get('TZ') + os.environ['TZ'] = 'US/Eastern' + time.tzset() self.error_format = """Usage: %s list-tagged [options] tag [package] (Specify the --help global option for a list of other help options) @@ -48,6 +53,13 @@ class TestCliListTagged(utils.CliTestCase): 'tag_name': 'tag', 'owner_name': 'owner'}] + def tearDown(self): + if self.original_timezone is None: + del os.environ['TZ'] + else: + os.environ['TZ'] = self.original_timezone + time.tzset() + @mock.patch('koji.util.eventFromOpts', return_value={'id': 1000, 'ts': 1000000.11}) @mock.patch('koji_cli.commands.activate_session') @@ -66,7 +78,7 @@ class TestCliListTagged(utils.CliTestCase): package='pkg') self.session.listTaggedRPMS.assert_not_called() self.assert_console_message(sys.stdout, - 'Querying at event 1000 (Mon Jan 12 13:46:40 1970)\n' + 'Querying at event 1000 (Mon Jan 12 08:46:40 1970)\n' 'Build Tag Built by\n' '---------------------------------------- -------------------- ----------------\n' 'n-v-r tag owner\n')