summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDolph Mathews <dolph.mathews@gmail.com>2012-12-19 10:04:21 -0600
committerDolph Mathews <dolph.mathews@gmail.com>2012-12-21 11:57:44 -0600
commit03eb2801a3ad38a39e9cf127c05ab710bf38ee1d (patch)
treec91ae80657c574cabcecf5abfa78f3940392c517
parentac2d92ca2eea1070f765be320acb62fd5bef6dd3 (diff)
downloadkeystone-03eb2801a3ad38a39e9cf127c05ab710bf38ee1d.tar.gz
keystone-03eb2801a3ad38a39e9cf127c05ab710bf38ee1d.tar.xz
keystone-03eb2801a3ad38a39e9cf127c05ab710bf38ee1d.zip
Driver registry
Uses automatic dependency injection to provide controllers with driver interfaces (identity_api, token_api, etc). See tests/test_injection.py for a self-contained example. Change-Id: I255087de534292fbf57a45b19f97488f831f607c
-rw-r--r--keystone/catalog/controllers.py11
-rw-r--r--keystone/catalog/core.py2
-rw-r--r--keystone/catalog/routers.py8
-rw-r--r--keystone/common/controller.py10
-rw-r--r--keystone/common/dependency.py67
-rw-r--r--keystone/contrib/admin_crud/core.py17
-rw-r--r--keystone/contrib/ec2/core.py17
-rw-r--r--keystone/contrib/user_crud/core.py11
-rw-r--r--keystone/identity/controllers.py4
-rw-r--r--keystone/identity/core.py8
-rw-r--r--keystone/identity/routers.py28
-rw-r--r--keystone/policy/core.py2
-rw-r--r--keystone/policy/routers.py4
-rw-r--r--keystone/routers.py13
-rw-r--r--keystone/service.py32
-rw-r--r--keystone/test.py22
-rw-r--r--keystone/token/controllers.py2
-rw-r--r--keystone/token/core.py4
-rw-r--r--keystone/token/routers.py6
-rw-r--r--tests/test_auth.py14
-rw-r--r--tests/test_injection.py141
21 files changed, 297 insertions, 126 deletions
diff --git a/keystone/catalog/controllers.py b/keystone/catalog/controllers.py
index 42ba4ed7..78afffe7 100644
--- a/keystone/catalog/controllers.py
+++ b/keystone/catalog/controllers.py
@@ -17,20 +17,16 @@
import uuid
-from keystone.catalog import core
from keystone.common import controller
-from keystone.common import wsgi
+from keystone.common import dependency
from keystone import exception
-from keystone import identity
-from keystone import policy
-from keystone import token
INTERFACES = ['public', 'internal', 'admin']
+@dependency.requires('catalog_api')
class Service(controller.V2Controller):
-
def get_services(self, context):
self.assert_admin(context)
service_list = self.catalog_api.list_services(context)
@@ -55,6 +51,7 @@ class Service(controller.V2Controller):
return {'OS-KSADM:service': new_service_ref}
+@dependency.requires('catalog_api')
class Endpoint(controller.V2Controller):
def get_endpoints(self, context):
"""Merge matching v3 endpoint refs into legacy refs."""
@@ -115,6 +112,7 @@ class Endpoint(controller.V2Controller):
raise exception.EndpointNotFound(endpoint_id=endpoint_id)
+@dependency.requires('catalog_api')
class ServiceV3(controller.V3Controller):
@controller.protected
def create_service(self, context, service):
@@ -147,6 +145,7 @@ class ServiceV3(controller.V3Controller):
return self.catalog_api.delete_service(context, service_id)
+@dependency.requires('catalog_api')
class EndpointV3(controller.V3Controller):
@controller.protected
def create_endpoint(self, context, endpoint):
diff --git a/keystone/catalog/core.py b/keystone/catalog/core.py
index 7a8e8be7..7afeed95 100644
--- a/keystone/catalog/core.py
+++ b/keystone/catalog/core.py
@@ -17,6 +17,7 @@
"""Main entry point into the Catalog service."""
+from keystone.common import dependency
from keystone.common import logging
from keystone.common import manager
from keystone import config
@@ -51,6 +52,7 @@ def format_url(url, data):
return result
+@dependency.provider('catalog_api')
class Manager(manager.Manager):
"""Default pivot point for the Catalog backend.
diff --git a/keystone/catalog/routers.py b/keystone/catalog/routers.py
index f0c547f8..b3af4bc1 100644
--- a/keystone/catalog/routers.py
+++ b/keystone/catalog/routers.py
@@ -13,13 +13,13 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
+
from keystone.catalog import controllers
from keystone.common import router
-from keystone.common import wsgi
-def append_v3_routers(mapper, routers, apis):
- routers.append(router.Router(controllers.ServiceV3(**apis),
+def append_v3_routers(mapper, routers):
+ routers.append(router.Router(controllers.ServiceV3(),
'services', 'service'))
- routers.append(router.Router(controllers.EndpointV3(**apis),
+ routers.append(router.Router(controllers.EndpointV3(),
'endpoints', 'endpoint'))
diff --git a/keystone/common/controller.py b/keystone/common/controller.py
index 5f351411..ee7ce5b5 100644
--- a/keystone/common/controller.py
+++ b/keystone/common/controller.py
@@ -1,6 +1,7 @@
import uuid
import functools
+from keystone.common import dependency
from keystone.common import logging
from keystone.common import wsgi
from keystone import exception
@@ -55,15 +56,10 @@ def protected(f):
return wrapper
+@dependency.requires('identity_api', 'policy_api', 'token_api')
class V2Controller(wsgi.Application):
"""Base controller class for Identity API v2."""
-
- def __init__(self, catalog_api, identity_api, policy_api, token_api):
- self.catalog_api = catalog_api
- self.identity_api = identity_api
- self.policy_api = policy_api
- self.token_api = token_api
- super(V2Controller, self).__init__()
+ pass
class V3Controller(V2Controller):
diff --git a/keystone/common/dependency.py b/keystone/common/dependency.py
new file mode 100644
index 00000000..dc3e4ac4
--- /dev/null
+++ b/keystone/common/dependency.py
@@ -0,0 +1,67 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack LLC
+#
+# 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.
+
+REGISTRY = {}
+
+
+class UnresolvableDependencyException(Exception):
+ def __init__(self, name):
+ msg = 'Unregistered dependency: %s' % name
+ super(UnresolvableDependencyException, self).__init__(msg)
+
+
+def provider(name):
+ """Register the wrapped dependency provider under the specified name."""
+ 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)
+ REGISTRY[name] = self
+
+ return __wrapped_init__
+
+ cls.__init__ = wrapped(cls.__init__)
+ return cls
+
+ return wrapper
+
+
+def requires(*dependencies):
+ """Inject specified dependencies from the registry into the instance."""
+ def wrapper(self, *args, **kwargs):
+ """Inject each dependency from the registry."""
+ self.__wrapped_init__(*args, **kwargs)
+
+ for dependency in self._dependencies:
+ if dependency not in REGISTRY:
+ raise UnresolvableDependencyException(dependency)
+ setattr(self, dependency, REGISTRY[dependency])
+
+ 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
diff --git a/keystone/contrib/admin_crud/core.py b/keystone/contrib/admin_crud/core.py
index fe8c33b0..a5cfe392 100644
--- a/keystone/contrib/admin_crud/core.py
+++ b/keystone/contrib/admin_crud/core.py
@@ -16,8 +16,6 @@
from keystone import catalog
from keystone.common import wsgi
from keystone import identity
-from keystone import policy
-from keystone import token
class CrudExtension(wsgi.ExtensionRouter):
@@ -28,16 +26,11 @@ class CrudExtension(wsgi.ExtensionRouter):
"""
def add_routes(self, mapper):
- apis = dict(catalog_api=catalog.Manager(),
- identity_api=identity.Manager(),
- policy_api=policy.Manager(),
- token_api=token.Manager())
-
- tenant_controller = identity.controllers.Tenant(**apis)
- user_controller = identity.controllers.User(**apis)
- role_controller = identity.controllers.Role(**apis)
- service_controller = catalog.controllers.Service(**apis)
- endpoint_controller = catalog.controllers.Endpoint(**apis)
+ tenant_controller = identity.controllers.Tenant()
+ user_controller = identity.controllers.User()
+ role_controller = identity.controllers.Role()
+ service_controller = catalog.controllers.Service()
+ endpoint_controller = catalog.controllers.Endpoint()
# Tenant Operations
mapper.connect(
diff --git a/keystone/contrib/ec2/core.py b/keystone/contrib/ec2/core.py
index 078a845c..baef0bd9 100644
--- a/keystone/contrib/ec2/core.py
+++ b/keystone/contrib/ec2/core.py
@@ -36,20 +36,20 @@ glance to list images needed to perform the requested task.
import uuid
-from keystone import catalog
+from keystone.common import controller
+from keystone.common import dependency
from keystone.common import manager
from keystone.common import utils
from keystone.common import wsgi
from keystone import config
from keystone import exception
-from keystone import identity
-from keystone import policy
from keystone import token
CONF = config.CONF
+@dependency.provider('ec2_api')
class Manager(manager.Manager):
"""Default pivot point for the EC2 Credentials backend.
@@ -95,15 +95,8 @@ class Ec2Extension(wsgi.ExtensionRouter):
conditions=dict(method=['DELETE']))
-class Ec2Controller(wsgi.Application):
- def __init__(self):
- self.catalog_api = catalog.Manager()
- self.identity_api = identity.Manager()
- self.token_api = token.Manager()
- self.policy_api = policy.Manager()
- self.ec2_api = Manager()
- super(Ec2Controller, self).__init__()
-
+@dependency.requires('catalog_api', 'ec2_api')
+class Ec2Controller(controller.V2Controller):
def check_signature(self, creds_ref, credentials):
signer = utils.Ec2Signer(creds_ref['secret'])
signature = signer.generate(credentials)
diff --git a/keystone/contrib/user_crud/core.py b/keystone/contrib/user_crud/core.py
index e1238509..ae9b4c59 100644
--- a/keystone/contrib/user_crud/core.py
+++ b/keystone/contrib/user_crud/core.py
@@ -18,13 +18,9 @@ import copy
import uuid
from keystone import exception
-from keystone.common import controller
from keystone.common import logging
from keystone.common import wsgi
-from keystone import catalog
from keystone import identity
-from keystone import policy
-from keystone import token
LOG = logging.getLogger(__name__)
@@ -81,12 +77,7 @@ class CrudExtension(wsgi.ExtensionRouter):
"""
def add_routes(self, mapper):
- apis = dict(catalog_api=catalog.Manager(),
- identity_api=identity.Manager(),
- policy_api=policy.Manager(),
- token_api=token.Manager())
-
- user_controller = UserController(**apis)
+ user_controller = UserController()
mapper.connect('/OS-KSCRUD/users/{user_id}',
controller=user_controller,
diff --git a/keystone/identity/controllers.py b/keystone/identity/controllers.py
index 6ba04a92..4e325264 100644
--- a/keystone/identity/controllers.py
+++ b/keystone/identity/controllers.py
@@ -22,11 +22,7 @@ import uuid
from keystone.common import controller
from keystone.common import logging
-from keystone.common import wsgi
from keystone import exception
-from keystone.identity import core
-from keystone import policy
-from keystone import token
LOG = logging.getLogger(__name__)
diff --git a/keystone/identity/core.py b/keystone/identity/core.py
index e189df23..6d54f500 100644
--- a/keystone/identity/core.py
+++ b/keystone/identity/core.py
@@ -16,14 +16,9 @@
"""Main entry point into the Identity service."""
-import urllib
-import urlparse
-import uuid
-
-from keystone.common import controller
+from keystone.common import dependency
from keystone.common import logging
from keystone.common import manager
-from keystone.common import wsgi
from keystone import config
from keystone import exception
@@ -51,6 +46,7 @@ def filter_user(user_ref):
return user_ref
+@dependency.provider('identity_api')
class Manager(manager.Manager):
"""Default pivot point for the Identity backend.
diff --git a/keystone/identity/routers.py b/keystone/identity/routers.py
index 8e251805..e4385c33 100644
--- a/keystone/identity/routers.py
+++ b/keystone/identity/routers.py
@@ -20,12 +20,8 @@ from keystone.common import router
class Public(wsgi.ComposableRouter):
- def __init__(self, apis):
- self.apis = apis
- super(Public, self).__init__()
-
def add_routes(self, mapper):
- tenant_controller = controllers.Tenant(**self.apis)
+ tenant_controller = controllers.Tenant()
mapper.connect('/tenants',
controller=tenant_controller,
action='get_tenants_for_token',
@@ -33,13 +29,9 @@ class Public(wsgi.ComposableRouter):
class Admin(wsgi.ComposableRouter):
- def __init__(self, apis):
- self.apis = apis
- super(Admin, self).__init__()
-
def add_routes(self, mapper):
# Tenant Operations
- tenant_controller = controllers.Tenant(**self.apis)
+ tenant_controller = controllers.Tenant()
mapper.connect('/tenants',
controller=tenant_controller,
action='get_all_tenants',
@@ -50,14 +42,14 @@ class Admin(wsgi.ComposableRouter):
conditions=dict(method=['GET']))
# User Operations
- user_controller = controllers.User(**self.apis)
+ user_controller = controllers.User()
mapper.connect('/users/{user_id}',
controller=user_controller,
action='get_user',
conditions=dict(method=['GET']))
# Role Operations
- roles_controller = controllers.Role(**self.apis)
+ roles_controller = controllers.Role()
mapper.connect('/tenants/{tenant_id}/users/{user_id}/roles',
controller=roles_controller,
action='get_user_roles',
@@ -68,11 +60,11 @@ class Admin(wsgi.ComposableRouter):
conditions=dict(method=['GET']))
-def append_v3_routers(mapper, routers, apis):
+def append_v3_routers(mapper, routers):
routers.append(
- router.Router(controllers.DomainV3(**apis),
+ router.Router(controllers.DomainV3(),
'domains', 'domain'))
- project_controller = controllers.ProjectV3(**apis)
+ project_controller = controllers.ProjectV3()
routers.append(
router.Router(project_controller,
'projects', 'project'))
@@ -81,12 +73,12 @@ def append_v3_routers(mapper, routers, apis):
action='list_user_projects',
conditions=dict(method=['GET']))
routers.append(
- router.Router(controllers.UserV3(**apis),
+ router.Router(controllers.UserV3(),
'users', 'user'))
routers.append(
- router.Router(controllers.CredentialV3(**apis),
+ router.Router(controllers.CredentialV3(),
'credentials', 'credential'))
- role_controller = controllers.RoleV3(**apis)
+ role_controller = controllers.RoleV3()
routers.append(router.Router(role_controller, 'roles', 'role'))
mapper.connect('/projects/{project_id}/users/{user_id}/roles/{role_id}',
controller=role_controller,
diff --git a/keystone/policy/core.py b/keystone/policy/core.py
index a8d01f2f..e3abb3ba 100644
--- a/keystone/policy/core.py
+++ b/keystone/policy/core.py
@@ -17,6 +17,7 @@
"""Main entry point into the Policy service."""
+from keystone.common import dependency
from keystone.common import manager
from keystone import config
from keystone import exception
@@ -25,6 +26,7 @@ from keystone import exception
CONF = config.CONF
+@dependency.provider('policy_api')
class Manager(manager.Manager):
"""Default pivot point for the Policy backend.
diff --git a/keystone/policy/routers.py b/keystone/policy/routers.py
index 6c050757..dbb53e9c 100644
--- a/keystone/policy/routers.py
+++ b/keystone/policy/routers.py
@@ -17,6 +17,6 @@ from keystone.policy import controllers
from keystone.common import router
-def append_v3_routers(mapper, routers, apis):
- policy_controller = controllers.PolicyV3(**apis)
+def append_v3_routers(mapper, routers):
+ policy_controller = controllers.PolicyV3()
routers.append(router.Router(policy_controller, 'policies', 'policy'))
diff --git a/keystone/routers.py b/keystone/routers.py
index fa980c2c..83f277dd 100644
--- a/keystone/routers.py
+++ b/keystone/routers.py
@@ -14,17 +14,18 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
-The only types of routers in this file should be ComposingRouters.
-The routers for the submodules should be in the module specific router files
-for example, the Composable Router for identity belongs in
-keystone/identity/routers.py
+The only types of routers in this file should be ``ComposingRouters``.
+
+The routers for the backends should be in the backend-specific router modules.
+For example, the ``ComposableRouter`` for ``identity`` belongs in::
+
+ keystone.identity.routers
+
"""
from keystone.common import wsgi
-from keystone import catalog
from keystone import controllers
-from keystone import exception
class Extension(wsgi.ComposableRouter):
diff --git a/keystone/service.py b/keystone/service.py
index 33bc4cc6..cf245d3e 100644
--- a/keystone/service.py
+++ b/keystone/service.py
@@ -17,22 +17,22 @@
import routes
from keystone import catalog
+from keystone.contrib import ec2
from keystone.common import logging
from keystone.common import wsgi
-from keystone import exception
from keystone import identity
from keystone import policy
from keystone import routers
from keystone import token
-LOG = logging.getLogger(__name__)
-
-def _apis():
- return dict(catalog_api=catalog.Manager(),
- identity_api=identity.Manager(),
- policy_api=policy.Manager(),
- token_api=token.Manager())
+LOG = logging.getLogger(__name__)
+DRIVERS = dict(
+ catalog_api=catalog.Manager(),
+ ec2_api=ec2.Manager(),
+ identity_api=identity.Manager(),
+ policy_api=policy.Manager(),
+ token_api=token.Manager())
@logging.fail_gracefully
@@ -40,10 +40,10 @@ def public_app_factory(global_conf, **local_conf):
conf = global_conf.copy()
conf.update(local_conf)
return wsgi.ComposingRouter(routes.Mapper(),
- [identity.routers.Public(_apis()),
- token.routers.Router(_apis()),
- routers.Version('public'),
- routers.Extension(False)])
+ [identity.routers.Public(),
+ token.routers.Router(),
+ routers.Version('public'),
+ routers.Extension(False)])
@logging.fail_gracefully
@@ -51,8 +51,8 @@ def admin_app_factory(global_conf, **local_conf):
conf = global_conf.copy()
conf.update(local_conf)
return wsgi.ComposingRouter(routes.Mapper(),
- [identity.routers.Admin(_apis()),
- token.routers.Router(_apis()),
+ [identity.routers.Admin(),
+ token.routers.Router(),
routers.Version('admin'),
routers.Extension()])
@@ -80,6 +80,6 @@ def v3_app_factory(global_conf, **local_conf):
mapper = routes.Mapper()
v3routers = []
for module in [catalog, identity, policy]:
- module.routers.append_v3_routers(mapper, v3routers, _apis())
- #TODO put token routes here
+ module.routers.append_v3_routers(mapper, v3routers)
+ # TODO(ayoung): put token routes here
return wsgi.ComposingRouter(mapper, v3routers)
diff --git a/keystone/test.py b/keystone/test.py
index 97ca95fa..8c8520d0 100644
--- a/keystone/test.py
+++ b/keystone/test.py
@@ -31,7 +31,10 @@ from keystone.common import logging
from keystone.common import utils
from keystone.common import wsgi
from keystone import config
-from keystone.openstack.common import importutils
+from keystone import catalog
+from keystone import identity
+from keystone import policy
+from keystone import token
do_monkeypatch = not os.getenv('STANDARD_THREADS')
@@ -44,6 +47,7 @@ VENDOR = os.path.join(ROOTDIR, 'vendor')
TESTSDIR = os.path.join(ROOTDIR, 'tests')
ETCDIR = os.path.join(ROOTDIR, 'etc')
CONF = config.CONF
+DRIVERS = {}
cd = os.chdir
@@ -61,6 +65,14 @@ def testsdir(*p):
return os.path.join(TESTSDIR, *p)
+def initialize_drivers():
+ DRIVERS['catalog_api'] = catalog.Manager()
+ DRIVERS['identity_api'] = identity.Manager()
+ DRIVERS['policy_api'] = policy.Manager()
+ DRIVERS['token_api'] = token.Manager()
+ return DRIVERS
+
+
def checkout_vendor(repo, rev):
# TODO(termie): this function is a good target for some optimizations :PERF
name = repo.split('/')[-1]
@@ -202,11 +214,9 @@ class TestCase(NoModule, unittest.TestCase):
CONF.set_override(k, v)
def load_backends(self):
- """Hacky shortcut to load the backends for data manipulation."""
- self.identity_api = importutils.import_object(CONF.identity.driver)
- self.token_api = importutils.import_object(CONF.token.driver)
- self.catalog_api = importutils.import_object(CONF.catalog.driver)
- self.policy_api = importutils.import_object(CONF.policy.driver)
+ """Create shortcut references to each driver for data manipulation."""
+ for name, manager in initialize_drivers().iteritems():
+ setattr(self, name, manager.driver)
def load_fixtures(self, fixtures):
"""Hacky basic and naive fixture loading based on a python module.
diff --git a/keystone/token/controllers.py b/keystone/token/controllers.py
index 3a2d710d..105fc9a0 100644
--- a/keystone/token/controllers.py
+++ b/keystone/token/controllers.py
@@ -4,6 +4,7 @@ import json
from keystone import config
from keystone.common import cms
from keystone.common import controller
+from keystone.common import dependency
from keystone.common import logging
from keystone import exception
from keystone.openstack.common import timeutils
@@ -18,6 +19,7 @@ class ExternalAuthNotApplicable(Exception):
pass
+@dependency.requires('catalog_api')
class Auth(controller.V2Controller):
def ca_cert(self, context, auth=None):
ca_file = open(config.CONF.signing.ca_certs, 'r')
diff --git a/keystone/token/core.py b/keystone/token/core.py
index bb7a705b..e8da9f1d 100644
--- a/keystone/token/core.py
+++ b/keystone/token/core.py
@@ -18,8 +18,9 @@
import datetime
-from keystone.common import manager
from keystone.common import cms
+from keystone.common import dependency
+from keystone.common import manager
from keystone import config
from keystone import exception
from keystone.openstack.common import timeutils
@@ -54,6 +55,7 @@ def default_expire_time():
return timeutils.utcnow() + expire_delta
+@dependency.provider('token_api')
class Manager(manager.Manager):
"""Default pivot point for the Token backend.
diff --git a/keystone/token/routers.py b/keystone/token/routers.py
index 7ff883f6..6b22f648 100644
--- a/keystone/token/routers.py
+++ b/keystone/token/routers.py
@@ -18,12 +18,8 @@ from keystone.token import controllers
class Router(wsgi.ComposableRouter):
- def __init__(self, apis):
- self.apis = apis
- super(Router, self).__init__()
-
def add_routes(self, mapper):
- token_controller = controllers.Auth(**self.apis)
+ token_controller = controllers.Auth()
mapper.connect('/tokens',
controller=token_controller,
action='authenticate',
diff --git a/tests/test_auth.py b/tests/test_auth.py
index c0dbbd2c..75f06174 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -17,13 +17,9 @@ import uuid
import default_fixtures
-from keystone import catalog
from keystone import config
from keystone import exception
-from keystone import identity
-from keystone.identity.backends import kvs as kvs_identity
from keystone.openstack.common import timeutils
-from keystone import policy
from keystone import test
from keystone import token
@@ -56,15 +52,11 @@ class AuthTest(test.TestCase):
def setUp(self):
super(AuthTest, self).setUp()
- # load_fixtures checks for 'identity_api' to be defined
- self.identity_api = kvs_identity.Identity()
+ CONF.identity.driver = 'keystone.identity.backends.kvs.Identity'
+ self.load_backends()
self.load_fixtures(default_fixtures)
- self.api = token.controllers.Auth(
- catalog_api=catalog.Manager(),
- identity_api=identity.Manager(),
- policy_api=policy.Manager(),
- token_api=token.Manager())
+ self.api = token.controllers.Auth()
def assertEqualTokens(self, a, b):
"""Assert that two tokens are equal.
diff --git a/tests/test_injection.py b/tests/test_injection.py
new file mode 100644
index 00000000..4953cfca
--- /dev/null
+++ b/tests/test_injection.py
@@ -0,0 +1,141 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack LLC
+#
+# 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 unittest2 as unittest
+import uuid
+
+from keystone.common import dependency
+
+
+class TestDependencyInjection(unittest.TestCase):
+ 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_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_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(
+ ParentConsumer._dependencies,
+ set(['first_api']))
+ self.assertEqual(
+ ChildConsumer._dependencies,
+ set(['first_api', 'second_api']))
+ self.assertEqual(
+ consumer._dependencies,
+ set(['first_api', 'second_api']))
+
+ # 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
+
+ with self.assertRaises(dependency.UnresolvableDependencyException):
+ Consumer()