Permalink
Browse files

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
  • Loading branch information...
1 parent ac2d92c commit 03eb2801a3ad38a39e9cf127c05ab710bf38ee1d @dolph dolph committed Dec 19, 2012
@@ -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 @@ def create_service(self, context, OS_KSADM_service):
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 @@ def delete_endpoint(self, context, endpoint_id):
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 @@ def delete_service(self, context, service_id):
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):
View
@@ -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.
@@ -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'))
@@ -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 wrapper(self, context, **kwargs):
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):
@@ -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
@@ -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(
@@ -36,20 +36,20 @@
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 @@ def add_routes(self, mapper):
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)
@@ -18,13 +18,9 @@
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,
@@ -22,11 +22,7 @@
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__)
@@ -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.
@@ -20,26 +20,18 @@
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',
conditions=dict(method=['GET']))
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 @@ def add_routes(self, mapper):
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 @@ def add_routes(self, mapper):
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,
Oops, something went wrong.

0 comments on commit 03eb280

Please sign in to comment.