Now we have a stand-alone server which can provide services for more than one backend at the same time

This commit is contained in:
Andres Riancho 2013-12-27 15:45:53 -03:00 committed by Steve Pulec
parent 56357d344c
commit c6515af8bf
3 changed files with 67 additions and 21 deletions

View File

@ -110,6 +110,13 @@ class BaseBackend(object):
return paths return paths
@property
def url_bases(self):
"""
A list containing the url_bases extracted from urls.py
"""
return self._url_module.url_bases
@property @property
def flask_paths(self): def flask_paths(self):
""" """

View File

@ -9,7 +9,7 @@ from moto.core.utils import camelcase_to_underscores, method_names_from_class
class BaseResponse(object): class BaseResponse(object):
def dispatch(self, request, full_url, headers): def dispatch(self, request, full_url, headers):
querystring = None querystring = {}
if hasattr(request, 'body'): if hasattr(request, 'body'):
# Boto # Boto
@ -26,12 +26,12 @@ class BaseResponse(object):
for key, value in request.form.iteritems(): for key, value in request.form.iteritems():
querystring[key] = [value, ] querystring[key] = [value, ]
if querystring is None:
querystring.update(parse_qs(urlparse(full_url).query))
if not querystring: if not querystring:
querystring = parse_qs(urlparse(full_url).query) querystring.update(parse_qs(self.body))
if not querystring: if not querystring:
querystring = parse_qs(self.body) querystring.update(headers)
if not querystring:
querystring = headers
self.uri = full_url self.uri = full_url
self.path = urlparse(full_url).path self.path = urlparse(full_url).path

View File

@ -1,16 +1,54 @@
import re
import sys import sys
import argparse import argparse
from threading import Lock
from flask import Flask from flask import Flask
from werkzeug.routing import BaseConverter from werkzeug.routing import BaseConverter
from werkzeug.serving import run_simple
from moto.backends import BACKENDS from moto.backends import BACKENDS
from moto.core.utils import convert_flask_to_httpretty_response from moto.core.utils import convert_flask_to_httpretty_response
app = Flask(__name__)
HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "HEAD"] HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "HEAD"]
class DomainDispatcherApplication(object):
"""
Dispatch requests to different applications based on the "Host:" header
value. We'll match the host header value with the url_bases of each backend.
"""
def __init__(self, create_app):
self.create_app = create_app
self.lock = Lock()
self.app_instances = {}
def get_backend_for_host(self, host):
for backend in BACKENDS.itervalues():
for url_base in backend.url_bases:
if re.match(url_base, 'http://%s' % host):
print url_base, 'http://%s' % host
return backend
raise RuntimeError('Invalid host: "%s"' % host)
def get_application(self, host):
host = host.split(':')[0]
with self.lock:
backend = self.get_backend_for_host(host)
app = self.app_instances.get(backend, None)
if app is None:
app = self.create_app(backend)
self.app_instances[backend] = app
return app
def __call__(self, environ, start_response):
backend_app = self.get_application(environ['HTTP_HOST'])
return backend_app(environ, start_response)
class RegexConverter(BaseConverter): class RegexConverter(BaseConverter):
# http://werkzeug.pocoo.org/docs/routing/#custom-converters # http://werkzeug.pocoo.org/docs/routing/#custom-converters
def __init__(self, url_map, *items): def __init__(self, url_map, *items):
@ -18,25 +56,25 @@ class RegexConverter(BaseConverter):
self.regex = items[0] self.regex = items[0]
def configure_urls(service): def create_backend_app(backend):
backend = BACKENDS[service]
from werkzeug.routing import Map from werkzeug.routing import Map
# Create the backend_app
backend_app = Flask(__name__)
backend_app.debug = True
# Reset view functions to reset the app # Reset view functions to reset the app
app.view_functions = {} backend_app.view_functions = {}
app.url_map = Map() backend_app.url_map = Map()
app.url_map.converters['regex'] = RegexConverter backend_app.url_map.converters['regex'] = RegexConverter
for url_path, handler in backend.flask_paths.iteritems(): for url_path, handler in backend.flask_paths.iteritems():
app.route(url_path, methods=HTTP_METHODS)(convert_flask_to_httpretty_response(handler)) backend_app.route(url_path, methods=HTTP_METHODS)(convert_flask_to_httpretty_response(handler))
return backend_app
def main(argv=sys.argv[1:]): def main(argv=sys.argv[1:]):
available_services = BACKENDS.keys()
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument(
'service', type=str,
choices=available_services,
help='Choose which mechanism you want to run')
parser.add_argument( parser.add_argument(
'-H', '--host', type=str, '-H', '--host', type=str,
help='Which host to bind', help='Which host to bind',
@ -48,10 +86,11 @@ def main(argv=sys.argv[1:]):
args = parser.parse_args(argv) args = parser.parse_args(argv)
configure_urls(args.service) # Wrap the main application
main_app = DomainDispatcherApplication(create_backend_app)
main_app.debug = True
app.testing = True run_simple(args.host, args.port, main_app)
app.run(host=args.host, port=args.port)
if __name__ == '__main__': if __name__ == '__main__':
main() main()