fix connection.Connection finalizer

finalizers execution order is not guaranteed in python3. This leads to
absence of builtins like "open" in the finalizers. Depending on the
keyring backend used inside of the set_auth_cache access to the file system
may be required.

Replace use of destructor to another builtin library "atexit" to
implement a more reliable exit handler.

Change-Id: I2d6882d64b57b65dbef086a08a3b3dc6b5faa2be
This commit is contained in:
Artem Goncharov 2023-06-18 16:48:34 +02:00
parent e7ee461d9b
commit 2b4aeff6d3
3 changed files with 27 additions and 5 deletions

View File

@ -658,7 +658,11 @@ class CloudRegion:
state = self._auth.get_auth_state()
try:
keyring.set_password('openstacksdk', cache_id, state)
if state:
# NOTE: under some conditions the method may be invoked when auth
# is empty. This may lead to exception in the keyring lib, thus do
# nothing.
keyring.set_password('openstacksdk', cache_id, state)
except RuntimeError: # the fail backend raises this
self.log.debug('Failed to set auth into keyring')

View File

@ -176,6 +176,7 @@ try to find it and if that fails, you would create it::
Additional information about the services can be found in the
:ref:`service-proxies` documentation.
"""
import atexit
import concurrent.futures
import warnings
import weakref
@ -472,9 +473,8 @@ class Connection(
'additional_metric_tags'
] = self.config.config['additional_metric_tags']
def __del__(self):
# try to force release of resources and save authorization
self.close()
# Register cleanup steps
atexit.register(self.close)
@property
def session(self):
@ -551,9 +551,9 @@ class Connection(
def close(self):
"""Release any resources held open."""
self.config.set_auth_cache()
if self.__pool_executor:
self.__pool_executor.shutdown()
self.config.set_auth_cache()
def set_global_request_id(self, global_request_id):
self._global_request_id = global_request_id

View File

@ -608,6 +608,21 @@ class TestConfig(base.TestCase):
)
ks_mock.assert_called_with(fake_auth)
@mock.patch('openstack.config.cloud_region.keyring')
def test_set_auth_cache_empty_auth(self, kr_mock):
c = config.OpenStackConfig(
config_files=[self.cloud_yaml], secure_files=[]
)
c._cache_auth = True
kr_mock.get_password = mock.Mock(side_effect=[RuntimeError])
kr_mock.set_password = mock.Mock()
region = c.get_one('_test-cloud_')
region.set_auth_cache()
kr_mock.set_password.assert_not_called()
@mock.patch('openstack.config.cloud_region.keyring')
def test_set_auth_cache(self, kr_mock):
c = config.OpenStackConfig(
@ -619,6 +634,9 @@ class TestConfig(base.TestCase):
kr_mock.set_password = mock.Mock()
region = c.get_one('_test-cloud_')
region._auth.set_auth_state(
'{"auth_token":"foo", "body":{"token":"bar"}}'
)
region.set_auth_cache()
kr_mock.set_password.assert_called_with(