api getLastHostUpdate returns timestamp

fixes: https://pagure.io/koji/issue/2497
This commit is contained in:
Jana Cupova 2021-03-16 10:25:29 +01:00 committed by Tomas Kopecek
parent 217704fd67
commit 9dc0efd476
6 changed files with 150 additions and 14 deletions

View file

@ -2888,14 +2888,14 @@ def anon_handle_list_hosts(goptions, session, args):
# pull in the last update using multicall to speed it up a bit
session.multicall = True
for host in hosts:
session.getLastHostUpdate(host['id'])
session.getLastHostUpdate(host['id'], ts=True)
updateList = session.multiCall()
for host, [update] in zip(hosts, updateList):
if update is None:
host['update'] = '-'
else:
host['update'] = update.split('.')[0]
host['update'] = koji.formatTimeLong(update)
host['enabled'] = yesno(host['enabled'])
host['ready'] = yesno(host['ready'])
host['arches'] = ','.join(host['arches'].split())
@ -3355,11 +3355,11 @@ def anon_handle_hostinfo(goptions, session, args):
print("Comment:")
print("Enabled: %s" % (info['enabled'] and 'yes' or 'no'))
print("Ready: %s" % (info['ready'] and 'yes' or 'no'))
update = session.getLastHostUpdate(info['id'])
update = session.getLastHostUpdate(info['id'], ts=True)
if update is None:
update = "never"
else:
update = update[:update.find('.')]
update = koji.formatTimeLong(update)
print("Last Update: %s" % update)
print("Channels: %s" % ' '.join([c['name']
for c in session.listChannels(hostID=info['id'])]))

View file

