PR#3891: Don't try to resolve server version for old hubs

Merges #3891
https://pagure.io/koji/pull-request/3891

Fixes: #3890
https://pagure.io/koji/issue/3890
Don't try to resolve server version for older hubs
This commit is contained in:
Tomas Kopecek 2024-02-29 15:40:12 +01:00
commit d6a59cb585
8 changed files with 79 additions and 24 deletions

View file

@ -324,9 +324,8 @@ def handle_add_channel(goptions, session, args):
if 'channel %s already exists' % channel_name in msg:
error("channel %s already exists" % channel_name)
elif 'Invalid method:' in msg:
version = session.getKojiVersion()
error("addChannel is available on hub from Koji 1.26 version, your version is %s" %
version)
session.hub_version_str)
else:
error(msg)
print("%s added: id %d" % (args[0], channel_id))
@ -356,9 +355,8 @@ def handle_edit_channel(goptions, session, args):
except koji.GenericError as ex:
msg = str(ex)
if 'Invalid method:' in msg:
version = session.getKojiVersion()
error("editChannel is available on hub from Koji 1.26 version, your version is %s" %
version)
session.hub_version_str)
else:
warn(msg)
if not result:
@ -6295,14 +6293,6 @@ def handle_cancel(goptions, session, args):
if len(args) == 0:
parser.error("You must specify at least one task id or build")
activate_session(session, goptions)
older_hub = False
try:
hub_version = session.getKojiVersion()
v = tuple([int(x) for x in hub_version.split('.')])
if v < (1, 33, 0):
older_hub = True
except koji.GenericError:
older_hub = True
tlist = []
blist = []
for arg in args:
@ -6329,7 +6319,7 @@ def handle_cancel(goptions, session, args):
for task_id in tlist:
results.append(remote_fn(task_id, **opts))
for build in blist:
if not older_hub:
if session.hub_version >= (1, 33, 0):
results.append(m.cancelBuild(build, strict=True))
else:
results.append(m.cancelBuild(build))
@ -7575,7 +7565,7 @@ def handle_moshimoshi(options, session, args):
u = {'name': 'anonymous user'}
print("%s, %s!" % (_printable_unicode(random.choice(greetings)), u["name"]))
print("")
print("You are using the hub at %s" % session.baseurl)
print("You are using the hub at %s (Koji %s)" % (session.baseurl, session.hub_version_str))
authtype = u.get('authtype', getattr(session, 'authtype', None))
if authtype == koji.AUTHTYPES['NORMAL']:
print("Authenticated via password")

View file

@ -2625,6 +2625,30 @@ class ClientSession(object):
self.opts.setdefault('timeout', DEFAULT_REQUEST_TIMEOUT)
self.exclusive = False
self.auth_method = auth_method
self.__hub_version = None
@property
def hub_version(self):
"""Return the hub version as a tuple of ints"""
return tuple([int(x) for x in self.hub_version_str.split('.')])
@property
def hub_version_str(self):
"""Return the hub version as string"""
# If any call was made before, it should be populated by Koji-Version header
# for hub >= 1.35
if self.__hub_version is None:
# no call was made yet OR hub_version < 1.35
try:
self.__hub_version = self.getKojiVersion()
except GenericError as e:
if 'Invalid method' in str(e):
# use latest version without the getKojiVersion handler
self.logger.debug("hub is older than 1.23, assuming 1.22.0")
self.__hub_version = '1.22.0'
else:
raise
return self.__hub_version
@property
def multicall(self):
@ -3056,6 +3080,9 @@ class ClientSession(object):
warnings.simplefilter("ignore")
r = self.rsession.post(handler, **callopts)
r.raise_for_status()
hub_version = r.headers.get('Koji-Version')
if hub_version:
self.__hub_version = hub_version
try:
ret = self._read_xmlrpc_response(r)
finally:

View file

