import collections from unittest import mock import charms_openstack.charm.core as chm_core import charms_openstack.charm.defaults as chm from unit_tests.charms_openstack.charm.utils import BaseOpenStackCharmTest TEST_CONFIG = {'config': True} class TestDefaults(BaseOpenStackCharmTest): def setUp(self): super().setUp(chm_core.BaseOpenStackCharm, TEST_CONFIG) def test_use_defaults(self): self.patch_object(chm, 'ALLOWED_DEFAULT_HANDLERS', new=['handler']) self.patch_object(chm, '_default_handler_map', new={}) # first check for a missing handler. with self.assertRaises(RuntimeError): chm.use_defaults('does not exist') # now check for an allowed handler, but no function. with self.assertRaises(RuntimeError): chm.use_defaults('handler') class TestException(Exception): pass # finally, have an actual handler. @chm._map_default_handler('handler') def do_handler(): raise TestException() with self.assertRaises(TestException): chm.use_defaults('handler') def test_map_default_handler(self): self.patch_object(chm, 'ALLOWED_DEFAULT_HANDLERS', new=['handler']) self.patch_object(chm, '_default_handler_map', new={}) # test that we can only map allowed handlers. with self.assertRaises(RuntimeError): @chm._map_default_handler('does-not-exist') def test_func1(): pass # test we can only map a handler once @chm._map_default_handler('handler') def test_func2(): pass with self.assertRaises(RuntimeError): @chm._map_default_handler('handler') def test_func3(): pass @staticmethod def mock_decorator_gen(): _map = {} def mock_generator(state): def wrapper(f): _map[state] = f def wrapped(*args, **kwargs): return f(*args, **kwargs) return wrapped return wrapper Handler = collections.namedtuple('Handler', ['map', 'decorator']) return Handler(_map, mock_generator) @staticmethod def mock_decorator_gen_simple(): _func = {} def wrapper(f): _func['function'] = f def wrapped(*args, **kwargs): return f(*args, **kwargs) return wrapped Handler = collections.namedtuple('Handler', ['map', 'decorator']) return Handler(_func, wrapper) def test_default_install_handler(self): self.assertIn('charm.installed', chm._default_handler_map) self.patch_object(chm.reactive, 'set_state') f = chm._default_handler_map['charm.installed'] f() self.set_state.assert_called_once_with( 'charms.openstack.do-default-charm.installed') def test_default_select_release_handler(self): self.assertIn('charm.default-select-release', chm._default_handler_map) self.patch_object(chm, 'register_os_release_selector') h = self.mock_decorator_gen_simple() self.register_os_release_selector.side_effect = h.decorator # call the default handler installer function, and check its map. f = chm._default_handler_map['charm.default-select-release'] f() self.assertIsNotNone(h.map['function']) # verify that the installed function works kv = mock.MagicMock() self.patch_object(chm.unitdata, 'kv', new=lambda: kv) self.patch_object(chm.os_utils, 'os_release') self.patch_object( chm.os_utils, 'get_installed_semantic_versioned_packages', return_value=['cinder-common']) singleton = mock.MagicMock() singleton.source_config_key = 'fake-config-key' singleton.get_os_codename_package.return_value = None self.patch_object(chm, 'get_charm_instance', return_value=singleton) # set a release kv.get.return_value = 'one' release = h.map['function']() self.assertEqual(release, 'one') kv.set.assert_not_called() kv.get.assert_called_once_with(chm.OPENSTACK_RELEASE_KEY, None) # No release_pkg set, ensure a RuntimeError raised kv.get.return_value = None singleton.release_pkg = None with self.assertRaises(RuntimeError): h.map['function']() singleton.release_pkg = "my-pkg" # No release set, ensure it calls os_release kv.reset_mock() kv.get.return_value = None self.os_release.return_value = 'two' release = h.map['function']() self.assertEqual(release, 'two') kv.set.assert_called_once_with(chm.OPENSTACK_RELEASE_KEY, 'two') self.os_release.assert_called_once_with( 'cinder-common', source_key='fake-config-key') # No release set, charm class provides package_codenames kv.reset_mock() singleton.get_os_codename_package.reset_mock() singleton.get_os_codename_package.side_effect = None singleton.get_os_codename_package.return_value = 'three' singleton.source_config_key = 'fake-config-key' release = h.map['function']() self.assertEqual(release, 'three') singleton.get_os_codename_package.assert_called_once_with( mock.ANY, mock.ANY, apt_cache_sufficient=False) kv.set.assert_called_once_with(chm.OPENSTACK_RELEASE_KEY, 'three') # No release set, charm class has empty ``source_config_key`` singleton.get_os_codename_package.reset_mock() singleton.source_config_key = '' singleton.get_os_codename_package.return_value = 'four' release = h.map['function']() self.assertEqual(release, 'four') singleton.get_os_codename_package.assert_called_once_with( mock.ANY, mock.ANY, apt_cache_sufficient=True) def test_default_select_package_type_handler(self): self.assertIn('charm.default-select-package-type', chm._default_handler_map) self.patch_object(chm, 'register_package_type_selector') h = self.mock_decorator_gen_simple() self.register_package_type_selector.side_effect = h.decorator # call the default handler installer function, and check its map. f = chm._default_handler_map['charm.default-select-package-type'] f() self.assertIsNotNone(h.map['function']) # verify that the installed function works kv = mock.MagicMock() self.patch_object(chm.unitdata, 'kv', new=lambda: kv) self.patch_object(chm.os_utils, 'snap_install_requested', return_value=False) # set a package_type kv.get.return_value = 'deb' package_type = h.map['function']() self.assertEqual(package_type, 'deb') kv.set.assert_not_called() kv.get.assert_called_once_with(chm.OPENSTACK_PACKAGE_TYPE_KEY, None) # No release set, ensure it calls snap_install_requested and # sets package_type to 'snap' kv.reset_mock() kv.get.return_value = None self.snap_install_requested.return_value = True package_type = h.map['function']() self.assertEqual(package_type, 'snap') kv.set.assert_called_once_with(chm.OPENSTACK_PACKAGE_TYPE_KEY, 'snap') self.snap_install_requested.assert_called_once_with() # No release set, ensure it calls snap_install_requested and # sets package_type to 'deb' kv.reset_mock() kv.get.return_value = None self.snap_install_requested.reset_mock() self.snap_install_requested.return_value = False package_type = h.map['function']() self.assertEqual(package_type, 'deb') kv.set.assert_called_once_with(chm.OPENSTACK_PACKAGE_TYPE_KEY, 'deb') self.snap_install_requested.assert_called_once_with() def test_default_amqp_connection_handler(self): self.assertIn('amqp.connected', chm._default_handler_map) self.patch_object(chm.reactive, 'set_state') # call the default handler installer function, and check its map. f = chm._default_handler_map['amqp.connected'] f() self.set_state.assert_called_once_with( 'charms.openstack.do-default-amqp.connected') def test_default_setup_datatbase_handler(self): self.assertIn('shared-db.connected', chm._default_handler_map) self.patch_object(chm.reactive, 'set_state') # call the default handler installer function, and check its map. f = chm._default_handler_map['shared-db.connected'] f() self.set_state.assert_called_once_with( 'charms.openstack.do-default-shared-db.connected') def test_default_setup_endpoint_handler(self): self.assertIn('identity-service.connected', chm._default_handler_map) self.patch_object(chm.reactive, 'set_state') f = chm._default_handler_map['identity-service.connected'] f() self.set_state.assert_called_once_with( 'charms.openstack.do-default-identity-service.connected') def test_default_setup_endpoint_available_handler(self): self.assertIn('identity-service.available', chm._default_handler_map) self.patch_object(chm.reactive, 'set_state') # call the default handler installer function, and check its map. f = chm._default_handler_map['identity-service.available'] f() self.set_state.assert_called_once_with( 'charms.openstack.do-default-identity-service.available') def test_default_config_changed_handler(self): self.assertIn('config.changed', chm._default_handler_map) self.patch_object(chm.reactive, 'set_state') f = chm._default_handler_map['config.changed'] f() self.set_state.assert_called_once_with( 'charms.openstack.do-default-config.changed') def test_default_update_status_handler(self): self.assertIn('update-status', chm._default_handler_map) self.patch_object(chm.reactive, 'set_state') f = chm._default_handler_map['update-status'] f() self.set_state.assert_called_once_with( 'charms.openstack.do-default-update-status') def test_default_upgrade_charm_handler(self): self.assertIn('upgrade-charm', chm._default_handler_map) self.patch_object(chm.reactive, 'set_state') f = chm._default_handler_map['upgrade-charm'] f() self.set_state.assert_called_once_with( 'charms.openstack.do-default-upgrade-charm') def test_default_render_configs(self): self.patch_object(chm, 'OpenStackCharm', name='charm') interfaces = ['a', 'b', 'c'] chm.default_render_configs(*interfaces) self.charm.singleton.render_configs.assert_called_once_with( tuple(interfaces)) self.charm.singleton.assess_status.assert_called_once_with()