-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dockerized dev environment #71
base: master
Are you sure you want to change the base?
Changes from all commits
639fb83
39df6e8
58c1f56
425208b
28a6947
c5f20c9
29ae8e7
a29bfbf
89ba770
ed98e9b
dd08a30
e5f44ca
c9f6401
d3da3f0
92d676e
b75aa1b
b06959a
279bcf9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
FROM python:3.7-slim | ||
|
||
ENV FLASK_APP=runserver.py | ||
ENV FLASK_RUN_HOST=0.0.0.0 | ||
|
||
WORKDIR /app | ||
|
||
# libz and libjpeg are needed for pillow (PIL) | ||
RUN apt-get update && apt-get install -y \ | ||
gcc \ | ||
libpq-dev \ | ||
libz-dev \ | ||
libjpeg-dev | ||
|
||
COPY requirements.txt /app/requirements.txt | ||
COPY dev-requirements.txt /app/dev-requirements.txt | ||
RUN pip install -r /app/requirements.txt | ||
# Just to fix an issue installing black, uninstall typing | ||
# https://stackoverflow.com/questions/55833509/attributeerror-type-object-callable-has-no-attribute-abc-registry | ||
RUN pip uninstall -y typing | ||
RUN pip install -r /app/dev-requirements.txt | ||
|
||
# Clean some space | ||
# RUN apt-get autoremove -y gcc | ||
|
||
# COPY . /app | ||
expose 5000 | ||
|
||
CMD [ "flask", "run" ] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,10 +34,15 @@ A Flask webapp using Python Image Library to reconstruct and display a summary o | |
|
||
`DEBUG` set to True for helpful debugging; never set to True in production environment | ||
|
||
`ASSETS_PATH` where images are stored | ||
|
||
`LANGUAGES` List of supported languages | ||
|
||
#### Database | ||
|
||
`USE_SQLITE` NOTE: in testing we've moved to Postgres, so this probably doesn't work any more | ||
|
||
|
||
##### SQLite | ||
|
||
`DB_SQLITE` | ||
|
@@ -93,6 +98,71 @@ FLASK_APP=runserver.py flask run | |
|
||
Assets for image generation go in `sdv\assets\[subfolder]`. Assets used as-is go in `sdv\static\assets\[subfolder]`. | ||
|
||
## Run With Docker compose | ||
|
||
### Create the DB | ||
|
||
Build all the containers but start only the postgres one for now. Then, run the | ||
`createadmin.py` from the webapps container: | ||
|
||
```bash | ||
docker compose build | ||
docker compose up postges | ||
docker run \ | ||
-it \ | ||
-e PYTHONPATH=. \ | ||
-v "$(pwd)"/:/app \ | ||
--network=sdv-summary_postgres \ | ||
sdv-summary_webapp \ | ||
bas -c "python sdv/createadmin.py; python sdv/createdb.py" | ||
docker-compose down | ||
``` | ||
|
||
### Run the webapp + database | ||
|
||
```bash | ||
docker compose up | ||
``` | ||
|
||
This command: | ||
- Download necessary docker images for python, postgres and pgAdmin, if not present. | ||
- Creates a database `postgres` with user:password `postgres:postgres` for administration. | ||
- Creates a database `sdv_summary_development` and a `user:password` | ||
`sdv_summary:sdv_summary` with all rights on the `sdv_summary_development` | ||
database. | ||
- TODO: Creates tables? | ||
- TODO: Inits data? | ||
- Runs PGAdmin on port 5050, and postgre on 5432 | ||
|
||
## PgAdmin | ||
You can access it on http://localhost:5050 with: | ||
- Username: [email protected] | ||
- Password: admin | ||
|
||
Then you should add a server. Call it "docker" for example. For the connection: | ||
- host: postgres | ||
- port: 5432 | ||
- maintenance database: postgres | ||
- username: admin | ||
- password: admin | ||
|
||
CREATE USER sdv_summary WITH PASSWORD = 'sdv_summary'; | ||
CREATE DATABASE sdv_summary_development; | ||
GRANT ALL PRIVILEGES ON DATABASE sdv_summary_development TO sdv_summary; | ||
|
||
|
||
When you are done for the day, just: | ||
|
||
```bash | ||
docker compose down | ||
``` | ||
|
||
And if you want to nuke the databases *LOSING ALL THE DATA*: | ||
|
||
```bash | ||
docker compose down --volumes | ||
``` | ||
|
||
## Code Style | ||
|
||
In order to keep the code style consistent, this project is formatted using [Black](https://github.com/psf/black). | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# With help from https://github.com/khezen/compose-postgres/blob/master/docker-compose.yml | ||
version: '3.5' | ||
|
||
services: | ||
|
||
postgres: | ||
container_name: postgres | ||
image: postgres | ||
restart: always | ||
environment: | ||
POSTGRES_DB: ${POSTGRES_DB:-postgres} | ||
POSTGRES_USER: ${POSTGRES_USER:-admin} | ||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-admin} | ||
POSTGRES_HOST_AUTH_METHOD: trust | ||
# PGDATA: /data/postgres | ||
volumes: | ||
- db-data:/var/lib/postgresql/data | ||
- ./init.sql:/docker-entrypoint-initdb.d/10-init.sql | ||
ports: | ||
- 5432:5432 | ||
networks: | ||
- postgres | ||
|
||
pgadmin: | ||
container_name: pgadmin | ||
image: dpage/pgadmin4 | ||
environment: | ||
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:[email protected]} | ||
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin} | ||
volumes: | ||
- pgadmin:/root/.pgadmin | ||
ports: | ||
- "${PGADMIN_PORT:-5050}:80" | ||
networks: | ||
- postgres | ||
restart: unless-stopped | ||
|
||
adminer: | ||
image: adminer | ||
restart: always | ||
ports: | ||
- 8080:8080 | ||
networks: | ||
- postgres | ||
|
||
webapp: | ||
container_name: webapp | ||
restart: always | ||
build: . | ||
depends_on: | ||
- "postgres" | ||
ports: | ||
- 5000:5000 | ||
volumes: | ||
- .:/app | ||
networks: | ||
- postgres | ||
environment: | ||
PYTHONPATH: .:sdv | ||
# FLASK_APP: ./runserver.py | ||
FLASK_RUN_HOST: 0.0.0.0 | ||
FLASK_ENV: development | ||
|
||
|
||
networks: | ||
postgres: | ||
driver: bridge | ||
|
||
volumes: | ||
db-data: | ||
pgadmin: |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
-- Initialization script | ||
-- Creates the database and tables | ||
|
||
-- This is not needed as the suer and database are created from the docker | ||
-- compose environment variables | ||
CREATE USER sdv_summary WITH PASSWORD 'sdv_summary'; | ||
CREATE DATABASE sdv_summary_development; | ||
GRANT ALL PRIVILEGES ON DATABASE sdv_summary_development TO sdv_summary; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,11 @@ | ||
import os | ||
|
||
os.chdir(os.path.join(os.path.dirname(__file__), "sdv")) | ||
# os.chdir(os.path.join(os.path.dirname(__file__), "sdv")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't got a local dev env set up so can't test this, but I feel like things won't work properly without this. It's possibly more of a patch for a legacy bug than needed for a new dev env though, so I'm not sure what the best approach is. Will need to test this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I should have added a comment there. I don't remember the details but I couldn't make either the container or the test to work with this, I had to fix this by adding sdv to the |
||
|
||
import sys | ||
|
||
sys.path.insert(0, "./") | ||
sys.path.insert(1, "./sdv/") | ||
|
||
# for some reason on Python 3.4 on Linux Mint, using runserver.py crashes on first reload if os.chdir() is used | ||
# so to avoid this (and break some of the site, but oh well...) remove os.chdir and add sdv to path | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,32 @@ | ||
#!/usr/bin/env python | ||
|
||
class DevelopmentConfig(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know this is |
||
DEBUG = True | ||
USE_SQLITE = False | ||
DB_NAME = 'sdv_summary_development' | ||
DB_USER = '' | ||
DB_PASSWORD = '' | ||
|
||
# Mandatory settings | ||
UPLOAD_FOLDER = 'uploads' | ||
SECRET_KEY = 'changeme' | ||
MAX_CONTENT_LENGTH = 16*1024*1024 | ||
PASSWORD_ATTEMPTS_LIMIT = None | ||
PASSWORD_MIN_LENGTH = 6 | ||
IMGUR_CLIENTID = '' | ||
IMGUR_SECRET = '' | ||
IMGUR_DIRECT_UPLOAD = True | ||
RECAPTCHA_ENABLED = False | ||
RECAPTCHA_SITE_KEY = None | ||
RECAPTCHA_SECRET_KEY = None | ||
ANALYTICS_ID = '' | ||
DEBUG = True | ||
ASSET_PATH = 'sdv/assets' | ||
LANGUAGES = ['en'] | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This possibly also needs
and may need:
though this was used for fixing some relative directories I think that might not apply in dockerized dev There's also a
Which might not be required |
||
# Database | ||
USE_SQLITE = False | ||
DB_NAME = 'sdv_summary_development' | ||
DB_USER = 'sdv_summary' | ||
DB_PASSWORD = 'sdv_summary' | ||
# NOTE: Leave commented to use unix socket | ||
# Or specify Host if DB is not in the same machine | ||
# DB_HOST = 'postgres' | ||
|
||
config = { | ||
'development': DevelopmentConfig | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,9 +5,10 @@ | |
import getpass | ||
from werkzeug import check_password_hash | ||
from config import config | ||
from sdv.utils.postgres import get_db_connection_string | ||
|
||
app = Flask(__name__) | ||
config_name = os.environ.get("SDV_APP_SETTINGS", None) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Possibly a bad idea from a security point of view? Defaulting to dev could introduce risk in a production env |
||
config_name = os.environ.get("SDV_APP_SETTINGS", 'development') | ||
app.config.from_object(config[config_name]) | ||
|
||
database_structure_dict = { | ||
|
@@ -267,15 +268,8 @@ def connect_db(): | |
connection = sqlite3.connect(app.config["DB_SQLITE"]) | ||
else: | ||
import psycopg2 | ||
|
||
connection = psycopg2.connect( | ||
"dbname=" | ||
+ app.config["DB_NAME"] | ||
+ " user=" | ||
+ app.config["DB_USER"] | ||
+ " password=" | ||
+ app.config["DB_PASSWORD"] | ||
) | ||
connstr = get_db_connection_string(app.config) | ||
connection = psycopg2.connect(connstr) | ||
return connection | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
|
||
def get_db_connection_string(config): | ||
"""Given app.config, returns the connection string for postgres""" | ||
params = dict( | ||
dbname=config["DB_NAME"], | ||
user=config["DB_USER"], | ||
) | ||
# Host is optional. | ||
# If not present, use unix sockets | ||
if 'DB_PASSWORD' in config: | ||
params['password'] = config["DB_PASSWORD"] | ||
if 'DB_HOST' in config: | ||
params['host'] = config["DB_HOST"] | ||
connstr = ' '.join(f'{key}={value}' for key, value in params.items()) | ||
return connstr |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import pytest | ||
from sdv.utils.postgres import get_db_connection_string | ||
|
||
class TestGetDBConnectionString: | ||
|
||
def test_empty_raises_exception(self): | ||
with pytest.raises(KeyError): | ||
get_db_connection_string({}) | ||
|
||
def test_db_name_and_user(self): | ||
assert get_db_connection_string(dict( | ||
DB_NAME="mydb", | ||
DB_USER="myusr", | ||
)) == "dbname=mydb user=myusr" | ||
|
||
def test_db_name_user_password(self): | ||
assert get_db_connection_string(dict( | ||
DB_NAME="mydb", | ||
DB_USER="myusr", | ||
DB_PASSWORD="mypass", | ||
)) == "dbname=mydb user=myusr password=mypass" | ||
|
||
def test_all_attributes(self): | ||
assert get_db_connection_string(dict( | ||
DB_HOST="thehost", | ||
DB_NAME="mydb", | ||
DB_USER="myusr", | ||
DB_PASSWORD="mypass", | ||
)) == "dbname=mydb user=myusr password=mypass host=thehost" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What required
typing
? Why can we remove it? (open to believing we can, not sure why it was there in the first place though)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again I don't remember all the details sadly, but I think it was because by enforcing the typing version black would refuse to install. And typing is a dependency of the other libraries anyway.