@ -12629,8 +12629,8 @@ class RootExports(object):
tables=tables, joins=joins, clauses=clauses, values=locals())
return query.execute()
def getLastHostUpdate(self, hostID):
"""Return the latest update timestampt for the host
def getLastHostUpdate(self, hostID, ts=False):
"""Return the latest update timestamp for the host
The timestamp represents the last time the host with the given
ID contacted the hub. Returns None if the host has never contacted
@ -12641,7 +12641,11 @@ class RootExports(object):
ORDER BY update_time DESC
LIMIT 1
"""
return _singleValue(query, locals(), strict=False)
date = _singleValue(query, locals(), strict=False)
if ts and date is not None:
return date.timestamp()
else:
return date
getAllArches = staticmethod(get_all_arches)

View file

@ -1,5 +1,8 @@
from __future__ import absolute_import
import mock
import os
import time
import locale
from six.moves import StringIO
import koji
@ -9,10 +12,15 @@ from . import utils
class TestHostinfo(utils.CliTestCase):
def setUp(self):
# force locale to compare 'expect' value
locale.setlocale(locale.LC_ALL, ('en_US', 'UTF-8'))
self.options = mock.MagicMock()
self.options.debug = False
self.session = mock.MagicMock()
self.session.getAPIVersion.return_value = koji.API_VERSION
self.original_timezone = os.environ.get('TZ')
os.environ['TZ'] = 'UTC'
time.tzset()
self.hostinfo = {'arches': 'x86_64',
'capacity': 2.0,
'comment': None,
@ -23,9 +31,55 @@ class TestHostinfo(utils.CliTestCase):
'ready': True,
'task_load': 0.0,
'user_id': 2}
self.last_update = '2021-03-16 06:19:14.862938+00:00'
self.last_update = 1615875554.862938
self.list_channels = [{'id': 1, 'name': 'default'}, {'id': 2, 'name': 'createrepo'}]
def tearDown(self):
locale.resetlocale()
if self.original_timezone is None:
del os.environ['TZ']
else:
os.environ['TZ'] = self.original_timezone
time.tzset()
@mock.patch('sys.stderr', new_callable=StringIO)
def test_hostinfo_without_option(self, stderr):
expected = "Usage: %s hostinfo [options] <hostname> [<hostname> ...]\n" \
"(Specify the --help global option for a list of other help options)\n\n" \
"%s: error: Please specify a host\n" % (self.progname, self.progname)
self.session.getChannel.return_value = None
with self.assertRaises(SystemExit) as ex:
anon_handle_hostinfo(self.options, self.session, [])
self.assertExitCode(ex, 2)
self.assert_console_message(stderr, expected)
@mock.patch('sys.stdout', new_callable=StringIO)
def test_hostinfo_valid(self, stdout):
expected = """Name: kojibuilder
ID: 1
Arches: x86_64
Capacity: 2.0
Task Load: 0.00
Description:
Comment:
Enabled: yes
Ready: yes
Last Update: Tue, 16 Mar 2021 06:19:14 UTC
Channels: default createrepo
Active Buildroots:
None
"""
self.session.getHost.return_value = self.hostinfo
self.session.getLastHostUpdate.return_value = self.last_update
self.session.listChannels.return_value = self.list_channels
rv = anon_handle_hostinfo(self.options, self.session, [self.hostinfo['name']])
self.assertEqual(rv, None)
self.assert_console_message(stdout, expected)
self.session.getHost.assert_called_once_with(self.hostinfo['name'])
self.session.getLastHostUpdate.assert_called_once_with(self.hostinfo['id'], ts=True)
self.session.listChannels.assert_called_once_with(hostID=self.hostinfo['id'])
self.assertEqual(self.session.listBuildroots.call_count, 3)
@mock.patch('sys.stderr', new_callable=StringIO)
@mock.patch('sys.stdout', new_callable=StringIO)
def test_hostinfo_more_hosts_with_non_exit_host(self, stdout, stderr):
@ -40,7 +94,7 @@ Description:
Comment:
Enabled: yes
Ready: yes
Last Update: 2021-03-16 06:19:14
Last Update: Tue, 16 Mar 2021 06:19:14 UTC
Channels: default createrepo
Active Buildroots:
None
@ -56,7 +110,7 @@ None
self.assert_console_message(stdout, expected_stdout)
self.assert_console_message(stderr, expected_error)
self.assertEqual(self.session.getHost.call_count, 2)
self.session.getLastHostUpdate.assert_called_once_with(self.hostinfo['id'])
self.session.getLastHostUpdate.assert_called_once_with(self.hostinfo['id'], ts=True)
self.session.listChannels.assert_called_once_with(hostID=self.hostinfo['id'])
self.assertEqual(self.session.listBuildroots.call_count, 3)

View file

@ -1,6 +1,8 @@
from __future__ import absolute_import
import mock
import os
import time
import locale
from six.moves import StringIO
import koji
@ -10,13 +12,51 @@ from . import utils
class TestListHosts(utils.CliTestCase):
def setUp(self):
# force locale to compare 'expect' value
locale.setlocale(locale.LC_ALL, ('en_US', 'UTF-8'))
self.options = mock.MagicMock()
self.options.debug = False
self.session = mock.MagicMock()
self.session.getAPIVersion.return_value = koji.API_VERSION
self.original_timezone = os.environ.get('TZ')
os.environ['TZ'] = 'UTC'
time.tzset()
def tearDown(self):
locale.resetlocale()
if self.original_timezone is None:
del os.environ['TZ']
else:
os.environ['TZ'] = self.original_timezone
time.tzset()
@mock.patch('sys.stdout', new_callable=StringIO)
@mock.patch('koji_cli.commands.ensure_connection')
def test_list_hosts_valid(self, ensure_connection, stdout):
host_update = 1615875554.862938
expected = """kojibuilder Y Y 0.0/2.0 x86_64 Tue, 16 Mar 2021 06:19:14 UTC
"""
list_hosts = [{'arches': 'x86_64',
'capacity': 2.0,
'comment': None,
'description': None,
'enabled': True,
'id': 1,
'name': 'kojibuilder',
'ready': True,
'task_load': 0.0,
'user_id': 2}]
self.session.getLastHostUpdate.return_value = host_update
self.session.multiCall.return_value = [[host_update]]
self.session.listHosts.return_value = list_hosts
rv = anon_handle_list_hosts(self.options, self.session, [])
self.assertEqual(rv, None)
self.assert_console_message(stdout, expected)
self.session.listHosts.assert_called_once_with()
self.session.getLastHostUpdate.assert_called_once_with(list_hosts[0]['id'], ts=True)
@mock.patch('sys.stderr', new_callable=StringIO)
def test_list_pkgs_non_exist_channel(self, stderr):
def test_list_hosts_non_exist_channel(self, stderr):
channel = 'test-channel'
expected = "Usage: %s list-hosts [options]\n" \
"(Specify the --help global option for a list of other help options)\n\n" \

View file

@ -0,0 +1,38 @@
import unittest
import mock
import datetime
import sys
import kojihub
class TestGetLastHostUpdate(unittest.TestCase):
def setUp(self):
self.exports = kojihub.RootExports()
@mock.patch('kojihub._singleValue')
def test_valid_ts(self, _singleValue):
expected = 1615875554.862938
if sys.version_info[1] <= 6:
dt = datetime.datetime.strptime(
"2021-03-16T06:19:14.862938+0000", "%Y-%m-%dT%H:%M:%S.%f%z")
else:
dt = datetime.datetime.strptime(
"2021-03-16T06:19:14.862938+00:00", "%Y-%m-%dT%H:%M:%S.%f%z")
_singleValue.return_value = dt
rv = self.exports.getLastHostUpdate(1, ts=True)
self.assertEqual(rv, expected)
@mock.patch('kojihub._singleValue')
def test_valid_datetime(self, _singleValue):
if sys.version_info[1] <= 6:
dt = datetime.datetime.strptime(
"2021-03-16T06:19:14.862938+0000", "%Y-%m-%dT%H:%M:%S.%f%z")
else:
dt = datetime.datetime.strptime(
"2021-03-16T06:19:14.862938+00:00", "%Y-%m-%dT%H:%M:%S.%f%z")
expected = "2021-03-16T06:19:14.862938+00:00"
_singleValue.return_value = dt
rv = self.exports.getLastHostUpdate(1)
self.assertEqual(rv, dt)

View file

@ -1637,7 +1637,7 @@ def hosts(environ, state='enabled', start=None, order='name'):
server.multicall = True
for host in hosts:
server.getLastHostUpdate(host['id'])
server.getLastHostUpdate(host['id'], ts=True)
updates = server.multiCall()
for host, [lastUpdate] in zip(hosts, updates):
host['last_update'] = lastUpdate
@ -1680,7 +1680,7 @@ def hostinfo(environ, hostID=None, userID=None):
values['host'] = host
values['channels'] = channels
values['buildroots'] = buildroots
values['lastUpdate'] = server.getLastHostUpdate(host['id'])
values['lastUpdate'] = server.getLastHostUpdate(host['id'], ts=True)
if environ['koji.currentUser']:
values['perms'] = server.getUserPerms(environ['koji.currentUser']['id'])
else: