Expose root cause plugin exceptions
Stevedore was being used to both discover configured plugins and to create instances of these plugins. If the plugin's __init__() function raised an exception however, stevedore logged the exception but otherwise kept processing plugins in the list. The logger also is disabled so no stack trace is seen in the log file, hence the init error is swallowed. It wasn't until the plugin was searched for later in the flow that an error was appearent to the client, but the original root cause of the issue was not found. This CR creates these plugins outside of stevedore so that issues are immediately raised for attention, as these represent configuration issues that must be fixed for proper operation. Change-Id: I2fd533302ac5e0364aee6e24fd03fcf962c7a3ef
This commit is contained in:
parent
4273c640e3
commit
70b9515352
@ -18,6 +18,8 @@ from barbican.common import utils
|
||||
from barbican import i18n as u
|
||||
from barbican.plugin.crypto import crypto
|
||||
from barbican.plugin.interface import secret_store
|
||||
from barbican.plugin.util import utils as plugin_utils
|
||||
|
||||
|
||||
_PLUGIN_MANAGER = None
|
||||
|
||||
@ -43,8 +45,7 @@ CONF.register_opts(crypto_opts, group=crypto_opt_group)
|
||||
|
||||
|
||||
class _CryptoPluginManager(named.NamedExtensionManager):
|
||||
def __init__(self, conf=CONF, invoke_on_load=True,
|
||||
invoke_args=(), invoke_kwargs={}):
|
||||
def __init__(self, conf=CONF, invoke_args=(), invoke_kwargs={}):
|
||||
"""Crypto Plugin Manager
|
||||
|
||||
Each time this class is initialized it will load a new instance
|
||||
@ -55,11 +56,14 @@ class _CryptoPluginManager(named.NamedExtensionManager):
|
||||
super(_CryptoPluginManager, self).__init__(
|
||||
conf.crypto.namespace,
|
||||
conf.crypto.enabled_crypto_plugins,
|
||||
invoke_on_load=invoke_on_load,
|
||||
invoke_on_load=False, # Defer creating plugins to utility below.
|
||||
invoke_args=invoke_args,
|
||||
invoke_kwds=invoke_kwargs
|
||||
)
|
||||
|
||||
plugin_utils.instantiate_plugins(
|
||||
self, invoke_args, invoke_kwargs)
|
||||
|
||||
def get_plugin_store_generate(self, type_needed, algorithm=None,
|
||||
bit_length=None, mode=None):
|
||||
"""Gets a secret store or generate plugin that supports provided type.
|
||||
@ -68,18 +72,19 @@ class _CryptoPluginManager(named.NamedExtensionManager):
|
||||
type of plugin required
|
||||
:returns: CryptoPluginBase plugin implementation
|
||||
"""
|
||||
active_plugins = plugin_utils.get_active_plugins(self)
|
||||
|
||||
if len(self.extensions) < 1:
|
||||
if len(active_plugins) < 1:
|
||||
raise crypto.CryptoPluginNotFound()
|
||||
|
||||
for ext in self.extensions:
|
||||
if ext.obj.supports(type_needed, algorithm, bit_length, mode):
|
||||
plugin = ext.obj
|
||||
for generating_plugin in active_plugins:
|
||||
if generating_plugin.supports(
|
||||
type_needed, algorithm, bit_length, mode):
|
||||
break
|
||||
else:
|
||||
raise secret_store.SecretStorePluginNotFound()
|
||||
|
||||
return plugin
|
||||
return generating_plugin
|
||||
|
||||
def get_plugin_retrieve(self, plugin_name_for_store):
|
||||
"""Gets a secret retrieve plugin that supports the provided type.
|
||||
@ -88,12 +93,12 @@ class _CryptoPluginManager(named.NamedExtensionManager):
|
||||
type of plugin required
|
||||
:returns: CryptoPluginBase plugin implementation
|
||||
"""
|
||||
active_plugins = plugin_utils.get_active_plugins(self)
|
||||
|
||||
if len(self.extensions) < 1:
|
||||
if len(active_plugins) < 1:
|
||||
raise crypto.CryptoPluginNotFound()
|
||||
|
||||
for ext in self.extensions:
|
||||
decrypting_plugin = ext.obj
|
||||
for decrypting_plugin in active_plugins:
|
||||
plugin_name = utils.generate_fullname_for(decrypting_plugin)
|
||||
if plugin_name == plugin_name_for_store:
|
||||
break
|
||||
|
@ -32,6 +32,7 @@ import barbican.common.utils as utils
|
||||
from barbican import i18n as u
|
||||
from barbican.model import models
|
||||
from barbican.model import repositories as repos
|
||||
from barbican.plugin.util import utils as plugin_utils
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -501,17 +502,19 @@ class BarbicanMetaDTO(object):
|
||||
|
||||
|
||||
class CertificatePluginManager(named.NamedExtensionManager):
|
||||
def __init__(self, conf=CONF, invoke_on_load=True,
|
||||
invoke_args=(), invoke_kwargs={}):
|
||||
def __init__(self, conf=CONF, invoke_args=(), invoke_kwargs={}):
|
||||
self.ca_repo = repos.get_ca_repository()
|
||||
super(CertificatePluginManager, self).__init__(
|
||||
conf.certificate.namespace,
|
||||
conf.certificate.enabled_certificate_plugins,
|
||||
invoke_on_load=invoke_on_load,
|
||||
invoke_on_load=False, # Defer creating plugins to utility below.
|
||||
invoke_args=invoke_args,
|
||||
invoke_kwds=invoke_kwargs
|
||||
)
|
||||
|
||||
plugin_utils.instantiate_plugins(
|
||||
self, invoke_args, invoke_kwargs)
|
||||
|
||||
def get_plugin(self, certificate_spec):
|
||||
"""Gets a supporting certificate plugin.
|
||||
|
||||
@ -523,13 +526,13 @@ class CertificatePluginManager(named.NamedExtensionManager):
|
||||
REQUEST_TYPE,
|
||||
CertificateRequestType.CUSTOM_REQUEST)
|
||||
|
||||
for ext in self.extensions:
|
||||
supported_request_types = ext.obj.supported_request_types()
|
||||
for plugin in plugin_utils.get_active_plugins(self):
|
||||
supported_request_types = plugin.supported_request_types()
|
||||
if request_type not in supported_request_types:
|
||||
continue
|
||||
|
||||
if ext.obj.supports(certificate_spec):
|
||||
return ext.obj
|
||||
if plugin.supports(certificate_spec):
|
||||
return plugin
|
||||
|
||||
raise CertificatePluginNotFound()
|
||||
|
||||
@ -539,9 +542,9 @@ class CertificatePluginManager(named.NamedExtensionManager):
|
||||
:param plugin_name: Name of the plugin to invoke
|
||||
:returns: CertificatePluginBase plugin implementation
|
||||
"""
|
||||
for ext in self.extensions:
|
||||
if utils.generate_fullname_for(ext.obj) == plugin_name:
|
||||
return ext.obj
|
||||
for plugin in plugin_utils.get_active_plugins(self):
|
||||
if utils.generate_fullname_for(plugin) == plugin_name:
|
||||
return plugin
|
||||
raise CertificatePluginNotFound(plugin_name)
|
||||
|
||||
def get_plugin_by_ca_id(self, ca_id):
|
||||
@ -558,8 +561,8 @@ class CertificatePluginManager(named.NamedExtensionManager):
|
||||
|
||||
def refresh_ca_table(self):
|
||||
"""Refreshes the CertificateAuthority table."""
|
||||
for ext in self.extensions:
|
||||
plugin_name = utils.generate_fullname_for(ext.obj)
|
||||
for plugin in plugin_utils.get_active_plugins(self):
|
||||
plugin_name = utils.generate_fullname_for(plugin)
|
||||
cas, offset, limit, total = self.ca_repo.get_by_create_date(
|
||||
plugin_name=plugin_name,
|
||||
suppress_exception=True)
|
||||
@ -567,7 +570,7 @@ class CertificatePluginManager(named.NamedExtensionManager):
|
||||
# if no entries are found, then the plugin has not yet been
|
||||
# queried or that plugin's entries have expired.
|
||||
# Most of the time, this will be a no-op for plugins.
|
||||
self.update_ca_info(ext.obj)
|
||||
self.update_ca_info(plugin)
|
||||
|
||||
def update_ca_info(self, cert_plugin):
|
||||
"""Update the CA info for a particular plugin."""
|
||||
@ -624,24 +627,26 @@ class _CertificateEventPluginManager(named.NamedExtensionManager,
|
||||
new instance of this class use the EVENT_PLUGIN_MANAGER at the module
|
||||
level.
|
||||
"""
|
||||
def __init__(self, conf=CONF, invoke_on_load=True,
|
||||
invoke_args=(), invoke_kwargs={}):
|
||||
def __init__(self, conf=CONF, invoke_args=(), invoke_kwargs={}):
|
||||
super(_CertificateEventPluginManager, self).__init__(
|
||||
conf.certificate_event.namespace,
|
||||
conf.certificate_event.enabled_certificate_event_plugins,
|
||||
invoke_on_load=invoke_on_load,
|
||||
invoke_on_load=False, # Defer creating plugins to utility below.
|
||||
invoke_args=invoke_args,
|
||||
invoke_kwds=invoke_kwargs
|
||||
)
|
||||
|
||||
plugin_utils.instantiate_plugins(
|
||||
self, invoke_args, invoke_kwargs)
|
||||
|
||||
def get_plugin_by_name(self, plugin_name):
|
||||
"""Gets a supporting certificate event plugin.
|
||||
|
||||
:returns: CertificateEventPluginBase plugin implementation
|
||||
"""
|
||||
for ext in self.extensions:
|
||||
if utils.generate_fullname_for(ext.obj) == plugin_name:
|
||||
return ext.obj
|
||||
for plugin in plugin_utils.get_active_plugins(self):
|
||||
if utils.generate_fullname_for(plugin) == plugin_name:
|
||||
return plugin
|
||||
raise CertificateEventPluginNotFound(plugin_name)
|
||||
|
||||
def notify_certificate_is_ready(
|
||||
@ -658,11 +663,13 @@ class _CertificateEventPluginManager(named.NamedExtensionManager,
|
||||
|
||||
def _invoke_certificate_plugins(self, method, *args, **kwargs):
|
||||
"""Invoke same function on plugins as calling function."""
|
||||
if len(self.extensions) < 1:
|
||||
active_plugins = plugin_utils.get_active_plugins(self)
|
||||
|
||||
if len(active_plugins) < 1:
|
||||
raise CertificateEventPluginNotFound()
|
||||
|
||||
for ext in self.extensions:
|
||||
getattr(ext.obj, method)(*args, **kwargs)
|
||||
for plugin in active_plugins:
|
||||
getattr(plugin, method)(*args, **kwargs)
|
||||
|
||||
|
||||
EVENT_PLUGIN_MANAGER = _CertificateEventPluginManager()
|
||||
|
@ -22,6 +22,8 @@ from stevedore import named
|
||||
from barbican.common import exception
|
||||
from barbican.common import utils
|
||||
from barbican import i18n as u
|
||||
from barbican.plugin.util import utils as plugin_utils
|
||||
|
||||
|
||||
_SECRET_STORE = None
|
||||
|
||||
@ -480,16 +482,18 @@ def _enforce_extensions_configured(plugin_related_function):
|
||||
|
||||
|
||||
class SecretStorePluginManager(named.NamedExtensionManager):
|
||||
def __init__(self, conf=CONF, invoke_on_load=True,
|
||||
invoke_args=(), invoke_kwargs={}):
|
||||
def __init__(self, conf=CONF, invoke_args=(), invoke_kwargs={}):
|
||||
super(SecretStorePluginManager, self).__init__(
|
||||
conf.secretstore.namespace,
|
||||
conf.secretstore.enabled_secretstore_plugins,
|
||||
invoke_on_load=invoke_on_load,
|
||||
invoke_on_load=False, # Defer creating plugins to utility below.
|
||||
invoke_args=invoke_args,
|
||||
invoke_kwds=invoke_kwargs
|
||||
)
|
||||
|
||||
plugin_utils.instantiate_plugins(
|
||||
self, invoke_args, invoke_kwargs)
|
||||
|
||||
@_enforce_extensions_configured
|
||||
def get_plugin_store(self, key_spec, plugin_name=None,
|
||||
transport_key_needed=False):
|
||||
@ -501,23 +505,24 @@ class SecretStorePluginManager(named.NamedExtensionManager):
|
||||
key is required.
|
||||
:returns: SecretStoreBase plugin implementation
|
||||
"""
|
||||
active_plugins = plugin_utils.get_active_plugins(self)
|
||||
|
||||
if plugin_name is not None:
|
||||
for ext in self.extensions:
|
||||
if utils.generate_fullname_for(ext.obj) == plugin_name:
|
||||
return ext.obj
|
||||
for plugin in active_plugins:
|
||||
if utils.generate_fullname_for(plugin) == plugin_name:
|
||||
return plugin
|
||||
raise SecretStorePluginNotFound(plugin_name)
|
||||
|
||||
if not transport_key_needed:
|
||||
for ext in self.extensions:
|
||||
if ext.obj.store_secret_supports(key_spec):
|
||||
return ext.obj
|
||||
for plugin in active_plugins:
|
||||
if plugin.store_secret_supports(key_spec):
|
||||
return plugin
|
||||
|
||||
else:
|
||||
for ext in self.extensions:
|
||||
if (ext.obj.get_transport_key() is not None and
|
||||
ext.obj.store_secret_supports(key_spec)):
|
||||
return ext.obj
|
||||
for plugin in active_plugins:
|
||||
if (plugin.get_transport_key() is not None and
|
||||
plugin.store_secret_supports(key_spec)):
|
||||
return plugin
|
||||
|
||||
raise SecretStoreSupportedPluginNotFound()
|
||||
|
||||
@ -537,9 +542,9 @@ class SecretStorePluginManager(named.NamedExtensionManager):
|
||||
configured on the database side.
|
||||
"""
|
||||
|
||||
for ext in self.extensions:
|
||||
if utils.generate_fullname_for(ext.obj) == plugin_name:
|
||||
return ext.obj
|
||||
for plugin in plugin_utils.get_active_plugins(self):
|
||||
if utils.generate_fullname_for(plugin) == plugin_name:
|
||||
return plugin
|
||||
raise StorePluginNotAvailableOrMisconfigured(plugin_name)
|
||||
|
||||
@_enforce_extensions_configured
|
||||
@ -551,9 +556,9 @@ class SecretStorePluginManager(named.NamedExtensionManager):
|
||||
:returns: SecretStoreBase plugin implementation
|
||||
"""
|
||||
|
||||
for ext in self.extensions:
|
||||
if ext.obj.generate_supports(key_spec):
|
||||
return ext.obj
|
||||
for plugin in plugin_utils.get_active_plugins(self):
|
||||
if plugin.generate_supports(key_spec):
|
||||
return plugin
|
||||
raise SecretStoreSupportedPluginNotFound()
|
||||
|
||||
|
||||
|
54
barbican/plugin/util/utils.py
Normal file
54
barbican/plugin/util/utils.py
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright (c) 2015 Rackspace, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Utilities to support plugins and plugin managers.
|
||||
"""
|
||||
from barbican.common import utils
|
||||
from barbican import i18n as u
|
||||
|
||||
LOG = utils.getLogger(__name__)
|
||||
|
||||
|
||||
def instantiate_plugins(extension_manager, invoke_args=(), invoke_kwargs={}):
|
||||
"""Attempt to create each plugin managed by a stevedore manager.
|
||||
|
||||
While we could have let the stevedore 'extension_manager' create our
|
||||
plugins by passing 'invoke_on_load=True' to its initializer, its logic
|
||||
handles and suppresses any root cause exceptions emanating from the
|
||||
plugins' initializers. This function allows those exceptions to be exposed.
|
||||
|
||||
:param extension_manager: A :class:`NamedExtensionManager` instance that
|
||||
has already processed the configured plugins, but has not yet created
|
||||
instances of these plugins.
|
||||
:param invoke_args: Arguments to pass to the new plugin instance.
|
||||
:param invoke_kwargs: Keyword arguments to pass to the new plugin instance.
|
||||
"""
|
||||
for ext in extension_manager.extensions:
|
||||
if not ext.obj:
|
||||
try:
|
||||
plugin_instance = ext.plugin(*invoke_args, **invoke_kwargs)
|
||||
except Exception:
|
||||
LOG.logger.disabled = False # Ensure not suppressing logs.
|
||||
LOG.exception(
|
||||
u._LE("Problem seen creating plugin: '%s'"),
|
||||
ext.name
|
||||
)
|
||||
else:
|
||||
ext.obj = plugin_instance
|
||||
|
||||
|
||||
def get_active_plugins(extension_manager):
|
||||
return [ext.obj for ext in extension_manager.extensions if ext.obj]
|
@ -15,6 +15,7 @@
|
||||
|
||||
import mock
|
||||
|
||||
from barbican.common import utils as common_utils
|
||||
from barbican.plugin.interface import secret_store as str
|
||||
from barbican.tests import utils
|
||||
|
||||
@ -92,7 +93,7 @@ class WhenTestingSecretStorePluginManager(utils.BaseTestCase):
|
||||
super(WhenTestingSecretStorePluginManager, self).setUp()
|
||||
self.manager = str.SecretStorePluginManager()
|
||||
|
||||
def test_get_store_supported_plugin(self):
|
||||
def test_get_store_supported_plugin_no_plugin_name(self):
|
||||
plugin = TestSecretStore([str.KeyAlgorithm.AES])
|
||||
plugin_mock = mock.MagicMock(obj=plugin)
|
||||
self.manager.extensions = [plugin_mock]
|
||||
@ -101,6 +102,15 @@ class WhenTestingSecretStorePluginManager(utils.BaseTestCase):
|
||||
self.assertEqual(plugin,
|
||||
self.manager.get_plugin_store(keySpec))
|
||||
|
||||
def test_get_store_supported_plugin_with_plugin_name(self):
|
||||
plugin = TestSecretStore([str.KeyAlgorithm.AES])
|
||||
plugin_mock = mock.MagicMock(obj=plugin)
|
||||
self.manager.extensions = [plugin_mock]
|
||||
|
||||
plugin_found = self.manager.get_plugin_store(
|
||||
None, plugin_name=common_utils.generate_fullname_for(plugin))
|
||||
self.assertEqual(plugin, plugin_found)
|
||||
|
||||
def test_get_generate_supported_plugin(self):
|
||||
plugin = TestSecretStore([str.KeyAlgorithm.AES])
|
||||
plugin_mock = mock.MagicMock(obj=plugin)
|
||||
|
70
barbican/tests/plugin/util/test_utils.py
Normal file
70
barbican/tests/plugin/util/test_utils.py
Normal file
@ -0,0 +1,70 @@
|
||||
# Copyright (c) 2015 Rackspace, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from barbican.plugin.util import utils as plugin_utils
|
||||
from barbican.tests import utils as test_utils
|
||||
|
||||
|
||||
class ExtensionStub(object):
|
||||
|
||||
def __init__(self):
|
||||
self.name = 'my_name'
|
||||
self.plugin_instance = 'my_instance'
|
||||
self.obj = None
|
||||
self.exc = None
|
||||
self.args = None
|
||||
self.kwargs = None
|
||||
|
||||
def plugin(self, *args, **kwargs):
|
||||
if self.exc:
|
||||
raise self.exc
|
||||
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
return self.plugin_instance
|
||||
|
||||
def set_raise_exception(self, exc):
|
||||
self.exc = exc
|
||||
|
||||
|
||||
class ManagerStub(object):
|
||||
def __init__(self, extensions):
|
||||
self.extensions = extensions
|
||||
|
||||
|
||||
class WhenInvokingInstantiatePlugins(test_utils.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(WhenInvokingInstantiatePlugins, self).setUp()
|
||||
|
||||
self.extension = ExtensionStub()
|
||||
self.manager = ManagerStub([self.extension])
|
||||
|
||||
def test_creates_plugin_instance(self):
|
||||
args = ('foo', 'bar')
|
||||
kwargs = {'foo': 1}
|
||||
|
||||
plugin_utils.instantiate_plugins(
|
||||
self.manager, invoke_args=args, invoke_kwargs=kwargs)
|
||||
|
||||
self.assertEqual('my_instance', self.extension.obj)
|
||||
self.assertEqual(args, self.extension.args)
|
||||
self.assertEqual(kwargs, self.extension.kwargs)
|
||||
|
||||
def test_does_not_create_plugin_instance_due_to_error(self):
|
||||
self.extension.set_raise_exception(ValueError())
|
||||
|
||||
plugin_utils.instantiate_plugins(self.manager)
|
||||
|
||||
self.assertIsNone(self.extension.obj)
|
Loading…
Reference in New Issue
Block a user