From f55dbc2f599ed56fb59c7f7a94cd81d3fd82c8dd Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 18:01:29 -0500 Subject: Rework how routing is done in ec2 endpoint. --- etc/nova-api.conf | 19 ++++++++++++++----- nova/api/ec2/__init__.py | 43 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/etc/nova-api.conf b/etc/nova-api.conf index c5dd0aaec..cf49b7254 100644 --- a/etc/nova-api.conf +++ b/etc/nova-api.conf @@ -12,19 +12,28 @@ openstack_address = 0.0.0.0 [composite:ec2] use = egg:Paste#urlmap /: ec2versions -/services: ec2api +/services/Cloud: ec2cloud +/services/Admin: ec2admin /latest: ec2metadata /200: ec2metadata /1.0: ec2metadata -[pipeline:ec2api] -pipeline = authenticate router authorizer ec2executor +[pipeline:ec2cloud] +pipeline = authenticate cloudrequest authorizer ec2executor + +[pipeline:ec2admin] +pipeline = authenticate adminrequest authorizer ec2executor [filter:authenticate] paste.filter_factory = nova.api.ec2:authenticate_factory -[filter:router] -paste.filter_factory = nova.api.ec2:router_factory +[filter:cloudrequest] +controller = nova.api.ec2.cloud.CloudController +paste.filter_factory = nova.api.ec2:requestify_factory + +[filter:adminrequest] +controller = nova.api.ec2.admin.AdminController +paste.filter_factory = nova.api.ec2:requestify_factory [filter:authorizer] paste.filter_factory = nova.api.ec2:authorizer_factory diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index aa3bfaeb4..a5810479e 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -29,6 +29,7 @@ import webob.exc from nova import context from nova import exception from nova import flags +from nova import utils from nova import wsgi from nova.api.ec2 import apirequest from nova.api.ec2 import admin @@ -157,6 +158,31 @@ class Authenticate(wsgi.Middleware): return self.application +class Requestify(wsgi.Middleware): + + def __init__(self, app, controller_name): + super(Requestify, self).__init__(app) + self.controller = utils.import_class(controller_name)() + + @webob.dec.wsgify + def __call__(self, req): + non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod', + 'SignatureVersion', 'Version', 'Timestamp'] + args = dict(req.params) + try: + # Raise KeyError if omitted + action = req.params['Action'] + for non_arg in non_args: + # Remove, but raise KeyError if omitted + args.pop(non_arg) + except: + raise webob.exc.HTTPBadRequest() + api_request = apirequest.APIRequest(self.controller, action) + req.environ['ec2.request'] = api_request + req.environ['ec2.action_args'] = args + return self.application + + class Router(wsgi.Middleware): """Add ec2.'controller', .'action', and .'action_args' to WSGI environ.""" @@ -256,10 +282,9 @@ class Authorizer(wsgi.Middleware): @webob.dec.wsgify def __call__(self, req): context = req.environ['ec2.context'] - controller_name = req.environ['ec2.controller'].__class__.__name__ - action = req.environ['ec2.action'] - allowed_roles = self.action_roles[controller_name].get(action, - ['none']) + controller = req.environ['ec2.request'].controller.__class__.__name__ + action = req.environ['ec2.request'].action + allowed_roles = self.action_roles[controller].get(action, ['none']) if self._matches_any_role(context, allowed_roles): return self.application else: @@ -289,11 +314,8 @@ class Executor(wsgi.Application): @webob.dec.wsgify def __call__(self, req): context = req.environ['ec2.context'] - controller = req.environ['ec2.controller'] - action = req.environ['ec2.action'] args = req.environ['ec2.action_args'] - - api_request = apirequest.APIRequest(controller, action) + api_request = req.environ['ec2.request'] result = None try: result = api_request.send(context, **args) @@ -369,3 +391,8 @@ def executor_factory(global_args, **local_args): def versions_factory(global_args, **local_args): return Versions() + +def requestify_factory(global_args, **local_args): + def requestifier(app): + return Requestify(app, local_args['controller']) + return requestifier -- cgit From 2491c2484f025cb3f061fcc6a5c6915006feb47b Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 18:16:16 -0500 Subject: Make paste the default api pattern. * get rid of the --use_lockout flag since it will be specified in paste config (Example line is commented out in etc/nova-api.conf, factory is in place) * remove old nova-api binary and promote nova-api-paste * change how we store ec2 parameters to bin the the ApiRequest * get rid of Router, since paste.urlmap is equally effective (Requestify now gets passed the name of the controller requests are to.) --- bin/nova-api | 75 ++++++++++++++++++++++++++----- bin/nova-api-paste | 109 --------------------------------------------- etc/nova-api.conf | 4 ++ nova/api/ec2/__init__.py | 74 +++--------------------------- nova/api/ec2/apirequest.py | 7 +-- 5 files changed, 79 insertions(+), 190 deletions(-) delete mode 100755 bin/nova-api-paste diff --git a/bin/nova-api b/bin/nova-api index 1c671201e..6ee833a18 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -21,9 +21,12 @@ """Starter script for Nova API.""" import gettext +import logging import os import sys +from paste import deploy + # If ../nova/__init__.py exists, add ../ to Python search path, so that # it will override what happens to be installed in /usr/(local/)lib/python... possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), @@ -34,23 +37,73 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): gettext.install('nova', unicode=1) -from nova import api from nova import flags -from nova import utils from nova import wsgi +LOG = logging.getLogger('nova.api') +LOG.setLevel(logging.DEBUG) +LOG.addHandler(logging.StreamHandler()) FLAGS = flags.FLAGS -flags.DEFINE_integer('osapi_port', 8774, 'OpenStack API port') -flags.DEFINE_string('osapi_host', '0.0.0.0', 'OpenStack API host') -flags.DEFINE_integer('ec2api_port', 8773, 'EC2 API port') -flags.DEFINE_string('ec2api_host', '0.0.0.0', 'EC2 API host') +API_ENDPOINTS = ['ec2', 'openstack'] -if __name__ == '__main__': - utils.default_flagfile() - FLAGS(sys.argv) + +def load_configuration(paste_config): + """Load the paste configuration from the config file and return it.""" + config = None + # Try each known name to get the global DEFAULTS, which will give ports + for name in API_ENDPOINTS: + try: + config = deploy.appconfig("config:%s" % paste_config, name=name) + except LookupError: + pass + if config: + verbose = config.get('verbose', None) + if verbose: + FLAGS.verbose = int(verbose) == 1 + if FLAGS.verbose: + logging.getLogger().setLevel(logging.DEBUG) + return config + LOG.debug(_("Paste config at %s has no secion for known apis"), + paste_config) + print _("Paste config at %s has no secion for any known apis") % \ + paste_config + os.exit(1) + + +def launch_api(paste_config_file, section, server, port, host): + """Launch an api server from the specified port and IP.""" + LOG.debug(_("Launching %s api on %s:%s"), section, host, port) + app = deploy.loadapp('config:%s' % paste_config_file, name=section) + server.start(app, int(port), host) + + +def run_app(paste_config_file): + LOG.debug(_("Using paste.deploy config at: %s"), configfile) + config = load_configuration(paste_config_file) + LOG.debug(_("Configuration: %r"), config) server = wsgi.Server() - server.start(api.API('os'), FLAGS.osapi_port, host=FLAGS.osapi_host) - server.start(api.API('ec2'), FLAGS.ec2api_port, host=FLAGS.ec2api_host) + ip = config.get('host', '0.0.0.0') + for api in API_ENDPOINTS: + port = config.get("%s_port" % api, None) + if not port: + continue + host = config.get("%s_host" % api, ip) + launch_api(configfile, api, server, port, host) + LOG.debug(_("All api servers launched, now waiting")) server.wait() + + +if __name__ == '__main__': + FLAGS(sys.argv) + configfiles = ['/etc/nova/nova-api.conf'] + if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): + configfiles.insert(0, + os.path.join(possible_topdir, 'etc', 'nova-api.conf')) + for configfile in configfiles: + if os.path.exists(configfile): + run_app(configfile) + break + else: + LOG.debug(_("Skipping missing configuration: %s"), configfile) diff --git a/bin/nova-api-paste b/bin/nova-api-paste deleted file mode 100755 index 6ee833a18..000000000 --- a/bin/nova-api-paste +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python -# pylint: disable-msg=C0103 -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# 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. - -"""Starter script for Nova API.""" - -import gettext -import logging -import os -import sys - -from paste import deploy - -# If ../nova/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): - sys.path.insert(0, possible_topdir) - -gettext.install('nova', unicode=1) - -from nova import flags -from nova import wsgi - -LOG = logging.getLogger('nova.api') -LOG.setLevel(logging.DEBUG) -LOG.addHandler(logging.StreamHandler()) - -FLAGS = flags.FLAGS - -API_ENDPOINTS = ['ec2', 'openstack'] - - -def load_configuration(paste_config): - """Load the paste configuration from the config file and return it.""" - config = None - # Try each known name to get the global DEFAULTS, which will give ports - for name in API_ENDPOINTS: - try: - config = deploy.appconfig("config:%s" % paste_config, name=name) - except LookupError: - pass - if config: - verbose = config.get('verbose', None) - if verbose: - FLAGS.verbose = int(verbose) == 1 - if FLAGS.verbose: - logging.getLogger().setLevel(logging.DEBUG) - return config - LOG.debug(_("Paste config at %s has no secion for known apis"), - paste_config) - print _("Paste config at %s has no secion for any known apis") % \ - paste_config - os.exit(1) - - -def launch_api(paste_config_file, section, server, port, host): - """Launch an api server from the specified port and IP.""" - LOG.debug(_("Launching %s api on %s:%s"), section, host, port) - app = deploy.loadapp('config:%s' % paste_config_file, name=section) - server.start(app, int(port), host) - - -def run_app(paste_config_file): - LOG.debug(_("Using paste.deploy config at: %s"), configfile) - config = load_configuration(paste_config_file) - LOG.debug(_("Configuration: %r"), config) - server = wsgi.Server() - ip = config.get('host', '0.0.0.0') - for api in API_ENDPOINTS: - port = config.get("%s_port" % api, None) - if not port: - continue - host = config.get("%s_host" % api, ip) - launch_api(configfile, api, server, port, host) - LOG.debug(_("All api servers launched, now waiting")) - server.wait() - - -if __name__ == '__main__': - FLAGS(sys.argv) - configfiles = ['/etc/nova/nova-api.conf'] - if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): - configfiles.insert(0, - os.path.join(possible_topdir, 'etc', 'nova-api.conf')) - for configfile in configfiles: - if os.path.exists(configfile): - run_app(configfile) - break - else: - LOG.debug(_("Skipping missing configuration: %s"), configfile) diff --git a/etc/nova-api.conf b/etc/nova-api.conf index cf49b7254..dcb4e7894 100644 --- a/etc/nova-api.conf +++ b/etc/nova-api.conf @@ -20,10 +20,14 @@ use = egg:Paste#urlmap [pipeline:ec2cloud] pipeline = authenticate cloudrequest authorizer ec2executor +#pipeline = ec2lockout authenticate cloudrequest authorizer ec2executor [pipeline:ec2admin] pipeline = authenticate adminrequest authorizer ec2executor +[filter:ec2lockout] +paste.filter_factory = nova.api.ec2:lockout_factory + [filter:authenticate] paste.filter_factory = nova.api.ec2:authenticate_factory diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index a5810479e..2f8f4f272 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -21,7 +21,6 @@ Starting point for routing EC2 requests. """ import logging -import routes import webob import webob.dec import webob.exc @@ -32,8 +31,6 @@ from nova import flags from nova import utils from nova import wsgi from nova.api.ec2 import apirequest -from nova.api.ec2 import admin -from nova.api.ec2 import cloud from nova.auth import manager @@ -41,8 +38,6 @@ FLAGS = flags.FLAGS flags.DEFINE_boolean('use_forwarded_for', False, 'Treat X-Forwarded-For as the canonical remote address. ' 'Only enable this if you have a sanitizing proxy.') -flags.DEFINE_boolean('use_lockout', False, - 'Whether or not to use lockout middleware.') flags.DEFINE_integer('lockout_attempts', 5, 'Number of failed auths before lockout.') flags.DEFINE_integer('lockout_minutes', 15, @@ -57,15 +52,6 @@ _log = logging.getLogger("api") _log.setLevel(logging.DEBUG) -class API(wsgi.Middleware): - """Routing for all EC2 API requests.""" - - def __init__(self): - self.application = Authenticate(Router(Authorizer(Executor()))) - if FLAGS.use_lockout: - self.application = Lockout(self.application) - - class Lockout(wsgi.Middleware): """Lockout for x minutes on y failed auths in a z minute period. @@ -177,55 +163,12 @@ class Requestify(wsgi.Middleware): args.pop(non_arg) except: raise webob.exc.HTTPBadRequest() - api_request = apirequest.APIRequest(self.controller, action) + api_request = apirequest.APIRequest(self.controller, action, args) req.environ['ec2.request'] = api_request req.environ['ec2.action_args'] = args return self.application -class Router(wsgi.Middleware): - - """Add ec2.'controller', .'action', and .'action_args' to WSGI environ.""" - - def __init__(self, application): - super(Router, self).__init__(application) - self.map = routes.Mapper() - self.map.connect("/{controller_name}/") - self.controllers = dict(Cloud=cloud.CloudController(), - Admin=admin.AdminController()) - - @webob.dec.wsgify - def __call__(self, req): - # Obtain the appropriate controller and action for this request. - try: - match = self.map.match(req.path_info) - controller_name = match['controller_name'] - controller = self.controllers[controller_name] - except: - raise webob.exc.HTTPNotFound() - non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod', - 'SignatureVersion', 'Version', 'Timestamp'] - args = dict(req.params) - try: - # Raise KeyError if omitted - action = req.params['Action'] - for non_arg in non_args: - # Remove, but raise KeyError if omitted - args.pop(non_arg) - except: - raise webob.exc.HTTPBadRequest() - - _log.debug(_('action: %s') % action) - for key, value in args.items(): - _log.debug(_('arg: %s\t\tval: %s') % (key, value)) - - # Success! - req.environ['ec2.controller'] = controller - req.environ['ec2.action'] = action - req.environ['ec2.action_args'] = args - return self.application - - class Authorizer(wsgi.Middleware): """Authorize an EC2 API request. @@ -314,13 +257,11 @@ class Executor(wsgi.Application): @webob.dec.wsgify def __call__(self, req): context = req.environ['ec2.context'] - args = req.environ['ec2.action_args'] api_request = req.environ['ec2.request'] result = None try: - result = api_request.send(context, **args) + result = api_request.invoke(context) except exception.ApiError as ex: - if ex.code: return self._error(req, ex.code, ex.message) else: @@ -373,12 +314,6 @@ def authenticate_factory(global_args, **local_args): return authenticator -def router_factory(global_args, **local_args): - def router(app): - return Router(app) - return router - - def authorizer_factory(global_args, **local_args): def authorizer(app): return Authorizer(app) @@ -396,3 +331,8 @@ def requestify_factory(global_args, **local_args): def requestifier(app): return Requestify(app, local_args['controller']) return requestifier + +def lockout_factory(global_args, **local_args): + def locksmith(app): + return Lockout(app) + return locksmith diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py index a90fbeb0c..8a1dd3978 100644 --- a/nova/api/ec2/apirequest.py +++ b/nova/api/ec2/apirequest.py @@ -83,11 +83,12 @@ def _try_convert(value): class APIRequest(object): - def __init__(self, controller, action): + def __init__(self, controller, action, args): self.controller = controller self.action = action + self.args = args - def send(self, context, **kwargs): + def invoke(self, context): try: method = getattr(self.controller, _camelcase_to_underscore(self.action)) @@ -100,7 +101,7 @@ class APIRequest(object): raise Exception(_error) args = {} - for key, value in kwargs.items(): + for key, value in self.args.items(): parts = key.split(".") key = _camelcase_to_underscore(parts[0]) if isinstance(value, str) or isinstance(value, unicode): -- cgit From 6dc2e665b5b6f690882e6029984a11dc7063b437 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 18:33:17 -0500 Subject: remove unused nova/api/__init__.py --- nova/api/__init__.py | 111 --------------------------------------------------- 1 file changed, 111 deletions(-) delete mode 100644 nova/api/__init__.py diff --git a/nova/api/__init__.py b/nova/api/__init__.py deleted file mode 100644 index 26fed847b..000000000 --- a/nova/api/__init__.py +++ /dev/null @@ -1,111 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# 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. -""" -Root WSGI middleware for all API controllers. - -**Related Flags** - -:osapi_subdomain: subdomain running the OpenStack API (default: api) -:ec2api_subdomain: subdomain running the EC2 API (default: ec2) - -""" -import logging - -import routes -import webob.dec - -from nova import flags -from nova import wsgi -from nova.api import ec2 -from nova.api import openstack -from nova.api.ec2 import metadatarequesthandler - - -flags.DEFINE_string('osapi_subdomain', 'api', - 'subdomain running the OpenStack API') -flags.DEFINE_string('ec2api_subdomain', 'ec2', - 'subdomain running the EC2 API') - -FLAGS = flags.FLAGS - - -class API(wsgi.Router): - """Routes top-level requests to the appropriate controller.""" - - def __init__(self, default_api): - osapi_subdomain = {'sub_domain': [FLAGS.osapi_subdomain]} - ec2api_subdomain = {'sub_domain': [FLAGS.ec2api_subdomain]} - if default_api == 'os': - osapi_subdomain = {} - elif default_api == 'ec2': - ec2api_subdomain = {} - mapper = routes.Mapper() - mapper.sub_domains = True - - mapper.connect("/", controller=self.osapi_versions, - conditions=osapi_subdomain) - mapper.connect("/v1.0/{path_info:.*}", controller=openstack.API(), - conditions=osapi_subdomain) - - mapper.connect("/", controller=self.ec2api_versions, - conditions=ec2api_subdomain) - mapper.connect("/services/{path_info:.*}", controller=ec2.API(), - conditions=ec2api_subdomain) - mrh = metadatarequesthandler.MetadataRequestHandler() - for s in ['/latest', - '/2009-04-04', - '/2008-09-01', - '/2008-02-01', - '/2007-12-15', - '/2007-10-10', - '/2007-08-29', - '/2007-03-01', - '/2007-01-19', - '/1.0']: - mapper.connect('%s/{path_info:.*}' % s, controller=mrh, - conditions=ec2api_subdomain) - - super(API, self).__init__(mapper) - - @webob.dec.wsgify - def osapi_versions(self, req): - """Respond to a request for all OpenStack API versions.""" - response = { - "versions": [ - dict(status="CURRENT", id="v1.0")]} - metadata = { - "application/xml": { - "attributes": dict(version=["status", "id"])}} - return wsgi.Serializer(req.environ, metadata).to_content_type(response) - - @webob.dec.wsgify - def ec2api_versions(self, req): - """Respond to a request for all EC2 versions.""" - # available api versions - versions = [ - '1.0', - '2007-01-19', - '2007-03-01', - '2007-08-29', - '2007-10-10', - '2007-12-15', - '2008-02-01', - '2008-09-01', - '2009-04-04', - ] - return ''.join('%s\n' % v for v in versions) -- cgit From 406c8cdf027b13636ab3c8fa609aabe929057d6f Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 18:46:12 -0500 Subject: Remove flags and unused API class from openstack api, since such things are specified in paste config now. --- nova/api/openstack/__init__.py | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index a1430caed..c5de03a09 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -43,40 +43,11 @@ from nova.api.openstack import sharedipgroups FLAGS = flags.FLAGS -flags.DEFINE_string('os_api_auth', - 'nova.api.openstack.auth.AuthMiddleware', - 'The auth mechanism to use for the OpenStack API implemenation') - -flags.DEFINE_string('os_api_ratelimiting', - 'nova.api.openstack.ratelimiting.RateLimitingMiddleware', - 'Default ratelimiting implementation for the Openstack API') - flags.DEFINE_bool('allow_admin_api', False, 'When True, this API service will accept admin operations.') -class API(wsgi.Middleware): - """WSGI entry point for all OpenStack API requests.""" - - def __init__(self): - auth_middleware = utils.import_class(FLAGS.os_api_auth) - ratelimiting_middleware = \ - utils.import_class(FLAGS.os_api_ratelimiting) - app = auth_middleware(ratelimiting_middleware(APIRouter())) - super(API, self).__init__(app) - - @webob.dec.wsgify - def __call__(self, req): - try: - return req.get_response(self.application) - except Exception as ex: - logging.warn(_("Caught error: %s") % str(ex)) - logging.error(traceback.format_exc()) - exc = webob.exc.HTTPInternalServerError(explanation=str(ex)) - return faults.Fault(exc) - - class APIRouter(wsgi.Router): """ Routes requests on the OpenStack API to the appropriate controller -- cgit From 8926f33d4da9def15dde68a5a15fd9477aee6452 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 19:02:56 -0500 Subject: pep8 fixes. --- nova/api/ec2/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 2f8f4f272..dfa919e07 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -327,11 +327,13 @@ def executor_factory(global_args, **local_args): def versions_factory(global_args, **local_args): return Versions() + def requestify_factory(global_args, **local_args): def requestifier(app): return Requestify(app, local_args['controller']) return requestifier + def lockout_factory(global_args, **local_args): def locksmith(app): return Lockout(app) -- cgit From a05edf5eebf093f6f1b48a9fcbeaf8a9ae7b3899 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 19:13:37 -0500 Subject: Make test_access use ec2.request instead of .controller and .action. --- nova/tests/test_access.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/nova/tests/test_access.py b/nova/tests/test_access.py index 58fdea3b5..e170ccee6 100644 --- a/nova/tests/test_access.py +++ b/nova/tests/test_access.py @@ -17,25 +17,34 @@ # under the License. import unittest -import logging import webob from nova import context -from nova import exception from nova import flags from nova import test from nova.api import ec2 from nova.auth import manager - FLAGS = flags.FLAGS -class Context(object): +class FakeControllerClass(object): pass +class FakeApiRequest(object): + def __init__(self, action): + self.controller = FakeControllerClass() + self.action = action + + class AccessTestCase(test.TestCase): + def _env_for(self, ctxt, action): + env = {} + env['ec2.context'] = ctxt + env['ec2.request'] = FakeApiRequest(action) + return env + def setUp(self): super(AccessTestCase, self).setUp() um = manager.AuthManager() @@ -65,7 +74,7 @@ class AccessTestCase(test.TestCase): return [''] self.mw = ec2.Authorizer(noopWSGIApp) - self.mw.action_roles = {'str': { + self.mw.action_roles = {'FakeControllerClass': { '_allow_all': ['all'], '_allow_none': [], '_allow_project_manager': ['projectmanager'], @@ -85,9 +94,7 @@ class AccessTestCase(test.TestCase): def response_status(self, user, methodName): ctxt = context.RequestContext(user, self.project) - environ = {'ec2.context': ctxt, - 'ec2.controller': 'some string', - 'ec2.action': methodName} + environ = self._env_for(ctxt, methodName) req = webob.Request.blank('/', environ) resp = req.get_response(self.mw) return resp.status_int -- cgit From f62a010717c3ac66284948870f9c8d8216e4221b Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 19:42:56 -0500 Subject: Build app manually for test_api since nova.ec2.API is gone. --- nova/tests/test_api.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index 33d4cb294..44894fd0b 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -26,9 +26,8 @@ import StringIO import webob from nova import context -from nova import flags from nova import test -from nova import api +from nova.api import ec2 from nova.api.ec2 import cloud from nova.api.ec2 import apirequest from nova.auth import manager @@ -100,12 +99,10 @@ class ApiEc2TestCase(test.TrialTestCase): """Unit test for the cloud controller on an EC2 API""" def setUp(self): super(ApiEc2TestCase, self).setUp() - self.manager = manager.AuthManager() - self.host = '127.0.0.1' - - self.app = api.API('ec2') + self.app = ec2.Authenticate(ec2.Requestify(ec2.Executor(), + 'nova.api.ec2.cloud.CloudController')) def expect_http(self, host=None, is_secure=False): """Returns a new EC2 connection""" -- cgit From 6f855be07afb598090184bacf6d709191012c807 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 19:50:17 -0500 Subject: Declare a flag for test to run in isolation. --- nova/tests/test_compute.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 1fb9143f1..e6a0ffa20 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -33,6 +33,7 @@ from nova.compute import api as compute_api FLAGS = flags.FLAGS +flags.DECLARE('stub_network', 'nova.compute.manager') class ComputeTestCase(test.TestCase): -- cgit From bccec6c8bac90517a972a5eb8bb91a82b3a13065 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 6 Jan 2011 02:08:01 -0500 Subject: Fix openstack api tests and add a FaultWrapper to turn exceptions to faults. --- etc/nova-api.conf | 10 ++++++-- nova/api/openstack/__init__.py | 24 +++++++++++++++---- nova/tests/api/openstack/fakes.py | 13 ++++++++++ nova/tests/api/openstack/test_adminapi.py | 10 +++++--- nova/tests/api/openstack/test_api.py | 26 +++++++++++++------- nova/tests/api/openstack/test_auth.py | 18 +++++++------- nova/tests/api/openstack/test_flavors.py | 2 +- nova/tests/api/openstack/test_images.py | 4 ++-- nova/tests/api/openstack/test_servers.py | 40 +++++++++++++++---------------- 9 files changed, 96 insertions(+), 51 deletions(-) diff --git a/etc/nova-api.conf b/etc/nova-api.conf index dcb4e7894..59b21f387 100644 --- a/etc/nova-api.conf +++ b/etc/nova-api.conf @@ -61,7 +61,10 @@ use = egg:Paste#urlmap /v1.0: openstackapi [pipeline:openstackapi] -pipeline = auth ratelimit osapi +pipeline = faultwrap auth ratelimit osapi + +[filter:faultwrap] +paste.filter_factory = nova.api.openstack:fault_wrapper_factory [filter:auth] paste.filter_factory = nova.api.openstack.auth:auth_factory @@ -72,5 +75,8 @@ paste.filter_factory = nova.api.openstack.ratelimiting:ratelimit_factory [app:osapi] paste.app_factory = nova.api.openstack:router_factory -[app:osversions] +[pipeline:osversions] +pipeline = faultwrap osversionapp + +[app:osversionapp] paste.app_factory = nova.api.openstack:versions_factory diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index c5de03a09..21b9b1d7d 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -20,8 +20,6 @@ WSGI middleware for OpenStack API controllers. """ -import time - import logging import routes import traceback @@ -29,15 +27,12 @@ import webob.dec import webob.exc import webob -from nova import context from nova import flags -from nova import utils from nova import wsgi from nova.api.openstack import faults from nova.api.openstack import backup_schedules from nova.api.openstack import flavors from nova.api.openstack import images -from nova.api.openstack import ratelimiting from nova.api.openstack import servers from nova.api.openstack import sharedipgroups @@ -48,6 +43,19 @@ flags.DEFINE_bool('allow_admin_api', 'When True, this API service will accept admin operations.') +class FaultWrapper(wsgi.Middleware): + """Calls down the middleware stack, making exceptions into faults.""" + + @webob.dec.wsgify + def __call__(self, req): + try: + return req.get_response(self.application) + except Exception as ex: + logging.warn(_("Caught error: %s") % str(ex)) + logging.error(traceback.format_exc()) + exc = webob.exc.HTTPInternalServerError(explanation=str(ex)) + return faults.Fault(exc) + class APIRouter(wsgi.Router): """ Routes requests on the OpenStack API to the appropriate controller @@ -105,3 +113,9 @@ def router_factory(global_cof, **local_conf): def versions_factory(global_conf, **local_conf): return Versions() + + +def fault_wrapper_factory(global_conf, **local_conf): + def fwrap(app): + return FaultWrapper(app) + return fwrap diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 961431154..2028024bb 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -22,6 +22,7 @@ import string import webob import webob.dec +from paste import urlmap from nova import auth from nova import context @@ -29,6 +30,7 @@ from nova import exception as exc from nova import flags from nova import utils import nova.api.openstack.auth +from nova.api import openstack from nova.api.openstack import auth from nova.api.openstack import ratelimiting from nova.image import glance @@ -69,6 +71,17 @@ def fake_wsgi(self, req): return self.application +def wsgi_app(inner_application=None): + if not inner_application: + inner_application = openstack.APIRouter() + mapper = urlmap.URLMap() + api = openstack.FaultWrapper(auth.AuthMiddleware( + ratelimiting.RateLimitingMiddleware(inner_application))) + mapper['/v1.0'] = api + mapper['/'] = openstack.FaultWrapper(openstack.Versions()) + return mapper + + def stub_out_key_pair_funcs(stubs): def key_pair(context, user_id): return [dict(name='key', public_key='public_key')] diff --git a/nova/tests/api/openstack/test_adminapi.py b/nova/tests/api/openstack/test_adminapi.py index 1b2e1654d..73120c31d 100644 --- a/nova/tests/api/openstack/test_adminapi.py +++ b/nova/tests/api/openstack/test_adminapi.py @@ -19,15 +19,19 @@ import unittest import stubout import webob +from paste import urlmap -import nova.api from nova import flags +from nova.api import openstack +from nova.api.openstack import ratelimiting +from nova.api.openstack import auth from nova.tests.api.openstack import fakes FLAGS = flags.FLAGS class AdminAPITest(unittest.TestCase): + def setUp(self): self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.auth_data = {} @@ -45,7 +49,7 @@ class AdminAPITest(unittest.TestCase): FLAGS.allow_admin_api = True # We should still be able to access public operations. req = webob.Request.blank('/v1.0/flavors') - res = req.get_response(nova.api.API('os')) + res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) # TODO: Confirm admin operations are available. @@ -53,7 +57,7 @@ class AdminAPITest(unittest.TestCase): FLAGS.allow_admin_api = False # We should still be able to access public operations. req = webob.Request.blank('/v1.0/flavors') - res = req.get_response(nova.api.API('os')) + res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) # TODO: Confirm admin operations are unavailable. diff --git a/nova/tests/api/openstack/test_api.py b/nova/tests/api/openstack/test_api.py index d8b202e21..1474148f5 100644 --- a/nova/tests/api/openstack/test_api.py +++ b/nova/tests/api/openstack/test_api.py @@ -19,14 +19,18 @@ import unittest import webob.exc import webob.dec -import nova.api.openstack -from nova.api.openstack import API -from nova.api.openstack import faults from webob import Request +from nova.api import openstack +from nova.api.openstack import faults + class APITest(unittest.TestCase): + def _wsgi_app(self, inner_app): + # simpler version of the app than fakes.wsgi_app + return openstack.FaultWrapper(inner_app) + def test_exceptions_are_converted_to_faults(self): @webob.dec.wsgify @@ -46,29 +50,33 @@ class APITest(unittest.TestCase): exc = webob.exc.HTTPNotFound(explanation='Raised a webob.exc') return faults.Fault(exc) - api = API() - api.application = succeed + #api.application = succeed + api = self._wsgi_app(succeed) resp = Request.blank('/').get_response(api) self.assertFalse('computeFault' in resp.body, resp.body) self.assertEqual(resp.status_int, 200, resp.body) - api.application = raise_webob_exc + #api.application = raise_webob_exc + api = self._wsgi_app(raise_webob_exc) resp = Request.blank('/').get_response(api) self.assertFalse('computeFault' in resp.body, resp.body) self.assertEqual(resp.status_int, 404, resp.body) - api.application = raise_api_fault + #api.application = raise_api_fault + api = self._wsgi_app(raise_api_fault) resp = Request.blank('/').get_response(api) self.assertTrue('itemNotFound' in resp.body, resp.body) self.assertEqual(resp.status_int, 404, resp.body) - api.application = fail + #api.application = fail + api = self._wsgi_app(fail) resp = Request.blank('/').get_response(api) self.assertTrue('{"computeFault' in resp.body, resp.body) self.assertEqual(resp.status_int, 500, resp.body) - api.application = fail + #api.application = fail + api = self._wsgi_app(fail) resp = Request.blank('/.xml').get_response(api) self.assertTrue(' Date: Thu, 6 Jan 2011 13:17:28 -0500 Subject: Add blank __init__ file for fixing importability. The stale .pyc masked this error locally. --- nova/api/__init__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 nova/api/__init__.py diff --git a/nova/api/__init__.py b/nova/api/__init__.py new file mode 100644 index 000000000..0fedbbfad --- /dev/null +++ b/nova/api/__init__.py @@ -0,0 +1,19 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# 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. + +"""No-op __init__ for directory full of api goodies.""" -- cgit From 8003dd2f5b027491f4e171f92ccd2a1cf2946315 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 6 Jan 2011 14:22:11 -0500 Subject: Pep8 --- nova/api/openstack/__init__.py | 1 + nova/tests/api/openstack/test_api.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 21b9b1d7d..452290505 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -56,6 +56,7 @@ class FaultWrapper(wsgi.Middleware): exc = webob.exc.HTTPInternalServerError(explanation=str(ex)) return faults.Fault(exc) + class APIRouter(wsgi.Router): """ Routes requests on the OpenStack API to the appropriate controller diff --git a/nova/tests/api/openstack/test_api.py b/nova/tests/api/openstack/test_api.py index 1474148f5..db0fe1060 100644 --- a/nova/tests/api/openstack/test_api.py +++ b/nova/tests/api/openstack/test_api.py @@ -50,7 +50,6 @@ class APITest(unittest.TestCase): exc = webob.exc.HTTPNotFound(explanation='Raised a webob.exc') return faults.Fault(exc) - #api.application = succeed api = self._wsgi_app(succeed) resp = Request.blank('/').get_response(api) -- cgit From 2997c6cd216089b569878ec93b142ee9485127ee Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 6 Jan 2011 14:22:18 -0500 Subject: Remove test for removed class. --- nova/tests/api/fakes.py | 26 ---------------- nova/tests/api/test.py | 81 ------------------------------------------------- 2 files changed, 107 deletions(-) delete mode 100644 nova/tests/api/fakes.py delete mode 100644 nova/tests/api/test.py diff --git a/nova/tests/api/fakes.py b/nova/tests/api/fakes.py deleted file mode 100644 index 0aedcaff0..000000000 --- a/nova/tests/api/fakes.py +++ /dev/null @@ -1,26 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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 webob.dec -from nova import wsgi - - -class APIStub(object): - """Class to verify request and mark it was called.""" - @webob.dec.wsgify - def __call__(self, req): - return req.path_info diff --git a/nova/tests/api/test.py b/nova/tests/api/test.py deleted file mode 100644 index 9caa8c9d0..000000000 --- a/nova/tests/api/test.py +++ /dev/null @@ -1,81 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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. - -""" -Test for the root WSGI middleware for all API controllers. -""" - -import unittest - -import stubout -import webob -import webob.dec - -import nova.exception -from nova import api -from nova.tests.api.fakes import APIStub - - -class Test(unittest.TestCase): - - def setUp(self): - self.stubs = stubout.StubOutForTesting() - - def tearDown(self): - self.stubs.UnsetAll() - - def _request(self, url, subdomain, **kwargs): - environ_keys = {'HTTP_HOST': '%s.example.com' % subdomain} - environ_keys.update(kwargs) - req = webob.Request.blank(url, environ_keys) - return req.get_response(api.API('ec2')) - - def test_openstack(self): - self.stubs.Set(api.openstack, 'API', APIStub) - result = self._request('/v1.0/cloud', 'api') - self.assertEqual(result.body, "/cloud") - - def test_ec2(self): - self.stubs.Set(api.ec2, 'API', APIStub) - result = self._request('/services/cloud', 'ec2') - self.assertEqual(result.body, "/cloud") - - def test_not_found(self): - self.stubs.Set(api.ec2, 'API', APIStub) - self.stubs.Set(api.openstack, 'API', APIStub) - result = self._request('/test/cloud', 'ec2') - self.assertNotEqual(result.body, "/cloud") - - def test_query_api_versions(self): - result = self._request('/', 'api') - self.assertTrue('CURRENT' in result.body) - - def test_metadata(self): - def go(url): - result = self._request(url, 'ec2', REMOTE_ADDR='128.192.151.2') - # Each should get to the ORM layer and fail to find the IP - self.assertRaises(nova.exception.NotFound, go, '/latest/') - self.assertRaises(nova.exception.NotFound, go, '/2009-04-04/') - self.assertRaises(nova.exception.NotFound, go, '/1.0/') - - def test_ec2_root(self): - result = self._request('/', 'ec2') - self.assertTrue('2007-12-15\n' in result.body) - - -if __name__ == '__main__': - unittest.main() -- cgit From 88641cd9784b562c9df25643d6b525dd7bf940ce Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 12 Jan 2011 14:30:12 -0500 Subject: Update paste config for ec2 request logging. --- etc/nova-api.conf | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/etc/nova-api.conf b/etc/nova-api.conf index 59b21f387..8d6ada737 100644 --- a/etc/nova-api.conf +++ b/etc/nova-api.conf @@ -19,11 +19,20 @@ use = egg:Paste#urlmap /1.0: ec2metadata [pipeline:ec2cloud] -pipeline = authenticate cloudrequest authorizer ec2executor -#pipeline = ec2lockout authenticate cloudrequest authorizer ec2executor +pipeline = logrequest authenticate cloudrequest authorizer ec2executor +#pipeline = logrequest ec2lockout authenticate cloudrequest authorizer ec2executor [pipeline:ec2admin] -pipeline = authenticate adminrequest authorizer ec2executor +pipeline = logrequest authenticate adminrequest authorizer ec2executor + +[pipeline:ec2metadata] +pipeline = logrequest ec2md + +[pipeline:ec2versions] +pipeline = logrequest ec2ver + +[filter:logrequest] +paste.filter_factory = nova.api.ec2:request_logging_factory [filter:ec2lockout] paste.filter_factory = nova.api.ec2:lockout_factory @@ -45,10 +54,10 @@ paste.filter_factory = nova.api.ec2:authorizer_factory [app:ec2executor] paste.app_factory = nova.api.ec2:executor_factory -[app:ec2versions] +[app:ec2ver] paste.app_factory = nova.api.ec2:versions_factory -[app:ec2metadata] +[app:ec2md] paste.app_factory = nova.api.ec2.metadatarequesthandler:metadata_factory ############# -- cgit From 6ebf9bb2db0aaad607e35e516bb6d7ffc971c5de Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 12 Jan 2011 14:36:36 -0500 Subject: Fix url matching for years 2010-forward. --- etc/nova-api.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/nova-api.conf b/etc/nova-api.conf index 8d6ada737..7c6a5b4f3 100644 --- a/etc/nova-api.conf +++ b/etc/nova-api.conf @@ -15,7 +15,7 @@ use = egg:Paste#urlmap /services/Cloud: ec2cloud /services/Admin: ec2admin /latest: ec2metadata -/200: ec2metadata +/20: ec2metadata /1.0: ec2metadata [pipeline:ec2cloud] -- cgit From 500b268d0ef83b4770f9883690564e458cf94247 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 13 Jan 2011 18:57:29 -0500 Subject: pep8. Someday I'll remember 2 blank lines between module methods. --- nova/wsgi.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nova/wsgi.py b/nova/wsgi.py index 817cd0478..b4cca9138 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -393,9 +393,10 @@ class Serializer(object): result.appendChild(node) return result + def paste_config_file(basename): """Find the best location in the system for a paste config file. - + Search Order ------------ @@ -424,6 +425,7 @@ def paste_config_file(basename): if os.path.exists(configfile): return configfile + def load_paste_configuration(filename, appname): """Returns a paste configuration dict, or None.""" filename = os.path.abspath(filename) @@ -434,6 +436,7 @@ def load_paste_configuration(filename, appname): pass return config + def load_paste_app(filename, appname): """Builds a wsgi app from a paste config, None if app not configured.""" filename = os.path.abspath(filename) @@ -444,8 +447,9 @@ def load_paste_app(filename, appname): pass return app + def paste_config_to_flags(config, mixins): - for k,v in mixins.iteritems(): + for k, v in mixins.iteritems(): value = config.get(k, v) converted_value = FLAGS[k].parser.Parse(value) setattr(FLAGS, k, converted_value) -- cgit From 6906137b99181925f091ca547d019499c3bc1701 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Mon, 17 Jan 2011 13:36:55 -0500 Subject: Clean up openstack api test fake. --- nova/tests/api/openstack/__init__.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/nova/tests/api/openstack/__init__.py b/nova/tests/api/openstack/__init__.py index 9e183bd0d..14eaaa62c 100644 --- a/nova/tests/api/openstack/__init__.py +++ b/nova/tests/api/openstack/__init__.py @@ -15,23 +15,28 @@ # License for the specific language governing permissions and limitations # under the License. +import webob.dec import unittest from nova import context from nova import flags from nova.api.openstack.ratelimiting import RateLimitingMiddleware from nova.api.openstack.common import limited -from nova.tests.api.fakes import APIStub -from nova import utils +from nova.tests.api.openstack import fakes from webob import Request FLAGS = flags.FLAGS +@webob.dec.wsgify +def simple_wsgi(req): + return "" + + class RateLimitingMiddlewareTest(unittest.TestCase): def test_get_action_name(self): - middleware = RateLimitingMiddleware(APIStub()) + middleware = RateLimitingMiddleware(simple_wsgi) def verify(method, url, action_name): req = Request.blank(url) @@ -61,19 +66,19 @@ class RateLimitingMiddlewareTest(unittest.TestCase): self.assertTrue('Retry-After' in resp.headers) def test_single_action(self): - middleware = RateLimitingMiddleware(APIStub()) + middleware = RateLimitingMiddleware(simple_wsgi) self.exhaust(middleware, 'DELETE', '/servers/4', 'usr1', 100) self.exhaust(middleware, 'DELETE', '/servers/4', 'usr2', 100) def test_POST_servers_action_implies_POST_action(self): - middleware = RateLimitingMiddleware(APIStub()) + middleware = RateLimitingMiddleware(simple_wsgi) self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 10) self.exhaust(middleware, 'POST', '/images/4', 'usr2', 10) self.assertTrue(set(middleware.limiter._levels) == \ set(['usr1:POST', 'usr1:POST servers', 'usr2:POST'])) def test_POST_servers_action_correctly_ratelimited(self): - middleware = RateLimitingMiddleware(APIStub()) + middleware = RateLimitingMiddleware(simple_wsgi) # Use up all of our "POST" allowance for the minute, 5 times for i in range(5): self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 10) @@ -83,9 +88,9 @@ class RateLimitingMiddlewareTest(unittest.TestCase): self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 0) def test_proxy_ctor_works(self): - middleware = RateLimitingMiddleware(APIStub()) + middleware = RateLimitingMiddleware(simple_wsgi) self.assertEqual(middleware.limiter.__class__.__name__, "Limiter") - middleware = RateLimitingMiddleware(APIStub(), service_host='foobar') + middleware = RateLimitingMiddleware(simple_wsgi, service_host='foobar') self.assertEqual(middleware.limiter.__class__.__name__, "WSGIAppProxy") -- cgit