A Python demo app written with Flask-RESTful.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rypel 9511cf9b79 [docker-compose] mount ./app read-only on dev environment to avoid container writing back into workspace 6 minutes ago
app [auth, env] simplify to 'Api-Key' header; add note 22 hours ago
nginx/conf.d [nginx] be case-sensitive and let gunicorn decide the rest 5 days ago
tests [app, wsgi, makefile] rename package to 'app' 1 month ago
.coveragerc [coveragerc] measure branch coverage and show missing statement lines 1 month ago
.editorconfig [makefile, editorconfig] fix final newlines 1 month ago
.env [makefile] include .env and adjust variable expansion 21 minutes ago
.gitignore [gitignore] clean-up 2 weeks ago
Dockerfile [makefile] include .env and adjust variable expansion 21 minutes ago
LICENSE [license] fill in copyright 2 months ago
README.md [readme] update 21 minutes ago
TODO.md [makefile, docker-compose, readme] add dev & prod compose files and conditionally append them to base config in docker-compose targets 17 hours ago
docker-compose.dev.yml [docker-compose] mount ./app read-only on dev environment to avoid container writing back into workspace 6 minutes ago
docker-compose.prod.yml [makefile, docker-compose, readme] add dev & prod compose files and conditionally append them to base config in docker-compose targets 17 hours ago
docker-compose.yml [makefile] include .env and adjust variable expansion 21 minutes ago
entrypoint.sh [dockerfile, compose, entrypoint] us 'pg_isready' binary for health/readiness checks 1 week ago
gunicorn.py [env, config, gunicorn] use POSTGRES_HOST & POSTGRES_PORT solely from env 1 week ago
makefile [makefile] cleanup 15 minutes ago
openapi.yaml [app, dockerfile, openapi] rename and document 'GET /health' route; add tags 1 week ago
requirements-dev.txt [requirements] update sqlalchemy and pip 1 week ago
requirements.txt [requirements] update sqlalchemy-utils 1 day ago
run.py [app, config, env] pass config directly to app factory 2 weeks ago

README.md

flask-restful-demo

A Python app written in with the Flask-RESTful framework. It comprises a user management API with basic CRUDL operations and SQL persistence.

The Flask app speaks to a Postgres database and is being served by a Gunicorn server. Any HTTP traffic towards the server is proxy’d through Nginx.

The server, database and proxy services run as containers via docker-compose in a bridge network.

Specification

There is an openapi.yaml spec for the API, see development -> view specification below on how to view it.

Prerequisites

Install these through your system’s package manager:

  • Python 3.7+
  • virtualenv 16+
  • cURL 7+
  • GNU Make 4+
  • Docker 18.09+
  • docker-compose 1.24+
  • Firefox (or any other browser)

Docker

Add yourself to the docker group with:

sudo gpasswd -a ${USER} docker

Usage

For a quick start simply type:

make all

Source the .env file and export needed variables into environment:

set -a && . .env && set +a

Query the API with cURL (-i for header output):

curl -iH "$API_KEY_HEADER" localhost/users                                                         # get all users
curl -iH "$API_KEY_HEADER" localhost/users?name-starts-with=foo                                    # get users, filtered
curl -iH "$API_KEY_HEADER" -H "$JSON_HEADER" -d '{"name":"foobar"}' localhost/users                # create a user
curl -iH "$API_KEY_HEADER" localhost/users/1234                                                    # get a user
curl -iH "$API_KEY_HEADER" -X PATCH -H "$JSON_HEADER" -d '{"name":"slurm"}' localhost/users/1234   # update a user
curl -iH "$API_KEY_HEADER" -X DELETE localhost/users/1234                                          # delete a user

Access the (proxy-restricted) app health route through the server container:

docker exec -it "${PROJECT_NAME}_server_1" sh -c 'curl -i ${GUNICORN_HOST}:${GUNICORN_PORT}/health'

Stop and remove the server, database and proxy containers:

make down

Development

The toolchain is defined in the makefile.

Configuration

Values that change often or are needed by multiple components should be defined as environment variables in .env. The rest should be defined in their respective config files.

Environment

The .env file can be sourced and the environment variables will be used by following files:

  • app/config.py
  • docker-compose*.yml
  • Dockerfile
  • entrypoint.sh
  • gunicorn.py
  • makefile
  • run.py

Server

The server config resides in the gunicorn.py file.

App

The app config resides in the app/config.py file, separated into environment-specific classes.

Containers

Base config is docker-compose.yml. Additionally, docker-compose.dev.yml or docker-compose.prod.yml config files can be appended:

docker-compose -f <BASE> -f <DEV-OR-PROD>

The makefile dynamically decides which one to append by reading the FLASK_ENV setting.

Clean-up

Remove all state:

make clean

Remove virtualenv and pip dependencies:

make clean-venv

Remove database volume (asks before actually removing):

make clean-volume

Remove app/server image:

make clean-image

Linting & Testing

Run Flake8 to lint all python modules:

make lint

Run nose2 with test coverage report:

make test

Virtualenv

Install virtualenv with Python, Pip and setuptools:

make venv

Source virtualenv activation script in local shell (for non-bash see: venv/bin/activate.[csh|fish|ps1]):

. venv/bin/activate

Dependencies

Install dependencies defined in both requirements.txt and requirements-dev.txt files:

make install

Upgrade installed dependencies and, if needed, update both requirements files:

make upgrade

Running

Pull database and proxy container images:

make pull

Build app/server container image:

make build

Create and start database, app/server and proxy containers in background:

make up

Attach to the logs of running containers:

make logs

Show final container config:

make config

Stop and remove running containers:

make down

To only stop a specific service use docker-compose stop server instead.

Specification

Optional – to use any other browser than Firefox, set this environment variable beforehand:

export BROWSER=chrome-or-whatever

Serve a local Swagger UI instance and open it in browser (auto-loads the openapi.yaml spec):

make spec

Serve a local Swagger Editor instance and open it in browser (there, import openapi.yaml manually):

make spec-edit