Support same pkg version for multiple releases
It is possible for the version of a package to remain the same
across multiple releases of openstack so this adds support to
get_os_codename_package() to allow this.
If releases A, B and C have version N and we are on release B
this will return release C so that it does not look like there
is an upgrade necessary. The main change here that allows this
is to support matching major and minor version up to X.Y.Z as
opposed to previously only matching the major version X.
Related-Bug: #1973303
Change-Id: I138f61312efb728544276483b1a459b9eeecafdb
(cherry picked from commit 55f98e80e4
)
This commit is contained in:
parent
018b72d734
commit
b956245392
|
@ -1156,13 +1156,48 @@ class BaseOpenStackCharmActions(object):
|
|||
|
||||
# x.y match only for 20XX.X
|
||||
# and ignore patch level for other packages
|
||||
match = re.match(r'^(\d+)\.(\d+)', vers)
|
||||
match = re.match(r'^(20\d+)\.(\d+)', vers)
|
||||
|
||||
if match:
|
||||
vers = match.group(0)
|
||||
|
||||
return vers
|
||||
|
||||
def get_closest_release_match(self, package_version, codenames):
|
||||
"""
|
||||
Multiple releases can share the same version of the package so this
|
||||
collects contiguous versions, maintaining order then picks the most
|
||||
recent version that is less than or equal to what is installed.
|
||||
|
||||
:param package_version: version of installed package.
|
||||
:type package_version: str
|
||||
:param codenames: OrderedDict of version prefixes and release names.
|
||||
:type codenames: OrderedDict
|
||||
:returns: tuple of release name and version or None if match was not
|
||||
found.
|
||||
:rtype: tuple(version, release_name)
|
||||
"""
|
||||
hookenv.log('getting rel name for pkg ver={} from codenames={}'.
|
||||
format(package_version, codenames), level=hookenv.DEBUG)
|
||||
reversed_codenames = collections.OrderedDict(
|
||||
reversed(codenames.items()))
|
||||
splitver = package_version.split('.')
|
||||
|
||||
# Starting from the leading dot and going backwards, we see if a
|
||||
# release name matches and pick the first we find. If the same release
|
||||
# name is registered for more than one version, we pick the highest
|
||||
# release to indicate that the version has no release upgrade
|
||||
# available.
|
||||
for dotpos in reversed(range(min(len(splitver), 3))):
|
||||
ver_pfix = '.'.join(splitver[:dotpos + 1])
|
||||
# Find, in descending order, the first closest match.
|
||||
for ver, rname in reversed_codenames.items():
|
||||
if not ver.startswith(ver_pfix):
|
||||
continue
|
||||
|
||||
if fetch.apt_pkg.version_compare(ver, package_version) <= 0:
|
||||
return (ver, rname)
|
||||
|
||||
def get_os_codename_package(self, package, codenames, fatal=True,
|
||||
apt_cache_sufficient=False):
|
||||
"""Derive OpenStack release codename from a package.
|
||||
|
@ -1220,20 +1255,22 @@ class BaseOpenStackCharmActions(object):
|
|||
if codename:
|
||||
return codename
|
||||
|
||||
vers = self.get_package_version(
|
||||
package_version = self.get_package_version(
|
||||
package,
|
||||
apt_cache_sufficient=apt_cache_sufficient)
|
||||
# Generate a major version number for newer semantic
|
||||
# versions of openstack projects
|
||||
major_vers = vers.split('.')[0]
|
||||
except Exception:
|
||||
if fatal:
|
||||
raise
|
||||
else:
|
||||
return None
|
||||
if (package in codenames and
|
||||
major_vers in codenames[package]):
|
||||
return codenames[package][major_vers]
|
||||
|
||||
if package not in codenames:
|
||||
return
|
||||
|
||||
vr_match = self.get_closest_release_match(package_version,
|
||||
codenames[package])
|
||||
if vr_match:
|
||||
return vr_match[1]
|
||||
|
||||
def get_os_version_snap(self, snap, fatal=True):
|
||||
"""Derive OpenStack version number from an installed snap.
|
||||
|
|
|
@ -885,6 +885,76 @@ class TestMyOpenStackCharm(BaseOpenStackCharmTest):
|
|||
for call in self.render.call_args_list:
|
||||
self.assertTrue(call[1]['context'])
|
||||
|
||||
def test_get_closest_release_match(self):
|
||||
# this is mocked universally in unit_tests/__init__.py so we have
|
||||
# to apply to not so great mock on top to allow the method to be
|
||||
# called.
|
||||
|
||||
def fake_version_compare(a, b):
|
||||
if a > b:
|
||||
return 1
|
||||
elif a < b:
|
||||
return -1
|
||||
|
||||
return 0
|
||||
|
||||
self.patch_object(chm_core.charmhelpers.fetch.apt_pkg,
|
||||
'version_compare')
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.side_effect = \
|
||||
fake_version_compare
|
||||
|
||||
codenames = collections.OrderedDict([('3.9', 'ussuri'),
|
||||
('4.0', 'victoria'),
|
||||
('4.0.1', 'yoga')])
|
||||
|
||||
pkg_ver = '4'
|
||||
release = self.target.get_closest_release_match(pkg_ver, codenames)
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.assert_has_calls([
|
||||
mock.call('4.0.1', pkg_ver), mock.call('4.0', pkg_ver)])
|
||||
self.assertEqual(release, None)
|
||||
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.reset_mock()
|
||||
pkg_ver = '4.0'
|
||||
release = self.target.get_closest_release_match(pkg_ver, codenames)
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.assert_has_calls([
|
||||
mock.call('4.0.1', pkg_ver), mock.call('4.0', pkg_ver)])
|
||||
self.assertEqual(release, ('4.0', 'victoria'))
|
||||
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.reset_mock()
|
||||
pkg_ver = '4.0.1'
|
||||
release = self.target.get_closest_release_match(pkg_ver, codenames)
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.assert_has_calls([
|
||||
mock.call('4.0.1', pkg_ver)])
|
||||
self.assertEqual(release, ('4.0.1', 'yoga'))
|
||||
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.reset_mock()
|
||||
pkg_ver = '4.0.2'
|
||||
release = self.target.get_closest_release_match(pkg_ver, codenames)
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.assert_has_calls([
|
||||
mock.call('4.0.1', pkg_ver)])
|
||||
self.assertEqual(release, ('4.0.1', 'yoga'))
|
||||
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.reset_mock()
|
||||
codenames['4.0.3'] = 'antelope'
|
||||
pkg_ver = '4.0.2'
|
||||
release = self.target.get_closest_release_match(pkg_ver, codenames)
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.assert_has_calls([
|
||||
mock.call('4.0.3', pkg_ver), mock.call('4.0.1', pkg_ver)])
|
||||
self.assertEqual(release, ('4.0.1', 'yoga'))
|
||||
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.reset_mock()
|
||||
release = self.target.get_closest_release_match('4.0.1.1', codenames)
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.assert_has_calls([
|
||||
mock.call('4.0.1', '4.0.1.1')])
|
||||
self.assertEqual(release, ('4.0.1', 'yoga'))
|
||||
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.reset_mock()
|
||||
pkg_ver = '4.4.1+git2022033113.2339b9e9-0ubuntu1'
|
||||
release = self.target.get_closest_release_match(pkg_ver, codenames)
|
||||
chm_core.charmhelpers.fetch.apt_pkg.version_compare.assert_has_calls([
|
||||
mock.call('4.0.3', pkg_ver)])
|
||||
self.assertEqual(release, ('4.0.3', 'antelope'))
|
||||
|
||||
def test_get_os_codename_package(self):
|
||||
codenames = {
|
||||
'testpkg': collections.OrderedDict([
|
||||
|
@ -902,6 +972,8 @@ class TestMyOpenStackCharm(BaseOpenStackCharmTest):
|
|||
self.patch_object(chm_core.os_utils, 'get_installed_os_version')
|
||||
self.get_installed_os_version.return_value = None
|
||||
self.upstream_version.return_value = '3.0.0~b1'
|
||||
self.patch_object(self.target, 'get_closest_release_match')
|
||||
self.get_closest_release_match.return_value = (3, 'newton')
|
||||
self.patch_object(self.target, 'configure_source')
|
||||
self.configure_source.side_effect = KeyError
|
||||
self.assertEqual(
|
||||
|
|
Loading…
Reference in New Issue