diff --git a/keystone/assignment/backends/sql.py b/keystone/assignment/backends/sql.py index ce0778daa1..541f294805 100644 --- a/keystone/assignment/backends/sql.py +++ b/keystone/assignment/backends/sql.py @@ -42,6 +42,7 @@ class AssignmentType(object): class Assignment(base.AssignmentDriverBase): + @classmethod def default_role_driver(self): return 'sql' diff --git a/keystone/assignment/controllers.py b/keystone/assignment/controllers.py index 248dd0ba9f..aa9229dc69 100644 --- a/keystone/assignment/controllers.py +++ b/keystone/assignment/controllers.py @@ -22,7 +22,6 @@ from oslo_log import log from keystone.assignment import schema from keystone.common import authorization from keystone.common import controller -from keystone.common import dependency from keystone.common import validation from keystone.common import wsgi import keystone.conf @@ -34,7 +33,6 @@ CONF = keystone.conf.CONF LOG = log.getLogger(__name__) -@dependency.requires('assignment_api', 'identity_api', 'token_provider_api') class TenantAssignment(controller.V2Controller): """The V2 Project APIs that are processing assignments.""" @@ -61,7 +59,6 @@ class TenantAssignment(controller.V2Controller): return self.format_project_list(tenant_refs, **params) -@dependency.requires('assignment_api', 'resource_api') class ProjectAssignmentV3(controller.V3Controller): """The V3 Project APIs that are processing assignments.""" @@ -81,7 +78,6 @@ class ProjectAssignmentV3(controller.V3Controller): hints=hints) -@dependency.requires('role_api') class RoleV3(controller.V3Controller): """The V3 Role CRUD APIs. @@ -246,7 +242,6 @@ class RoleV3(controller.V3Controller): return hints -@dependency.requires('role_api') class ImpliedRolesV3(controller.V3Controller): """The V3 ImpliedRoles CRD APIs. There is no Update.""" @@ -383,8 +378,6 @@ class ImpliedRolesV3(controller.V3Controller): return results -@dependency.requires('assignment_api', 'identity_api', 'resource_api', - 'role_api') class GrantAssignmentV3(controller.V3Controller): """The V3 Grant Assignment APIs.""" @@ -503,7 +496,6 @@ class GrantAssignmentV3(controller.V3Controller): context=request.context_dict) -@dependency.requires('assignment_api', 'identity_api', 'resource_api') class RoleAssignmentV3(controller.V3Controller): """The V3 Role Assignment APIs, really just list_role_assignment().""" diff --git a/keystone/assignment/core.py b/keystone/assignment/core.py index 1fa50bab25..29fcfef301 100644 --- a/keystone/assignment/core.py +++ b/keystone/assignment/core.py @@ -20,7 +20,6 @@ import functools from oslo_log import log from keystone.common import cache -from keystone.common import dependency from keystone.common import driver_hints from keystone.common import manager import keystone.conf @@ -46,9 +45,6 @@ MEMOIZE_COMPUTED_ASSIGNMENTS = cache.get_memoization_decorator( @notifications.listener -@dependency.provider('assignment_api') -@dependency.requires('credential_api', 'identity_api', 'resource_api', - 'role_api') class Manager(manager.Manager): """Default pivot point for the Assignment backend. @@ -58,6 +54,7 @@ class Manager(manager.Manager): """ driver_namespace = 'keystone.assignment' + _provides_api = 'assignment_api' _PROJECT = 'project' _ROLE_REMOVED_FROM_USER = 'role_removed_from_user' @@ -1057,12 +1054,11 @@ class Manager(manager.Manager): ) -@dependency.provider('role_api') -@dependency.requires('assignment_api') class RoleManager(manager.Manager): """Default pivot point for the Role backend.""" driver_namespace = 'keystone.role' + _provides_api = 'role_api' _ROLE = 'role' @@ -1072,8 +1068,12 @@ class RoleManager(manager.Manager): role_driver = CONF.role.driver if role_driver is None: - assignment_manager = dependency.get_provider('assignment_api') - role_driver = assignment_manager.default_role_driver() + # Explicitly load the assignment manager object + assignment_driver = CONF.assignment.driver + assignment_manager_obj = manager.load_driver( + Manager.driver_namespace, + assignment_driver) + role_driver = assignment_manager_obj.default_role_driver() super(RoleManager, self).__init__(role_driver) diff --git a/keystone/auth/controllers.py b/keystone/auth/controllers.py index 1cc47d8cf8..09debf454b 100644 --- a/keystone/auth/controllers.py +++ b/keystone/auth/controllers.py @@ -21,7 +21,6 @@ from keystone.auth import core from keystone.auth import schema from keystone.common import authorization from keystone.common import controller -from keystone.common import dependency from keystone.common import utils from keystone.common import validation from keystone.common import wsgi @@ -82,8 +81,6 @@ def validate_issue_token_auth(auth=None): raise exception.SchemaValidationError(detail=msg) -@dependency.requires('assignment_api', 'catalog_api', 'identity_api', - 'resource_api', 'token_provider_api', 'trust_api') class Auth(controller.V3Controller): # Note(atiwari): From V3 auth controller code we are diff --git a/keystone/auth/core.py b/keystone/auth/core.py index 4860cdf762..63fc82fa46 100644 --- a/keystone/auth/core.py +++ b/keystone/auth/core.py @@ -18,7 +18,7 @@ from oslo_utils import importutils import six import stevedore -from keystone.common import dependency +from keystone.common import provider_api from keystone.common import utils import keystone.conf from keystone import exception @@ -132,8 +132,7 @@ class AuthContext(dict): self[key] = val -@dependency.requires('resource_api', 'trust_api') -class AuthInfo(object): +class AuthInfo(provider_api.ProviderAPIMixin, object): """Encapsulation of "auth" request.""" @staticmethod @@ -353,8 +352,7 @@ class AuthInfo(object): self._scope_data = (domain_id, project_id, trust, unscoped) -@dependency.requires('identity_api') -class UserMFARulesValidator(object): +class UserMFARulesValidator(provider_api.ProviderAPIMixin, object): """Helper object that can validate the MFA Rules.""" @property diff --git a/keystone/auth/plugins/base.py b/keystone/auth/plugins/base.py index 374a813a9d..d4bbca2783 100644 --- a/keystone/auth/plugins/base.py +++ b/keystone/auth/plugins/base.py @@ -17,6 +17,7 @@ import collections import six +from keystone.common import provider_api from keystone import exception @@ -25,7 +26,7 @@ AuthHandlerResponse = collections.namedtuple( @six.add_metaclass(abc.ABCMeta) -class AuthMethodHandler(object): +class AuthMethodHandler(provider_api.ProviderAPIMixin, object): """Abstract base class for an authentication plugin.""" def __init__(self): diff --git a/keystone/auth/plugins/core.py b/keystone/auth/plugins/core.py index f50349831c..5dcef88a97 100644 --- a/keystone/auth/plugins/core.py +++ b/keystone/auth/plugins/core.py @@ -17,7 +17,7 @@ import sys from oslo_log import log import six -from keystone.common import dependency +from keystone.common import provider_api import keystone.conf from keystone import exception @@ -95,8 +95,7 @@ def convert_integer_to_method_list(method_int): return methods -@dependency.requires('identity_api', 'resource_api') -class BaseUserInfo(object): +class BaseUserInfo(provider_api.ProviderAPIMixin, object): @classmethod def create(cls, auth_payload, method_name): diff --git a/keystone/auth/plugins/external.py b/keystone/auth/plugins/external.py index 3f856bf713..b3edac6ed0 100644 --- a/keystone/auth/plugins/external.py +++ b/keystone/auth/plugins/external.py @@ -19,7 +19,6 @@ import abc import six from keystone.auth.plugins import base -from keystone.common import dependency import keystone.conf from keystone import exception from keystone.i18n import _ @@ -65,7 +64,6 @@ class Base(base.AuthMethodHandler): pass -@dependency.requires('identity_api') class DefaultDomain(Base): def _authenticate(self, request): """Use remote_user to look up the user in the identity backend.""" @@ -74,7 +72,6 @@ class DefaultDomain(Base): CONF.identity.default_domain_id) -@dependency.requires('identity_api', 'resource_api') class Domain(Base): def _authenticate(self, request): """Use remote_user to look up the user in the identity backend. diff --git a/keystone/auth/plugins/mapped.py b/keystone/auth/plugins/mapped.py index 17a5147076..988b97bf5a 100644 --- a/keystone/auth/plugins/mapped.py +++ b/keystone/auth/plugins/mapped.py @@ -19,7 +19,6 @@ from six.moves.urllib import parse from keystone.auth import plugins as auth_plugins from keystone.auth.plugins import base -from keystone.common import dependency from keystone import exception from keystone.federation import constants as federation_constants from keystone.federation import utils @@ -32,8 +31,6 @@ LOG = log.getLogger(__name__) METHOD_NAME = 'mapped' -@dependency.requires('assignment_api', 'federation_api', 'identity_api', - 'resource_api', 'token_provider_api', 'role_api') class Mapped(base.AuthMethodHandler): def _get_token_ref(self, auth_payload): diff --git a/keystone/auth/plugins/oauth1.py b/keystone/auth/plugins/oauth1.py index ec936e0704..a046578463 100644 --- a/keystone/auth/plugins/oauth1.py +++ b/keystone/auth/plugins/oauth1.py @@ -16,14 +16,12 @@ from oslo_utils import timeutils from keystone.auth.plugins import base from keystone.common import controller -from keystone.common import dependency from keystone import exception from keystone.i18n import _ from keystone.oauth1 import core as oauth from keystone.oauth1 import validator -@dependency.requires('oauth_api') class OAuth(base.AuthMethodHandler): def authenticate(self, request, auth_payload): """Turn a signed request with an access key into a keystone token.""" diff --git a/keystone/auth/plugins/password.py b/keystone/auth/plugins/password.py index 33d2039f2c..4c5290ae4c 100644 --- a/keystone/auth/plugins/password.py +++ b/keystone/auth/plugins/password.py @@ -14,7 +14,6 @@ from keystone.auth import plugins as auth_plugins from keystone.auth.plugins import base -from keystone.common import dependency from keystone import exception from keystone.i18n import _ @@ -22,7 +21,6 @@ from keystone.i18n import _ METHOD_NAME = 'password' -@dependency.requires('identity_api') class Password(base.AuthMethodHandler): def authenticate(self, request, auth_payload): diff --git a/keystone/auth/plugins/token.py b/keystone/auth/plugins/token.py index 6cc737202e..67041afc5f 100644 --- a/keystone/auth/plugins/token.py +++ b/keystone/auth/plugins/token.py @@ -17,7 +17,6 @@ import six from keystone.auth.plugins import base from keystone.auth.plugins import mapped -from keystone.common import dependency from keystone.common import wsgi import keystone.conf from keystone import exception @@ -30,7 +29,6 @@ LOG = log.getLogger(__name__) CONF = keystone.conf.CONF -@dependency.requires('federation_api', 'identity_api', 'token_provider_api') class Token(base.AuthMethodHandler): def _get_token_ref(self, auth_payload): diff --git a/keystone/auth/plugins/totp.py b/keystone/auth/plugins/totp.py index 40561c7e84..806b673ca7 100644 --- a/keystone/auth/plugins/totp.py +++ b/keystone/auth/plugins/totp.py @@ -33,7 +33,6 @@ import six from keystone.auth import plugins from keystone.auth.plugins import base -from keystone.common import dependency from keystone import exception from keystone.i18n import _ @@ -69,7 +68,6 @@ def _generate_totp_passcode(secret): return totp.generate(timeutils.utcnow_ts(microsecond=True)).decode('utf-8') -@dependency.requires('credential_api') class TOTP(base.AuthMethodHandler): def authenticate(self, request, auth_payload): diff --git a/keystone/catalog/backends/base.py b/keystone/catalog/backends/base.py index 74e1b54659..03e805ec5b 100644 --- a/keystone/catalog/backends/base.py +++ b/keystone/catalog/backends/base.py @@ -16,6 +16,7 @@ import abc import six +from keystone.common import provider_api import keystone.conf from keystone import exception @@ -24,7 +25,7 @@ CONF = keystone.conf.CONF @six.add_metaclass(abc.ABCMeta) -class CatalogDriverBase(object): +class CatalogDriverBase(provider_api.ProviderAPIMixin, object): """Interface description for the Catalog driver.""" def _get_list_limit(self): diff --git a/keystone/catalog/backends/sql.py b/keystone/catalog/backends/sql.py index 329481c6c6..6e423294d9 100644 --- a/keystone/catalog/backends/sql.py +++ b/keystone/catalog/backends/sql.py @@ -19,7 +19,6 @@ import sqlalchemy from sqlalchemy.sql import true from keystone.catalog.backends import base -from keystone.common import dependency from keystone.common import driver_hints from keystone.common import sql from keystone.common import utils @@ -90,7 +89,6 @@ class Endpoint(sql.ModelBase, sql.ModelDictMixinWithExtras): return super(Endpoint, cls).from_dict(new_dict) -@dependency.requires('catalog_api') class Catalog(base.CatalogDriverBase): # Regions def list_regions(self, hints): diff --git a/keystone/catalog/controllers.py b/keystone/catalog/controllers.py index b5bfb4e73c..aadcf24dcb 100644 --- a/keystone/catalog/controllers.py +++ b/keystone/catalog/controllers.py @@ -17,7 +17,6 @@ from six.moves import http_client from keystone.catalog import schema from keystone.common import controller -from keystone.common import dependency from keystone.common import utils from keystone.common import validation from keystone.common import wsgi @@ -30,7 +29,6 @@ from keystone import resource INTERFACES = ['public', 'internal', 'admin'] -@dependency.requires('catalog_api') class RegionV3(controller.V3Controller): collection_name = 'regions' member_name = 'region' @@ -95,7 +93,6 @@ class RegionV3(controller.V3Controller): ) -@dependency.requires('catalog_api') class ServiceV3(controller.V3Controller): collection_name = 'services' member_name = 'service' @@ -142,7 +139,6 @@ class ServiceV3(controller.V3Controller): ) -@dependency.requires('catalog_api') class EndpointV3(controller.V3Controller): collection_name = 'endpoints' member_name = 'endpoint' @@ -232,7 +228,6 @@ class EndpointV3(controller.V3Controller): ) -@dependency.requires('catalog_api', 'resource_api') class EndpointFilterV3Controller(controller.V3Controller): def __init__(self): @@ -303,7 +298,6 @@ class EndpointFilterV3Controller(controller.V3Controller): request.context_dict, projects) -@dependency.requires('catalog_api', 'resource_api') class EndpointGroupV3Controller(controller.V3Controller): collection_name = 'endpoint_groups' member_name = 'endpoint_group' @@ -408,7 +402,6 @@ class EndpointGroupV3Controller(controller.V3Controller): filtered_endpoints) -@dependency.requires('catalog_api', 'resource_api') class ProjectEndpointGroupV3Controller(controller.V3Controller): collection_name = 'project_endpoint_groups' member_name = 'project_endpoint_group' diff --git a/keystone/catalog/core.py b/keystone/catalog/core.py index fb1c6f2fa6..6bfaaaa0b9 100644 --- a/keystone/catalog/core.py +++ b/keystone/catalog/core.py @@ -16,7 +16,6 @@ """Main entry point into the Catalog service.""" from keystone.common import cache -from keystone.common import dependency from keystone.common import driver_hints from keystone.common import manager import keystone.conf @@ -41,8 +40,6 @@ MEMOIZE_COMPUTED_CATALOG = cache.get_memoization_decorator( region=COMPUTED_CATALOG_REGION) -@dependency.provider('catalog_api') -@dependency.requires('resource_api') class Manager(manager.Manager): """Default pivot point for the Catalog backend. @@ -52,6 +49,7 @@ class Manager(manager.Manager): """ driver_namespace = 'keystone.catalog' + _provides_api = 'catalog_api' _ENDPOINT = 'endpoint' _SERVICE = 'service' diff --git a/keystone/common/controller.py b/keystone/common/controller.py index 2f85eb0a86..985c7ac79e 100644 --- a/keystone/common/controller.py +++ b/keystone/common/controller.py @@ -20,8 +20,8 @@ from oslo_log import versionutils import six from keystone.common import authorization -from keystone.common import dependency from keystone.common import driver_hints +from keystone.common import provider_api from keystone.common import utils from keystone.common import wsgi import keystone.conf @@ -146,8 +146,7 @@ def protected_wrapper(self, f, check_function, request, filter_attr, check_function(self, request, prep_info, *args, **kwargs) -@dependency.requires('policy_api') -class V2Controller(wsgi.Application): +class V2Controller(provider_api.ProviderAPIMixin, wsgi.Application): """Base controller class for Identity API v2.""" @staticmethod @@ -218,8 +217,7 @@ class V2Controller(wsgi.Application): raise ValueError(_('Expected dict or list: %s') % type(ref)) -@dependency.requires('policy_api', 'token_provider_api') -class V3Controller(wsgi.Application): +class V3Controller(provider_api.ProviderAPIMixin, wsgi.Application): """Base controller class for Identity API v3. Child classes should set the ``collection_name`` and ``member_name`` class diff --git a/keystone/common/dependency.py b/keystone/common/dependency.py index 9a75db9d22..356c219dc4 100644 --- a/keystone/common/dependency.py +++ b/keystone/common/dependency.py @@ -25,23 +25,11 @@ See also: """ -import traceback - +from keystone.common import provider_api from keystone.i18n import _ -_REGISTRY = {} - -_future_dependencies = {} -_factories = {} - - -def _set_provider(name, provider): - _original_provider, where_registered = _REGISTRY.get(name, (None, None)) - if where_registered: - raise Exception('%s already has a registered provider, at\n%s' % - (name, ''.join(where_registered))) - _REGISTRY[name] = (provider, traceback.format_stack()) +REGISTRY = provider_api.ProviderAPIs GET_REQUIRED = object() @@ -49,9 +37,7 @@ GET_OPTIONAL = object() def get_provider(name, optional=GET_REQUIRED): - if optional is GET_REQUIRED: - return _REGISTRY[name][0] - return _REGISTRY.get(name, (None, None))[0] + return None class UnresolvableDependencyException(Exception): @@ -68,163 +54,23 @@ class UnresolvableDependencyException(Exception): def provider(name): - """A class decorator used to register providers. - - When ``@provider()`` is used to decorate a class, members of that class - will register themselves as providers for the named dependency. As an - example, In the code fragment:: - - @dependency.provider('foo_api') - class Foo: - def __init__(self): - ... - - ... - - foo = Foo() - - The object ``foo`` will be registered as a provider for ``foo_api``. No - more than one such instance should be created; additional instances will - replace the previous ones, possibly resulting in different instances being - used by different consumers. - - """ + """Deprecated, does nothing.""" def wrapper(cls): - def wrapped(init): - def __wrapped_init__(self, *args, **kwargs): - """Initialize the wrapped object and add it to the registry.""" - init(self, *args, **kwargs) - _set_provider(name, self) - resolve_future_dependencies(__provider_name=name) - - return __wrapped_init__ - - cls.__init__ = wrapped(cls.__init__) - _factories[name] = cls return cls return wrapper -def _process_dependencies(obj): - # Any dependencies that can be resolved immediately are resolved. - # Dependencies that cannot be resolved immediately are stored for - # resolution in resolve_future_dependencies. - - def process(obj, attr_name, unresolved_in_out): - for dependency in getattr(obj, attr_name, []): - if dependency not in _REGISTRY: - # We don't know about this dependency, so save it for later. - unresolved_in_out.setdefault(dependency, []).append(obj) - continue - - setattr(obj, dependency, get_provider(dependency)) - - process(obj, '_dependencies', _future_dependencies) - - def requires(*dependencies): - """A class decorator used to inject providers into consumers. - - The required providers will be made available to instances of the decorated - class via an attribute with the same name as the provider. For example, in - the code fragment:: - - @dependency.requires('foo_api', 'bar_api') - class FooBarClient: - def __init__(self): - ... - - ... - - client = FooBarClient() - - The object ``client`` will have attributes named ``foo_api`` and - ``bar_api``, which are instances of the named providers. - - Objects must not rely on the existence of these attributes until after - ``resolve_future_dependencies()`` has been called; they may not exist - beforehand. - - Dependencies registered via ``@required()`` must have providers; if not, - an ``UnresolvableDependencyException`` will be raised when - ``resolve_future_dependencies()`` is called. - - """ - def wrapper(self, *args, **kwargs): - """Inject each dependency from the registry.""" - self.__wrapped_init__(*args, **kwargs) - _process_dependencies(self) - + """Deprecated, does nothing.""" def wrapped(cls): - """Note the required dependencies on the object for later injection. - - The dependencies of the parent class are combined with that of the - child class to create a new set of dependencies. - - """ - existing_dependencies = getattr(cls, '_dependencies', set()) - cls._dependencies = existing_dependencies.union(dependencies) - if not hasattr(cls, '__wrapped_init__'): - cls.__wrapped_init__ = cls.__init__ - cls.__init__ = wrapper return cls - return wrapped def resolve_future_dependencies(__provider_name=None): - """Force injection of all dependencies. - - Before this function is called, circular dependencies may not have been - injected. This function should be called only once, after all global - providers are registered. If an object needs to be created after this - call, it must not have circular dependencies. - - If any required dependencies are unresolvable, this function will raise an - ``UnresolvableDependencyException``. - - Outside of this module, this function should be called with no arguments; - the optional argument, ``__provider_name`` is used internally, and should - be treated as an implementation detail. - - """ - new_providers = dict() - if __provider_name: - # A provider was registered, so take care of any objects depending on - # it. - targets = _future_dependencies.pop(__provider_name, []) - - for target in targets: - setattr(target, __provider_name, get_provider(__provider_name)) - - return - - # Resolve future dependencies, raises UnresolvableDependencyException if - # there's no provider registered. - try: - for dependency, targets in _future_dependencies.copy().items(): - if dependency not in _REGISTRY: - # a Class was registered that could fulfill the dependency, but - # it has not yet been initialized. - factory = _factories.get(dependency) - if factory: - provider = factory() - new_providers[dependency] = provider - else: - raise UnresolvableDependencyException(dependency, targets) - - for target in targets: - setattr(target, dependency, get_provider(dependency)) - finally: - _future_dependencies.clear() - return new_providers + """Deprecated, does nothing.""" + return {} def reset(): - """Reset the registry of providers. - - This is useful for unit testing to ensure that tests don't use providers - from previous tests. - """ - _REGISTRY.clear() - _future_dependencies.clear() + """Deprecated, does nothing.""" diff --git a/keystone/common/manager.py b/keystone/common/manager.py index daec40a8d9..4db41516dc 100644 --- a/keystone/common/manager.py +++ b/keystone/common/manager.py @@ -21,6 +21,7 @@ from oslo_log import log import six import stevedore +from keystone.common import provider_api from keystone.i18n import _ @@ -164,12 +165,33 @@ class Manager(object): """ driver_namespace = None + _provides_api = None def __init__(self, driver_name): - self.driver = load_driver(self.driver_namespace, driver_name) + if self._provides_api is None: + raise ValueError('Programming Error: All managers must provide an ' + 'API that can be referenced by other components ' + 'of Keystone.') + if driver_name is not None: + self.driver = load_driver(self.driver_namespace, driver_name) + self.__register_provider_api() + + def __register_provider_api(self): + provider_api.ProviderAPIs._register_provider_api( + name=self._provides_api, obj=self) def __getattr__(self, name): - """Forward calls to the underlying driver.""" + """Forward calls to the underlying driver. + + This method checks for a provider api before forwarding. + """ + try: + return getattr(provider_api.ProviderAPIs, name) + except AttributeError: + # NOTE(morgan): We didn't find a provider api, move on and + # forward to the driver as expected. + pass + f = getattr(self.driver, name) if callable(f): # NOTE(dstanek): only if this is callable (class or function) diff --git a/keystone/common/provider_api.py b/keystone/common/provider_api.py new file mode 100644 index 0000000000..02d15d8785 --- /dev/null +++ b/keystone/common/provider_api.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. + +ProviderAPIs = None + + +def _create_provider_api_instance(): + class _ProviderAPIs(object): + + def __init__(self): + self.__registry = {} + self.__locked = False + + self.__iter__ = self.__registry.__iter__ + self.__getitem__ = self.__registry.__getitem__ + + def __getattr__(self, item): + """Do attr lookup.""" + try: + return self.__registry[item] + except KeyError: + raise AttributeError( + "'ProviderAPIs' has no attribute %s" % item) + + def _register_provider_api(self, name, obj): + """Register an instance of a class as a provider api.""" + if name == 'driver': + raise ValueError('A provider may not be named "driver".') + + if self.__locked: + raise RuntimeError( + 'Programming Error: The provider api registry has been ' + 'locked (post configuration). Ensure all provider api ' + 'managers are instantiated before locking.') + + if name in self.__registry: + raise DuplicateProviderError( + '`%(name)s` has already been registered as an api ' + 'provider by `%(prov)r`' % {'name': name, + 'prov': self.__registry[name]}) + self.__registry[name] = obj + + def _clear_registry_instances(self): + """ONLY USED FOR TESTING.""" + self.__registry.clear() + self.__locked = False + + def lock_provider_registry(self): + self.__locked = True + + global ProviderAPIs + if ProviderAPIs is None: + ProviderAPIs = _ProviderAPIs() + else: + raise RuntimeError('Programming Error: ProviderAPIs object cannot be ' + 'instatiated more than one time. It is meant to ' + 'act as a singleton.') + + +class DuplicateProviderError(Exception): + """Attempting to register a duplicate API provider.""" + + +class ProviderAPIMixin(object): + """Allow referencing provider apis on self via __getattr__. + + Be sure this class is first in the class definition for inheritance. + """ + + def __getattr__(self, item): + """Magic getattr method.""" + try: + return getattr(ProviderAPIs, item) + except AttributeError: + return self.__getattribute__(item) + + +_create_provider_api_instance() diff --git a/keystone/common/tokenless_auth.py b/keystone/common/tokenless_auth.py index 09ce367e64..f702fee5a1 100644 --- a/keystone/common/tokenless_auth.py +++ b/keystone/common/tokenless_auth.py @@ -18,7 +18,7 @@ import hashlib from oslo_log import log from keystone.auth import core -from keystone.common import dependency +from keystone.common import provider_api import keystone.conf from keystone import exception from keystone.federation import constants as federation_constants @@ -30,9 +30,7 @@ CONF = keystone.conf.CONF LOG = log.getLogger(__name__) -@dependency.requires('assignment_api', 'federation_api', - 'identity_api', 'resource_api') -class TokenlessAuthHelper(object): +class TokenlessAuthHelper(provider_api.ProviderAPIMixin, object): def __init__(self, env): """A init class for TokenlessAuthHelper. diff --git a/keystone/contrib/ec2/controllers.py b/keystone/contrib/ec2/controllers.py index 3883a16251..d0227f5706 100644 --- a/keystone/contrib/ec2/controllers.py +++ b/keystone/contrib/ec2/controllers.py @@ -43,7 +43,7 @@ from six.moves import http_client from keystone.common import authorization from keystone.common import controller -from keystone.common import dependency +from keystone.common import provider_api from keystone.common import utils from keystone.common import wsgi from keystone import exception @@ -53,11 +53,8 @@ from keystone.token import controllers as token_controllers CRED_TYPE_EC2 = 'ec2' -@dependency.requires('assignment_api', 'catalog_api', 'credential_api', - 'identity_api', 'resource_api', 'role_api', - 'token_provider_api') @six.add_metaclass(abc.ABCMeta) -class Ec2ControllerCommon(object): +class Ec2ControllerCommon(provider_api.ProviderAPIMixin, object): def check_signature(self, creds_ref, credentials): signer = ec2_utils.Ec2Signer(creds_ref['secret']) signature = signer.generate(credentials) @@ -270,7 +267,6 @@ class Ec2ControllerCommon(object): headers=headers) -@dependency.requires('policy_api', 'token_provider_api') class Ec2Controller(Ec2ControllerCommon, controller.V2Controller): @controller.v2_ec2_deprecated @@ -358,7 +354,6 @@ class Ec2Controller(Ec2ControllerCommon, controller.V2Controller): raise exception.Forbidden(_('Credential belongs to another user')) -@dependency.requires('policy_api', 'token_provider_api') class Ec2ControllerV3(Ec2ControllerCommon, controller.V3Controller): collection_name = 'credentials' diff --git a/keystone/credential/controllers.py b/keystone/credential/controllers.py index 867694e319..93e215742f 100644 --- a/keystone/credential/controllers.py +++ b/keystone/credential/controllers.py @@ -17,14 +17,12 @@ import hashlib from oslo_serialization import jsonutils from keystone.common import controller -from keystone.common import dependency from keystone.common import validation from keystone.credential import schema from keystone import exception from keystone.i18n import _ -@dependency.requires('credential_api') class CredentialV3(controller.V3Controller): collection_name = 'credentials' member_name = 'credential' diff --git a/keystone/credential/core.py b/keystone/credential/core.py index 978fda20a1..b849cf1613 100644 --- a/keystone/credential/core.py +++ b/keystone/credential/core.py @@ -16,7 +16,6 @@ import json -from keystone.common import dependency from keystone.common import driver_hints from keystone.common import manager import keystone.conf @@ -26,8 +25,6 @@ from keystone import exception CONF = keystone.conf.CONF -@dependency.provider('credential_api') -@dependency.requires('credential_provider_api') class Manager(manager.Manager): """Default pivot point for the Credential backend. @@ -37,6 +34,7 @@ class Manager(manager.Manager): """ driver_namespace = 'keystone.credential' + _provides_api = 'credential_api' def __init__(self): super(Manager, self).__init__(CONF.credential.driver) diff --git a/keystone/credential/provider.py b/keystone/credential/provider.py index 60653145e9..defd49f239 100644 --- a/keystone/credential/provider.py +++ b/keystone/credential/provider.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from keystone.common import dependency from keystone.common import manager import keystone.conf @@ -18,10 +17,10 @@ import keystone.conf CONF = keystone.conf.CONF -@dependency.provider('credential_provider_api') class Manager(manager.Manager): driver_namespace = 'keystone.credential.provider' + _provides_api = 'credential_provider_api' def __init__(self): super(Manager, self).__init__(CONF.credential.provider) diff --git a/keystone/endpoint_policy/controllers.py b/keystone/endpoint_policy/controllers.py index 02dfbcf1cc..9bde3b32d1 100644 --- a/keystone/endpoint_policy/controllers.py +++ b/keystone/endpoint_policy/controllers.py @@ -13,11 +13,9 @@ # under the License. from keystone.common import controller -from keystone.common import dependency from keystone import notifications -@dependency.requires('policy_api', 'catalog_api', 'endpoint_policy_api') class EndpointPolicyV3Controller(controller.V3Controller): collection_name = 'endpoints' member_name = 'endpoint' diff --git a/keystone/endpoint_policy/core.py b/keystone/endpoint_policy/core.py index 196b90c032..04c5d4b6e4 100644 --- a/keystone/endpoint_policy/core.py +++ b/keystone/endpoint_policy/core.py @@ -14,7 +14,6 @@ from oslo_log import log -from keystone.common import dependency from keystone.common import manager import keystone.conf from keystone import exception @@ -25,8 +24,6 @@ CONF = keystone.conf.CONF LOG = log.getLogger(__name__) -@dependency.provider('endpoint_policy_api') -@dependency.requires('catalog_api', 'policy_api') class Manager(manager.Manager): """Default pivot point for the Endpoint Policy backend. @@ -36,6 +33,7 @@ class Manager(manager.Manager): """ driver_namespace = 'keystone.endpoint_policy' + _provides_api = 'endpoint_policy_api' def __init__(self): super(Manager, self).__init__(CONF.endpoint_policy.driver) diff --git a/keystone/federation/controllers.py b/keystone/federation/controllers.py index 0b8b542afb..cd39b9d517 100644 --- a/keystone/federation/controllers.py +++ b/keystone/federation/controllers.py @@ -22,7 +22,6 @@ import webob from keystone.auth import controllers as auth_controllers from keystone.common import controller -from keystone.common import dependency from keystone.common import utils as k_utils from keystone.common import validation from keystone.common import wsgi @@ -49,7 +48,6 @@ class _ControllerBase(controller.V3Controller): return super(_ControllerBase, cls).base_url(context, path=path) -@dependency.requires('federation_api') class IdentityProvider(_ControllerBase): """Identity Provider representation.""" @@ -128,7 +126,6 @@ class IdentityProvider(_ControllerBase): return IdentityProvider.wrap_member(request.context_dict, idp_ref) -@dependency.requires('federation_api') class FederationProtocol(_ControllerBase): """A federation protocol representation. @@ -219,7 +216,6 @@ class FederationProtocol(_ControllerBase): self.federation_api.delete_protocol(idp_id, protocol_id) -@dependency.requires('federation_api') class MappingController(_ControllerBase): collection_name = 'mappings' member_name = 'mapping' @@ -257,7 +253,6 @@ class MappingController(_ControllerBase): return MappingController.wrap_member(request.context_dict, mapping_ref) -@dependency.requires('federation_api') class Auth(auth_controllers.Auth): def _get_sso_origin_host(self, request): @@ -432,7 +427,6 @@ class Auth(auth_controllers.Auth): headers=headers) -@dependency.requires('assignment_api', 'resource_api') class DomainV3(controller.V3Controller): collection_name = 'domains' member_name = 'domain' @@ -462,7 +456,6 @@ class DomainV3(controller.V3Controller): return DomainV3.wrap_collection(request.context_dict, domains) -@dependency.requires('assignment_api', 'resource_api') class ProjectAssignmentV3(controller.V3Controller): collection_name = 'projects' member_name = 'project' @@ -493,7 +486,6 @@ class ProjectAssignmentV3(controller.V3Controller): projects) -@dependency.requires('federation_api') class ServiceProvider(_ControllerBase): """Service Provider representation.""" diff --git a/keystone/federation/core.py b/keystone/federation/core.py index 03a408d916..bda5691e2e 100644 --- a/keystone/federation/core.py +++ b/keystone/federation/core.py @@ -15,7 +15,6 @@ import uuid from keystone.common import cache -from keystone.common import dependency from keystone.common import extension from keystone.common import manager import keystone.conf @@ -44,8 +43,6 @@ extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA) extension.register_public_extension(EXTENSION_DATA['alias'], EXTENSION_DATA) -@dependency.provider('federation_api') -@dependency.requires('resource_api') class Manager(manager.Manager): """Default pivot point for the Federation backend. @@ -55,6 +52,7 @@ class Manager(manager.Manager): """ driver_namespace = 'keystone.federation' + _provides_api = 'federation_api' def __init__(self): super(Manager, self).__init__(CONF.federation.driver) diff --git a/keystone/identity/controllers.py b/keystone/identity/controllers.py index b79247792c..481c1fa1fd 100644 --- a/keystone/identity/controllers.py +++ b/keystone/identity/controllers.py @@ -17,7 +17,6 @@ from oslo_log import log from keystone.common import controller -from keystone.common import dependency from keystone.common import validation import keystone.conf from keystone import exception @@ -29,7 +28,6 @@ CONF = keystone.conf.CONF LOG = log.getLogger(__name__) -@dependency.requires('identity_api') class UserV3(controller.V3Controller): collection_name = 'users' member_name = 'user' @@ -139,7 +137,6 @@ class UserV3(controller.V3Controller): 'Error when changing user password: %s') % e) -@dependency.requires('identity_api') class GroupV3(controller.V3Controller): collection_name = 'groups' member_name = 'group' diff --git a/keystone/identity/core.py b/keystone/identity/core.py index b53d269aae..06d3e1322b 100644 --- a/keystone/identity/core.py +++ b/keystone/identity/core.py @@ -28,9 +28,9 @@ from pycadf import reason from keystone import assignment # TODO(lbragstad): Decouple this dependency from keystone.common import cache from keystone.common import clean -from keystone.common import dependency from keystone.common import driver_hints from keystone.common import manager +from keystone.common import provider_api from keystone.common.validation import validators import keystone.conf from keystone import exception @@ -62,8 +62,7 @@ REGISTRATION_ATTEMPTS = 10 SQL_DRIVER = 'SQL' -@dependency.requires('domain_config_api', 'resource_api') -class DomainConfigs(dict): +class DomainConfigs(provider_api.ProviderAPIMixin, dict): """Discover, store and provide access to domain specific configs. The setup_domain_drivers() call will be made via the wrapper from @@ -436,9 +435,6 @@ def exception_translated(exception_type): @notifications.listener -@dependency.provider('identity_api') -@dependency.requires('assignment_api', 'credential_api', 'id_mapping_api', - 'resource_api', 'shadow_users_api', 'federation_api') class Manager(manager.Manager): """Default pivot point for the Identity backend. @@ -479,6 +475,7 @@ class Manager(manager.Manager): """ driver_namespace = 'keystone.identity' + _provides_api = 'identity_api' _USER = 'user' _GROUP = 'group' @@ -1423,11 +1420,11 @@ class Manager(manager.Manager): return user_dict -@dependency.provider('id_mapping_api') class MappingManager(manager.Manager): """Default pivot point for the ID Mapping backend.""" driver_namespace = 'keystone.identity.id_mapping' + _provides_api = 'id_mapping_api' def __init__(self): super(MappingManager, self).__init__(CONF.identity_mapping.driver) @@ -1475,11 +1472,11 @@ class MappingManager(manager.Manager): ID_MAPPING_REGION.invalidate() -@dependency.provider('shadow_users_api') class ShadowUsersManager(manager.Manager): """Default pivot point for the Shadow Users backend.""" driver_namespace = 'keystone.identity.shadow_users' + _provides_api = 'shadow_users_api' def __init__(self): shadow_driver = CONF.shadow_users.driver diff --git a/keystone/identity/generator.py b/keystone/identity/generator.py index 7879f63b31..015d296947 100644 --- a/keystone/identity/generator.py +++ b/keystone/identity/generator.py @@ -18,7 +18,6 @@ import abc import six -from keystone.common import dependency from keystone.common import manager import keystone.conf from keystone import exception @@ -27,11 +26,11 @@ from keystone import exception CONF = keystone.conf.CONF -@dependency.provider('id_generator_api') class Manager(manager.Manager): """Default pivot point for the identifier generator backend.""" driver_namespace = 'keystone.identity.id_generator' + _provides_api = 'id_generator_api' def __init__(self): super(Manager, self).__init__(CONF.identity_mapping.generator) diff --git a/keystone/identity/mapping_backends/base.py b/keystone/identity/mapping_backends/base.py index 97553616be..320d60ac16 100644 --- a/keystone/identity/mapping_backends/base.py +++ b/keystone/identity/mapping_backends/base.py @@ -16,11 +16,12 @@ import abc import six +from keystone.common import provider_api from keystone import exception @six.add_metaclass(abc.ABCMeta) -class MappingDriverBase(object): +class MappingDriverBase(provider_api.ProviderAPIMixin, object): """Interface description for an ID Mapping driver.""" @abc.abstractmethod diff --git a/keystone/identity/mapping_backends/sql.py b/keystone/identity/mapping_backends/sql.py index 9c82de237d..991a890ae8 100644 --- a/keystone/identity/mapping_backends/sql.py +++ b/keystone/identity/mapping_backends/sql.py @@ -12,7 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. -from keystone.common import dependency from keystone.common import sql from keystone.identity.mapping_backends import base from keystone.identity.mapping_backends import mapping as identity_mapping @@ -35,7 +34,6 @@ class IDMapping(sql.ModelBase, sql.ModelDictMixin): sql.UniqueConstraint('domain_id', 'local_id', 'entity_type'),) -@dependency.requires('id_generator_api') class Mapping(base.MappingDriverBase): def get_public_id(self, local_entity): diff --git a/keystone/middleware/auth.py b/keystone/middleware/auth.py index 6f8b7d440a..8f5ffc9ada 100644 --- a/keystone/middleware/auth.py +++ b/keystone/middleware/auth.py @@ -15,7 +15,7 @@ from oslo_log import log from keystone.common import authorization from keystone.common import context -from keystone.common import dependency +from keystone.common import provider_api from keystone.common import tokenless_auth from keystone.common import wsgi import keystone.conf @@ -32,8 +32,8 @@ LOG = log.getLogger(__name__) __all__ = ('AuthContextMiddleware',) -@dependency.requires('token_provider_api') -class AuthContextMiddleware(auth_token.BaseAuthProtocol): +class AuthContextMiddleware(provider_api.ProviderAPIMixin, + auth_token.BaseAuthProtocol): """Build the authentication context from the request auth token.""" kwargs_to_fetch_token = True diff --git a/keystone/notifications.py b/keystone/notifications.py index 7dc27d1dbb..7fc41ae040 100644 --- a/keystone/notifications.py +++ b/keystone/notifications.py @@ -30,7 +30,7 @@ from pycadf import eventfactory from pycadf import reason from pycadf import resource -from keystone.common import dependency +from keystone.common import provider_api from keystone.common import utils import keystone.conf from keystone import exception @@ -655,8 +655,7 @@ def send_saml_audit_notification(action, request, user_id, group_ids, _send_audit_notification(action, initiator, outcome, target, event_type) -@dependency.requires('catalog_api') -class _CatalogHelperObj(object): +class _CatalogHelperObj(provider_api.ProviderAPIMixin, object): """A helper object to allow lookups of identity service id.""" diff --git a/keystone/oauth1/controllers.py b/keystone/oauth1/controllers.py index cffb6ebff1..f1378043b7 100644 --- a/keystone/oauth1/controllers.py +++ b/keystone/oauth1/controllers.py @@ -22,7 +22,6 @@ from six.moves.urllib import parse as urlparse from keystone.common import authorization from keystone.common import controller -from keystone.common import dependency from keystone.common import validation from keystone.common import wsgi import keystone.conf @@ -49,7 +48,6 @@ def _emit_user_oauth_consumer_token_invalidate(payload): ) -@dependency.requires('oauth_api', 'token_provider_api') class ConsumerCrudV3(controller.V3Controller): collection_name = 'consumers' member_name = 'consumer' @@ -102,7 +100,6 @@ class ConsumerCrudV3(controller.V3Controller): ) -@dependency.requires('oauth_api') class AccessTokenCrudV3(controller.V3Controller): collection_name = 'access_tokens' member_name = 'access_token' @@ -172,7 +169,6 @@ class AccessTokenCrudV3(controller.V3Controller): return formatted_entity -@dependency.requires('oauth_api', 'role_api') class AccessTokenRolesV3(controller.V3Controller): collection_name = 'roles' member_name = 'role' @@ -212,8 +208,6 @@ class AccessTokenRolesV3(controller.V3Controller): return formatted_entity -@dependency.requires('assignment_api', 'oauth_api', - 'resource_api', 'token_provider_api') class OAuthControllerV3(controller.V3Controller): collection_name = 'not_used' member_name = 'not_used' diff --git a/keystone/oauth1/core.py b/keystone/oauth1/core.py index 63d06be58d..7f6e50bd80 100644 --- a/keystone/oauth1/core.py +++ b/keystone/oauth1/core.py @@ -22,7 +22,6 @@ import oauthlib.common from oauthlib import oauth1 from oslo_log import log -from keystone.common import dependency from keystone.common import extension from keystone.common import manager import keystone.conf @@ -123,7 +122,6 @@ def validate_oauth_params(query_string): raise exception.ValidationError(message=msg) -@dependency.provider('oauth_api') class Manager(manager.Manager): """Default pivot point for the OAuth1 backend. @@ -133,6 +131,7 @@ class Manager(manager.Manager): """ driver_namespace = 'keystone.oauth1' + _provides_api = 'oauth_api' _ACCESS_TOKEN = "OS-OAUTH1:access_token" _REQUEST_TOKEN = "OS-OAUTH1:request_token" diff --git a/keystone/oauth1/validator.py b/keystone/oauth1/validator.py index 84761185d5..77a2245fab 100644 --- a/keystone/oauth1/validator.py +++ b/keystone/oauth1/validator.py @@ -16,7 +16,7 @@ import six -from keystone.common import dependency +from keystone.common import provider_api from keystone import exception from keystone.oauth1.backends import base from keystone.oauth1 import core as oauth1 @@ -25,8 +25,7 @@ from keystone.oauth1 import core as oauth1 METHOD_NAME = 'oauth_validator' -@dependency.requires('oauth_api') -class OAuthValidator(oauth1.RequestValidator): +class OAuthValidator(provider_api.ProviderAPIMixin, oauth1.RequestValidator): # TODO(mhu) set as option probably? @property diff --git a/keystone/policy/controllers.py b/keystone/policy/controllers.py index e061873e11..c9cfd876c5 100644 --- a/keystone/policy/controllers.py +++ b/keystone/policy/controllers.py @@ -16,7 +16,6 @@ from oslo_log import versionutils import six from keystone.common import controller -from keystone.common import dependency from keystone.common import validation from keystone.policy import schema @@ -31,7 +30,6 @@ def policy_deprecated(f): return wrapper() -@dependency.requires('policy_api') class PolicyV3(controller.V3Controller): collection_name = 'policies' member_name = 'policy' diff --git a/keystone/policy/core.py b/keystone/policy/core.py index 3f11f9d146..e4578a9954 100644 --- a/keystone/policy/core.py +++ b/keystone/policy/core.py @@ -14,7 +14,6 @@ """Main entry point into the Policy service.""" -from keystone.common import dependency from keystone.common import manager import keystone.conf from keystone import exception @@ -24,7 +23,6 @@ from keystone import notifications CONF = keystone.conf.CONF -@dependency.provider('policy_api') class Manager(manager.Manager): """Default pivot point for the Policy backend. @@ -34,6 +32,7 @@ class Manager(manager.Manager): """ driver_namespace = 'keystone.policy' + _provides_api = 'policy_api' _POLICY = 'policy' diff --git a/keystone/resource/controllers.py b/keystone/resource/controllers.py index c397f6d38e..b51cc7b6fe 100644 --- a/keystone/resource/controllers.py +++ b/keystone/resource/controllers.py @@ -18,7 +18,6 @@ from six.moves import http_client from keystone.common import controller -from keystone.common import dependency from keystone.common import validation from keystone.common import wsgi import keystone.conf @@ -30,7 +29,6 @@ from keystone.resource import schema CONF = keystone.conf.CONF -@dependency.requires('resource_api') class DomainV3(controller.V3Controller): collection_name = 'domains' member_name = 'domain' @@ -76,8 +74,6 @@ class DomainV3(controller.V3Controller): ) -@dependency.requires('domain_config_api') -@dependency.requires('resource_api') class DomainConfigV3(controller.V3Controller): member_name = 'config' @@ -151,7 +147,6 @@ class DomainConfigV3(controller.V3Controller): return {self.member_name: ref} -@dependency.requires('resource_api') class ProjectV3(controller.V3Controller): collection_name = 'projects' member_name = 'project' @@ -266,7 +261,6 @@ class ProjectV3(controller.V3Controller): initiator=request.audit_initiator) -@dependency.requires('resource_api') class ProjectTagV3(controller.V3Controller): collection_name = 'projects' member_name = 'tags' diff --git a/keystone/resource/core.py b/keystone/resource/core.py index e3304b7b00..d8335e4fe7 100644 --- a/keystone/resource/core.py +++ b/keystone/resource/core.py @@ -18,7 +18,6 @@ import six from keystone import assignment from keystone.common import cache from keystone.common import clean -from keystone.common import dependency from keystone.common import driver_hints from keystone.common import manager from keystone.common import utils @@ -37,9 +36,6 @@ MEMOIZE = cache.get_memoization_decorator(group='resource') TAG_SEARCH_FILTERS = ('tags', 'tags-any', 'not-tags', 'not-tags-any') -@dependency.provider('resource_api') -@dependency.requires('assignment_api', 'credential_api', 'domain_config_api', - 'identity_api', 'trust_api') class Manager(manager.Manager): """Default pivot point for the Resource backend. @@ -49,6 +45,7 @@ class Manager(manager.Manager): """ driver_namespace = 'keystone.resource' + _provides_api = 'resource_api' _DOMAIN = 'domain' _PROJECT = 'project' @@ -60,7 +57,9 @@ class Manager(manager.Manager): # SQL Identity in some form. Even if SQL Identity is not used, there # is almost no reason to have non-SQL Resource. Keystone requires # SQL in a number of ways, this simply codifies it plainly for resource + # the driver_name = None simply implies we don't need to load a driver. self.driver = resource_sql.Resource() + super(Manager, self).__init__(driver_name=None) def _get_hierarchy_depth(self, parents_list): return len(parents_list) + 1 @@ -946,7 +945,6 @@ class Manager(manager.Manager): MEMOIZE_CONFIG = cache.get_memoization_decorator(group='domain_config') -@dependency.provider('domain_config_api') class DomainConfigManager(manager.Manager): """Default pivot point for the Domain Config backend.""" @@ -960,6 +958,7 @@ class DomainConfigManager(manager.Manager): # the identity manager are supported. driver_namespace = 'keystone.resource.domain_config' + _provides_api = 'domain_config_api' # We explicitly state each whitelisted option instead of pulling all ldap # options from CONF and selectively pruning them to prevent a security diff --git a/keystone/revoke/controllers.py b/keystone/revoke/controllers.py index 997f70a052..6a8dc297c4 100644 --- a/keystone/revoke/controllers.py +++ b/keystone/revoke/controllers.py @@ -13,12 +13,10 @@ from oslo_utils import timeutils from keystone.common import controller -from keystone.common import dependency from keystone import exception from keystone.i18n import _ -@dependency.requires('revoke_api') class RevokeController(controller.V3Controller): @controller.protected() def list_revoke_events(self, request): diff --git a/keystone/revoke/core.py b/keystone/revoke/core.py index 321c7eb4a1..a4c0e2ddf2 100644 --- a/keystone/revoke/core.py +++ b/keystone/revoke/core.py @@ -13,7 +13,6 @@ """Main entry point into the Revoke service.""" from keystone.common import cache -from keystone.common import dependency from keystone.common import extension from keystone.common import manager import keystone.conf @@ -53,7 +52,6 @@ MEMOIZE = cache.get_memoization_decorator( region=REVOKE_REGION) -@dependency.provider('revoke_api') class Manager(manager.Manager): """Default pivot point for the Revoke backend. @@ -65,6 +63,7 @@ class Manager(manager.Manager): """ driver_namespace = 'keystone.revoke' + _provides_api = 'revoke_api' def __init__(self): super(Manager, self).__init__(CONF.revoke.driver) diff --git a/keystone/server/backends.py b/keystone/server/backends.py index eb8181c916..565dd619e7 100644 --- a/keystone/server/backends.py +++ b/keystone/server/backends.py @@ -14,6 +14,7 @@ from keystone import assignment from keystone import auth from keystone import catalog from keystone.common import cache +from keystone.common import provider_api from keystone import credential from keystone import endpoint_policy from keystone import federation @@ -23,6 +24,7 @@ from keystone import policy from keystone import resource from keystone import revoke from keystone import token +from keystone.token import persistence from keystone import trust @@ -37,29 +39,21 @@ def load_backends(): cache.configure_cache(region=identity.ID_MAPPING_REGION) cache.configure_invalidation_region() - # NOTE(knikolla): The assignment manager must be instantiated before the - # resource manager. The current dictionary ordering ensures that. - DRIVERS = dict( - assignment_api=assignment.Manager(), - catalog_api=catalog.Manager(), - credential_api=credential.Manager(), - credential_provider_api=credential.provider.Manager(), - domain_config_api=resource.DomainConfigManager(), - endpoint_policy_api=endpoint_policy.Manager(), - federation_api=federation.Manager(), - id_generator_api=identity.generator.Manager(), - id_mapping_api=identity.MappingManager(), - identity_api=identity.Manager(), - shadow_users_api=identity.ShadowUsersManager(), - oauth_api=oauth1.Manager(), - policy_api=policy.Manager(), - resource_api=resource.Manager(), - revoke_api=revoke.Manager(), - role_api=assignment.RoleManager(), - token_api=token.persistence.Manager(), - trust_api=trust.Manager(), - token_provider_api=token.provider.Manager()) + managers = [assignment.Manager, catalog.Manager, credential.Manager, + credential.provider.Manager, resource.DomainConfigManager, + endpoint_policy.Manager, federation.Manager, + identity.generator.Manager, identity.MappingManager, + identity.Manager, identity.ShadowUsersManager, oauth1.Manager, + policy.Manager, resource.Manager, revoke.Manager, + assignment.RoleManager, trust.Manager, token.provider.Manager, + persistence.PersistenceManager] + + drivers = {d._provides_api: d() for d in managers} + + # NOTE(morgan): lock the APIs, these should only ever be instantiated + # before running keystone. + provider_api.ProviderAPIs.lock_provider_registry() auth.core.load_auth_methods() - return DRIVERS + return drivers diff --git a/keystone/server/common.py b/keystone/server/common.py index d83afdf497..33e13a9d40 100644 --- a/keystone/server/common.py +++ b/keystone/server/common.py @@ -14,7 +14,6 @@ from oslo_log import log -from keystone.common import dependency from keystone.common import sql import keystone.conf from keystone.server import backends @@ -47,5 +46,4 @@ def setup_backends(load_extra_backends_fn=lambda: {}, drivers = backends.load_backends() drivers.update(load_extra_backends_fn()) res = startup_application_fn() - drivers.update(dependency.resolve_future_dependencies()) return drivers, res diff --git a/keystone/tests/unit/common/test_injection.py b/keystone/tests/unit/common/test_injection.py deleted file mode 100644 index 9a5d1e7d2e..0000000000 --- a/keystone/tests/unit/common/test_injection.py +++ /dev/null @@ -1,238 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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 uuid - -from keystone.common import dependency -from keystone.tests import unit - - -class TestDependencyInjection(unit.BaseTestCase): - def setUp(self): - super(TestDependencyInjection, self).setUp() - dependency.reset() - self.addCleanup(dependency.reset) - - def test_dependency_injection(self): - class Interface(object): - def do_work(self): - assert False - - @dependency.provider('first_api') - class FirstImplementation(Interface): - def do_work(self): - return True - - @dependency.provider('second_api') - class SecondImplementation(Interface): - def do_work(self): - return True - - @dependency.requires('first_api', 'second_api') - class Consumer(object): - def do_work_with_dependencies(self): - assert self.first_api.do_work() - assert self.second_api.do_work() - - # initialize dependency providers - first_api = FirstImplementation() - second_api = SecondImplementation() - - # ... sometime later, initialize a dependency consumer - consumer = Consumer() - - # the expected dependencies should be available to the consumer - self.assertIs(consumer.first_api, first_api) - self.assertIs(consumer.second_api, second_api) - self.assertIsInstance(consumer.first_api, Interface) - self.assertIsInstance(consumer.second_api, Interface) - consumer.do_work_with_dependencies() - - def test_dependency_provider_configuration(self): - @dependency.provider('api') - class Configurable(object): - def __init__(self, value=None): - self.value = value - - def get_value(self): - return self.value - - @dependency.requires('api') - class Consumer(object): - def get_value(self): - return self.api.get_value() - - # initialize dependency providers - api = Configurable(value=True) - - # ... sometime later, initialize a dependency consumer - consumer = Consumer() - - # the expected dependencies should be available to the consumer - self.assertIs(consumer.api, api) - self.assertIsInstance(consumer.api, Configurable) - self.assertTrue(consumer.get_value()) - - def test_dependency_consumer_configuration(self): - @dependency.provider('api') - class Provider(object): - def get_value(self): - return True - - @dependency.requires('api') - class Configurable(object): - def __init__(self, value=None): - self.value = value - - def get_value(self): - if self.value: - return self.api.get_value() - - # initialize dependency providers - api = Provider() - - # ... sometime later, initialize a dependency consumer - consumer = Configurable(value=True) - - # the expected dependencies should be available to the consumer - self.assertIs(consumer.api, api) - self.assertIsInstance(consumer.api, Provider) - self.assertTrue(consumer.get_value()) - - def test_inherited_dependency(self): - class Interface(object): - def do_work(self): - assert False - - @dependency.provider('first_api') - class FirstImplementation(Interface): - def do_work(self): - return True - - @dependency.provider('second_api') - class SecondImplementation(Interface): - def do_work(self): - return True - - @dependency.requires('first_api') - class ParentConsumer(object): - def do_work_with_dependencies(self): - assert self.first_api.do_work() - - @dependency.requires('second_api') - class ChildConsumer(ParentConsumer): - def do_work_with_dependencies(self): - assert self.second_api.do_work() - super(ChildConsumer, self).do_work_with_dependencies() - - # initialize dependency providers - first_api = FirstImplementation() - second_api = SecondImplementation() - - # ... sometime later, initialize a dependency consumer - consumer = ChildConsumer() - - # dependencies should be naturally inherited - self.assertEqual( - set(['first_api']), - ParentConsumer._dependencies) - self.assertEqual( - set(['first_api', 'second_api']), - ChildConsumer._dependencies) - self.assertEqual( - set(['first_api', 'second_api']), - consumer._dependencies) - - # the expected dependencies should be available to the consumer - self.assertIs(consumer.first_api, first_api) - self.assertIs(consumer.second_api, second_api) - self.assertIsInstance(consumer.first_api, Interface) - self.assertIsInstance(consumer.second_api, Interface) - consumer.do_work_with_dependencies() - - def test_unresolvable_dependency(self): - @dependency.requires(uuid.uuid4().hex) - class Consumer(object): - pass - - def for_test(): - Consumer() - dependency.resolve_future_dependencies() - - self.assertRaises(dependency.UnresolvableDependencyException, for_test) - - def test_circular_dependency(self): - p1_name = uuid.uuid4().hex - p2_name = uuid.uuid4().hex - - @dependency.provider(p1_name) - @dependency.requires(p2_name) - class P1(object): - pass - - @dependency.provider(p2_name) - @dependency.requires(p1_name) - class P2(object): - pass - - p1 = P1() - p2 = P2() - - dependency.resolve_future_dependencies() - - self.assertIs(getattr(p1, p2_name), p2) - self.assertIs(getattr(p2, p1_name), p1) - - def test_reset(self): - # Can reset the registry of providers. - - p_id = uuid.uuid4().hex - - @dependency.provider(p_id) - class P(object): - pass - - p_inst = P() - - self.assertIs(dependency.get_provider(p_id), p_inst) - - dependency.reset() - - self.assertFalse(dependency._REGISTRY) - - def test_get_provider(self): - # Can get the instance of a provider using get_provider - - provider_name = uuid.uuid4().hex - - @dependency.provider(provider_name) - class P(object): - pass - - provider_instance = P() - retrieved_provider_instance = dependency.get_provider(provider_name) - self.assertIs(provider_instance, retrieved_provider_instance) - - def test_get_provider_not_provided_error(self): - # If no provider and provider is required then fails. - - provider_name = uuid.uuid4().hex - self.assertRaises(KeyError, dependency.get_provider, provider_name) - - def test_get_provider_not_provided_optional(self): - # If no provider and provider is optional then returns None. - - provider_name = uuid.uuid4().hex - self.assertIsNone(dependency.get_provider(provider_name, - dependency.GET_OPTIONAL)) diff --git a/keystone/tests/unit/common/test_provider_api.py b/keystone/tests/unit/common/test_provider_api.py new file mode 100644 index 0000000000..285ae1c319 --- /dev/null +++ b/keystone/tests/unit/common/test_provider_api.py @@ -0,0 +1,64 @@ +# Copyright 2012 OpenStack Foundation +# +# 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 uuid + +from keystone.common import manager +from keystone.common import provider_api +from keystone.tests import unit + + +class TestProviderAPIRegistry(unit.BaseTestCase): + def setUp(self): + super(TestProviderAPIRegistry, self).setUp() + provider_api.ProviderAPIs._clear_registry_instances() + self.addCleanup(provider_api.ProviderAPIs._clear_registry_instances) + + def _create_manager_instance(self, provides_api=None): + provides_api = provides_api or '%s_api' % uuid.uuid4().hex + + class TestManager(manager.Manager): + _provides_api = provides_api + driver_namespace = '_TEST_NOTHING' + + return TestManager(driver_name=None) + + def test_registry_lock(self): + provider_api.ProviderAPIs.lock_provider_registry() + self.assertRaises(RuntimeError, self._create_manager_instance) + + def test_registry_duplicate(self): + test_manager = self._create_manager_instance() + self.assertRaises( + provider_api.DuplicateProviderError, + self._create_manager_instance, + provides_api=test_manager._provides_api) + + def test_provider_api_mixin(self): + test_manager = self._create_manager_instance() + + class Testing(provider_api.ProviderAPIMixin, object): + pass + + instance = Testing() + self.assertIs(test_manager, getattr(instance, + test_manager._provides_api)) + + def test_manager_api_reference(self): + manager = self._create_manager_instance() + second_manager = self._create_manager_instance() + self.assertIs(second_manager, getattr(manager, + second_manager._provides_api)) + self.assertIs(manager, getattr(second_manager, + manager._provides_api)) diff --git a/keystone/tests/unit/core.py b/keystone/tests/unit/core.py index 4f18e8fcfe..5e3e69fa12 100644 --- a/keystone/tests/unit/core.py +++ b/keystone/tests/unit/core.py @@ -41,7 +41,7 @@ import testtools from testtools import testcase from keystone.common import context -from keystone.common import dependency +from keystone.common import provider_api from keystone.common import request from keystone.common import sql import keystone.conf @@ -677,7 +677,7 @@ class TestCase(BaseTestCase): # Clear the registry of providers so that providers from previous # tests aren't used. - self.addCleanup(dependency.reset) + self.addCleanup(provider_api.ProviderAPIs._clear_registry_instances) # Ensure Notification subscriptions and resource types are empty self.addCleanup(notifications.clear_subscribers) @@ -691,6 +691,10 @@ class TestCase(BaseTestCase): def load_backends(self): """Initialize each manager and assigns them to an attribute.""" + # TODO(morgan): Ensure our tests only ever call load_backends + # a single time via this method. for now just clear the registry + # if we are reloading. + provider_api.ProviderAPIs._clear_registry_instances() self.useFixture(ksfixtures.BackendLoader(self)) def load_fixtures(self, fixtures): diff --git a/keystone/tests/unit/ksfixtures/backendloader.py b/keystone/tests/unit/ksfixtures/backendloader.py index 565c2be5fd..217b090a56 100644 --- a/keystone/tests/unit/ksfixtures/backendloader.py +++ b/keystone/tests/unit/ksfixtures/backendloader.py @@ -14,7 +14,6 @@ import fixtures from keystone import auth -from keystone.common import dependency from keystone.server import common @@ -28,11 +27,6 @@ class BackendLoader(fixtures.Fixture): def setUp(self): super(BackendLoader, self).setUp() - # TODO(blk-u): Shouldn't need to clear the registry here, but some - # tests call load_backends multiple times. These should be fixed to - # only call load_backends once. - dependency.reset() - self.clear_auth_plugin_registry() drivers, _unused = common.setup_backends() diff --git a/keystone/tests/unit/test_cli.py b/keystone/tests/unit/test_cli.py index 0050af1673..83ef7dae0f 100644 --- a/keystone/tests/unit/test_cli.py +++ b/keystone/tests/unit/test_cli.py @@ -37,7 +37,7 @@ from keystone.cmd.doctor import ldap from keystone.cmd.doctor import security_compliance from keystone.cmd.doctor import tokens from keystone.cmd.doctor import tokens_fernet -from keystone.common import dependency +from keystone.common import provider_api from keystone.common.sql import upgrades import keystone.conf from keystone import exception @@ -61,6 +61,11 @@ class CliTestCase(unit.SQLDriverOverrides, unit.TestCase): def test_token_flush(self): self.useFixture(database.Database()) self.load_backends() + # NOTE(morgan): we are testing a direct instantiation of the + # persistence manager for flushing. We should clear this out so we + # don't error. CLI should never impact a running service + # and should never actually lock the registry for dependencies. + provider_api.ProviderAPIs._clear_registry_instances() cli.TokenFlush.main() @@ -463,7 +468,7 @@ class CliDomainConfigAllTestCase(unit.SQLDriverOverrides, unit.TestCase): } # Clear backend dependencies, since cli loads these manually - dependency.reset() + provider_api.ProviderAPIs._clear_registry_instances() cli.DomainConfigUpload.main() res = self.domain_config_api.get_config_with_sensitive_info( @@ -495,7 +500,7 @@ class CliDomainConfigSingleDomainTestCase(CliDomainConfigAllTestCase): } # Clear backend dependencies, since cli loads these manually - dependency.reset() + provider_api.ProviderAPIs._clear_registry_instances() cli.DomainConfigUpload.main() res = self.domain_config_api.get_config_with_sensitive_info( @@ -519,7 +524,7 @@ class CliDomainConfigSingleDomainTestCase(CliDomainConfigAllTestCase): # Now try and upload the settings in the configuration file for the # default domain - dependency.reset() + provider_api.ProviderAPIs._clear_registry_instances() with mock.patch('six.moves.builtins.print') as mock_print: self.assertRaises(unit.UnexpectedExit, cli.DomainConfigUpload.main) file_name = ('keystone.%s.conf' % self.default_domain['name']) @@ -544,7 +549,7 @@ class CliDomainConfigNoOptionsTestCase(CliDomainConfigAllTestCase): project='keystone', default_config_files=config_files) def test_config_upload(self): - dependency.reset() + provider_api.ProviderAPIs._clear_registry_instances() with mock.patch('six.moves.builtins.print') as mock_print: self.assertRaises(unit.UnexpectedExit, cli.DomainConfigUpload.main) mock_print.assert_has_calls( @@ -561,7 +566,7 @@ class CliDomainConfigTooManyOptionsTestCase(CliDomainConfigAllTestCase): project='keystone', default_config_files=config_files) def test_config_upload(self): - dependency.reset() + provider_api.ProviderAPIs._clear_registry_instances() with mock.patch('six.moves.builtins.print') as mock_print: self.assertRaises(unit.UnexpectedExit, cli.DomainConfigUpload.main) mock_print.assert_has_calls( @@ -578,7 +583,7 @@ class CliDomainConfigInvalidDomainTestCase(CliDomainConfigAllTestCase): project='keystone', default_config_files=config_files) def test_config_upload(self): - dependency.reset() + provider_api.ProviderAPIs._clear_registry_instances() with mock.patch('six.moves.builtins.print') as mock_print: self.assertRaises(unit.UnexpectedExit, cli.DomainConfigUpload.main) file_name = 'keystone.%s.conf' % self.invalid_domain_name @@ -742,7 +747,8 @@ class TestMappingPopulate(unit.SQLDriverOverrides, unit.TestCase): 'entity_type': identity_mapping.EntityType.USER} self.assertIsNone(self.id_mapping_api.get_public_id(local_entity)) - dependency.reset() # backends are loaded again in the command handler + # backends are loaded again in the command handler + provider_api.ProviderAPIs._clear_registry_instances() cli.MappingPopulate.main() for user in users: @@ -756,7 +762,8 @@ class TestMappingPopulate(unit.SQLDriverOverrides, unit.TestCase): def test_bad_domain_name(self): CONF(args=['mapping_populate', '--domain-name', uuid.uuid4().hex], project='keystone') - dependency.reset() # backends are loaded again in the command handler + # backends are loaded again in the command handler + provider_api.ProviderAPIs._clear_registry_instances() # NOTE: assertEqual is used on purpose. assertFalse passes with None. self.assertEqual(False, cli.MappingPopulate.main()) diff --git a/keystone/tests/unit/test_token_provider.py b/keystone/tests/unit/test_token_provider.py index 5ab5ae094a..3bca9a30bd 100644 --- a/keystone/tests/unit/test_token_provider.py +++ b/keystone/tests/unit/test_token_provider.py @@ -16,7 +16,6 @@ import datetime from oslo_utils import timeutils -from keystone.common import dependency from keystone.common import utils import keystone.conf from keystone import exception @@ -24,8 +23,6 @@ from keystone.tests import unit from keystone.tests.unit import ksfixtures from keystone.tests.unit.ksfixtures import database from keystone import token -from keystone.token.providers import fernet -from keystone.token.providers import uuid CONF = keystone.conf.CONF @@ -464,21 +461,6 @@ class TestTokenProvider(unit.TestCase): self.token_provider_api.get_token_version, 'bogus') - def test_supported_token_providers(self): - # test default config - - dependency.reset() - self.assertIsInstance(token.provider.Manager().driver, - fernet.Provider) - - dependency.reset() - self.config_fixture.config(group='token', provider='uuid') - self.assertIsInstance(token.provider.Manager().driver, uuid.Provider) - - dependency.reset() - self.config_fixture.config(group='token', provider='fernet') - self.assertIsInstance(token.provider.Manager().driver, fernet.Provider) - def test_unsupported_token_provider(self): self.config_fixture.config(group='token', provider='MyProvider') diff --git a/keystone/tests/unit/test_v3_auth.py b/keystone/tests/unit/test_v3_auth.py index 85ebb138bf..2abea1891c 100644 --- a/keystone/tests/unit/test_v3_auth.py +++ b/keystone/tests/unit/test_v3_auth.py @@ -2143,7 +2143,8 @@ class TokenAPITests(object): # flush the tokens, this will only have an effect on sql try: - self.token_provider_api._persistence.flush_expired_tokens() + provider_api = self.token_provider_api + provider_api._persistence.flush_expired_tokens() except exception.NotImplemented: pass diff --git a/keystone/tests/unit/test_v3_oauth1.py b/keystone/tests/unit/test_v3_oauth1.py index b5cb5025ae..f77951fae3 100644 --- a/keystone/tests/unit/test_v3_oauth1.py +++ b/keystone/tests/unit/test_v3_oauth1.py @@ -535,13 +535,15 @@ class AuthTokenTests(object): def test_delete_keystone_tokens_by_consumer_id(self): self.test_oauth_flow() - self.token_provider_api._persistence.get_token(self.keystone_token_id) + self.token_provider_api._persistence.get_token( + self.keystone_token_id) self.token_provider_api._persistence.delete_tokens( self.user_id, consumer_id=self.consumer['key']) - self.assertRaises(exception.TokenNotFound, - self.token_provider_api._persistence.get_token, - self.keystone_token_id) + self.assertRaises( + exception.TokenNotFound, + self.token_provider_api._persistence.get_token, + self.keystone_token_id) def _create_trust_get_token(self): ref = unit.new_trust_ref( diff --git a/keystone/token/_simple_cert.py b/keystone/token/_simple_cert.py index 301ea383cd..793b1820c5 100644 --- a/keystone/token/_simple_cert.py +++ b/keystone/token/_simple_cert.py @@ -17,7 +17,6 @@ import functools import webob from keystone.common import controller -from keystone.common import dependency from keystone.common import extension from keystone.common import json_home from keystone.common import wsgi @@ -69,7 +68,6 @@ class Routers(wsgi.RoutersBase): rel=build_resource_relation(resource_name='certificates')) -@dependency.requires('token_provider_api') class SimpleCert(controller.V3Controller): def _get_certificate(self, name): diff --git a/keystone/token/controllers.py b/keystone/token/controllers.py index db56a21c41..336cb7b3f4 100644 --- a/keystone/token/controllers.py +++ b/keystone/token/controllers.py @@ -13,7 +13,7 @@ # under the License. from keystone.common import controller -from keystone.common import dependency +from keystone.common import provider_api from keystone.common import utils from keystone.common import wsgi import keystone.conf @@ -58,8 +58,7 @@ class ExternalAuthNotApplicable(Exception): pass -@dependency.requires('resource_api', 'identity_api') -class BaseAuthenticationMethod(object): +class BaseAuthenticationMethod(provider_api.ProviderAPIMixin, object): """Common utilities/dependencies for all authentication method classes.""" def _get_project_id_from_auth(self, auth): @@ -99,7 +98,6 @@ class BaseAuthenticationMethod(object): return project_id -@dependency.requires('token_provider_api', 'trust_api') class TokenAuthenticationMethod(BaseAuthenticationMethod): """Authenticate using an existing token.""" @@ -280,9 +278,7 @@ class ExternalAuthenticationMethod(BaseAuthenticationMethod): return (user_ref, tenant_id, expiry, bind, audit_id) -@dependency.requires('catalog_api', 'resource_api', - 'trust_api', 'identity_api') -class V2TokenDataHelper(object): +class V2TokenDataHelper(provider_api.ProviderAPIMixin, object): """Create V2 token data.""" def v3_to_v2_token(self, v3_token_data, token_id): diff --git a/keystone/token/persistence/__init__.py b/keystone/token/persistence/__init__.py index 49a8e93ed3..1cb6f3a444 100644 --- a/keystone/token/persistence/__init__.py +++ b/keystone/token/persistence/__init__.py @@ -13,4 +13,4 @@ from keystone.token.persistence.core import * # noqa -__all__ = ('Manager', 'TokenDriverBase') +__all__ = ('PersistenceManager', 'TokenDriverBase') diff --git a/keystone/token/persistence/core.py b/keystone/token/persistence/core.py index 25bd45e48c..fb4b9072db 100644 --- a/keystone/token/persistence/core.py +++ b/keystone/token/persistence/core.py @@ -21,7 +21,6 @@ from oslo_log import log import six from keystone.common import cache -from keystone.common import dependency from keystone.common import manager import keystone.conf from keystone import exception @@ -34,8 +33,6 @@ REVOCATION_MEMOIZE = cache.get_memoization_decorator(group='token', expiration_group='revoke') -@dependency.requires('assignment_api', 'identity_api', 'resource_api', - 'trust_api') class PersistenceManager(manager.Manager): """Default pivot point for the Token Persistence backend. @@ -45,6 +42,7 @@ class PersistenceManager(manager.Manager): """ driver_namespace = 'keystone.token.persistence' + _provides_api = '_token_persistence_manager' def __init__(self): super(PersistenceManager, self).__init__(CONF.token.driver) @@ -167,37 +165,6 @@ class PersistenceManager(manager.Manager): self._get_token.invalidate(self, token_id) -@dependency.requires('token_provider_api') -@dependency.provider('token_api') -class Manager(object): - """The token_api provider. - - This class is a proxy class to the token_provider_api's persistence - manager. - """ - - def __init__(self): - # NOTE(morganfainberg): __init__ is required for dependency processing. - super(Manager, self).__init__() - - def __getattr__(self, item): - """Forward calls to the `token_provider_api` persistence manager.""" - # NOTE(morganfainberg): Prevent infinite recursion, raise an - # AttributeError for 'token_provider_api' ensuring that the dep - # injection doesn't infinitely try and lookup self.token_provider_api - # on _process_dependencies. This doesn't need an exception string as - # it should only ever be hit on instantiation. - if item == 'token_provider_api': - raise AttributeError() - - f = getattr(self.token_provider_api._persistence, item) - LOG.warning('`token_api.%s` is deprecated as of Juno in favor of ' - 'utilizing methods on `token_provider_api` and may be ' - 'removed in Kilo.', item) - setattr(self, item, f) - return f - - @six.add_metaclass(abc.ABCMeta) class TokenDriverBase(object): """Interface description for a Token driver.""" diff --git a/keystone/token/provider.py b/keystone/token/provider.py index df312fc849..b730419d17 100644 --- a/keystone/token/provider.py +++ b/keystone/token/provider.py @@ -22,14 +22,12 @@ from oslo_utils import timeutils import six from keystone.common import cache -from keystone.common import dependency from keystone.common import manager import keystone.conf from keystone import exception from keystone.i18n import _ from keystone.models import token_model from keystone import notifications -from keystone.token import persistence CONF = keystone.conf.CONF @@ -49,8 +47,6 @@ V3 = token_model.V3 VERSIONS = token_model.VERSIONS -@dependency.provider('token_provider_api') -@dependency.requires('assignment_api', 'revoke_api') class Manager(manager.Manager): """Default pivot point for the token provider backend. @@ -60,6 +56,7 @@ class Manager(manager.Manager): """ driver_namespace = 'keystone.token.provider' + _provides_api = 'token_provider_api' V3 = V3 VERSIONS = VERSIONS @@ -111,7 +108,7 @@ class Manager(manager.Manager): # the provider manager requires the token persistence manager, which # requires the token provider manager). if self._persistence_manager is None: - self._persistence_manager = persistence.PersistenceManager() + self._persistence_manager = self._token_persistence_manager return self._persistence_manager def _create_token(self, token_id, token_data): diff --git a/keystone/token/providers/common.py b/keystone/token/providers/common.py index d0e6512d52..eea8b0685a 100644 --- a/keystone/token/providers/common.py +++ b/keystone/token/providers/common.py @@ -24,7 +24,7 @@ from oslo_utils import timeutils import six from six.moves.urllib import parse -from keystone.common import dependency +from keystone.common import provider_api from keystone.common import utils import keystone.conf from keystone import exception @@ -82,10 +82,7 @@ def build_audit_info(parent_audit_id=None): return [audit_id] -@dependency.requires('assignment_api', 'catalog_api', 'federation_api', - 'identity_api', 'resource_api', 'role_api', 'trust_api', - 'oauth_api') -class V3TokenDataHelper(object): +class V3TokenDataHelper(provider_api.ProviderAPIMixin, object): """Token data helper.""" def __init__(self): @@ -451,9 +448,7 @@ class V3TokenDataHelper(object): return {'token': token_data} -@dependency.requires('catalog_api', 'identity_api', 'oauth_api', - 'resource_api', 'role_api', 'trust_api') -class BaseProvider(base.Provider): +class BaseProvider(provider_api.ProviderAPIMixin, base.Provider): def __init__(self, *args, **kwargs): super(BaseProvider, self).__init__(*args, **kwargs) self.v3_token_data_helper = V3TokenDataHelper() diff --git a/keystone/token/providers/fernet/core.py b/keystone/token/providers/fernet/core.py index bbcfdf4370..4696e71834 100644 --- a/keystone/token/providers/fernet/core.py +++ b/keystone/token/providers/fernet/core.py @@ -13,7 +13,6 @@ import os -from keystone.common import dependency from keystone.common import utils as ks_utils import keystone.conf from keystone.federation import constants as federation_constants @@ -25,7 +24,6 @@ from keystone.token.providers.fernet import token_formatters as tf CONF = keystone.conf.CONF -@dependency.requires('trust_api', 'oauth_api', 'identity_api') class Provider(common.BaseProvider): def __init__(self, *args, **kwargs): super(Provider, self).__init__(*args, **kwargs) diff --git a/keystone/trust/controllers.py b/keystone/trust/controllers.py index dac848c1f2..ebb5842abc 100644 --- a/keystone/trust/controllers.py +++ b/keystone/trust/controllers.py @@ -18,7 +18,6 @@ from oslo_utils import timeutils from keystone import assignment from keystone.common import controller -from keystone.common import dependency from keystone.common import driver_hints from keystone.common import utils from keystone.common import validation @@ -34,8 +33,6 @@ def _trustor_trustee_only(trust, user_id): action=_('Requested user has no relation to this trust')) -@dependency.requires('assignment_api', 'identity_api', 'resource_api', - 'role_api', 'token_provider_api', 'trust_api') class TrustV3(controller.V3Controller): collection_name = "trusts" member_name = "trust" diff --git a/keystone/trust/core.py b/keystone/trust/core.py index 84f3b707e2..d4d17266e1 100644 --- a/keystone/trust/core.py +++ b/keystone/trust/core.py @@ -16,7 +16,6 @@ from six.moves import zip -from keystone.common import dependency from keystone.common import manager import keystone.conf from keystone import exception @@ -27,8 +26,6 @@ from keystone import notifications CONF = keystone.conf.CONF -@dependency.requires('identity_api') -@dependency.provider('trust_api') class Manager(manager.Manager): """Default pivot point for the Trust backend. @@ -38,6 +35,7 @@ class Manager(manager.Manager): """ driver_namespace = 'keystone.trust' + _provides_api = 'trust_api' _TRUST = "OS-TRUST:trust"