From 5e74fed6b5619ab83f955faa87517ce5c76c8f0c Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Wed, 5 Aug 2015 13:42:12 +1000 Subject: [PATCH] Remove oslo_config from auth plugin loading There is still the possibility of a dependency in session, however this is the main usage. Defines a new option type schema. Change-Id: Ie508dff449a92c9ea01c46285c10888135e93a97 --- keystoneauth1/loading/__init__.py | 4 + .../loading/_plugins/identity/base.py | 4 +- .../loading/_plugins/identity/generic.py | 43 +++++---- keystoneauth1/loading/_plugins/identity/v2.py | 19 ++-- keystoneauth1/loading/_plugins/identity/v3.py | 51 ++++++----- .../loading/_plugins/token_endpoint.py | 12 ++- keystoneauth1/loading/base.py | 29 ++----- keystoneauth1/loading/conf.py | 11 ++- keystoneauth1/loading/opts.py | 87 +++++++++++++++++++ keystoneauth1/tests/unit/auth/utils.py | 15 ++-- keystoneauth1/tests/unit/loading/test_cli.py | 5 +- keystoneauth1/tests/unit/loading/test_conf.py | 5 +- requirements.txt | 1 - test-requirements.txt | 1 + 14 files changed, 177 insertions(+), 110 deletions(-) create mode 100644 keystoneauth1/loading/opts.py diff --git a/keystoneauth1/loading/__init__.py b/keystoneauth1/loading/__init__.py index 8ae25684..fbf17ffb 100644 --- a/keystoneauth1/loading/__init__.py +++ b/keystoneauth1/loading/__init__.py @@ -13,6 +13,7 @@ from keystoneauth1.loading.base import * # noqa from keystoneauth1.loading.cli import * # noqa from keystoneauth1.loading.conf import * # noqa +from keystoneauth1.loading.opts import * # noqa __all__ = [ @@ -32,4 +33,7 @@ __all__ = [ 'get_plugin_options', 'load_from_conf_options', 'register_conf_options', + + # loading.opts + 'Opt', ] diff --git a/keystoneauth1/loading/_plugins/identity/base.py b/keystoneauth1/loading/_plugins/identity/base.py index 9b9e5318..cdf548cf 100644 --- a/keystoneauth1/loading/_plugins/identity/base.py +++ b/keystoneauth1/loading/_plugins/identity/base.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_config import cfg - from keystoneauth1 import loading @@ -21,7 +19,7 @@ class BaseIdentityLoader(loading.BaseLoader): options = super(BaseIdentityLoader, self).get_options() options.extend([ - cfg.StrOpt('auth-url', help='Authentication URL'), + loading.Opt('auth-url', help='Authentication URL'), ]) return options diff --git a/keystoneauth1/loading/_plugins/identity/generic.py b/keystoneauth1/loading/_plugins/identity/generic.py index 049248fd..abe3f36d 100644 --- a/keystoneauth1/loading/_plugins/identity/generic.py +++ b/keystoneauth1/loading/_plugins/identity/generic.py @@ -10,9 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_config import cfg - from keystoneauth1 import identity +from keystoneauth1 import loading from keystoneauth1.loading._plugins.identity import base @@ -22,17 +21,17 @@ class GenericBaseLoader(base.BaseIdentityLoader): options = super(GenericBaseLoader, self).get_options() options.extend([ - cfg.StrOpt('domain-id', help='Domain ID to scope to'), - cfg.StrOpt('domain-name', help='Domain name to scope to'), - cfg.StrOpt('tenant-id', help='Tenant ID to scope to'), - cfg.StrOpt('tenant-name', help='Tenant name to scope to'), - cfg.StrOpt('project-id', help='Project ID to scope to'), - cfg.StrOpt('project-name', help='Project name to scope to'), - cfg.StrOpt('project-domain-id', - help='Domain ID containing project'), - cfg.StrOpt('project-domain-name', - help='Domain name containing project'), - cfg.StrOpt('trust-id', help='Trust ID'), + loading.Opt('domain-id', help='Domain ID to scope to'), + loading.Opt('domain-name', help='Domain name to scope to'), + loading.Opt('tenant-id', help='Tenant ID to scope to'), + loading.Opt('tenant-name', help='Tenant name to scope to'), + loading.Opt('project-id', help='Project ID to scope to'), + loading.Opt('project-name', help='Project name to scope to'), + loading.Opt('project-domain-id', + help='Domain ID containing project'), + loading.Opt('project-domain-name', + help='Domain name containing project'), + loading.Opt('trust-id', help='Trust ID'), ]) return options @@ -48,7 +47,7 @@ class Token(GenericBaseLoader): options = super(Token, self).get_options() options.extend([ - cfg.StrOpt('token', help='Token to authenticate with'), + loading.Opt('token', help='Token to authenticate with'), ]) return options @@ -63,13 +62,13 @@ class Password(GenericBaseLoader): def get_options(cls): options = super(Password, cls).get_options() options.extend([ - cfg.StrOpt('user-id', help='User id'), - cfg.StrOpt('user-name', - dest='username', - help='Username', - deprecated_name='username'), - cfg.StrOpt('user-domain-id', help="User's domain id"), - cfg.StrOpt('user-domain-name', help="User's domain name"), - cfg.StrOpt('password', help="User's password"), + loading.Opt('user-id', help='User id'), + loading.Opt('user-name', + dest='username', + help='Username', + deprecated=[loading.Opt('username')]), + loading.Opt('user-domain-id', help="User's domain id"), + loading.Opt('user-domain-name', help="User's domain name"), + loading.Opt('password', help="User's password"), ]) return options diff --git a/keystoneauth1/loading/_plugins/identity/v2.py b/keystoneauth1/loading/_plugins/identity/v2.py index da7f77bc..76d127c7 100644 --- a/keystoneauth1/loading/_plugins/identity/v2.py +++ b/keystoneauth1/loading/_plugins/identity/v2.py @@ -10,9 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_config import cfg - from keystoneauth1 import identity +from keystoneauth1 import loading from keystoneauth1.loading._plugins.identity import base @@ -22,9 +21,9 @@ class BaseV2Loader(base.BaseIdentityLoader): options = super(BaseV2Loader, self).get_options() options.extend([ - cfg.StrOpt('tenant-id', help='Tenant ID'), - cfg.StrOpt('tenant-name', help='Tenant Name'), - cfg.StrOpt('trust-id', help='Trust ID'), + loading.Opt('tenant-id', help='Tenant ID'), + loading.Opt('tenant-name', help='Tenant Name'), + loading.Opt('trust-id', help='Trust ID'), ]) return options @@ -40,7 +39,7 @@ class V2Token(BaseV2Loader): options = super(V2Token, self).get_options() options.extend([ - cfg.StrOpt('token', secret=True, help='Token'), + loading.Opt('token', secret=True, help='Token'), ]) return options @@ -56,12 +55,12 @@ class V2Password(BaseV2Loader): options = super(V2Password, self).get_options() options.extend([ - cfg.StrOpt('user-name', + loading.Opt('user-name', dest='username', - deprecated_name='username', + deprecated=[loading.Opt('username')], help='Username to login with'), - cfg.StrOpt('user-id', help='User ID to longin with'), - cfg.StrOpt('password', secret=True, help='Password to use'), + loading.Opt('user-id', help='User ID to longin with'), + loading.Opt('password', secret=True, help='Password to use'), ]) return options diff --git a/keystoneauth1/loading/_plugins/identity/v3.py b/keystoneauth1/loading/_plugins/identity/v3.py index a311665a..e5a8ae7c 100644 --- a/keystoneauth1/loading/_plugins/identity/v3.py +++ b/keystoneauth1/loading/_plugins/identity/v3.py @@ -10,9 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_config import cfg - from keystoneauth1 import identity +from keystoneauth1 import loading from keystoneauth1.loading._plugins.identity import base @@ -22,15 +21,15 @@ class BaseV3Loader(base.BaseIdentityLoader): options = super(BaseV3Loader, self).get_options() options.extend([ - cfg.StrOpt('domain-id', help='Domain ID to scope to'), - cfg.StrOpt('domain-name', help='Domain name to scope to'), - cfg.StrOpt('project-id', help='Project ID to scope to'), - cfg.StrOpt('project-name', help='Project name to scope to'), - cfg.StrOpt('project-domain-id', - help='Domain ID containing project'), - cfg.StrOpt('project-domain-name', - help='Domain name containing project'), - cfg.StrOpt('trust-id', help='Trust ID'), + loading.Opt('domain-id', help='Domain ID to scope to'), + loading.Opt('domain-name', help='Domain name to scope to'), + loading.Opt('project-id', help='Project ID to scope to'), + loading.Opt('project-name', help='Project name to scope to'), + loading.Opt('project-domain-id', + help='Domain ID containing project'), + loading.Opt('project-domain-name', + help='Domain name containing project'), + loading.Opt('trust-id', help='Trust ID'), ]) return options @@ -46,14 +45,14 @@ class Password(BaseV3Loader): options = super(Password, self).get_options() options.extend([ - cfg.StrOpt('user-id', help='User ID'), - cfg.StrOpt('user-name', - dest='username', - help='Username', - deprecated_name='username'), - cfg.StrOpt('user-domain-id', help="User's domain id"), - cfg.StrOpt('user-domain-name', help="User's domain name"), - cfg.StrOpt('password', secret=True, help="User's password"), + loading.Opt('user-id', help='User ID'), + loading.Opt('user-name', + dest='username', + help='Username', + deprecated=[loading.Opt('username')]), + loading.Opt('user-domain-id', help="User's domain id"), + loading.Opt('user-domain-name', help="User's domain name"), + loading.Opt('password', secret=True, help="User's password"), ]) return options @@ -69,9 +68,9 @@ class Token(BaseV3Loader): options = super(Token, self).get_options() options.extend([ - cfg.StrOpt('token', - secret=True, - help='Token to authenticate with'), + loading.Opt('token', + secret=True, + help='Token to authenticate with'), ]) return options @@ -83,10 +82,10 @@ class FederatedBase(BaseV3Loader): options = super(FederatedBase, self).get_options() options.extend([ - cfg.StrOpt('identity-provider', - help="Identity Provider's name"), - cfg.StrOpt('protocol', - help='Protocol for federated plugin'), + loading.Opt('identity-provider', + help="Identity Provider's name"), + loading.Opt('protocol', + help='Protocol for federated plugin'), ]) return options diff --git a/keystoneauth1/loading/_plugins/token_endpoint.py b/keystoneauth1/loading/_plugins/token_endpoint.py index 361408ca..9d7185a6 100644 --- a/keystoneauth1/loading/_plugins/token_endpoint.py +++ b/keystoneauth1/loading/_plugins/token_endpoint.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_config import cfg - from keystoneauth1 import loading from keystoneauth1 import token_endpoint @@ -26,11 +24,11 @@ class TokenEndpoint(loading.BaseLoader): options = super(TokenEndpoint, self).get_options() options.extend([ - cfg.StrOpt('endpoint', - help='The endpoint that will always be used'), - cfg.StrOpt('token', - secret=True, - help='The token that will always be used'), + loading.Opt('endpoint', + help='The endpoint that will always be used'), + loading.Opt('token', + secret=True, + help='The token that will always be used'), ]) return options diff --git a/keystoneauth1/loading/base.py b/keystoneauth1/loading/base.py index 588fa5cf..802f70a9 100644 --- a/keystoneauth1/loading/base.py +++ b/keystoneauth1/loading/base.py @@ -11,7 +11,6 @@ # under the License. import abc -import os import six import stevedore @@ -104,27 +103,9 @@ class BaseLoader(object): :param parser: the parser to attach argparse options. :type parser: argparse.ArgumentParser """ - - # NOTE(jamielennox): ideally oslo_config would be smart enough to - # handle all the Opt manipulation that goes on in this file. However it - # is currently not. Options are handled in as similar a way as - # possible to oslo_config such that when available we should be able to - # transition. - for opt in self.get_options(): - args = [] - envs = [] - - for o in [opt] + opt.deprecated_opts: - args.append('--os-%s' % o.name) - envs.append('OS_%s' % o.name.replace('-', '_').upper()) - - # select the first ENV that is not false-y or return None - env_vars = (os.environ.get(e) for e in envs) - default = six.next(six.moves.filter(None, env_vars), None) - - parser.add_argument(*args, - default=default or opt.default, + parser.add_argument(*opt.argparse_args, + default=opt.argparse_default, metavar=opt.metavar, help=opt.help, dest='os_%s' % opt.dest) @@ -153,7 +134,7 @@ class BaseLoader(object): :type conf: oslo_config.cfg.ConfigOpts :param string group: The group name that options should be read from. """ - plugin_opts = self.get_options() + plugin_opts = [o._to_oslo_opt() for o in self.get_options()] conf.register_opts(plugin_opts, group=group) def load_from_conf_options(self, conf, group, **kwargs): @@ -181,8 +162,8 @@ class BaseLoader(object): specify a custom loader function that will be queried for the option value. - The getter is a function that takes one value, an - :py:class:`oslo_config.cfg.Opt` and returns a value to load with. + The getter is a function that takes one value, a + :py:class:`keystoneauth1.loading.Opt` and returns a value to load with. :param getter: A function that returns a value for the given opt. :type getter: callable diff --git a/keystoneauth1/loading/conf.py b/keystoneauth1/loading/conf.py index 4f316b43..d47075e3 100644 --- a/keystoneauth1/loading/conf.py +++ b/keystoneauth1/loading/conf.py @@ -10,14 +10,13 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_config import cfg - from keystoneauth1.loading import base +from keystoneauth1.loading import opts -_AUTH_PLUGIN_OPT = cfg.StrOpt('auth_plugin', help='Name of the plugin to load') +_AUTH_PLUGIN_OPT = opts.Opt('auth_plugin', help='Name of the plugin to load') _section_help = 'Config Section from which to load plugin specific options' -_AUTH_SECTION_OPT = cfg.StrOpt('auth_section', help=_section_help) +_AUTH_SECTION_OPT = opts.Opt('auth_section', help=_section_help) def get_common_conf_options(): @@ -64,7 +63,7 @@ def register_conf_options(conf, group): :type conf: oslo_config.cfg.ConfigOpts :param string group: The ini group to register options in. """ - conf.register_opt(_AUTH_SECTION_OPT, group=group) + conf.register_opt(_AUTH_SECTION_OPT._to_oslo_opt(), group=group) # NOTE(jamielennox): plugins are allowed to specify a 'section' which is # the group that auth options should be taken from. If not present they @@ -74,7 +73,7 @@ def register_conf_options(conf, group): if conf[group].auth_section: group = conf[group].auth_section - conf.register_opt(_AUTH_PLUGIN_OPT, group=group) + conf.register_opt(_AUTH_PLUGIN_OPT._to_oslo_opt(), group=group) def load_from_conf_options(conf, group, **kwargs): diff --git a/keystoneauth1/loading/opts.py b/keystoneauth1/loading/opts.py new file mode 100644 index 00000000..073e2c21 --- /dev/null +++ b/keystoneauth1/loading/opts.py @@ -0,0 +1,87 @@ +# 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. + +import itertools +import os + +try: + from oslo_config import cfg +except ImportError: + cfg = None + +from keystoneauth1 import _utils as utils + + +class Opt(object): + + @utils.positional() + def __init__(self, + name, + type=str, + help=None, + secret=False, + dest=None, + deprecated=None, + default=None, + metavar=None): + if not callable(type): + raise TypeError('type must be callable') + + if dest is None: + dest = name.replace('-', '_') + + self.name = name + self.type = type + self.help = help + self.secret = secret + self.dest = dest + self.deprecated = [] if deprecated is None else deprecated + self.default = default + self.metavar = metavar + + def __repr__(self): + return '' % self.name + + def _to_oslo_opt(self): + if not cfg: + raise ImportError("oslo.config is not an automatic dependency of " + "keystoneauth. If you wish to use oslo.config " + "you need to import it into your application's " + "requirements file. ") + + deprecated_opts = [o._to_oslo_opt() for o in self.deprecated] + + return cfg.Opt(name=self.name, + type=self.type, + help=self.help, + secret=self.secret, + dest=self.dest, + deprecated_opts=deprecated_opts, + metavar=self.metavar) + + @property + def _all_opts(self): + return itertools.chain([self], self.deprecated) + + @property + def argparse_args(self): + return ['--os-%s' % o.name for o in self._all_opts] + + @property + def argparse_default(self): + # select the first ENV that is not false-y or return None + for o in self._all_opts: + v = os.environ.get('OS_%s' % self.name.replace('-', '_').upper()) + if v: + return v + + return self.default diff --git a/keystoneauth1/tests/unit/auth/utils.py b/keystoneauth1/tests/unit/auth/utils.py index 4dd86558..e9583b13 100644 --- a/keystoneauth1/tests/unit/auth/utils.py +++ b/keystoneauth1/tests/unit/auth/utils.py @@ -14,7 +14,6 @@ import functools import uuid import mock -from oslo_config import cfg import six from keystoneauth1 import access @@ -41,6 +40,12 @@ class MockPlugin(plugin.BaseAuthPlugin): return 'http://test' +class BoolType(object): + + def __call__(self, value): + return value.lower() in ('1', 'true', 't', 'yes', 'y') + + class MockLoader(loading.BaseLoader): INT_DESC = 'test int' @@ -55,10 +60,10 @@ class MockLoader(loading.BaseLoader): def get_options(self): return [ - cfg.IntOpt('a-int', default='3', help=self.INT_DESC), - cfg.BoolOpt('a-bool', help=self.BOOL_DESC), - cfg.FloatOpt('a-float', help=self.FLOAT_DESC), - cfg.StrOpt('a-str', help=self.STR_DESC, default=self.STR_DEFAULT), + loading.Opt('a-int', default=3, type=int, help=self.INT_DESC), + loading.Opt('a-bool', type=BoolType(), help=self.BOOL_DESC), + loading.Opt('a-float', type=float, help=self.FLOAT_DESC), + loading.Opt('a-str', help=self.STR_DESC, default=self.STR_DEFAULT), ] diff --git a/keystoneauth1/tests/unit/loading/test_cli.py b/keystoneauth1/tests/unit/loading/test_cli.py index 11f3369c..aaa009dc 100644 --- a/keystoneauth1/tests/unit/loading/test_cli.py +++ b/keystoneauth1/tests/unit/loading/test_cli.py @@ -15,7 +15,6 @@ import uuid import fixtures import mock -from oslo_config import cfg from keystoneauth1 import base from keystoneauth1 import loading @@ -38,9 +37,9 @@ class TesterLoader(loading.BaseLoader): # NOTE(jamielennox): this is kind of horrible. If you specify this as # a deprecated_name= value it will convert - to _ which is not what we # want for a CLI option. - deprecated = [cfg.DeprecatedOpt('test-other')] + deprecated = [loading.Opt('test-other')] return [ - cfg.StrOpt('test-opt', help='tester', deprecated_opts=deprecated) + loading.Opt('test-opt', help='tester', deprecated=deprecated) ] diff --git a/keystoneauth1/tests/unit/loading/test_conf.py b/keystoneauth1/tests/unit/loading/test_conf.py index 48a857d1..36553b1d 100644 --- a/keystoneauth1/tests/unit/loading/test_conf.py +++ b/keystoneauth1/tests/unit/loading/test_conf.py @@ -13,7 +13,6 @@ import uuid import mock -from oslo_config import cfg from oslo_config import fixture as config import stevedore @@ -157,14 +156,14 @@ class ConfTests(utils.TestCase): def inner(driver): for p in driver.plugin().get_options(): - self.assertIsInstance(p, cfg.Opt) + self.assertIsInstance(p, loading.Opt) manager.map(inner) def test_get_common(self): opts = loading.get_common_conf_options() for opt in opts: - self.assertIsInstance(opt, cfg.Opt) + self.assertIsInstance(opt, loading.Opt) self.assertEqual(2, len(opts)) def test_get_named(self): diff --git a/requirements.txt b/requirements.txt index b84329b6..f1e4df11 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,6 @@ argparse iso8601>=0.1.9 -oslo.config>=1.11.0 # Apache-2.0 requests>=2.5.2 six>=1.9.0 stevedore>=1.5.0 # Apache-2.0 diff --git a/test-requirements.txt b/test-requirements.txt index 2de4c8c6..30e6db41 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -11,6 +11,7 @@ keyring!=3.3,>=2.1 mock>=1.2 mox3>=0.7.0 oauthlib>=0.6 +oslo.config>=1.11.0 # Apache-2.0 oslosphinx>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 pycrypto>=2.6