@ -44,6 +44,12 @@ from . import db
from . import scheduler
# HTTP headers included in every request
GLOBAL_HEADERS = [
('Koji-Version', koji.__version__),
]
class Marshaller(ExtendedMarshaller):
dispatch = ExtendedMarshaller.dispatch.copy()
@ -389,7 +395,7 @@ def offline_reply(start_response, msg=None):
else:
faultString = msg
response = dumps(Fault(faultCode, faultString)).encode()
headers = [
headers = GLOBAL_HEADERS + [
('Content-Length', str(len(response))),
('Content-Type', "text/xml"),
]
@ -399,7 +405,7 @@ def offline_reply(start_response, msg=None):
def error_reply(start_response, status, response, extra_headers=None):
response = response.encode()
headers = [
headers = GLOBAL_HEADERS + [
('Content-Length', str(len(response))),
('Content-Type', "text/plain"),
]
@ -816,7 +822,7 @@ def application(environ, start_response):
except RequestTimeout as e:
return error_reply(start_response, '408 Request Timeout', str(e) + '\n')
response = response.encode()
headers = [
headers = GLOBAL_HEADERS + [
('Content-Length', str(len(response))),
('Content-Type', "text/xml"),
]

View file

@ -60,7 +60,7 @@ class TestAddChannel(utils.CliTestCase):
expected_api = 'Invalid method: addChannel'
expected = 'addChannel is available on hub from Koji 1.26 version, your version ' \
'is 1.25.1\n'
self.session.getKojiVersion.return_value = '1.25.1'
self.session.hub_version_str = '1.25.1'
self.session.addChannel.side_effect = koji.GenericError(expected_api)
arguments = ['--description', self.description, self.channel_name]

View file

@ -32,7 +32,8 @@ class TestCancel(utils.CliTestCase):
%s: error: {message}
""" % (self.progname, self.progname)
self.session.getKojiVersion.return_value = '1.33.0'
self.session.hub_version = (1, 33, 0)
self.session.hub_version_str = '1.33.0'
def test_anon_cancel(self):
args = ['123']
@ -154,7 +155,7 @@ No such build: '%s'
self.session.cancelBuild.assert_called_once_with(args[1], strict=True)
def test_non_exist_build_and_task_older_hub(self):
self.session.getKojiVersion.return_value = '1.32.0'
self.session.hub_version = (1, 32, 0)
args = ['11111', 'nvr-1-30.1']
expected_warn = """No such task: %s
""" % (args[0])

View file

@ -78,7 +78,7 @@ Options:
expected_api = 'Invalid method: editChannel'
expected = 'editChannel is available on hub from Koji 1.26 version, your version ' \
'is 1.25.1\n'
self.session.getKojiVersion.return_value = '1.25.1'
self.session.hub_version_str = '1.25.1'
self.session.editChannel.side_effect = koji.GenericError(expected_api)
self.assert_system_exit(
@ -94,7 +94,6 @@ Options:
self.session.editChannel.assert_called_once_with(self.channel_old, name=self.channel_new,
description=self.description)
self.session.getChannel.assert_called_once_with(self.channel_old)
self.session.getKojiVersion.assert_called_once_with()
def test_handle_edit_channel_non_exist_channel(self):
expected = 'No such channel: %s\n' % self.channel_old

View file

@ -50,6 +50,8 @@ class TestHello(utils.CliTestCase):
# Mock out the xmlrpc server
session.getLoggedInUser.return_value = None
session.krb_principal = user['krb_principal']
mock_hub_version = '1.35.0'
session.hub_version_str = mock_hub_version
print_unicode_mock.return_value = "Hello"
self.assert_system_exit(
@ -63,7 +65,7 @@ class TestHello(utils.CliTestCase):
# annonymous user
message = "Not authenticated\n" + "Hello, anonymous user!"
hubinfo = "You are using the hub at %s" % self.huburl
hubinfo = "You are using the hub at %s (Koji %s)" % (self.huburl, mock_hub_version)
handle_moshimoshi(self.options, session, [])
self.assert_console_message(stdout, "{0}\n\n{1}\n".format(message, hubinfo))
self.activate_session_mock.assert_called_once_with(session, self.options)
@ -79,7 +81,7 @@ class TestHello(utils.CliTestCase):
user['krb_principal'],
koji.AUTHTYPES['SSL']: 'Authenticated via client certificate %s' % cert
}
hubinfo = "You are using the hub at %s" % self.huburl
# same hubinfo
session.getLoggedInUser.return_value = user
message = "Hello, %s!" % self.progname
self.options.cert = cert

View file

@ -30,6 +30,36 @@ class TestClientSession(unittest.TestCase):
my_rsession.close.assert_called()
self.assertNotEqual(ksession.rsession, my_rsession)
@mock.patch('requests.Session')
def test_hub_version_old(self, rsession):
ksession = koji.ClientSession('http://koji.example.com/kojihub')
ksession.getKojiVersion = mock.MagicMock()
ksession.getKojiVersion.side_effect = koji.GenericError('Invalid method: getKojiVersion')
self.assertEqual(ksession.hub_version, (1, 22, 0))
ksession.getKojiVersion.assert_called_once()
@mock.patch('requests.Session')
def test_hub_version_interim(self, rsession):
ksession = koji.ClientSession('http://koji.example.com/kojihub')
ksession.getKojiVersion = mock.MagicMock()
ksession.getKojiVersion.return_value = '1.23.1'
self.assertEqual(ksession.hub_version, (1, 23, 1))
ksession.getKojiVersion.assert_called_once()
def test_hub_version_str_interim(self):
ksession = koji.ClientSession('http://koji.example.com/kojihub')
ksession.getKojiVersion = mock.MagicMock()
ksession.getKojiVersion.return_value = '1.23.1'
self.assertEqual(ksession.hub_version_str, '1.23.1')
def test_hub_version_new(self):
ksession = koji.ClientSession('http://koji.example.com/kojihub')
ksession.getKojiVersion = mock.MagicMock()
# would be filled by random call
ksession._ClientSession__hub_version = '1.35.0'
self.assertEqual(ksession.hub_version, (1, 35, 0))
ksession.getKojiVersion.assert_not_called()
class TestFastUpload(unittest.TestCase):