Fix exception in set-temp-url-secret action

There are two issues fixed in this bug. The first is that the
keystone_session exception block does not return on failure. The
second, and the reason that create_keystone_session is failing,
is due to the use of setuptools 60.9.0+ with the
importlib-metadata in wheelhouse.txt that is pinned to <3.0.0.
For more details see: https://github.com/pypa/setuptools/issues/3452

Closes-Bug: #2018018
Change-Id: I266c1fb92d531aded2f3253766de0a79accd9577
(cherry picked from commit 9f221f1087)
This commit is contained in:
Corey Bryant 2023-04-28 15:56:55 -04:00
parent 0ec212e237
commit 8c2ed98bc1
6 changed files with 155 additions and 4 deletions

View File

@ -56,6 +56,7 @@ def set_temp_url_secret(*args):
except Exception as e:
ch_core.hookenv.action_fail('Failed to create keystone session ("{}")'
.format(e))
return
os_cli = api_utils.OSClients(keystone_session)
if os_cli.has_swift() is False:

View File

@ -164,7 +164,7 @@
"type": "python_module",
"package": "importlib_metadata",
"vcs": null,
"version": "2.1.3"
"version": "4.3.0"
},
{
"type": "python_module",
@ -260,7 +260,7 @@
"type": "python_module",
"package": "setuptools",
"vcs": null,
"version": "62.1.0"
"version": "60.8.2"
},
{
"type": "python_module",
@ -441,4 +441,4 @@
"version": "5.9.2"
}
]
}
}

View File

@ -21,8 +21,9 @@ cryptography
pyrsistent<0.18.0
iso8601<1.0.0
importlib-metadata<3.0.0
importlib-metadata==4.3.0
importlib-resources<3.0.0
setuptools<60.9.0
git+https://github.com/openstack/charms.openstack.git@stable/zed#egg=charms.openstack

View File

@ -10,6 +10,10 @@ charms_openstack.test_mocks.mock_charmhelpers()
sys.modules['charmhelpers.core.decorators'] = (
charms_openstack.test_mocks.charmhelpers.core.decorators)
global snap
snap = mock.MagicMock()
sys.modules['charms.layer'] = snap
class _fake_decorator(object):

View File

@ -0,0 +1,46 @@
import src.actions.actions as actions
import unit_tests.test_utils
import reactive.ironic_handlers as handlers
import charm.openstack.ironic.api_utils as api_utils
import charmhelpers.core as ch_core
import charms.leadership as leadership
import mock
class TestActions(unit_tests.test_utils.CharmTestCase):
def setUp(self):
super(TestActions, self).setUp()
self.patches = []
self.patch_all()
self.ironic_charm = mock.MagicMock()
self.patch_object(handlers.charm, 'provide_charm_instance',
new=mock.MagicMock())
self.provide_charm_instance().__enter__.return_value = \
self.ironic_charm
self.provide_charm_instance().__exit__.return_value = None
def test_set_temp_url_secret_keystone_session_successful(self):
self.patch_object(ch_core.hookenv, 'action_fail')
self.patch_object(leadership, 'leader_get')
actions.set_temp_url_secret()
self.leader_get.assert_called_with('temp_url_secret')
def test_set_temp_url_secret_keystone_session_exception(self):
self.patch_object(api_utils, 'create_keystone_session')
self.patch_object(ch_core.hookenv, 'action_fail')
self.patch_object(leadership, 'leader_get')
def raise_exception(*args):
raise Exception("doh!")
self.create_keystone_session.side_effect = raise_exception
actions.set_temp_url_secret()
self.action_fail.assert_called_with(
'Failed to create keystone session ("doh!")')
self.leader_get.assert_not_called()

99
unit_tests/test_utils.py Normal file
View File

@ -0,0 +1,99 @@
from unittest import mock
import unittest
class CharmTestCase(unittest.TestCase):
def setUp(self):
self._patches = {}
self._patches_start = {}
def tearDown(self):
for k, v in self._patches.items():
v.stop()
setattr(self, k, None)
self._patches = None
self._patches_start = None
def _patch(self, method):
_m = unittest.mock.patch.object(self.obj, method)
mock = _m.start()
self.addCleanup(_m.stop)
return mock
def patch_all(self):
for method in self.patches:
setattr(self, method, self._patch(method))
def patch_object(self, obj, attr, return_value=None, name=None, new=None,
**kwargs):
if name is None:
name = attr
if new is not None:
mocked = mock.patch.object(obj, attr, new=new, **kwargs)
else:
mocked = mock.patch.object(obj, attr, **kwargs)
self._patches[name] = mocked
started = mocked.start()
if new is None:
started.return_value = return_value
self._patches_start[name] = started
setattr(self, name, started)
def patch(self, item, return_value=None, name=None, new=None, **kwargs):
if name is None:
raise RuntimeError("Must pass 'name' to .patch()")
if new is not None:
mocked = mock.patch(item, new=new, **kwargs)
else:
mocked = mock.patch(item, **kwargs)
self._patches[name] = mocked
started = mocked.start()
if new is None:
started.return_value = return_value
self._patches_start[name] = started
class TestConfig(object):
def __init__(self):
self.config = {}
self.config_prev = {}
def __call__(self, key=None):
if key:
return self.get(key)
else:
return self
def previous(self, k):
return self.config_prev[k] if k in self.config_prev else self.config[k]
def set_previous(self, k, v):
self.config_prev[k] = v
def unset_previous(self, k):
if k in self.config_prev:
self.config_prev.pop(k)
def changed(self, k):
if not self.config_prev:
return True
return self.get(k) != self.previous(k)
def get(self, attr=None):
if not attr:
return self
try:
return self.config[attr]
except KeyError:
return None
def get_all(self):
return self.config
def set(self, attr, value):
self.config[attr] = value
def __getitem__(self, k):
return self.get(k)