Publish releases packages map to principal charm

For principal - subordinate plugin type relations where the
principal Python payload imports code from packages managed by a
subordinate, upgrades can be problematic.

This change will allow a subordinate charm that have opted into the
feature to inform its principal about all implemented release -
packages combinations ahead of time. With this information in place
the principal can do the upgrade in one operation without risk of
charm relation RPC type processing at a critical moment.

This is similar to
https://review.opendev.org/c/openstack/charm-interface-keystone-domain-backend/+/781658
https://review.opendev.org/c/openstack/charm-layer-openstack/+/781624

Change-Id: Ibd5bdcb141fc3103ee97123ff284fb2957802eba
Closes-Bug: #1927277
This commit is contained in:
Aurelien Lourot 2021-09-28 14:19:08 +02:00
parent 5a333004c3
commit be45f77945
4 changed files with 85 additions and 14 deletions

View File

@ -59,6 +59,7 @@ from ceilometer_utils import (
NOVA_SETTINGS,
assess_status,
get_packages,
releases_packages_map,
pause_unit_helper,
resume_unit_helper,
remove_old_packages,
@ -83,8 +84,14 @@ def install():
@hooks.hook('nova-ceilometer-relation-joined')
def nova_ceilometer_joined(relation_id=None):
relation_set(relation_id=relation_id,
subordinate_configuration=json.dumps(NOVA_SETTINGS))
relation_set(
relation_id=relation_id,
relation_settings={
'subordinate_configuration': json.dumps(NOVA_SETTINGS),
'releases-packages-map': json.dumps(
releases_packages_map(), sort_keys=True),
'services': json.dumps(services())
})
@hooks.hook("ceilometer-service-relation-changed")

View File

@ -115,8 +115,7 @@ def register_configs():
# if called without anything installed (eg during install hook)
# just default to earliest supported release. configs dont get touched
# till post-install, anyway.
release = get_os_codename_package('ceilometer-common', fatal=False) \
or 'icehouse'
release = _get_current_release()
configs = templating.OSConfigRenderer(templates_dir=TEMPLATES,
openstack_release=release)
@ -137,8 +136,7 @@ def register_configs():
def get_packages():
release = CompareOpenStackReleases(get_os_codename_package(
'ceilometer-common', fatal=False) or 'icehouse')
release = _get_current_release()
packages = deepcopy(CEILOMETER_AGENT_PACKAGES)
packages.extend(token_cache_pkgs(release=release))
@ -157,8 +155,7 @@ def determine_purge_packages():
:returns: list of package names
'''
release = CompareOpenStackReleases(get_os_codename_package(
'ceilometer-common', fatal=False) or 'icehouse')
release = _get_current_release()
if release >= 'rocky':
pkgs = [p for p in CEILOMETER_AGENT_PACKAGES
if p.startswith('python-')]
@ -166,6 +163,40 @@ def determine_purge_packages():
return []
def releases_packages_map():
'''Provide a map of all supported releases and their packages.
NOTE(lourot): this is a simplified version of a more generic
implementation:
https://github.com/openstack/charms.openstack/blob/master/charms_openstack/charm/core.py
:returns: Map of release, package type and install / purge packages.
Example:
{
'mitaka': {
'deb': {
'install': ['python-ldappool'],
'purge': []
}
},
'rocky': {
'deb': {
'install': ['python3-ldap', 'python3-ldappool'],
'purge': ['python-ldap', 'python-ldappool']}
}
}
:rtype: Dict[str,Dict[str,List[str]]]
'''
return {
_get_current_release(): {
'deb': {
'install': get_packages(),
'purge': determine_purge_packages(),
}
}
}
def remove_old_packages():
'''Purge any packages that need ot be removed.
@ -183,8 +214,7 @@ def remove_old_packages():
def determine_held_packages():
'''Return a list of packages to mark as candidates for removal
for the current OS release'''
release = CompareOpenStackReleases(get_os_codename_package(
'ceilometer-common', fatal=False) or 'icehouse')
release = _get_current_release()
if release >= 'rocky':
return HELD_PACKAGES
return []
@ -198,8 +228,7 @@ def restart_map():
:returns: dict: A dictionary mapping config file to lists of services
that should be restarted when file changes.
'''
release = (get_os_codename_package('ceilometer-common', fatal=False) or
'icehouse')
release = _get_current_release()
if CompareOpenStackReleases(release) >= 'queens':
_config_files = QUEENS_CONFIG_FILES
@ -291,3 +320,8 @@ def _pause_resume_helper(f, configs):
f(assess_status_func(configs),
services=services(),
ports=None)
def _get_current_release():
return (get_os_codename_package('ceilometer-common', fatal=False)
or 'icehouse')

View File

@ -29,6 +29,8 @@ TO_PATCH = [
'apt_update',
'filter_installed_packages',
'get_packages',
'releases_packages_map',
'services',
'is_relation_made',
'is_unit_paused_set',
'relation_set',
@ -65,11 +67,27 @@ class CeilometerHooksTest(CharmTestCase):
@patch('charmhelpers.core.hookenv.config')
def test_nova_ceilometer_joined(self, mock_config):
mocked_releases_packages_map = {
'ussuri': {
'deb': {
'install': [
'ceilometer-common', 'ceilometer-agent-compute',
'python3-ceilometer', 'python3-memcache'],
'purge': ['python-ceilometer'],
}}}
mocked_services = ['ceilometer-agent-compute']
self.releases_packages_map.return_value = mocked_releases_packages_map
self.services.return_value = mocked_services
hooks.hooks.execute(['hooks/nova-ceilometer-relation-joined'])
self.relation_set.assert_called_with(
relation_id=None,
subordinate_configuration=json.dumps(
ceilometer_utils.NOVA_SETTINGS))
relation_settings={
'subordinate_configuration': json.dumps(
ceilometer_utils.NOVA_SETTINGS),
'releases-packages-map': json.dumps(
mocked_releases_packages_map, sort_keys=True),
'services': json.dumps(mocked_services)})
@patch('charmhelpers.core.hookenv.config')
def test_config_changed(self, mock_config):

View File

@ -157,3 +157,15 @@ class CeilometerUtilsTest(CharmTestCase):
sorted([p for p in utils.CEILOMETER_AGENT_PACKAGES
if not p.startswith('python-')] +
['python3-ceilometer', 'python3-memcache']))
def test_releases_packages_map(self):
self.get_os_codename_package.return_value = 'ussuri'
self.token_cache_pkgs.return_value = []
self.assertEqual(utils.releases_packages_map(), {
'ussuri': {
'deb': {
'install': [
'ceilometer-common', 'ceilometer-agent-compute',
'python3-ceilometer', 'python3-memcache'],
'purge': ['python-ceilometer'],
}}})