#2840 New CLI command userinfo
Merged 2 years ago by tkopecek. Opened 2 years ago by jcupova.
jcupova/koji issue-2836  into  master

file modified
+54
@@ -7796,3 +7796,57 @@ 

          print("Hub:    %s" % version)

      except koji.GenericError:

          print("Hub:    Can' determine (older than 1.23)")

+ 

+ 

+ def anon_handle_userinfo(goptions, session, args):

+     """[admin] Show information about an user"""

+     usage = _("usage: %prog userinfo [options] <username> [<username> ...]")

+     parser = OptionParser(usage=get_usage_str(usage))

+     (options, args) = parser.parse_args(args)

+     if len(args) < 1:

+         parser.error(_("You must specify at least one username"))

+ 

+     ensure_connection(session, goptions)

+ 

+     with session.multicall() as m:

+         userinfos = [m.getUser(user) for user in args]

+     user_infos = []

+     for username, userinfo in zip(args, userinfos):

+         if userinfo.result is None:

+             warn(_("No such user: %s\n") % username)

+             continue

+         user_infos.append(userinfo.result)

+     user_infos = list(filter(None, user_infos))

+ 

+     calls = []

+     with session.multicall() as m:

+         for user in user_infos:

+             results = []

+             if not user:

+                 warn(_("No such user: %s\n") % user)

+                 continue

+             results.append(m.getUserPerms(user['id']))

+             results.append(m.listPackages(userID=user['id'], with_dups=True,

+                                           queryOpts={'countOnly': True}))

+             results.append(m.listTasks(opts={'owner': user['id'], 'parent': None},

+                                        queryOpts={'countOnly': True}))

+             results.append(m.listBuilds(userID=user['id'], queryOpts={'countOnly': True}))

+             calls.append(results)

+ 

+     for userinfo, (perms, pkgs, tasks, builds) in zip(user_infos, calls):

+         print("User name: %s" % user['name'])

+         print("User ID: %d" % user['id'])

+         if 'krb_principals' in user:

+             print("krb principals:")

+             for krb in user['krb_principals']:

+                 print("  %s" % krb)

+         if perms.result:

+             print("Permissions:")

+             for perm in perms.result:

+                 print("  %s" % perm)

+         print("Status: %s" % koji.USER_STATUS[user['status']])

+         print("Usertype: %s" % koji.USERTYPES[user['usertype']])

+         print("Number of packages: %d" % pkgs.result)

+         print("Number of tasks: %d" % tasks.result)

+         print("Number of builds: %d" % builds.result)

+         print('')

@@ -62,6 +62,7 @@ 

          unblock-group-req         Unblock a group's requirement listing

          unblock-pkg               Unblock a package in the listing for tag

          unlock-tag                Unlock a tag

+         userinfo                  Show information about an user

          write-signed-rpm          Write signed RPMs to disk

  

  Try "{progname} --help" for help about global options

@@ -62,6 +62,7 @@ 

          unblock-group-req         Unblock a group's requirement listing

          unblock-pkg               Unblock a package in the listing for tag

          unlock-tag                Unlock a tag

+         userinfo                  Show information about an user

          write-signed-rpm          Write signed RPMs to disk

  

  bind commands:

@@ -0,0 +1,84 @@ 

+ from __future__ import absolute_import

+ 

+ import mock

+ from six.moves import StringIO

+ 

+ import koji

+ from koji_cli.commands import anon_handle_userinfo

+ from . import utils

+ 

+ 

+ class TestUserinfo(utils.CliTestCase):

+     def setUp(self):

+         self.options = mock.MagicMock()

+         self.options.debug = False

+         self.session = mock.MagicMock()

+         self.session.getAPIVersion.return_value = koji.API_VERSION

+ 

+         self.user = 'test-user'

+         self.user_info = {'id': 1,

+                           'krb_principals': ['test-principal'],

+                           'name': self.user,

+                           'status': 0,

+                           'usertype': 0}

+         self.user_perms = ['admin', 'tag']

+         self.count_list_packages = 2

+         self.count_list_builds = 3

+         self.count_list_tasks = 5

+ 

+     def __vm(self, result):

+         m = koji.VirtualCall('mcall_method', [], {})

+         if isinstance(result, dict) and result.get('faultCode'):

+             m._result = result

+         else:

+             m._result = (result,)

+         return m

+ 

+     @mock.patch('sys.stderr', new_callable=StringIO)

+     def test_userinfo_without_option(self, stderr):

+         expected = "Usage: %s userinfo [options] <username> [<username> ...]\n" \

+                    "(Specify the --help global option for a list of other help options)\n\n" \

+                    "%s: error: You must specify at least one " \

+                    "username\n" % (self.progname, self.progname)

+         with self.assertRaises(SystemExit) as ex:

+             anon_handle_userinfo(self.options, self.session, [])

+         self.assertExitCode(ex, 2)

+         self.assert_console_message(stderr, expected)

+ 

+     @mock.patch('sys.stderr', new_callable=StringIO)

+     def test_userinfo_non_exist_tag(self, stderr):

+         expected_warn = "No such user: %s\n\n" % self.user

+         mcall = self.session.multicall.return_value.__enter__.return_value

+ 

+         mcall.getUser.return_value = self.__vm(None)

+ 

+         anon_handle_userinfo(self.options, self.session, [self.user])

+         self.assert_console_message(stderr, expected_warn)

+ 

+     @mock.patch('sys.stdout', new_callable=StringIO)

+     @mock.patch('koji_cli.commands.ensure_connection')

+     def test_userinfo(self, ensure_connection, stdout):

+         expected = """User name: test-user

+ User ID: 1

+ krb principals:

+   test-principal

+ Permissions:

+   admin

+   tag

+ Status: NORMAL

+ Usertype: NORMAL

+ Number of packages: 2

+ Number of tasks: 5

+ Number of builds: 3

+ 

+ """

+         mcall = self.session.multicall.return_value.__enter__.return_value

+ 

+         mcall.getUser.return_value = self.__vm(self.user_info)

+         mcall.getUserPerms.return_value = self.__vm(self.user_perms)

+         mcall.listPackages.return_value = self.__vm(self.count_list_packages)

+         mcall.listBuilds.return_value = self.__vm(self.count_list_builds)

+         mcall.listTasks.return_value = self.__vm(self.count_list_tasks)

+ 

+         anon_handle_userinfo(self.options, self.session, [self.user])

+         self.assert_console_message(stdout, expected)

It will probably not used anywhere else so it is ok to inline it.

string formatting doesn't make sense here (output of join is already string) (+ I would print one per line as these can be longer)

It should be sorted, so it is always same order - it would be better even for tests.

This should be converted to human-readble format (koji.USER_STATUS[info['status']])

with_dups=True (align with web page userinfo), opts = {'countOnly': True) for all listTasks,Packages,Builds to improve performance

just base tasks (parent=None)

Is there any authenticated call used? From the first sight it should work with just ensure_connection.

If code will be inlined (and even for one user it should be faster), you should benefit from using multicall.

rebased onto cccd6ce06b8f1802264bdc3519f5f869efe0d1bb

2 years ago

It is probably more fit for [info] section.

rebased onto 1aab1955b14f9f40d30644d20c44e3fe0b309369

2 years ago

Last update - print also one permission per line, so it is displayed in the same way as krb principals.

rebased onto e9f7abb

2 years ago

Metadata Update from @tkopecek:
- Pull-request tagged with: testing-ready

2 years ago

Metadata Update from @mfilip:
- Pull-request tagged with: testing-done

2 years ago

Commit da9e931 fixes this pull-request

Pull-Request has been merged by tkopecek

2 years ago