diff --git a/keystone/auth/controllers.py b/keystone/auth/controllers.py index 2b3005e256..9a31da23d2 100644 --- a/keystone/auth/controllers.py +++ b/keystone/auth/controllers.py @@ -17,9 +17,11 @@ import sys from keystoneclient.common import cms from oslo_config import cfg from oslo_log import log +from oslo_log import versionutils from oslo_serialization import jsonutils from oslo_utils import importutils import six +import stevedore from keystone.common import controller from keystone.common import dependency @@ -43,7 +45,23 @@ AUTH_PLUGINS_LOADED = False def load_auth_method(method): plugin_name = CONF.auth[method] - return importutils.import_object(plugin_name) + try: + namespace = 'keystone.auth.%s' % method + driver_manager = stevedore.DriverManager(namespace, plugin_name, + invoke_on_load=True) + return driver_manager.driver + except RuntimeError: + LOG.debug('Failed to load the %s driver (%s) using stevedore, will ' + 'attempt to load using import_object instead.', + method, plugin_name) + + @versionutils.deprecated(as_of=versionutils.deprecated.LIBERTY, + in_favor_of='entrypoints', + what='direct import of driver') + def _load_using_import(plugin_name): + return importutils.import_object(plugin_name) + + return _load_using_import(plugin_name) def load_auth_methods(): diff --git a/keystone/tests/unit/auth/test_controllers.py b/keystone/tests/unit/auth/test_controllers.py index f63e845c57..76f2776a56 100644 --- a/keystone/tests/unit/auth/test_controllers.py +++ b/keystone/tests/unit/auth/test_controllers.py @@ -19,13 +19,15 @@ from oslo_config import cfg from oslo_config import fixture as config_fixture from oslo_utils import importutils from oslotest import mockpatch +import stevedore +from stevedore import extension from keystone.auth import controllers from keystone.tests import unit class TestLoadAuthMethod(unit.BaseTestCase): - def test_import_works(self): + def test_entrypoint_works(self): method = uuid.uuid4().hex plugin_name = self.getUniqueString() @@ -34,13 +36,46 @@ class TestLoadAuthMethod(unit.BaseTestCase): cf.register_opt(cfg.StrOpt(method), group='auth') cf.config(group='auth', **{method: plugin_name}) + # Setup stevedore.DriverManager to return a driver for the plugin + extension_ = extension.Extension( + plugin_name, entry_point=mock.sentinel.entry_point, + plugin=mock.sentinel.plugin, + obj=mock.sentinel.driver) + auth_plugin_namespace = 'keystone.auth.%s' % method + fake_driver_manager = stevedore.DriverManager.make_test_instance( + extension_, namespace=auth_plugin_namespace) + + driver_manager_mock = self.useFixture(mockpatch.PatchObject( + stevedore, 'DriverManager', return_value=fake_driver_manager)).mock + + driver = controllers.load_auth_method(method) + + self.assertEqual(auth_plugin_namespace, fake_driver_manager.namespace) + driver_manager_mock.assert_called_once_with( + auth_plugin_namespace, plugin_name, invoke_on_load=True) + self.assertIs(driver, mock.sentinel.driver) + + def test_entrypoint_fails_import_works(self): + method = uuid.uuid4().hex + plugin_name = self.getUniqueString() + + # Register the method using the given plugin + cf = self.useFixture(config_fixture.Config()) + cf.register_opt(cfg.StrOpt(method), group='auth') + cf.config(group='auth', **{method: plugin_name}) + + # stevedore.DriverManager raises RuntimeError if it can't load the + # driver. + self.useFixture(mockpatch.PatchObject( + stevedore, 'DriverManager', side_effect=RuntimeError)) + self.useFixture(mockpatch.PatchObject( importutils, 'import_object', return_value=mock.sentinel.driver)) driver = controllers.load_auth_method(method) self.assertIs(driver, mock.sentinel.driver) - def test_import_fails(self): + def test_entrypoint_fails_import_fails(self): method = uuid.uuid4().hex plugin_name = self.getUniqueString() @@ -49,6 +84,11 @@ class TestLoadAuthMethod(unit.BaseTestCase): cf.register_opt(cfg.StrOpt(method), group='auth') cf.config(group='auth', **{method: plugin_name}) + # stevedore.DriverManager raises RuntimeError if it can't load the + # driver. + self.useFixture(mockpatch.PatchObject( + stevedore, 'DriverManager', side_effect=RuntimeError)) + class TestException(Exception): pass diff --git a/setup.cfg b/setup.cfg index 672a4d176a..2634faa91f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -67,6 +67,32 @@ keystone.assignment = ldap = keystone.assignment.backends.ldap:Assignment sql = keystone.assignment.backends.sql:Assignment +keystone.auth.external = + keystone.auth.plugins.external.DefaultDomain = keystone.auth.plugins.external:DefaultDomain + keystone.auth.plugins.external.Domain = keystone.auth.plugins.external:Domain + +keystone.auth.kerberos = + keystone.auth.plugins.external.KerberosDomain = keystone.auth.plugins.external:KerberosDomain + +keystone.auth.oauth1 = + keystone.auth.plugins.oauth1.OAuth = keystone.auth.plugins.oauth1:OAuth + +keystone.auth.openid = + keystone.auth.plugins.mapped.Mapped = keystone.auth.plugins.mapped:Mapped + +keystone.auth.password = + keystone.auth.plugins.password.Password = keystone.auth.plugins.password:Password + +keystone.auth.saml2 = + keystone.auth.plugins.mapped.Mapped = keystone.auth.plugins.mapped:Mapped + keystone.auth.plugins.saml2.Saml2 = keystone.auth.plugins.saml2:Saml2 + +keystone.auth.token = + keystone.auth.plugins.token.Token = keystone.auth.plugins.token:Token + +keystone.auth.x509 = + keystone.auth.plugins.mapped.Mapped = keystone.auth.plugins.mapped:Mapped + keystone.catalog = kvs = keystone.catalog.backends.kvs:Catalog sql = keystone.catalog.backends.sql:Catalog