Allow bespoke get_charm_instance method to be used

Allow a charm to use the new register_get_charm_instance decorator
to register the method to be used when getting a charm instance.
Currently get_charm_instance expects standard OpenStack
versioning, this change allows are a charm to register an
alternative get_charm_instance method that can handle an
alternative versioning system.

Perhaps controversially the default method is not defined in
charms_openstack.charm.defaults. I have kept it in core
because it felt a more natural fit there given it relies on
core._releases but I am open to moving it if that is
preferable.

Change-Id: I42574654bc1f314b49049e80861d4039f8484dff
This commit is contained in:
Liam Young 2020-12-03 10:03:02 +00:00
parent 4907569ebc
commit 38de241ce6
2 changed files with 80 additions and 1 deletions

View File

@ -45,6 +45,10 @@ _singleton = None
# This is to enable the defining code to define which release is used.
_release_selector_function = None
# `_get_charm_instance_function` holds a function that takes optionally takes a
# release and returns the corresponding charm class.
_get_charm_instance_function = None
# `_package_type_selector_function` holds a function that optionally takes a
# package type and commutes it to another package type or just returns a
# package type. This is to enable the defining code to define which
@ -100,7 +104,8 @@ class provide_charm_instance(object):
return False
def get_charm_instance(release=None, package_type='deb', *args, **kwargs):
def default_get_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).
@ -147,6 +152,24 @@ def get_charm_instance(release=None, package_type='deb', *args, **kwargs):
return cls(release=release, *args, **kwargs)
def get_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).
Use a bespoke method if one is registered otherwise uses the default
default_get_charm_instance.
:param release: lc string representing release wanted.
:param package_type: string representing the package type required
:returns: BaseOpenStackCharm() derived class according to cls.releases
"""
return (_get_charm_instance_function or default_get_charm_instance)(
release=release,
package_type=package_type,
*args,
**kwargs)
def register_os_release_selector(f):
"""Register a function that determines what the release is for the
invocation run. This allows the charm to define HOW the release is
@ -171,6 +194,31 @@ def register_os_release_selector(f):
return f
def register_get_charm_instance(f):
"""Register a function that supplies a charm class for a given
release.
Usage:
@register_get_charm_instance
def my_get_charm_instance(release=None, *args, **kwargs):
if release == X:
cls = CharmClassX
return cls(release=release, *args, **kwargs)
The function should return a string which is an OS release.
"""
global _get_charm_instance_function
if _get_charm_instance_function is None:
# we can only do this once in a system invocation.
_get_charm_instance_function = f
else:
raise RuntimeError(
"Only a single get_charm_instance is supported."
" Called with {}".format(f.__name__))
return f
def register_package_type_selector(f):
"""Register a function that determines what the package type is for the
invocation run. This allows the charm to define HOW the package type is

View File

@ -57,6 +57,36 @@ class TestRegisterOSReleaseSelector(unittest.TestCase):
chm_core._release_selector_function = save_rsf
class TestRegisterGetCharmInstance(unittest.TestCase):
def test_register(self):
save_rsf = chm_core._get_charm_instance_function
chm_core._get_charm_instance_function = None
@chm_core.register_get_charm_instance
def test_func():
pass
self.assertEqual(chm_core._get_charm_instance_function, test_func)
chm_core._get_charm_instance_function = save_rsf
def test_cant_register_more_than_once(self):
save_rsf = chm_core._get_charm_instance_function
chm_core._get_charm_instance_function = None
@chm_core.register_get_charm_instance
def test_func1():
pass
with self.assertRaises(RuntimeError):
@chm_core.register_get_charm_instance
def test_func2():
pass
self.assertEqual(chm_core._get_charm_instance_function, test_func1)
chm_core._get_charm_instance_function = save_rsf
class TestBaseOpenStackCharmMeta(BaseOpenStackCharmTest):
def setUp(self):
@ -103,6 +133,7 @@ class TestFunctions(BaseOpenStackCharmTest):
def setUp(self):
super().setUp(chm_core.BaseOpenStackCharm, TEST_CONFIG)
self.patch_object(chm_core, '_releases', new={})
chm_core._get_charm_instance_function = None
class TestC1(chm_core.BaseOpenStackCharm):
release = 'icehouse'