From ba68a74e65ad893e4beeb45b984b2514e60cbdea Mon Sep 17 00:00:00 2001 From: Mehdi Abaakouk Date: Tue, 4 Aug 2015 10:32:47 +0200 Subject: [PATCH] Allow to use oslo.config without global CONF If an application doesn't use a global configuration object and load the middleware with api paste, they are no ways to read the configuration options from the application configuration file. This change fixes that, the api paste config will looks like: [filter:authtoken] paste.filter_factory = keystonemiddleware.auth_token:filter_factory oslo_config_project = aodh With this, the keystonemiddleware will automatically load the configuration of the project aodh with a local oslo.config object instead of the global one. This allows application to not rely of the global oslo.config object and continue to use paste and keystonemiddleware. Closes-bug: #1482078 Related-bug: #1406218 Change-Id: I48c3d6a6a5486c9c035a15a75c025be7f5abaab4 --- doc/source/middlewarearchitecture.rst | 14 +++++++++ keystonemiddleware/auth_token/__init__.py | 29 ++++++++++++++++- .../auth_token/test_auth_token_middleware.py | 31 +++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/doc/source/middlewarearchitecture.rst b/doc/source/middlewarearchitecture.rst index ce760ef..8d84d08 100644 --- a/doc/source/middlewarearchitecture.rst +++ b/doc/source/middlewarearchitecture.rst @@ -271,6 +271,20 @@ and set in ``nova.conf``: Note that middleware parameters in paste config take priority, they must be removed to use values in [keystone_authtoken] section. +If the service doesn't use the global oslo.config object (CONF), then the +olso config project name can be set it in paste config and +keystonemiddleware will load the project configuration itself. +Optionally the location of the configuration file can be set if oslo.config +is not able to discover it. + +.. code-block:: ini + + [filter:authtoken] + paste.filter_factory = keystonemiddleware.auth_token:filter_factory + oslo_config_project = nova + # oslo_config_file = /not_discoverable_location/nova.conf + + Configuration Options --------------------- diff --git a/keystonemiddleware/auth_token/__init__.py b/keystonemiddleware/auth_token/__init__.py index 930e847..942567c 100644 --- a/keystonemiddleware/auth_token/__init__.py +++ b/keystonemiddleware/auth_token/__init__.py @@ -622,6 +622,27 @@ class AuthProtocol(_BaseAuthProtocol): # conf value into correct type. self._conf = _conf_values_type_convert(conf) + # NOTE(sileht): If we don't want to use oslo.config global object + # we can set the paste "oslo_config_project" and the middleware + # will load the configuration with a local oslo.config object. + self._local_oslo_config = None + if 'oslo_config_project' in conf: + if 'oslo_config_file' in conf: + default_config_files = [conf['oslo_config_file']] + else: + default_config_files = None + + self._local_oslo_config = cfg.ConfigOpts() + self._local_oslo_config( + {}, project=conf['oslo_config_project'], + default_config_files=default_config_files, + validate_default_values=True) + + self._local_oslo_config.register_opts( + _OPTS, group=_base.AUTHTOKEN_GROUP) + auth.register_conf_options(self._local_oslo_config, + group=_base.AUTHTOKEN_GROUP) + super(AuthProtocol, self).__init__( app, log=log, @@ -668,6 +689,8 @@ class AuthProtocol(_BaseAuthProtocol): # try config from paste-deploy first if name in self._conf: return self._conf[name] + elif self._local_oslo_config: + return self._local_oslo_config[group][name] else: return CONF[group][name] @@ -937,7 +960,8 @@ class AuthProtocol(_BaseAuthProtocol): plugin_kwargs['log'] = self.log plugin_opts = plugin_class.get_options() - CONF.register_opts(plugin_opts, group=group) + (self._local_oslo_config or CONF).register_opts(plugin_opts, + group=group) for opt in plugin_opts: val = self._conf_get(opt.dest, group=group) @@ -960,6 +984,9 @@ class AuthProtocol(_BaseAuthProtocol): try: return self._conf_get('project') except cfg.NoSuchOptError: + # Prefer local oslo config object + if self._local_oslo_config: + return self._local_oslo_config.project try: # CONF.project will exist only if the service uses # oslo.config. It will only be set when the project diff --git a/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py b/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py index 1de09b7..d78dd85 100644 --- a/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py +++ b/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py @@ -32,6 +32,7 @@ import mock from oslo_config import cfg from oslo_serialization import jsonutils from oslo_utils import timeutils +from oslotest import createfile import six import testresources import testtools @@ -2470,5 +2471,35 @@ class TestAuthPluginUserAgentGeneration(BaseAuthTokenMiddlewareTest): self.assertEqual(expected_ua, sess.user_agent) +class TestAuthPluginLocalOsloConfig(BaseAuthTokenMiddlewareTest): + def test_project_in_local_oslo_configuration(self): + options = { + 'auth_plugin': 'password', + 'auth_uri': uuid.uuid4().hex, + 'password': uuid.uuid4().hex, + } + + content = ("[keystone_authtoken]\n" + "auth_plugin=%(auth_plugin)s\n" + "auth_uri=%(auth_uri)s\n" + "password=%(password)s\n" % options) + conf_file_fixture = self.useFixture( + createfile.CreateFileWithContent("my_app", content)) + conf = {'oslo_config_project': 'my_app', + 'oslo_config_file': conf_file_fixture.path} + app = self._create_app(conf, uuid.uuid4().hex) + for option in options: + self.assertEqual(options[option], app._conf_get(option)) + + def _create_app(self, conf, project_version): + fake_pkg_resources = mock.Mock() + fake_pkg_resources.get_distribution().version = project_version + + body = uuid.uuid4().hex + with mock.patch('keystonemiddleware.auth_token.pkg_resources', + new=fake_pkg_resources): + return self.create_simple_middleware(body=body, conf=conf) + + def load_tests(loader, tests, pattern): return testresources.OptimisingTestSuite(tests)