diff --git a/barbican/plugin/crypto/manager.py b/barbican/plugin/crypto/manager.py index bf25b3816..01dc2d0e5 100644 --- a/barbican/plugin/crypto/manager.py +++ b/barbican/plugin/crypto/manager.py @@ -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 diff --git a/barbican/plugin/interface/certificate_manager.py b/barbican/plugin/interface/certificate_manager.py index 5b3c2ec00..6d6983cba 100644 --- a/barbican/plugin/interface/certificate_manager.py +++ b/barbican/plugin/interface/certificate_manager.py @@ -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() diff --git a/barbican/plugin/interface/secret_store.py b/barbican/plugin/interface/secret_store.py index 6febe9141..348343a89 100644 --- a/barbican/plugin/interface/secret_store.py +++ b/barbican/plugin/interface/secret_store.py @@ -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() diff --git a/barbican/plugin/util/utils.py b/barbican/plugin/util/utils.py new file mode 100644 index 000000000..a6b1f5659 --- /dev/null +++ b/barbican/plugin/util/utils.py @@ -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] diff --git a/barbican/tests/plugin/interface/test_secret_store.py b/barbican/tests/plugin/interface/test_secret_store.py index d89532bc1..9cecbffc8 100644 --- a/barbican/tests/plugin/interface/test_secret_store.py +++ b/barbican/tests/plugin/interface/test_secret_store.py @@ -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) diff --git a/barbican/tests/plugin/util/test_utils.py b/barbican/tests/plugin/util/test_utils.py new file mode 100644 index 000000000..df2a65da4 --- /dev/null +++ b/barbican/tests/plugin/util/test_utils.py @@ -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)