Remove Dependency Injection

Refactors all of keystone's dependency injection to maintain a
single centralized repository of instantiated objects. This
means that we are no longer having to resolve order. All
objects that need to reference the various manager APIs simply
do so via the __getattr__ built into the Manager common object
or the ProviderAPIMixin object.

This is also the first step towards correcting our tests to
where they cannot run "load_backends" multiple times.

This forces any/all managers to properly run super()
as the way to register the api is via __init__.

This eliminates all use of the @dependency.requires and
@dependency.provides decorators, simplifying the objects
all around.

Any instantiations of a Manager after keystone is running
will now generate an error, ensuring everything for keystone
is running before handling requests. An exception is for
CLI and CLI tests, as the CLI may directly instantiate
managers and will not lock the registry.

Change-Id: I4ba17855efd797c0db9f4824936b49e4bff54b6a
This commit is contained in:
Morgan Fainberg 2017-12-04 15:49:48 -08:00
parent 3cc3986a89
commit 81f9fe6fed
65 changed files with 293 additions and 678 deletions

View File

@ -42,6 +42,7 @@ class AssignmentType(object):
class Assignment(base.AssignmentDriverBase):
@classmethod
def default_role_driver(self):
return 'sql'

View File

@ -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()."""

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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):

View File

@ -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.

View File

@ -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):

View File

@ -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."""

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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'

View File

@ -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'

View File

@ -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

View File

@ -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."""

View File

@ -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):
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)

View File

@ -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()

View File

@ -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.

View File

@ -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'

View File

@ -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'

View File

@ -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)

View File

@ -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)

View File

@ -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'

View File

@ -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)

View File

@ -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."""

View File

@ -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)

View File

@ -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'

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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."""

View File

@ -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'

View File

@ -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"

View File

@ -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

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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

View File

@ -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):

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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))

View File

@ -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):

View File

@ -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()

View File

@ -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())

View File

@ -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')

View File

@ -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

View File

@ -535,11 +535,13 @@ 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.assertRaises(
exception.TokenNotFound,
self.token_provider_api._persistence.get_token,
self.keystone_token_id)

View File

@ -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):

View File

@ -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):

View File

@ -13,4 +13,4 @@
from keystone.token.persistence.core import * # noqa
__all__ = ('Manager', 'TokenDriverBase')
__all__ = ('PersistenceManager', 'TokenDriverBase')

View File

@ -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."""

View File

@ -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):

View File

@ -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()

View File

@ -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)

View File

@ -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"

View File

@ -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"