added ability to build and run with docker w/ burnettk

This commit is contained in:
jasquat 2022-06-01 14:50:44 -04:00
parent d235c64915
commit c6131a04d3
12 changed files with 204 additions and 17 deletions

28
Dockerfile Normal file
View File

@ -0,0 +1,28 @@
FROM ghcr.io/sartography/python:3.9
RUN pip install poetry
RUN useradd _gunicorn --no-create-home --user-group
RUN apt-get update && \
apt-get install -y -q \
gcc libssl-dev \
curl git-core libpq-dev \
gunicorn3 default-mysql-client
WORKDIR /app
COPY pyproject.toml poetry.lock /app/
RUN poetry install
RUN set -xe \
&& apt-get remove -y gcc python3-dev libssl-dev \
&& apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*
COPY . /app/
# run poetry install again AFTER copying the app into the image
# otherwise it does not know what the main app module is
RUN poetry install
CMD ./bin/boot_server_in_docker

33
bin/boot_server_in_docker Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env bash
function error_handler() {
>&2 echo "Exited with BAD EXIT CODE '${2}' in ${0} script at line: ${1}."
exit "$2"
}
trap 'error_handler ${LINENO} $?' ERR
set -o errtrace -o errexit -o nounset -o pipefail
# run migrations
export FLASK_APP=/app/src/spiffworkflow_backend
if [ "${DOWNGRADE_DB:-}" = "true" ]; then
echo 'Downgrading database...'
poetry run flask db downgrade
fi
if [ "${UPGRADE_DB:-}" = "true" ]; then
echo 'Upgrading database...'
poetry run flask db upgrade
fi
port="${PORT0:-}"
if [[ -z "$port" ]]; then
port=7000
fi
# THIS MUST BE THE LAST COMMAND!
if [ "${APPLICATION_ROOT:-}" = "/" ]; then
exec poetry run gunicorn --bind "0.0.0.0:$PORT0" wsgi:app
else
exec poetry run gunicorn -e SCRIPT_NAME="$APPLICATION_ROOT" --bind "0.0.0.0:$PORT0" wsgi:app
fi

View File

@ -0,0 +1,15 @@
#!/usr/bin/env bash
function error_handler() {
>&2 echo "Exited with BAD EXIT CODE '${2}' in ${0} script at line: ${1}."
exit "$2"
}
trap 'error_handler ${LINENO} $?' ERR
set -o errtrace -o errexit -o nounset -o pipefail
if [[ -z "${BPMN_SPEC_ABSOLUTE_DIR:-}" ]]; then
script_dir="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
export BPMN_SPEC_ABSOLUTE_DIR="$script_dir/../../sample-process-models"
fi
docker compose up --build

View File

@ -16,4 +16,6 @@ if [[ -z "${BPMN_SPEC_ABSOLUTE_DIR:-}" ]]; then
export BPMN_SPEC_ABSOLUTE_DIR="$script_dir/../../sample-process-models"
fi
export FLASK_SESSION_SECRET_KEY=super_secret_key
FLASK_APP=src/spiffworkflow_backend poetry run flask run

43
docker-compose.yml Normal file
View File

@ -0,0 +1,43 @@
version: "3.8"
services:
db:
container_name: db
image: mysql:8.0.29
cap_add:
- SYS_NICE
restart: always
environment:
- MYSQL_DATABASE=spiffworkflow_backend_staging
- MYSQL_ROOT_PASSWORD=St4g3Th1515
ports:
- '7001:3306'
volumes:
- spiffworkflow_backend:/var/lib/mysql
spiffworkflow-backend:
container_name: spiffworkflow-backend
# command: tail -f /etc/hostname
depends_on:
- db
# image: sartography/cr-connect-workflow:dev
build:
context: .
environment:
- APPLICATION_ROOT=/
- FLASK_ENV=staging
- FLASK_SESSION_SECRET_KEY=super_secret_key
- DEVELOPMENT=true
- LDAP_URL=mock
- PORT0=7000
- PRODUCTION=false
- UPGRADE_DB=true
- DATABASE_URI=mysql+mysqlconnector://root:St4g3Th1515@db/spiffworkflow_backend_staging
- BPMN_SPEC_ABSOLUTE_DIR=/app/process_models
ports:
- "7000:7000"
volumes:
- ${BPMN_SPEC_ABSOLUTE_DIR}:/app/process_models
volumes:
spiffworkflow_backend:
driver: local

36
poetry.lock generated
View File

