diff --git a/Procfile b/Procfile index b6ed6aa..9e0ff22 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: invoke serve --port $VCAP_APP_PORT +web: gunicorn main:app --bind 0.0.0.0:$VCAP_APP_PORT --config hooks.py diff --git a/app.py b/app.py new file mode 100644 index 0000000..559e8ec --- /dev/null +++ b/app.py @@ -0,0 +1,42 @@ +import os + +from sandman import app + +from flask import request +from flask.ext.cors import CORS +from flask.ext.basicauth import BasicAuth + +import aws +import utils +import config + +def make_app(): + app.json_encoder = utils.APIJSONEncoder + app.config['CASE_INSENSITIVE'] = config.CASE_INSENSITIVE + app.config['SQLALCHEMY_DATABASE_URI'] = config.SQLA_URI + app.config['BASIC_AUTH_USERNAME'] = os.environ.get('AUTOAPI_ADMIN_USERNAME', '') + app.config['BASIC_AUTH_PASSWORD'] = os.environ.get('AUTOAPI_ADMIN_PASSWORD', '') + + CORS(app) + basic_auth = BasicAuth(app) + + @app.before_request + def refresh(): + tables = utils.get_tables() + if tables != app.config['SQLALCHEMY_TABLES']: + utils.refresh_tables() + app.config['SQLALCHEMY_TABLES'] = tables + app.config['SQLALCHEMY_TABLES'] = utils.get_tables() + + @app.before_request + def protect_admin(): + if request.path.startswith('/admin/'): + if not basic_auth.authenticate(): + return basic_auth.challenge() + + blueprint = aws.make_blueprint() + app.register_blueprint(blueprint) + + utils.activate(admin=True) + + return app diff --git a/hooks.py b/hooks.py new file mode 100644 index 0000000..4a20cb9 --- /dev/null +++ b/hooks.py @@ -0,0 +1,11 @@ +import os +import sys +import multiprocessing + +sys.path.insert(0, os.getcwd()) +import aws + +def when_ready(server): + pool = multiprocessing.Pool(processes=1) + pool.apply_async(aws.fetch_bucket) + pool.close() diff --git a/main.py b/main.py new file mode 100644 index 0000000..845be14 --- /dev/null +++ b/main.py @@ -0,0 +1,3 @@ +from app import make_app + +app = make_app() diff --git a/requirements.txt b/requirements.txt index fa81795..ce4866a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ csvkit>=0.9.1 invoke>=0.10.1 pandas>=0.16.2 requests>=2.7.0 +gunicorn>=19.3.0 cryptography>=1.0 Flask-Cors>=2.1.0 Flask-BasicAuth>=0.2.0 diff --git a/tasks.py b/tasks.py index 934dc20..d1d5a1d 100644 --- a/tasks.py +++ b/tasks.py @@ -1,13 +1,7 @@ -import os import logging -import concurrent.futures from invoke import task, run -from flask import request -from flask.ext.cors import CORS -from flask.ext.basicauth import BasicAuth -import aws import utils import config @@ -28,38 +22,4 @@ def apify(filename, tablename=None): utils.drop_table(tablename) utils.load_table(filename, tablename) utils.index_table(tablename, config.CASE_INSENSITIVE) - utils.activate(base=utils.ReadOnlyModel) logger.info('Finished importing {0}'.format(filename)) - -@task -def serve(host='0.0.0.0', port=5000, debug=False): - from sandman import app - app.json_encoder = utils.APIJSONEncoder - app.config['SERVER_PORT'] = port - app.config['CASE_INSENSITIVE'] = config.CASE_INSENSITIVE - app.config['SQLALCHEMY_DATABASE_URI'] = config.SQLA_URI - app.config['BASIC_AUTH_USERNAME'] = os.environ.get('AUTOAPI_ADMIN_USERNAME', '') - app.config['BASIC_AUTH_PASSWORD'] = os.environ.get('AUTOAPI_ADMIN_PASSWORD', '') - - CORS(app) - basic_auth = BasicAuth(app) - - @app.before_request - def protect_admin(): - if request.path.startswith('/admin/'): - if not basic_auth.authenticate(): - return basic_auth.challenge() - - blueprint = aws.make_blueprint() - app.register_blueprint(blueprint) - - # Activate sandman with existing models - utils.activate(admin=True) - - # Load bucket in a separate process, then re-activate sandman in the main process - pool = concurrent.futures.ProcessPoolExecutor() - future = pool.submit(aws.fetch_bucket) - future.add_done_callback(utils.activate) - pool.shutdown(wait=False) - - app.run(host=host, port=port, debug=debug) diff --git a/utils.py b/utils.py index dda9a32..c21c744 100644 --- a/utils.py +++ b/utils.py @@ -79,6 +79,11 @@ def drop_table(tablename, metadata=None, engine=None): pass refresh_tables() +def get_tables(engine=None): + engine = engine or sa.create_engine(config.SQLA_URI) + inspector = sa.engine.reflection.Inspector.from_engine(db.engine) + return set(inspector.get_table_names()) + def refresh_tables(): db.metadata.clear() - activate(base=ReadOnlyModel, reflect_all=True, browser=False, admin=False) + activate()