Support upgrades to Trilio 4.1
Adding support for Trilio 4.1 includes the following changes: * Add Trilio_properties config property to enables templates to distinguish between 4.0 and 4.1 release. * Add get_trilio_codename_install_source to attempt to derive the Trilio version supported by an apt repo. * Add get_trilio_charm_instance which overrides the default get_charm_instance. This will pick the correct charm class based on both the Trilio release and the OpenStack release. * Add select_trilio_release which overrides the default select_release and calculates the target OpenStack and Trilio release. * Add a specialist Trilio metaclass BaseTrilioCharmMeta. This registers charm classes using their OpenStack release, Trilio release and package type. * Move code shared between TrilioVaultCharm & TrilioVaultSubordinateCharm to TrilioVaultCharmMixin. Add support for Trilio upgrades to TrilioVaultCharmMixina. NOTE: An earlier version of this change (I5a5e5721d9a713b66f8c796896c400481e9733a2) was landed and reverted. It was reverted because get_trilio_charm_instance and select_trilio_release were both registered as handlers irrespective of whether a charm explicitly imported charms_openstack.plugins.trilio This caused reactive charms which used the default handlers to fail as they imported the Trilio functions and the default ones and only one can ever registered. This patch fixes this by wrapping the trilio function definitions inside make_* methods, so the decorator only registers the methods when the make_* methods are explicitly called. Change-Id: Id3bb13aff6d0e6df2d5ec144689c992cf09c1b4c
This commit is contained in:
parent
390a426d31
commit
2520764264
|
@ -15,16 +15,85 @@
|
|||
import base64
|
||||
import os
|
||||
|
||||
import re
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import charms_openstack.adapters
|
||||
import charms_openstack.charm
|
||||
|
||||
import charmhelpers.core as ch_core
|
||||
import charmhelpers.fetch as fetch
|
||||
import charmhelpers.core.unitdata as unitdata
|
||||
import charmhelpers.contrib.openstack.utils as os_utils
|
||||
|
||||
import charms.reactive as reactive
|
||||
|
||||
|
||||
TV_MOUNTS = "/var/triliovault-mounts"
|
||||
|
||||
# Used to store the discovered release version for caching between invocations
|
||||
TRILIO_RELEASE_KEY = 'charmers.trilio-release-version'
|
||||
|
||||
# _trilio_releases{} is a dictionary of release -> class that is instantiated
|
||||
# according to the release that is being requested. i.e. a charm can
|
||||
# handle more than one release. The BaseOpenStackCharm() derived class sets the
|
||||
# `release` variable to indicate which OpenStack release that the charm
|
||||
# supports # and `trilio_release` to indicate which Trilio release the charm
|
||||
# supports. # Any subsequent releases that need a different/specialised charm
|
||||
# uses the # `release` and `trilio_release` class properties to indicate that
|
||||
# it handles those releases onwards.
|
||||
_trilio_releases = {}
|
||||
|
||||
|
||||
@charms_openstack.adapters.config_property
|
||||
def trilio_properties(cls):
|
||||
"""Trilio properties additions for config adapter.
|
||||
|
||||
:param cls: Configuration Adapter class
|
||||
:type cls: charms_openstack.adapters.DefaultConfigurationAdapter
|
||||
"""
|
||||
cur_ver = cls.charm_instance.release_pkg_version()
|
||||
comp = fetch.apt_pkg.version_compare(cur_ver, '4.1')
|
||||
if comp >= 0:
|
||||
return {
|
||||
'db_type': 'dedicated',
|
||||
'transport_type': 'dmapi'}
|
||||
else:
|
||||
return {
|
||||
'db_type': 'legacy',
|
||||
'transport_type': 'legacy'}
|
||||
|
||||
|
||||
class AptPkgVersion():
|
||||
"""Allow package version to be compared."""
|
||||
|
||||
def __init__(self, version):
|
||||
self.version = version
|
||||
|
||||
def __lt__(self, other):
|
||||
return fetch.apt_pkg.version_compare(self.version, other.version) == -1
|
||||
|
||||
def __le__(self, other):
|
||||
return self.__lt__(other) or self.__eq__(other)
|
||||
|
||||
def __gt__(self, other):
|
||||
return fetch.apt_pkg.version_compare(self.version, other.version) == 1
|
||||
|
||||
def __ge__(self, other):
|
||||
return self.__gt__(other) or self.__eq__(other)
|
||||
|
||||
def __eq__(self, other):
|
||||
return fetch.apt_pkg.version_compare(self.version, other.version) == 0
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __repr__(self):
|
||||
return self.version
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
|
||||
class NFSShareNotMountedException(Exception):
|
||||
"""Signal that the trilio nfs share is not mount"""
|
||||
|
@ -85,7 +154,248 @@ def _install_triliovault(charm):
|
|||
reactive.clear_flag('upgrade.triliovault')
|
||||
|
||||
|
||||
class TrilioVaultCharm(charms_openstack.charm.HAOpenStackCharm):
|
||||
def get_trilio_codename_install_source(trilio_source):
|
||||
"""Derive codename from trilio source string.
|
||||
|
||||
Try and derive a trilio version from a deb string like:
|
||||
'deb [trusted=yes] https://apt.fury.io/triliodata-4-0/ /'
|
||||
|
||||
:param trilio_source: Trilio source
|
||||
:type trilio_source: str
|
||||
:returns: Trilio version
|
||||
:rtype: str
|
||||
:raises: AssertionError
|
||||
"""
|
||||
deb_url = trilio_source.split()[-2]
|
||||
code = re.findall(r'-(\d*-\d*)', urlparse(deb_url).path)
|
||||
assert len(code) == 1, "Cannot derive release from {}".format(deb_url)
|
||||
new_os_rel = code[0].replace('-', '.')
|
||||
return new_os_rel
|
||||
|
||||
|
||||
def make_trilio_get_charm_instance_handler():
|
||||
"""This handler sets the get_charm_instance function.
|
||||
"""
|
||||
|
||||
@charms_openstack.charm.core.register_get_charm_instance
|
||||
def get_trilio_charm_instance(release=None, package_type='deb', *args,
|
||||
**kwargs):
|
||||
"""Get an instance of the charm based on the release (or use the
|
||||
default if release is None).
|
||||
|
||||
Note that it passes args and kwargs to the class __init__() method.
|
||||
|
||||
:param release: String representing release wanted. Should be of the
|
||||
form '<openstack_release>_<trilio_release>'
|
||||
eg 'queens_4.0'
|
||||
:type release: str
|
||||
:param package_type: The package type required
|
||||
:type package_type: str
|
||||
:returns: Charm class
|
||||
:rtype: BaseOpenStackCharm() derived class according to cls.releases
|
||||
"""
|
||||
cls = None
|
||||
known_os_releases = sorted(_trilio_releases.keys())
|
||||
if release is None:
|
||||
# If release is None then select the class(es) which supports the
|
||||
# most recent OpenStack release, from within this set select the
|
||||
# class that supports the most recent Trilio release.
|
||||
os_release = known_os_releases[-1]
|
||||
known_trilio_releases = sorted(_trilio_releases[os_release].keys())
|
||||
trilio_release = known_trilio_releases[-1]
|
||||
cls = _trilio_releases[os_release][trilio_release][package_type]
|
||||
else:
|
||||
os_release, trilio_release = release.split('_')
|
||||
trilio_release = AptPkgVersion(trilio_release)
|
||||
if os_release not in os_utils.OPENSTACK_RELEASES:
|
||||
raise RuntimeError(
|
||||
"Release {} is not a known OpenStack release?".format(
|
||||
os_release))
|
||||
os_release_index = os_utils.OPENSTACK_RELEASES.index(os_release)
|
||||
if (os_release_index <
|
||||
os_utils.OPENSTACK_RELEASES.index(known_os_releases[0])):
|
||||
raise RuntimeError(
|
||||
"Release {} is not supported by this charm. Earliest "
|
||||
"support is {} release".format(
|
||||
os_release,
|
||||
known_os_releases[0]))
|
||||
else:
|
||||
known_trilio_releases = []
|
||||
# Search through the dictionary of registered charm classes
|
||||
# looking for the most recent group which can support
|
||||
# `os_release`
|
||||
for known_os_release in reversed(known_os_releases):
|
||||
_idx = os_utils.OPENSTACK_RELEASES.index(known_os_release)
|
||||
if os_release_index >= _idx:
|
||||
trilio_classes = _trilio_releases[known_os_release]
|
||||
known_trilio_releases = sorted(trilio_classes.keys())
|
||||
break
|
||||
# Search through the dictionary of registered charm classes
|
||||
# that support `known_os_release` onwards and look for the
|
||||
# class # which supports the most recent trilio release which
|
||||
# is <= `trilio_release`
|
||||
for known_trilio_release in reversed(known_trilio_releases):
|
||||
if known_trilio_release <= trilio_release:
|
||||
cls = trilio_classes[known_trilio_release][
|
||||
package_type]
|
||||
# Found a class so exit loop
|
||||
break
|
||||
if cls is None:
|
||||
raise RuntimeError("Release {} is not supported".format(release))
|
||||
return cls(release=os_release, *args, **kwargs)
|
||||
|
||||
|
||||
def make_trilio_handlers():
|
||||
"""This handler sets the trilio release selector get_charm_instance funcs.
|
||||
"""
|
||||
make_trilio_get_charm_instance_handler()
|
||||
make_trilio_select_release_handler()
|
||||
|
||||
|
||||
def make_trilio_select_release_handler():
|
||||
"""This handler sets the release selector function.
|
||||
"""
|
||||
|
||||
@charms_openstack.charm.core.register_os_release_selector
|
||||
def select_trilio_release():
|
||||
"""Determine the OpenStack and Trilio release
|
||||
|
||||
Determine the OpenStack release based on the `singleton.os_release_pkg`
|
||||
that is installed. If it is not installed look for and exanine other
|
||||
semantic versioned packages. If both those tactics fail fall back to
|
||||
checking the charm `openstack-origin` option.
|
||||
|
||||
Determine the Trilio release based on the `singleton.version_package`
|
||||
that is installed. If it is not installed fall back to checking the
|
||||
charm `triliovault-pkg-source` option.
|
||||
|
||||
Note that this function caches the release after the first install so
|
||||
that it doesn't need to keep going and getting it from the package
|
||||
information.
|
||||
"""
|
||||
|
||||
singleton = None
|
||||
# Search for target OpenStack Release
|
||||
os_release_version = unitdata.kv().get(
|
||||
charms_openstack.charm.core.OPENSTACK_RELEASE_KEY,
|
||||
None)
|
||||
if os_release_version is None:
|
||||
try:
|
||||
# First make an attempt of determining release from a charm
|
||||
# instance defined package codename dictionary.
|
||||
singleton = charms_openstack.charm.core.get_charm_instance()
|
||||
if singleton.release_pkg is None:
|
||||
raise RuntimeError("release_pkg is not set")
|
||||
os_release_version = singleton.get_os_codename_package(
|
||||
singleton.os_release_pkg, singleton.package_codenames,
|
||||
apt_cache_sufficient=(not singleton.source_config_key))
|
||||
if os_release_version is None:
|
||||
# Surprisingly get_os_codename_package called with
|
||||
# ``Fatal=True`` does not raise an error when the charm
|
||||
# class ``package_codenames`` map does not contain package
|
||||
# or major version. We'll handle it here instead of
|
||||
# changing the API of the method.
|
||||
raise ValueError
|
||||
except (AttributeError, ValueError):
|
||||
try:
|
||||
pkgs = os_utils.get_installed_semantic_versioned_packages()
|
||||
pkg = pkgs[0]
|
||||
except IndexError:
|
||||
# A non-existent package will cause os_release to try other
|
||||
# tactics for deriving the release.
|
||||
pkg = 'dummy-package'
|
||||
os_release_version = os_utils.os_release(
|
||||
pkg, source_key=singleton.source_config_key)
|
||||
unitdata.kv().set(
|
||||
charms_openstack.charm.core.OPENSTACK_RELEASE_KEY,
|
||||
os_release_version)
|
||||
unitdata.kv().flush()
|
||||
|
||||
# Search for target Trilio Release
|
||||
trilio_release_version = unitdata.kv().get(TRILIO_RELEASE_KEY, None)
|
||||
if trilio_release_version is None:
|
||||
if not singleton:
|
||||
singleton = charms_openstack.charm.core.get_charm_instance()
|
||||
if singleton.version_package is None:
|
||||
raise RuntimeError("version_package is not set")
|
||||
try:
|
||||
trilio_release_version = singleton.get_package_version(
|
||||
singleton.version_package)
|
||||
except (AttributeError, ValueError):
|
||||
trilio_release_version = get_trilio_codename_install_source(
|
||||
singleton.trilio_source)
|
||||
unitdata.kv().set(TRILIO_RELEASE_KEY, trilio_release_version)
|
||||
unitdata.kv().flush()
|
||||
|
||||
return '{}_{}'.format(os_release_version, trilio_release_version)
|
||||
|
||||
|
||||
class BaseTrilioCharmMeta(charms_openstack.charm.core.BaseOpenStackCharmMeta):
|
||||
"""Metaclass to handle registering charm classes by their supported
|
||||
OpenStack release, Trilio release and package typea
|
||||
|
||||
_trilio_releases has the form::
|
||||
|
||||
{
|
||||
'Openstack Code Name': {
|
||||
'Trilio Package Veersion': {
|
||||
'Package Type': <charm class>}},
|
||||
"""
|
||||
|
||||
def __init__(cls, name, mro, members):
|
||||
"""Receive the BaseOpenStackCharm() (derived) class and store the
|
||||
release that it works against. Each class defines a 'release' which
|
||||
corresponds to the Openstack release that it handles. The class should
|
||||
also specify 'trilio_release' which defines the Trilio releases it can
|
||||
handle.
|
||||
|
||||
:param name: string for class name.
|
||||
:param mro: tuple of base classes.
|
||||
:param members: dictionary of name to class attribute (f, p, a, etc.)
|
||||
"""
|
||||
# Do not attempt to calculate the release for an abstract class
|
||||
if members.get('abstract_class', False):
|
||||
return
|
||||
if all(key in members.keys() for key in ['release', 'trilio_release']):
|
||||
package_type = members.get('package_type', 'deb')
|
||||
if package_type not in ('deb', 'snap'):
|
||||
raise RuntimeError(
|
||||
"Package type {} is not a known type"
|
||||
.format(package_type))
|
||||
release = members['release']
|
||||
trilio_release = AptPkgVersion(members['trilio_release'])
|
||||
if release not in os_utils.OPENSTACK_RELEASES:
|
||||
raise RuntimeError(
|
||||
"Release {} is not a known OpenStack release"
|
||||
.format(release))
|
||||
try:
|
||||
_pre = _trilio_releases[release][trilio_release][package_type]
|
||||
except KeyError:
|
||||
# All good this comination has not been registered yet.
|
||||
pass
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"Release {} + {} defined more than once in classes {} and "
|
||||
"{} (at least)"
|
||||
.format(release,
|
||||
trilio_release,
|
||||
_pre.__name__,
|
||||
name))
|
||||
# store the class against the release.
|
||||
if release not in _trilio_releases:
|
||||
_trilio_releases[release] = {}
|
||||
if trilio_release not in _trilio_releases[release]:
|
||||
_trilio_releases[release][trilio_release] = {}
|
||||
_trilio_releases[release][trilio_release][package_type] = cls
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"class '{}' must define both the release it supports using "
|
||||
"the 'release' class property and the trilio release it "
|
||||
"supports using the 'trilio_release' class property.".format(
|
||||
name))
|
||||
|
||||
|
||||
class TrilioVaultCharmMixin():
|
||||
"""The TrilioVaultCharm class provides common specialisation of certain
|
||||
functions for the Trilio charm set and is designed for use alongside
|
||||
other base charms.openstack classes
|
||||
|
@ -94,7 +404,11 @@ class TrilioVaultCharm(charms_openstack.charm.HAOpenStackCharm):
|
|||
abstract_class = True
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(TrilioVaultCharm, self).__init__(**kwargs)
|
||||
try:
|
||||
del kwargs['trilio_release']
|
||||
except KeyError:
|
||||
pass
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def configure_source(self):
|
||||
"""Configure triliovault specific package sources in addition to
|
||||
|
@ -114,17 +428,112 @@ class TrilioVaultCharm(charms_openstack.charm.HAOpenStackCharm):
|
|||
super().series_upgrade_complete()
|
||||
self.configure_source()
|
||||
|
||||
@property
|
||||
def trilio_source(self):
|
||||
"""Trilio source config option"""
|
||||
return self.config.get("triliovault-pkg-source")
|
||||
|
||||
class TrilioVaultSubordinateCharm(charms_openstack.charm.OpenStackCharm):
|
||||
"""The TrilioVaultSubordinateCharm class provides common specialisation
|
||||
of certain functions for the Trilio charm set and is designed for use
|
||||
alongside other base charms.openstack classes for subordinate charms
|
||||
"""
|
||||
def do_trilio_pkg_upgrade(self):
|
||||
"""Upgrade Trilio packages
|
||||
"""
|
||||
new_os_rel = get_trilio_codename_install_source(
|
||||
self.trilio_source)
|
||||
ch_core.hookenv.log('Performing Trilio upgrade to %s.' % (new_os_rel))
|
||||
|
||||
dpkg_opts = [
|
||||
'--option', 'Dpkg::Options::=--force-confnew',
|
||||
'--option', 'Dpkg::Options::=--force-confdef',
|
||||
]
|
||||
fetch.apt_update()
|
||||
fetch.apt_install(
|
||||
packages=self.all_packages,
|
||||
options=dpkg_opts,
|
||||
fatal=True)
|
||||
self.remove_obsolete_packages()
|
||||
|
||||
def do_trilio_upgrade_db_migration(self):
|
||||
"""Run Trilio DB sync
|
||||
|
||||
Trilio charms sync_cmd refers to a trilio db sync.
|
||||
|
||||
"""
|
||||
super().do_openstack_upgrade_db_migration()
|
||||
|
||||
def run_trilio_upgrade(self, interfaces_list=None):
|
||||
"""
|
||||
:param interfaces_list: List of instances of interface classes
|
||||
:returns: None
|
||||
"""
|
||||
ch_core.hookenv.status_set('maintenance', 'Running openstack upgrade')
|
||||
cur_os_release = self.get_os_codename_package(
|
||||
self.os_release_pkg,
|
||||
self.package_codenames)
|
||||
new_trilio_release = get_trilio_codename_install_source(
|
||||
self.trilio_source)
|
||||
new_release = '{}_{}'.format(cur_os_release, new_trilio_release)
|
||||
unitdata.kv().set(TRILIO_RELEASE_KEY, new_trilio_release)
|
||||
_configure_triliovault_source()
|
||||
target_charm = charms_openstack.charm.core.get_charm_instance(
|
||||
new_release)
|
||||
target_charm.do_trilio_pkg_upgrade()
|
||||
target_charm.render_with_interfaces(interfaces_list)
|
||||
target_charm.do_trilio_upgrade_db_migration()
|
||||
|
||||
def trilio_upgrade_available(self, package=None):
|
||||
"""Check if an OpenStack upgrade is available
|
||||
|
||||
:param package: str Package name to use to check upgrade availability
|
||||
:returns: bool
|
||||
"""
|
||||
cur_vers = self.get_package_version(package)
|
||||
avail_vers = get_trilio_codename_install_source(
|
||||
self.trilio_source)
|
||||
return fetch.apt_pkg.version_compare(avail_vers, cur_vers) == 1
|
||||
|
||||
def upgrade_if_available(self, interfaces_list):
|
||||
if self.openstack_upgrade_available(self.os_release_pkg):
|
||||
if self.config.get('action-managed-upgrade', False):
|
||||
ch_core.hookenv.log('Not performing OpenStack upgrade as '
|
||||
'action-managed-upgrade is enabled')
|
||||
else:
|
||||
self.run_upgrade(interfaces_list=interfaces_list)
|
||||
if self.trilio_upgrade_available(
|
||||
package=self.trilio_version_package()):
|
||||
if self.config.get('action-managed-upgrade', False):
|
||||
ch_core.hookenv.log('Not performing Trilio upgrade as '
|
||||
'action-managed-upgrade is enabled')
|
||||
else:
|
||||
self.run_trilio_upgrade(interfaces_list=interfaces_list)
|
||||
|
||||
@classmethod
|
||||
def trilio_version_package(cls):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def version_package(self):
|
||||
return self.trilio_version_package()
|
||||
|
||||
@property
|
||||
def release_pkg(self):
|
||||
return self.trilio_version_package()
|
||||
|
||||
@classmethod
|
||||
def release_pkg_version(cls):
|
||||
return cls.get_package_version(cls.trilio_version_package())
|
||||
|
||||
|
||||
class TrilioVaultCharm(TrilioVaultCharmMixin,
|
||||
charms_openstack.charm.HAOpenStackCharm,
|
||||
metaclass=BaseTrilioCharmMeta):
|
||||
|
||||
abstract_class = True
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(TrilioVaultSubordinateCharm, self).__init__(**kwargs)
|
||||
|
||||
class TrilioVaultSubordinateCharm(TrilioVaultCharmMixin,
|
||||
charms_openstack.charm.OpenStackCharm,
|
||||
metaclass=BaseTrilioCharmMeta):
|
||||
|
||||
abstract_class = True
|
||||
|
||||
def configure_source(self):
|
||||
"""Configure TrilioVault specific package sources
|
||||
|
@ -132,17 +541,6 @@ class TrilioVaultSubordinateCharm(charms_openstack.charm.OpenStackCharm):
|
|||
_configure_triliovault_source()
|
||||
fetch.apt_update(fatal=True)
|
||||
|
||||
def install(self):
|
||||
"""Install packages dealing with Trilio nuances for upgrades as well
|
||||
"""
|
||||
self.configure_source()
|
||||
_install_triliovault(self)
|
||||
|
||||
def series_upgrade_complete(self):
|
||||
"""Re-configure sources post series upgrade"""
|
||||
super().series_upgrade_complete()
|
||||
self.configure_source()
|
||||
|
||||
|
||||
class TrilioVaultCharmGhostAction(object):
|
||||
"""Shared 'ghost share' action for TrilioVault charms
|
||||
|
|
|
@ -375,12 +375,22 @@ class TestBaseOpenStackCharmAssessStatus(BaseOpenStackCharmTest):
|
|||
class TestMyOpenStackCharm(BaseOpenStackCharmTest):
|
||||
|
||||
def setUp(self):
|
||||
self.save_rsf = chm_core._release_selector_function
|
||||
chm_core._release_selector_function = None
|
||||
self.save_cif = chm_core._get_charm_instance_function
|
||||
chm_core._get_charm_instance_function = None
|
||||
|
||||
def make_open_stack_charm():
|
||||
return MyOpenStackCharm(['interface1', 'interface2'])
|
||||
|
||||
super(TestMyOpenStackCharm, self).setUp(make_open_stack_charm,
|
||||
TEST_CONFIG)
|
||||
|
||||
def tearDown(self):
|
||||
chm_core._release_selector_function = self.save_rsf
|
||||
chm_core._get_charm_instance_function = self.save_cif
|
||||
super().tearDown()
|
||||
|
||||
def test_singleton(self):
|
||||
# because we have two releases, we expect this to be the latter.
|
||||
# e.g. MyNextOpenStackCharm
|
||||
|
|
|
@ -2,8 +2,9 @@ import unittest.mock as mock
|
|||
import os
|
||||
|
||||
from unit_tests.charms_openstack.charm.utils import BaseOpenStackCharmTest
|
||||
from unit_tests.utils import patch_open
|
||||
from unit_tests.utils import BaseTestCase, patch_open
|
||||
|
||||
import charms_openstack.charm.core as co_core
|
||||
import charms_openstack.plugins.trilio as trilio
|
||||
|
||||
|
||||
|
@ -12,6 +13,11 @@ class TrilioVaultFoobar(trilio.TrilioVaultCharm):
|
|||
abstract_class = True
|
||||
name = 'test'
|
||||
all_packages = ['foo', 'bar']
|
||||
os_release_pkg = 'nova-common'
|
||||
|
||||
@classmethod
|
||||
def trilio_version_package(cls):
|
||||
return "dmapi"
|
||||
|
||||
|
||||
class TrilioVaultFoobarSubordinate(trilio.TrilioVaultSubordinateCharm):
|
||||
|
@ -114,6 +120,7 @@ class TestTrilioCommonBehaviours(BaseOpenStackCharmTest):
|
|||
self.patch_object(trilio.ch_core.hookenv, "status_set")
|
||||
self.patch_object(trilio.fetch, "filter_installed_packages")
|
||||
self.patch_object(trilio.fetch, "apt_install")
|
||||
self.patch_object(trilio.fetch.apt_pkg, 'version_compare')
|
||||
self.patch_object(trilio.reactive, "is_flag_set")
|
||||
self.patch_object(trilio.reactive, "clear_flag")
|
||||
self.patch_target('update_api_ports')
|
||||
|
@ -162,13 +169,216 @@ class TestTrilioCommonBehaviours(BaseOpenStackCharmTest):
|
|||
)
|
||||
_file.write.assert_called_once_with('testsource')
|
||||
|
||||
def test_trilio_properties(self):
|
||||
cls_mock = mock.MagicMock()
|
||||
cls_mock.charm_instance.release_pkg_version = lambda: '4.0'
|
||||
self.version_compare.return_value = 0
|
||||
self.assertEqual(
|
||||
trilio.trilio_properties(cls_mock),
|
||||
{'db_type': 'dedicated', 'transport_type': 'dmapi'})
|
||||
self.version_compare.return_value = -1
|
||||
self.assertEqual(
|
||||
trilio.trilio_properties(cls_mock),
|
||||
{'db_type': 'legacy', 'transport_type': 'legacy'})
|
||||
|
||||
def test_get_trilio_codename_install_source(self):
|
||||
self.assertEqual(
|
||||
trilio.get_trilio_codename_install_source(
|
||||
'deb [trusted=yes] https://apt.fury.io/triliodata-4-0/ /'),
|
||||
'4.0')
|
||||
self.assertEqual(
|
||||
trilio.get_trilio_codename_install_source(
|
||||
'deb [trusted=yes] https://apt.fury.io/triliodata-4-0-0/ /'),
|
||||
'4.0')
|
||||
with self.assertRaises(AssertionError):
|
||||
trilio.get_trilio_codename_install_source(
|
||||
'deb [trusted=yes] https://apt.fury.io/triliodata/ /')
|
||||
|
||||
def test_get_trilio_charm_instance(self):
|
||||
_safe_gcif = co_core._get_charm_instance_function
|
||||
co_core._get_charm_instance_function = None
|
||||
|
||||
class BaseClass():
|
||||
def __init__(self, release, *args, **kwargs):
|
||||
pass
|
||||
|
||||
class Pike39(BaseClass):
|
||||
release = 'pike'
|
||||
trilio_release = '3.9'
|
||||
|
||||
class Queens40(BaseClass):
|
||||
release = 'queens'
|
||||
trilio_release = '4.0'
|
||||
|
||||
class Queens41(BaseClass):
|
||||
release = 'queens'
|
||||
trilio_release = '4.1'
|
||||
|
||||
class Rocky40(BaseClass):
|
||||
release = 'rocky'
|
||||
trilio_release = '4.0'
|
||||
|
||||
def _version_compare(ver1, ver2):
|
||||
if float(ver1) > float(ver2):
|
||||
return 1
|
||||
elif float(ver1) < float(ver2):
|
||||
return -1
|
||||
else:
|
||||
return 0
|
||||
|
||||
save_releases = trilio._trilio_releases
|
||||
self.version_compare.side_effect = _version_compare
|
||||
trilio._trilio_releases = {
|
||||
'pike': {
|
||||
trilio.AptPkgVersion('3.9'): {
|
||||
'deb': Pike39}},
|
||||
'queens': {
|
||||
trilio.AptPkgVersion('4.0'): {
|
||||
'deb': Queens40},
|
||||
trilio.AptPkgVersion('4.1'): {
|
||||
'deb': Queens41}},
|
||||
'rocky': {
|
||||
trilio.AptPkgVersion('4.0'): {
|
||||
'deb': Rocky40}}}
|
||||
trilio.make_trilio_get_charm_instance_handler()
|
||||
# Check with no release being supplied. Should return the
|
||||
# highest release class.
|
||||
self.assertIsInstance(
|
||||
co_core.get_charm_instance(),
|
||||
Rocky40)
|
||||
self.assertIsInstance(
|
||||
co_core.get_charm_instance(release='queens_4.0'),
|
||||
Queens40)
|
||||
self.assertIsInstance(
|
||||
co_core.get_charm_instance(release='queens_4.1'),
|
||||
Queens41)
|
||||
# Ensure an error is raised if a class satisfying the trilio condition
|
||||
# is not found for the highest matching OpenStack class.
|
||||
with self.assertRaises(RuntimeError):
|
||||
co_core.get_charm_instance(release='rocky_3.9')
|
||||
# Match the openstack release and then the closest trilio releases
|
||||
# within that subset.
|
||||
self.assertIsInstance(
|
||||
co_core.get_charm_instance(release='rocky_4.1'),
|
||||
Rocky40)
|
||||
with self.assertRaises(RuntimeError):
|
||||
co_core.get_charm_instance(release='icehouse_4.1')
|
||||
trilio._trilio_releases = save_releases
|
||||
co_core._get_charm_instance_function = _safe_gcif
|
||||
|
||||
def test_select_trilio_release(self):
|
||||
def get_charm_class(release_pkg='trilio_pkg', package_version='4.0',
|
||||
os_codename_exception=None,
|
||||
version_package='trilio_pkg',
|
||||
package_version_exception=None,
|
||||
os_release_pkg='nova_pkg',
|
||||
os_codename_pkg='queens',
|
||||
trilio_source='deb https://a.io/trilio-4-2-0/ /'):
|
||||
|
||||
class _TrilioCharm():
|
||||
|
||||
def __init__(self):
|
||||
self.release_pkg = release_pkg
|
||||
self.version_package = version_package
|
||||
self.os_release_pkg = os_release_pkg
|
||||
self.source_config_key = 'openstack-origin'
|
||||
self.package_codenames = {}
|
||||
self.package_version = package_version
|
||||
self.os_codename_exception = os_codename_exception
|
||||
self.os_codename_pkg = os_codename_pkg
|
||||
self.trilio_source = trilio_source
|
||||
|
||||
@staticmethod
|
||||
def get_os_codename_package(pkg, code_names,
|
||||
apt_cache_sufficient=True):
|
||||
if os_codename_exception:
|
||||
raise os_codename_exception
|
||||
else:
|
||||
return os_codename_pkg
|
||||
|
||||
@staticmethod
|
||||
def get_package_version(pkg, apt_cache_sufficient=True):
|
||||
if package_version_exception:
|
||||
raise package_version_exception
|
||||
else:
|
||||
return package_version
|
||||
|
||||
return _TrilioCharm()
|
||||
|
||||
_safe_rsf = co_core._release_selector_function
|
||||
co_core._release_selector_function = None
|
||||
self.patch_object(
|
||||
trilio.os_utils,
|
||||
"get_installed_semantic_versioned_packages")
|
||||
self.patch_object(trilio.os_utils, "os_release")
|
||||
self.patch_object(trilio.unitdata, "kv")
|
||||
kv_mock = mock.MagicMock()
|
||||
self.kv.return_value = kv_mock
|
||||
kv_mock.get.return_value = None
|
||||
self.patch_object(
|
||||
trilio.charms_openstack.charm.core,
|
||||
"get_charm_instance")
|
||||
|
||||
trilio.make_trilio_get_charm_instance_handler()
|
||||
trilio.make_trilio_select_release_handler()
|
||||
select_trilio_release = co_core._release_selector_function
|
||||
self.get_charm_instance.return_value = get_charm_class()
|
||||
self.assertEqual(
|
||||
select_trilio_release(),
|
||||
'queens_4.0')
|
||||
|
||||
# Check RuntimeError is raised if release_pkg is missing from charm
|
||||
# class
|
||||
self.get_charm_instance.return_value = get_charm_class(
|
||||
release_pkg=None)
|
||||
with self.assertRaises(RuntimeError):
|
||||
select_trilio_release()
|
||||
|
||||
# Test falling back to get_installed_semantic_versioned_packages
|
||||
self.os_release.return_value = 'pike'
|
||||
self.get_installed_semantic_versioned_packages.reset_mock()
|
||||
self.get_installed_semantic_versioned_packages.return_value = ['nova']
|
||||
self.get_charm_instance.return_value = get_charm_class(
|
||||
os_codename_pkg=None)
|
||||
self.assertEqual(
|
||||
select_trilio_release(),
|
||||
'pike_4.0')
|
||||
|
||||
# Check RuntimeError is raised if version_package is missing from charm
|
||||
# class
|
||||
self.get_charm_instance.return_value = get_charm_class(
|
||||
version_package=None)
|
||||
with self.assertRaises(RuntimeError):
|
||||
select_trilio_release()
|
||||
|
||||
# Test falling back to get_trilio_codename_install_source
|
||||
self.get_charm_instance.return_value = get_charm_class(
|
||||
package_version_exception=ValueError)
|
||||
self.assertEqual(
|
||||
select_trilio_release(),
|
||||
'queens_4.2')
|
||||
co_core._release_selector_function = _safe_rsf
|
||||
|
||||
|
||||
class TestTrilioVaultCharm(BaseOpenStackCharmTest):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp(TrilioVaultFoobar, {})
|
||||
self.patch_object(trilio.ch_core.hookenv, "log")
|
||||
self.patch_object(trilio.ch_core.hookenv, "status_set")
|
||||
self.patch_object(
|
||||
trilio.charms_openstack.charm.core,
|
||||
"get_charm_instance")
|
||||
self.patch_object(trilio, "_install_triliovault")
|
||||
self.patch_object(trilio, "_configure_triliovault_source")
|
||||
self.patch_object(trilio.fetch, "apt_update")
|
||||
self.patch_object(trilio.fetch, "apt_install")
|
||||
self.patch_object(trilio.fetch.apt_pkg, "version_compare")
|
||||
self.patch_target('config')
|
||||
self._conf = {
|
||||
'triliovault-pkg-source': 'deb https://a.io/trilio-4-2-0/ /'
|
||||
}
|
||||
self.config.get.side_effect = lambda x, b=None: self._conf.get(x, b)
|
||||
|
||||
def test_series_upgrade_complete(self):
|
||||
self.patch_object(trilio.charms_openstack.charm.HAOpenStackCharm,
|
||||
|
@ -190,6 +400,66 @@ class TestTrilioVaultCharm(BaseOpenStackCharmTest):
|
|||
self.target.install()
|
||||
self._install_triliovault.assert_called_once_with(self.target)
|
||||
|
||||
def test_trilio_source(self):
|
||||
self.assertEqual(
|
||||
self.target.trilio_source,
|
||||
'deb https://a.io/trilio-4-2-0/ /')
|
||||
|
||||
def test_do_trilio_pkg_upgrade(self):
|
||||
self.target.do_trilio_pkg_upgrade()
|
||||
self.apt_update.assert_called_once_with()
|
||||
self.apt_install.assert_called_once_with(
|
||||
packages=['foo', 'bar'],
|
||||
options=[
|
||||
'--option', 'Dpkg::Options::=--force-confnew',
|
||||
'--option', 'Dpkg::Options::=--force-confdef'],
|
||||
fatal=True)
|
||||
|
||||
def test_run_trilio_upgrade(self):
|
||||
self.patch_target('get_os_codename_package')
|
||||
self.get_os_codename_package.return_value = 'queens'
|
||||
charm_cls = mock.MagicMock()
|
||||
interface_mocks = [mock.MagicMock(), mock.MagicMock()]
|
||||
self.get_charm_instance.return_value = charm_cls
|
||||
self.target.run_trilio_upgrade(interfaces_list=interface_mocks)
|
||||
self._configure_triliovault_source.assert_called_once_with()
|
||||
charm_cls.do_trilio_pkg_upgrade.assert_called_once_with()
|
||||
charm_cls.render_with_interfaces.assert_called_once_with(
|
||||
interface_mocks)
|
||||
charm_cls.do_trilio_upgrade_db_migration.assert_called_once_with()
|
||||
|
||||
def test_trilio_upgrade_available(self):
|
||||
self.patch_target('get_package_version')
|
||||
self.get_package_version.return_value = '4.1'
|
||||
self.version_compare.return_value = 1
|
||||
self.assertTrue(self.target.trilio_upgrade_available())
|
||||
self.version_compare.assert_called_once_with('4.2', '4.1')
|
||||
|
||||
def test_upgrade_if_available(self):
|
||||
self.patch_target('openstack_upgrade_available')
|
||||
self.patch_target('trilio_upgrade_available')
|
||||
self.patch_target('run_upgrade')
|
||||
self.patch_target('run_trilio_upgrade')
|
||||
interface_mocks = [mock.MagicMock(), mock.MagicMock()]
|
||||
|
||||
self._conf['action-managed-upgrade'] = False
|
||||
self.openstack_upgrade_available.return_value = True
|
||||
self.trilio_upgrade_available.return_value = True
|
||||
self.target.upgrade_if_available(interface_mocks)
|
||||
self.run_upgrade.assert_called_once_with(
|
||||
interfaces_list=interface_mocks)
|
||||
self.run_trilio_upgrade.assert_called_once_with(
|
||||
interfaces_list=interface_mocks)
|
||||
|
||||
self.run_upgrade.reset_mock()
|
||||
self.run_trilio_upgrade.reset_mock()
|
||||
self._conf['action-managed-upgrade'] = True
|
||||
self.openstack_upgrade_available.return_value = True
|
||||
self.trilio_upgrade_available.return_value = True
|
||||
self.target.upgrade_if_available(interface_mocks)
|
||||
self.assertFalse(self.run_upgrade.called)
|
||||
self.assertFalse(self.run_trilio_upgrade.called)
|
||||
|
||||
|
||||
class TestTrilioVaultSubordinateCharm(BaseOpenStackCharmTest):
|
||||
|
||||
|
@ -197,13 +467,7 @@ class TestTrilioVaultSubordinateCharm(BaseOpenStackCharmTest):
|
|||
super().setUp(TrilioVaultFoobarSubordinate, {})
|
||||
self.patch_object(trilio, "_install_triliovault")
|
||||
self.patch_object(trilio, "_configure_triliovault_source")
|
||||
|
||||
def test_series_upgrade_complete(self):
|
||||
self.patch_object(trilio.charms_openstack.charm.OpenStackCharm,
|
||||
'series_upgrade_complete')
|
||||
self.patch_target('configure_source')
|
||||
self.target.series_upgrade_complete()
|
||||
self.configure_source.assert_called_once_with()
|
||||
self.patch_object(trilio.fetch, "apt_update")
|
||||
|
||||
def test_configure_source(self):
|
||||
self.patch_object(trilio.charms_openstack.charm.OpenStackCharm,
|
||||
|
@ -211,9 +475,100 @@ class TestTrilioVaultSubordinateCharm(BaseOpenStackCharmTest):
|
|||
self.target.configure_source()
|
||||
self._configure_triliovault_source.assert_called_once_with()
|
||||
self.configure_source.assert_not_called()
|
||||
self.apt_update.assert_called_once_with(fatal=True)
|
||||
|
||||
def test_install(self):
|
||||
self.patch_object(trilio.charms_openstack.charm.OpenStackCharm,
|
||||
'configure_source')
|
||||
self.target.install()
|
||||
self._install_triliovault.assert_called_once_with(self.target)
|
||||
|
||||
class TestBaseTrilioCharmMeta(BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.save_releases = trilio._trilio_releases
|
||||
super().setUp()
|
||||
self.patch_object(trilio.fetch.apt_pkg, 'version_compare')
|
||||
|
||||
def _version_compare(ver1, ver2):
|
||||
if float(ver1) > float(ver2):
|
||||
return 1
|
||||
elif float(ver1) < float(ver2):
|
||||
return -1
|
||||
else:
|
||||
return 0
|
||||
self.version_compare.side_effect = _version_compare
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
trilio._trilio_releases = self.save_releases
|
||||
|
||||
def register_classes(self):
|
||||
|
||||
class TrilioQueens40(metaclass=trilio.BaseTrilioCharmMeta):
|
||||
|
||||
release = 'queens'
|
||||
trilio_release = '4.0'
|
||||
|
||||
class TrilioQueens41(metaclass=trilio.BaseTrilioCharmMeta):
|
||||
|
||||
release = 'queens'
|
||||
trilio_release = '4.1'
|
||||
|
||||
class TrilioRocky40(metaclass=trilio.BaseTrilioCharmMeta):
|
||||
|
||||
release = 'rocky'
|
||||
trilio_release = '4.0'
|
||||
|
||||
return {
|
||||
'queens_4.0': TrilioQueens40,
|
||||
'queens_4.1': TrilioQueens41,
|
||||
'rocky_4.0': TrilioRocky40}
|
||||
|
||||
def register_classes_missing_key(self):
|
||||
|
||||
class TrilioQueens40(metaclass=trilio.BaseTrilioCharmMeta):
|
||||
|
||||
release = 'queens'
|
||||
|
||||
def register_classes_wrong_pkg_type(self):
|
||||
|
||||
class TrilioQueens40(metaclass=trilio.BaseTrilioCharmMeta):
|
||||
|
||||
release = 'queens'
|
||||
trilio_release = '4.1'
|
||||
package_type = 'up2date'
|
||||
|
||||
def register_classes_duplicate(self):
|
||||
|
||||
class TrilioQueens40A(metaclass=trilio.BaseTrilioCharmMeta):
|
||||
|
||||
release = 'queens'
|
||||
trilio_release = '4.0'
|
||||
|
||||
class TrilioQueens40B(metaclass=trilio.BaseTrilioCharmMeta):
|
||||
|
||||
release = 'queens'
|
||||
trilio_release = '4.0'
|
||||
|
||||
def test_class_register(self):
|
||||
charm_classes = self.register_classes()
|
||||
self.maxDiff = None
|
||||
self.assertEqual(
|
||||
trilio._trilio_releases,
|
||||
{
|
||||
'queens': {
|
||||
trilio.AptPkgVersion('4.0'): {
|
||||
'deb': charm_classes['queens_4.0']},
|
||||
trilio.AptPkgVersion('4.1'): {
|
||||
'deb': charm_classes['queens_4.1']}},
|
||||
'rocky': {
|
||||
trilio.AptPkgVersion('4.0'): {
|
||||
'deb': charm_classes['rocky_4.0']}}})
|
||||
|
||||
def test_class_register_missing_key(self):
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.register_classes_missing_key()
|
||||
|
||||
def test_class_register_wrong_pkg_type(self):
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.register_classes_wrong_pkg_type()
|
||||
|
||||
def test_class_register_duplicate(self):
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.register_classes_duplicate()
|
||||
|
|
Loading…
Reference in New Issue