From beab7d8f5f4871009c8975b033fabf794ce43035 Mon Sep 17 00:00:00 2001 From: Saejin Mahlau-Heinert Date: Sun, 28 Apr 2019 16:31:13 -0400 Subject: [PATCH 1/6] Add appfactory instead of app.py --- application/{app.py => appfactory.py} | 0 run.py | 6 +++--- tests/test_app.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename application/{app.py => appfactory.py} (100%) diff --git a/application/app.py b/application/appfactory.py similarity index 100% rename from application/app.py rename to application/appfactory.py diff --git a/run.py b/run.py index 9bf4c5f..eea1829 100644 --- a/run.py +++ b/run.py @@ -1,12 +1,12 @@ -"""Run this file to run the application""" +"""Run this file to run the development server""" import argparse -from application import app as appfactory +from application import appfactory app = appfactory.create_app() -app.secret_key = "$JLmL!eCQXyajbdu2LCJ&Vwqs2JGagg3B&FRfexCmKBV" +app.secret_key = "dev" parser = argparse.ArgumentParser() diff --git a/tests/test_app.py b/tests/test_app.py index fed8297..f07ecef 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -1,7 +1,7 @@ """Basic Tests""" import pytest -from application import app as appfactory +from application import appfactory @pytest.fixture() From 54ed83d6e12a757bf0a388af63ef6513b929efd0 Mon Sep 17 00:00:00 2001 From: Saejin Mahlau-Heinert Date: Sun, 28 Apr 2019 16:32:00 -0400 Subject: [PATCH 2/6] Use flask.g as context_globals --- application/db_connect.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/application/db_connect.py b/application/db_connect.py index a481e66..30d69f3 100644 --- a/application/db_connect.py +++ b/application/db_connect.py @@ -5,6 +5,7 @@ # from flask import g, session, escape from flask import current_app as app +from flask import g as context_globals DATABASE = "database.db" @@ -12,17 +13,19 @@ def get_db(): """ Get database """ - db = getattr(flask.g, "_database", None) - if db is None: - db = flask.g._database = sqlite3.connect(DATABASE) - return db + if "db" not in context_globals: + context_globals.db = sqlite3.connect(DATABASE) + + return context_globals.db @app.teardown_appcontext -# pylint: disable=unused-argument -def close_connection(exception): +def teardown_db(exception=None): """ Close database connection """ - db = getattr(flask.g, "_database", None) + if exception is not None: + print(f"ERROR: {exception}") + db = context_globals.pop("db", None) + if db is not None: db.close() From 1b26d75519d2e5518dea5b40875d8a3902ad1788 Mon Sep 17 00:00:00 2001 From: Saejin Mahlau-Heinert Date: Sun, 28 Apr 2019 16:32:15 -0400 Subject: [PATCH 3/6] Enable gunicorn config --- Pipfile | 1 + Pipfile.lock | 10 +++++++++- application/wsgi.py | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 application/wsgi.py diff --git a/Pipfile b/Pipfile index ecd3016..5361854 100644 --- a/Pipfile +++ b/Pipfile @@ -5,6 +5,7 @@ name = "pypi" [packages] flask = "*" +gunicorn = "*" [dev-packages] black = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 3210742..63ea19e 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "14732a648d936c1456d08d289d37f5723ef3ba26e4b1b66bcc88e126ef0d8674" + "sha256": "43673d2b6bf1d65c9292620ee4ab7cdf5bd58d4547420cd8ae943c638a2dbd36" }, "pipfile-spec": 6, "requires": {}, @@ -29,6 +29,14 @@ "index": "pypi", "version": "==1.0.2" }, + "gunicorn": { + "hashes": [ + "sha256:aa8e0b40b4157b36a5df5e599f45c9c76d6af43845ba3b3b0efe2c70473c2471", + "sha256:fa2662097c66f920f53f70621c6c58ca4a3c4d3434205e608e121b5b3b71f4f3" + ], + "index": "pypi", + "version": "==19.9.0" + }, "itsdangerous": { "hashes": [ "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", diff --git a/application/wsgi.py b/application/wsgi.py new file mode 100644 index 0000000..3487dac --- /dev/null +++ b/application/wsgi.py @@ -0,0 +1,7 @@ +"""WSGI entry point for use with gunicorn in production""" +import os + +from application import appfactory + +app = appfactory.create_app() +app.secret_key = os.environ["FLASK_SECRET_KEY"] From 1edbf58f2a0e4df7dba997e7122a9079e668772c Mon Sep 17 00:00:00 2001 From: Saejin Mahlau-Heinert Date: Sun, 28 Apr 2019 16:32:38 -0400 Subject: [PATCH 4/6] Add gunicorn to docker image --- .dockerignore | 3 +++ .gitignore | 3 +++ Dockerfile | 7 +++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.dockerignore b/.dockerignore index 65a38a5..baec5b9 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,6 +11,9 @@ Dockerfile # Ignore git's ignore +# Config python file +config.py + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/.gitignore b/.gitignore index 894a44c..96ebb2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Config python file +config.py + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/Dockerfile b/Dockerfile index b75e090..274881f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,9 +2,6 @@ FROM python:3.7.3-alpine MAINTAINER gkapfham@allegheny.edu ENV APP_DIR /quizagator -ENV FLASK_PORT 5000 - -EXPOSE ${FLASK_PORT} # create and use the quizagator directory WORKDIR ${APP_DIR} @@ -17,5 +14,7 @@ COPY . ${APP_DIR} # (Don't use pipenv run to run things) RUN set -ex && pip install pipenv && pipenv install --deploy --system +EXPOSE 80 + # the start command will run the production server -CMD python run.py --host 0.0.0.0 --port ${FLASK_PORT} +CMD ["gunicorn", "--workers", "3", "--access-logfile", "-", "--bind", "0.0.0.0:80", "application.wsgi:app"] From f0b49f9a15f6018d5817733ea4833b8e2bffed1e Mon Sep 17 00:00:00 2001 From: Saejin Mahlau-Heinert Date: Sun, 28 Apr 2019 16:32:50 -0400 Subject: [PATCH 5/6] Configure scrips to use new docker image --- scripts/create-docker-image.sh | 2 +- scripts/run-docker-image.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/create-docker-image.sh b/scripts/create-docker-image.sh index 153fdfd..209aec6 100755 --- a/scripts/create-docker-image.sh +++ b/scripts/create-docker-image.sh @@ -10,6 +10,6 @@ if ! test -z "$TAG"; then NAME="$NAME:$TAG" fi -docker image rm --force "$NAME" +#docker image rm --force "$NAME" docker build -t "$NAME" . diff --git a/scripts/run-docker-image.sh b/scripts/run-docker-image.sh index 1818d4c..e5d158f 100755 --- a/scripts/run-docker-image.sh +++ b/scripts/run-docker-image.sh @@ -13,4 +13,4 @@ OUTER_PORT="4201" docker stop "$NAME" docker rm "$NAME" -docker run -d --name "$NAME" -p "${OUTER_PORT}:${INNER_PORT}" "$IMAGE" +docker run --name "$NAME" -e "FLASK_SECRET_KEY=dev" -p "${OUTER_PORT}:${INNER_PORT}" "$IMAGE" From 59e5a35bc1d09fdef7883a3f2317b209aca39ddc Mon Sep 17 00:00:00 2001 From: Saejin Mahlau-Heinert Date: Sun, 28 Apr 2019 16:33:28 -0400 Subject: [PATCH 6/6] Update README.md to reflect docker image --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5836888..7bc2fb4 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ project, inspect the `[scripts]` tag in `Pipfile`: cat Pipfile ``` -Finally, to run the project locally: +Finally, to run the project locally in development mode: ``` pipenv run server @@ -80,6 +80,23 @@ Or use the following to see all the options: pipenv run python run.py --help ``` +## Docker + +There is a docker image published to +[gatoreducator/quizagator](https://hub.docker.com/r/gatoreducator/quizagator). +There are two main parts of configuration: specifying a secret in the +`FLASK_SECRET_KEY` environment variable, and forwarding the desired outer port +to `80` inside the container. The following command does both: + +``` +docker run --name quizagator -p 5000:80 -e FLASK_SECRET_KEY=d2dbb3 gatoreducator/quizagator:0.0.1-dev +``` + +Additionally, developers can use `pipenv run create-image` and `pipenv run +image` to run a development container -- this is not to be deployed to +production. + + ## Contributors Check out our contributors!