summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorSoren Hansen <soren.hansen@rackspace.com>2010-10-12 22:52:10 +0200
committerSoren Hansen <soren.hansen@rackspace.com>2010-10-12 22:52:10 +0200
commite2b7c99c6266b24dc8b53c47db3587aebd2381fe (patch)
tree67120b7a4e14cfc244fe3159447366bb7420e220 /nova/api
parent5be81520196c21aa9b60425bca7bf49935772cd1 (diff)
parent4f529fe118283164ccb2756f2001805c69c1cc4a (diff)
Merge trunk
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/__init__.py30
-rw-r--r--nova/api/ec2/cloud.py13
-rw-r--r--nova/api/openstack/__init__.py (renamed from nova/api/rackspace/__init__.py)26
-rw-r--r--nova/api/openstack/_id_translator.py (renamed from nova/api/rackspace/_id_translator.py)0
-rw-r--r--nova/api/openstack/auth.py (renamed from nova/api/rackspace/auth.py)4
-rw-r--r--nova/api/openstack/backup_schedules.py (renamed from nova/api/rackspace/backup_schedules.py)3
-rw-r--r--nova/api/openstack/context.py (renamed from nova/api/rackspace/context.py)0
-rw-r--r--nova/api/openstack/faults.py (renamed from nova/api/rackspace/faults.py)0
-rw-r--r--nova/api/openstack/flavors.py (renamed from nova/api/rackspace/flavors.py)8
-rw-r--r--nova/api/openstack/images.py (renamed from nova/api/rackspace/images.py)16
-rw-r--r--nova/api/openstack/notes.txt (renamed from nova/api/rackspace/notes.txt)4
-rw-r--r--nova/api/openstack/ratelimiting/__init__.py (renamed from nova/api/rackspace/ratelimiting/__init__.py)0
-rw-r--r--nova/api/openstack/servers.py (renamed from nova/api/rackspace/servers.py)38
-rw-r--r--nova/api/openstack/sharedipgroups.py (renamed from nova/api/rackspace/sharedipgroups.py)0
-rw-r--r--nova/api/rackspace/ratelimiting/tests.py237
15 files changed, 64 insertions, 315 deletions
diff --git a/nova/api/__init__.py b/nova/api/__init__.py
index 744abd621..8ec7094d7 100644
--- a/nova/api/__init__.py
+++ b/nova/api/__init__.py
@@ -27,16 +27,16 @@ from nova import flags
from nova import wsgi
from nova.api import cloudpipe
from nova.api import ec2
-from nova.api import rackspace
+from nova.api import openstack
from nova.api.ec2 import metadatarequesthandler
-flags.DEFINE_string('rsapi_subdomain', 'rs',
- 'subdomain running the RS API')
+flags.DEFINE_string('osapi_subdomain', 'api',
+ 'subdomain running the OpenStack API')
flags.DEFINE_string('ec2api_subdomain', 'ec2',
'subdomain running the EC2 API')
flags.DEFINE_string('FAKE_subdomain', None,
- 'set to rs or ec2 to fake the subdomain of the host for testing')
+ 'set to api or ec2 to fake the subdomain of the host for testing')
FLAGS = flags.FLAGS
@@ -44,21 +44,21 @@ class API(wsgi.Router):
"""Routes top-level requests to the appropriate controller."""
def __init__(self):
- rsdomain = {'sub_domain': [FLAGS.rsapi_subdomain]}
+ osapidomain = {'sub_domain': [FLAGS.osapi_subdomain]}
ec2domain = {'sub_domain': [FLAGS.ec2api_subdomain]}
- # If someone wants to pretend they're hitting the RS subdomain
- # on their local box, they can set FAKE_subdomain to 'rs', which
- # removes subdomain restrictions from the RS routes below.
- if FLAGS.FAKE_subdomain == 'rs':
- rsdomain = {}
+ # If someone wants to pretend they're hitting the OSAPI subdomain
+ # on their local box, they can set FAKE_subdomain to 'api', which
+ # removes subdomain restrictions from the OpenStack API routes below.
+ if FLAGS.FAKE_subdomain == 'api':
+ osapidomain = {}
elif FLAGS.FAKE_subdomain == 'ec2':
ec2domain = {}
mapper = routes.Mapper()
mapper.sub_domains = True
- mapper.connect("/", controller=self.rsapi_versions,
- conditions=rsdomain)
- mapper.connect("/v1.0/{path_info:.*}", controller=rackspace.API(),
- conditions=rsdomain)
+ mapper.connect("/", controller=self.osapi_versions,
+ conditions=osapidomain)
+ mapper.connect("/v1.0/{path_info:.*}", controller=openstack.API(),
+ conditions=osapidomain)
mapper.connect("/", controller=self.ec2api_versions,
conditions=ec2domain)
@@ -81,7 +81,7 @@ class API(wsgi.Router):
super(API, self).__init__(mapper)
@webob.dec.wsgify
- def rsapi_versions(self, req):
+ def osapi_versions(self, req):
"""Respond to a request for all OpenStack API versions."""
response = {
"versions": [
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 65cbe20ea..d7b9280f2 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -258,9 +258,9 @@ class CloudController(object):
def delete_security_group(self, context, group_name, **kwargs):
return True
- def get_console_output(self, context, ec2_id_list, **kwargs):
- # ec2_id_list is passed in as a list of instances
- ec2_id = ec2_id_list[0]
+ def get_console_output(self, context, instance_id, **kwargs):
+ # instance_id is passed in as a list of instances
+ ec2_id = instance_id[0]
internal_id = ec2_id_to_internal_id(ec2_id)
instance_ref = db.instance_get_by_internal_id(context, internal_id)
output = rpc.call('%s.%s' % (FLAGS.compute_topic,
@@ -664,7 +664,12 @@ class CloudController(object):
return self._format_run_instances(context, reservation_id)
- def terminate_instances(self, context, ec2_id_list, **kwargs):
+ def terminate_instances(self, context, instance_id, **kwargs):
+ """Terminate each instance in instance_id, which is a list of ec2 ids.
+
+ instance_id is a kwarg so its name cannot be modified.
+ """
+ ec2_id_list = instance_id
logging.debug("Going to start terminating instances")
for id_str in ec2_id_list:
internal_id = ec2_id_to_internal_id(id_str)
diff --git a/nova/api/rackspace/__init__.py b/nova/api/openstack/__init__.py
index 89a4693ad..5e81ba2bd 100644
--- a/nova/api/rackspace/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -17,7 +17,7 @@
# under the License.
"""
-WSGI middleware for Rackspace API controllers.
+WSGI middleware for OpenStack API controllers.
"""
import json
@@ -31,30 +31,30 @@ import webob
from nova import flags
from nova import utils
from nova import wsgi
-from nova.api.rackspace import faults
-from nova.api.rackspace import backup_schedules
-from nova.api.rackspace import flavors
-from nova.api.rackspace import images
-from nova.api.rackspace import ratelimiting
-from nova.api.rackspace import servers
-from nova.api.rackspace import sharedipgroups
+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
from nova.auth import manager
FLAGS = flags.FLAGS
flags.DEFINE_string('nova_api_auth',
- 'nova.api.rackspace.auth.BasicApiAuthManager',
- 'The auth mechanism to use for the Rackspace API implemenation')
+ 'nova.api.openstack.auth.BasicApiAuthManager',
+ 'The auth mechanism to use for the OpenStack API implemenation')
class API(wsgi.Middleware):
- """WSGI entry point for all Rackspace API requests."""
+ """WSGI entry point for all OpenStack API requests."""
def __init__(self):
app = AuthMiddleware(RateLimitingMiddleware(APIRouter()))
super(API, self).__init__(app)
class AuthMiddleware(wsgi.Middleware):
- """Authorize the rackspace API request or return an HTTP Forbidden."""
+ """Authorize the openstack API request or return an HTTP Forbidden."""
def __init__(self, application):
self.auth_driver = utils.import_class(FLAGS.nova_api_auth)()
@@ -145,7 +145,7 @@ class RateLimitingMiddleware(wsgi.Middleware):
class APIRouter(wsgi.Router):
"""
- Routes requests on the Rackspace API to the appropriate controller
+ Routes requests on the OpenStack API to the appropriate controller
and method.
"""
diff --git a/nova/api/rackspace/_id_translator.py b/nova/api/openstack/_id_translator.py
index 333aa8434..333aa8434 100644
--- a/nova/api/rackspace/_id_translator.py
+++ b/nova/api/openstack/_id_translator.py
diff --git a/nova/api/rackspace/auth.py b/nova/api/openstack/auth.py
index c45156ebd..4c909293e 100644
--- a/nova/api/rackspace/auth.py
+++ b/nova/api/openstack/auth.py
@@ -11,7 +11,7 @@ from nova import db
from nova import flags
from nova import manager
from nova import utils
-from nova.api.rackspace import faults
+from nova.api.openstack import faults
FLAGS = flags.FLAGS
@@ -19,7 +19,7 @@ class Context(object):
pass
class BasicApiAuthManager(object):
- """ Implements a somewhat rudimentary version of Rackspace Auth"""
+ """ Implements a somewhat rudimentary version of OpenStack Auth"""
def __init__(self, host=None, db_driver=None):
if not host:
diff --git a/nova/api/rackspace/backup_schedules.py b/nova/api/openstack/backup_schedules.py
index cb83023bc..76ad6ef87 100644
--- a/nova/api/rackspace/backup_schedules.py
+++ b/nova/api/openstack/backup_schedules.py
@@ -19,8 +19,7 @@ import time
from webob import exc
from nova import wsgi
-from nova.api.rackspace import _id_translator
-from nova.api.rackspace import faults
+from nova.api.openstack import faults
import nova.image.service
class Controller(wsgi.Controller):
diff --git a/nova/api/rackspace/context.py b/nova/api/openstack/context.py
index 77394615b..77394615b 100644
--- a/nova/api/rackspace/context.py
+++ b/nova/api/openstack/context.py
diff --git a/nova/api/rackspace/faults.py b/nova/api/openstack/faults.py
index 32e5c866f..32e5c866f 100644
--- a/nova/api/rackspace/faults.py
+++ b/nova/api/openstack/faults.py
diff --git a/nova/api/rackspace/flavors.py b/nova/api/openstack/flavors.py
index 916449854..793984a5d 100644
--- a/nova/api/rackspace/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -17,13 +17,13 @@
from webob import exc
-from nova.api.rackspace import faults
+from nova.api.openstack import faults
from nova.compute import instance_types
from nova import wsgi
-import nova.api.rackspace
+import nova.api.openstack
class Controller(wsgi.Controller):
- """Flavor controller for the Rackspace API."""
+ """Flavor controller for the OpenStack API."""
_serialization_metadata = {
'application/xml': {
@@ -41,7 +41,7 @@ class Controller(wsgi.Controller):
def detail(self, req):
"""Return all flavors in detail."""
items = [self.show(req, id)['flavor'] for id in self._all_ids()]
- items = nova.api.rackspace.limited(items, req)
+ items = nova.api.openstack.limited(items, req)
return dict(flavors=items)
def show(self, req, id):
diff --git a/nova/api/rackspace/images.py b/nova/api/openstack/images.py
index d4ab8ce3c..aa438739c 100644
--- a/nova/api/rackspace/images.py
+++ b/nova/api/openstack/images.py
@@ -20,10 +20,9 @@ from webob import exc
from nova import flags
from nova import utils
from nova import wsgi
-from nova.api.rackspace import _id_translator
-import nova.api.rackspace
+import nova.api.openstack
import nova.image.service
-from nova.api.rackspace import faults
+from nova.api.openstack import faults
FLAGS = flags.FLAGS
@@ -41,8 +40,6 @@ class Controller(wsgi.Controller):
def __init__(self):
self._service = utils.import_object(FLAGS.image_service)
- self._id_translator = _id_translator.RackspaceAPIIdTranslator(
- "image", self._service.__class__.__name__)
def index(self, req):
"""Return all public images in brief."""
@@ -52,17 +49,12 @@ class Controller(wsgi.Controller):
def detail(self, req):
"""Return all public images in detail."""
data = self._service.index()
- data = nova.api.rackspace.limited(data, req)
- for img in data:
- img['id'] = self._id_translator.to_rs_id(img['id'])
+ data = nova.api.openstack.limited(data, req)
return dict(images=data)
def show(self, req, id):
"""Return data about the given image id."""
- opaque_id = self._id_translator.from_rs_id(id)
- img = self._service.show(opaque_id)
- img['id'] = id
- return dict(image=img)
+ return dict(image=self._service.show(id))
def delete(self, req, id):
# Only public images are supported for now.
diff --git a/nova/api/rackspace/notes.txt b/nova/api/openstack/notes.txt
index e133bf5ea..2330f1002 100644
--- a/nova/api/rackspace/notes.txt
+++ b/nova/api/openstack/notes.txt
@@ -10,11 +10,11 @@ image ids are URIs.
LocalImageService(ImageService):
image ids are random strings.
-RackspaceAPITranslationStore:
+OpenstackAPITranslationStore:
translates RS server/images/flavor/etc ids into formats required
by a given ImageService strategy.
-api.rackspace.images.Controller:
+api.openstack.images.Controller:
uses an ImageService strategy behind the scenes to do its fetching; it just
converts int image id into a strategy-specific image id.
diff --git a/nova/api/rackspace/ratelimiting/__init__.py b/nova/api/openstack/ratelimiting/__init__.py
index f843bac0f..f843bac0f 100644
--- a/nova/api/rackspace/ratelimiting/__init__.py
+++ b/nova/api/openstack/ratelimiting/__init__.py
diff --git a/nova/api/rackspace/servers.py b/nova/api/openstack/servers.py
index b23867bbf..5d1ed9822 100644
--- a/nova/api/rackspace/servers.py
+++ b/nova/api/openstack/servers.py
@@ -25,27 +25,15 @@ from nova import rpc
from nova import utils
from nova import wsgi
from nova.api import cloud
-from nova.api.rackspace import _id_translator
-from nova.api.rackspace import context
-from nova.api.rackspace import faults
+from nova.api.openstack import context
+from nova.api.openstack import faults
from nova.compute import instance_types
from nova.compute import power_state
-import nova.api.rackspace
+import nova.api.openstack
import nova.image.service
FLAGS = flags.FLAGS
-def _instance_id_translator():
- """ Helper method for initializing an id translator for Rackspace instance
- ids """
- return _id_translator.RackspaceAPIIdTranslator( "instance", 'nova')
-
-def _image_service():
- """ Helper method for initializing the image id translator """
- service = utils.import_object(FLAGS.image_service)
- return (service, _id_translator.RackspaceAPIIdTranslator(
- "image", service.__class__.__name__))
-
def _filter_params(inst_dict):
""" Extracts all updatable parameters for a server update request """
keys = dict(name='name', admin_pass='adminPass')
@@ -60,7 +48,7 @@ def _entity_list(entities):
return dict(servers=entities)
def _entity_detail(inst):
- """ Maps everything to Rackspace-like attributes for return"""
+ """ Maps everything to valid attributes for return"""
power_mapping = {
power_state.NOSTATE: 'build',
power_state.RUNNING: 'active',
@@ -90,7 +78,7 @@ def _entity_inst(inst):
return dict(server=dict(id=inst['id'], name=inst['server_name']))
class Controller(wsgi.Controller):
- """ The Server API controller for the Openstack API """
+ """ The Server API controller for the OpenStack API """
_serialization_metadata = {
'application/xml': {
@@ -122,7 +110,7 @@ class Controller(wsgi.Controller):
"""
user_id = req.environ['nova.context']['user']['id']
instance_list = self.db_driver.instance_get_all_by_user(None, user_id)
- limited_list = nova.api.rackspace.limited(instance_list, req)
+ limited_list = nova.api.openstack.limited(instance_list, req)
res = [entity_maker(inst)['server'] for inst in limited_list]
return _entity_list(res)
@@ -182,13 +170,16 @@ class Controller(wsgi.Controller):
def action(self, req, id):
""" multi-purpose method used to reboot, rebuild, and
resize a server """
+ user_id = req.environ['nova.context']['user']['id']
input_dict = self._deserialize(req.body, req)
try:
reboot_type = input_dict['reboot']['type']
except Exception:
raise faults.Fault(webob.exc.HTTPNotImplemented())
- opaque_id = _instance_id_translator().from_rs_id(int(id))
- cloud.reboot(opaque_id)
+ inst_ref = self.db.instance_get_by_internal_id(None, int(id))
+ if not inst_ref or (inst_ref and not inst_ref.user_id == user_id):
+ return faults.Fault(exc.HTTPUnprocessableEntity())
+ cloud.reboot(id)
def _build_server_instance(self, req, env):
"""Build instance data structure and save it to the data store."""
@@ -205,16 +196,15 @@ class Controller(wsgi.Controller):
image_id = env['server']['imageId']
- img_service, image_id_trans = _image_service()
+ img_service = utils.import_object(FLAGS.image_service)
- opaque_image_id = image_id_trans.to_rs_id(image_id)
- image = img_service.show(opaque_image_id)
+ image = img_service.show(image_id)
if not image:
raise Exception, "Image not found"
inst['server_name'] = env['server']['name']
- inst['image_id'] = opaque_image_id
+ inst['image_id'] = image_id
inst['user_id'] = user_id
inst['launch_time'] = ltime
inst['mac_address'] = utils.generate_mac()
diff --git a/nova/api/rackspace/sharedipgroups.py b/nova/api/openstack/sharedipgroups.py
index 4d2d0ede1..4d2d0ede1 100644
--- a/nova/api/rackspace/sharedipgroups.py
+++ b/nova/api/openstack/sharedipgroups.py
diff --git a/nova/api/rackspace/ratelimiting/tests.py b/nova/api/rackspace/ratelimiting/tests.py
deleted file mode 100644
index 4c9510917..000000000
--- a/nova/api/rackspace/ratelimiting/tests.py
+++ /dev/null
@@ -1,237 +0,0 @@
-import httplib
-import StringIO
-import time
-import unittest
-import webob
-
-import nova.api.rackspace.ratelimiting as ratelimiting
-
-class LimiterTest(unittest.TestCase):
-
- def setUp(self):
- self.limits = {
- 'a': (5, ratelimiting.PER_SECOND),
- 'b': (5, ratelimiting.PER_MINUTE),
- 'c': (5, ratelimiting.PER_HOUR),
- 'd': (1, ratelimiting.PER_SECOND),
- 'e': (100, ratelimiting.PER_SECOND)}
- self.rl = ratelimiting.Limiter(self.limits)
-
- def exhaust(self, action, times_until_exhausted, **kwargs):
- for i in range(times_until_exhausted):
- when = self.rl.perform(action, **kwargs)
- self.assertEqual(when, None)
- num, period = self.limits[action]
- delay = period * 1.0 / num
- # Verify that we are now thoroughly delayed
- for i in range(10):
- when = self.rl.perform(action, **kwargs)
- self.assertAlmostEqual(when, delay, 2)
-
- def test_second(self):
- self.exhaust('a', 5)
- time.sleep(0.2)
- self.exhaust('a', 1)
- time.sleep(1)
- self.exhaust('a', 5)
-
- def test_minute(self):
- self.exhaust('b', 5)
-
- def test_one_per_period(self):
- def allow_once_and_deny_once():
- when = self.rl.perform('d')
- self.assertEqual(when, None)
- when = self.rl.perform('d')
- self.assertAlmostEqual(when, 1, 2)
- return when
- time.sleep(allow_once_and_deny_once())
- time.sleep(allow_once_and_deny_once())
- allow_once_and_deny_once()
-
- def test_we_can_go_indefinitely_if_we_spread_out_requests(self):
- for i in range(200):
- when = self.rl.perform('e')
- self.assertEqual(when, None)
- time.sleep(0.01)
-
- def test_users_get_separate_buckets(self):
- self.exhaust('c', 5, username='alice')
- self.exhaust('c', 5, username='bob')
- self.exhaust('c', 5, username='chuck')
- self.exhaust('c', 0, username='chuck')
- self.exhaust('c', 0, username='bob')
- self.exhaust('c', 0, username='alice')
-
-
-class FakeLimiter(object):
- """Fake Limiter class that you can tell how to behave."""
- def __init__(self, test):
- self._action = self._username = self._delay = None
- self.test = test
- def mock(self, action, username, delay):
- self._action = action
- self._username = username
- self._delay = delay
- def perform(self, action, username):
- self.test.assertEqual(action, self._action)
- self.test.assertEqual(username, self._username)
- return self._delay
-
-
-class WSGIAppTest(unittest.TestCase):
-
- def setUp(self):
- self.limiter = FakeLimiter(self)
- self.app = ratelimiting.WSGIApp(self.limiter)
-
- def test_invalid_methods(self):
- requests = []
- for method in ['GET', 'PUT', 'DELETE']:
- req = webob.Request.blank('/limits/michael/breakdance',
- dict(REQUEST_METHOD=method))
- requests.append(req)
- for req in requests:
- self.assertEqual(req.get_response(self.app).status_int, 405)
-
- def test_invalid_urls(self):
- requests = []
- for prefix in ['limit', '', 'limiter2', 'limiter/limits', 'limiter/1']:
- req = webob.Request.blank('/%s/michael/breakdance' % prefix,
- dict(REQUEST_METHOD='POST'))
- requests.append(req)
- for req in requests:
- self.assertEqual(req.get_response(self.app).status_int, 404)
-
- def verify(self, url, username, action, delay=None):
- """Make sure that POSTing to the given url causes the given username
- to perform the given action. Make the internal rate limiter return
- delay and make sure that the WSGI app returns the correct response.
- """
- req = webob.Request.blank(url, dict(REQUEST_METHOD='POST'))
- self.limiter.mock(action, username, delay)
- resp = req.get_response(self.app)
- if not delay:
- self.assertEqual(resp.status_int, 200)
- else:
- self.assertEqual(resp.status_int, 403)
- self.assertEqual(resp.headers['X-Wait-Seconds'], "%.2f" % delay)
-
- def test_good_urls(self):
- self.verify('/limiter/michael/hoot', 'michael', 'hoot')
-
- def test_escaping(self):
- self.verify('/limiter/michael/jump%20up', 'michael', 'jump up')
-
- def test_response_to_delays(self):
- self.verify('/limiter/michael/hoot', 'michael', 'hoot', 1)
- self.verify('/limiter/michael/hoot', 'michael', 'hoot', 1.56)
- self.verify('/limiter/michael/hoot', 'michael', 'hoot', 1000)
-
-
-class FakeHttplibSocket(object):
- """a fake socket implementation for httplib.HTTPResponse, trivial"""
-
- def __init__(self, response_string):
- self._buffer = StringIO.StringIO(response_string)
-
- def makefile(self, _mode, _other):
- """Returns the socket's internal buffer"""
- return self._buffer
-
-
-class FakeHttplibConnection(object):
- """A fake httplib.HTTPConnection
-
- Requests made via this connection actually get translated and routed into
- our WSGI app, we then wait for the response and turn it back into
- an httplib.HTTPResponse.
- """
- def __init__(self, app, host, is_secure=False):
- self.app = app
- self.host = host
-
- def request(self, method, path, data='', headers={}):
- req = webob.Request.blank(path)
- req.method = method
- req.body = data
- req.headers = headers
- req.host = self.host
- # Call the WSGI app, get the HTTP response
- resp = str(req.get_response(self.app))
- # For some reason, the response doesn't have "HTTP/1.0 " prepended; I
- # guess that's a function the web server usually provides.
- resp = "HTTP/1.0 %s" % resp
- sock = FakeHttplibSocket(resp)
- self.http_response = httplib.HTTPResponse(sock)
- self.http_response.begin()
-
- def getresponse(self):
- return self.http_response
-
-
-def wire_HTTPConnection_to_WSGI(host, app):
- """Monkeypatches HTTPConnection so that if you try to connect to host, you
- are instead routed straight to the given WSGI app.
-
- After calling this method, when any code calls
-
- httplib.HTTPConnection(host)
-
- the connection object will be a fake. Its requests will be sent directly
- to the given WSGI app rather than through a socket.
-
- Code connecting to hosts other than host will not be affected.
-
- This method may be called multiple times to map different hosts to
- different apps.
- """
- class HTTPConnectionDecorator(object):
- """Wraps the real HTTPConnection class so that when you instantiate
- the class you might instead get a fake instance."""
- def __init__(self, wrapped):
- self.wrapped = wrapped
- def __call__(self, connection_host, *args, **kwargs):
- if connection_host == host:
- return FakeHttplibConnection(app, host)
- else:
- return self.wrapped(connection_host, *args, **kwargs)
- httplib.HTTPConnection = HTTPConnectionDecorator(httplib.HTTPConnection)
-
-
-class WSGIAppProxyTest(unittest.TestCase):
-
- def setUp(self):
- """Our WSGIAppProxy is going to call across an HTTPConnection to a
- WSGIApp running a limiter. The proxy will send input, and the proxy
- should receive that same input, pass it to the limiter who gives a
- result, and send the expected result back.
-
- The HTTPConnection isn't real -- it's monkeypatched to point straight
- at the WSGIApp. And the limiter isn't real -- it's a fake that
- behaves the way we tell it to.
- """
- self.limiter = FakeLimiter(self)
- app = ratelimiting.WSGIApp(self.limiter)
- wire_HTTPConnection_to_WSGI('100.100.100.100:80', app)
- self.proxy = ratelimiting.WSGIAppProxy('100.100.100.100:80')
-
- def test_200(self):
- self.limiter.mock('conquer', 'caesar', None)
- when = self.proxy.perform('conquer', 'caesar')
- self.assertEqual(when, None)
-
- def test_403(self):
- self.limiter.mock('grumble', 'proletariat', 1.5)
- when = self.proxy.perform('grumble', 'proletariat')
- self.assertEqual(when, 1.5)
-
- def test_failure(self):
- def shouldRaise():
- self.limiter.mock('murder', 'brutus', None)
- self.proxy.perform('stab', 'brutus')
- self.assertRaises(AssertionError, shouldRaise)
-
-
-if __name__ == '__main__':
- unittest.main()