diff --git a/bigchaindb/__init__.py b/bigchaindb/__init__.py index 9283913f..28e2384e 100644 --- a/bigchaindb/__init__.py +++ b/bigchaindb/__init__.py @@ -24,6 +24,10 @@ def e(key, default=None, conv=None): config = { + 'server': { + 'bind': ':'.join([e('BIGCHAIN_SERVER_BINDHOST', default='0.0.0.0'), + e('BIGCHAIN_SERVER_BINDPORT', default='5000')]) + }, 'database': { 'host': e('BIGCHAIN_DATABASE_HOST', default='localhost'), 'port': e('BIGCHAIN_DATABASE_PORT', default=28015), @@ -49,3 +53,4 @@ config = { # for more info. _config = copy.deepcopy(config) from bigchaindb.core import Bigchain # noqa + diff --git a/bigchaindb/commands/bigchain.py b/bigchaindb/commands/bigchain.py index 35b2de2f..c1be6761 100644 --- a/bigchaindb/commands/bigchain.py +++ b/bigchaindb/commands/bigchain.py @@ -55,6 +55,10 @@ def run_configure(args, skip_if_exists=False): conf['keypair']['private'], conf['keypair']['public'] = crypto.generate_key_pair() if not args.yes: + for key in ('host', 'port'): + val = conf['server'][key] + conf['server'][key] = input('API Server {}? (default `{}`): '.format(key, val)) or val + for key in ('host', 'port', 'name'): val = conf['database'][key] conf['database'][key] = input('Database {}? (default `{}`): '.format(key, val)) or val diff --git a/bigchaindb/processes.py b/bigchaindb/processes.py index 661cd9db..51844d6c 100644 --- a/bigchaindb/processes.py +++ b/bigchaindb/processes.py @@ -1,8 +1,10 @@ +import copy import logging import multiprocessing as mp import rethinkdb as r +import bigchaindb from bigchaindb import Bigchain from bigchaindb.voter import Voter from bigchaindb.block import Block @@ -68,8 +70,8 @@ class Processes(object): block = Block(self.q_new_transaction) # start the web api - webapi = server.create_app() - p_webapi = mp.Process(name='webapi', target=webapi.run, kwargs={'host': 'localhost'}) + app_server = server.create_server(bigchaindb.config['server']) + p_webapi = mp.Process(name='webapi', target=app_server.run) p_webapi.start() # initialize the processes diff --git a/bigchaindb/web/server.py b/bigchaindb/web/server.py index 0872fae5..208f2458 100644 --- a/bigchaindb/web/server.py +++ b/bigchaindb/web/server.py @@ -1,16 +1,56 @@ -"""This module contains basic functions to instantiate the BigchainDB API. """ +"""This module contains basic functions to instantiate the BigchainDB API. + +The application is implemented in Flask and runs using Gunicorn. +""" + +import copy +import multiprocessing from flask import Flask from bigchaindb import Bigchain from bigchaindb.web import views +import gunicorn.app.base + + +class StandaloneApplication(gunicorn.app.base.BaseApplication): + """Run a **wsgi** app wrapping it in a Gunicorn Base Application. + + Adapted from: + - http://docs.gunicorn.org/en/latest/custom.html + """ + + def __init__(self, app, options=None): + '''Initialize a new standalone application. + + Args: + app: A wsgi Python application. + options (dict): the configuration. + + ''' + self.options = options or {} + self.application = app + super(StandaloneApplication, self).__init__() + + def load_config(self): + config = dict((key, value) for key, value in self.options.items() + if key in self.cfg.settings and value is not None) + + for key, value in config.items(): + # not sure if we need the `key.lower` here, will just keep + # keep it for now. + self.cfg.set(key.lower(), value) + + def load(self): + return self.application def create_app(debug=False): """Return an instance of the Flask application. Args: - debug (bool): a flag to activate the debug mode for the app (default: False). + debug (bool): a flag to activate the debug mode for the app + (default: False). """ app = Flask(__name__) @@ -18,3 +58,26 @@ def create_app(debug=False): app.config['bigchain'] = Bigchain() app.register_blueprint(views.basic_views, url_prefix='/api/v1') return app + + +def create_server(settings): + """Wrap and return an application ready to be run. + + Args: + settings (dict): a dictionary containing the settings, more info + here http://docs.gunicorn.org/en/latest/settings.html + + Return: + an initialized instance of the application. + """ + + settings = copy.deepcopy(settings) + + if not settings.get('workers'): + settings['workers'] = (multiprocessing.cpu_count() * 2) + 1 + + debug = settings.pop('debug', False) + app = create_app(debug) + standalone = StandaloneApplication(app, settings) + return standalone + diff --git a/tests/web/test_server.py b/tests/web/test_server.py new file mode 100644 index 00000000..874567d7 --- /dev/null +++ b/tests/web/test_server.py @@ -0,0 +1,13 @@ +import copy + + +def test_settings(monkeypatch): + import bigchaindb + from bigchaindb.web import server + + s = server.create_server(bigchaindb.config['server']) + + # for whatever reason the value is wrapped in a list + # needs further investigation + assert s.cfg.bind[0] == bigchaindb.config['server']['bind'] +