@ -384,6 +384,7 @@ jsonschema = ">=2.5.1,<5"
packaging = ">=20"
PyYAML = ">=5.1,<7"
requests = ">=2.9.1,<3"
swagger-ui-bundle = {version = ">=0.0.2,<0.1", optional = true, markers = "extra == \"swagger-ui\""}
werkzeug = ">=1.0,<3"
[package.extras]
@ -765,6 +766,20 @@ python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
[package.extras]
docs = ["sphinx"]
[[package]]
name = "gunicorn"
version = "20.1.0"
description = "WSGI HTTP Server for UNIX"
category = "main"
optional = false
python-versions = ">=3.5"
[package.extras]
eventlet = ["eventlet (>=0.24.1)"]
gevent = ["gevent (>=1.4.0)"]
setproctitle = ["setproctitle"]
tornado = ["tornado (>=0.2)"]
[[package]]
name = "identify"
version = "2.5.1"
@ -1779,6 +1794,17 @@ python-versions = ">=3.6"
[package.dependencies]
pbr = ">=2.0.0,<2.1.0 || >2.1.0"
[[package]]
name = "swagger-ui-bundle"
version = "0.0.9"
description = "swagger_ui_bundle - swagger-ui files in a pip package"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
Jinja2 = ">=2.0"
[[package]]
name = "tokenize-rt"
version = "4.2.1"
@ -1991,7 +2017,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "217b8108526defaaac72e0d98f15e82efda8b8f4698ea8b52623dc45708a8d70"
content-hash = "b9b8fb8a5f20adbc1ea2a1e8990c3d1e58e84c62d0a9e9f33e21137686630695"
[metadata.files]
alabaster = [
@ -2395,6 +2421,10 @@ greenlet = [
{file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"},
{file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"},
]
gunicorn = [
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
{file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"},
]
identify = [
{file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"},
{file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"},
@ -3111,6 +3141,10 @@ stevedore = [
{file = "stevedore-3.5.0-py3-none-any.whl", hash = "sha256:a547de73308fd7e90075bb4d301405bebf705292fa90a90fc3bcf9133f58616c"},
{file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"},
]
swagger-ui-bundle = [
{file = "swagger_ui_bundle-0.0.9-py3-none-any.whl", hash = "sha256:cea116ed81147c345001027325c1ddc9ca78c1ee7319935c3c75d3669279d575"},
{file = "swagger_ui_bundle-0.0.9.tar.gz", hash = "sha256:b462aa1460261796ab78fd4663961a7f6f347ce01760f1303bbbdf630f11f516"},
]
tokenize-rt = [
{file = "tokenize_rt-4.2.1-py2.py3-none-any.whl", hash = "sha256:08a27fa032a81cf45e8858d0ac706004fcd523e8463415ddf1442be38e204ea8"},
{file = "tokenize_rt-4.2.1.tar.gz", hash = "sha256:0d4f69026fed520f8a1e0103aa36c406ef4661417f20ca643f913e33531b3b94"},

View File

@ -37,11 +37,12 @@ pytest-flask = "^1.2.0"
pytest-flask-sqlalchemy = "^1.1.0"
psycopg2 = "^2.9.3"
typing-extensions = "^4.2.0"
connexion = "^2.13.1"
connexion = {extras = [ "swagger-ui",], version = "^2.13.1"}
lxml = "^4.8.0"
marshmallow-enum = "^1.5.1"
marshmallow-sqlalchemy = "^0.28.0"
PyJWT = "^2.4.0"
gunicorn = "^20.1.0"
[tool.poetry.dev-dependencies]

View File

@ -26,9 +26,13 @@ def create_app() -> flask.app.Flask:
)
app = connexion_app.app
app.config["CONNEXION_APP"] = connexion_app
app.secret_key = "super secret key"
app.config["SESSION_TYPE"] = "filesystem"
if os.environ.get("FLASK_SESSION_SECRET_KEY") is None:
raise KeyError("Cannot find the secret_key from the environment. Please set FLASK_SESSION_SECRET_KEY")
app.secret_key = os.environ.get("FLASK_SESSION_SECRET_KEY")
setup_config(app)
db.init_app(app)
migrate.init_app(app, db)
@ -40,7 +44,4 @@ def create_app() -> flask.app.Flask:
app.register_blueprint(admin_blueprint, url_prefix="/admin")
connexion_app.add_api("api.yml", base_path="/v1.0")
for name, value in app.config.items():
print(f"{name} = {value}")
return app

View File

@ -16,18 +16,21 @@ def setup_config(app: Flask) -> None:
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config.from_object("spiffworkflow_backend.config.default")
if os.environ.get("DATABASE_URI") is None:
if os.environ.get("SPIFF_DATABASE_TYPE") == "sqlite":
app.config[
"SQLALCHEMY_DATABASE_URI"
] = f"sqlite:///{app.instance_path}/db_{app.env}.sqlite3"
else:
# use pswd to trick flake8 with hardcoded passwords
mysql_pswd = os.environ.get("MYSQL_PASSWORD")
if mysql_pswd is None:
mysql_pswd = ""
db_pswd = os.environ.get("DB_PASSWORD")
if db_pswd is None:
db_pswd = ""
app.config[
"SQLALCHEMY_DATABASE_URI"
] = f"mysql+mysqlconnector://root:{mysql_pswd}@localhost/spiffworkflow_backend_{app.env}"
] = f"mysql+mysqlconnector://root:{db_pswd}@localhost/spiffworkflow_backend_{app.env}"
else:
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DATABASE_URI")
env_config_module = "spiffworkflow_backend.config." + app.env
try:

View File

@ -0,0 +1 @@
"""Staging."""

25
wsgi.py Normal file
View File

@ -0,0 +1,25 @@
from werkzeug.exceptions import NotFound
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from werkzeug.middleware.proxy_fix import ProxyFix
from spiffworkflow_backend import create_app
app = create_app()
if __name__ == "__main__":
def no_app(environ, start_response):
return NotFound()(environ, start_response)
# Remove trailing slash, but add leading slash
base_url = '/' + app.config['APPLICATION_ROOT'].strip('/')
routes = {'/': app.wsgi_app}
if base_url != '/':
routes[base_url] = app.wsgi_app
app.wsgi_app = DispatcherMiddleware(no_app, routes)
app.wsgi_app = ProxyFix(app.wsgi_app)
flask_port = app.config['FLASK_PORT']
app.run(host='0.0.0.0', port=flask_port)