diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..03f537ede --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.github +.venv +*.pyc +__pycache__ +*.egg-info +tags + +# Docker +docker/ +!docker/entrypoint.sh diff --git a/.gitignore b/.gitignore index b823e313b..5d4e5e8f6 100644 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,8 @@ celerybeat-schedule # dotenv .env +docker/.env.* +!docker/.env.*.example # virtualenv .venv diff --git a/README.md b/README.md index c2807eb2c..af9c88aaa 100644 --- a/README.md +++ b/README.md @@ -238,14 +238,21 @@ PostgreSQL 11, la imagen `pgautoupgrade` se encargará de migrar automáticament datos al formato de PostgreSQL 14 en el primer arranque. Este proceso puede tardar unos minutos dependiendo del tamaño de la base de datos. -Se puede editar el fichero docker-settings.py para modificar el settings -del proyecto django antes de crear las imágenes del contenedor. +La configuración se realiza mediante variables de entorno. Copiar los +ficheros de ejemplo y editarlos según sea necesario: + + $ cd docker + $ cp .env.decide.example .env.decide + $ cp .env.postgres.example .env.postgres Crear imágenes y lanzar contenedores: - $ cd docker $ docker-compose up -d +Generar ficheros estáticos (solo si es necesario): + + $ docker exec -ti decide_web ./manage.py collectstatic + Parar contenedores: $ docker-compose down diff --git a/decide/decide/settings.py b/decide/decide/settings.py index 1a1eab5e1..b046e5ae5 100644 --- a/decide/decide/settings.py +++ b/decide/decide/settings.py @@ -12,6 +12,15 @@ import os + +def env_list(env_name, default=None): + """Get environment var and convert in python list. Example .env: APPS=x1,y2,z3""" + if default is None: + default = [] + list_vars = os.environ.get(env_name, None) + return list_vars.split(",") if list_vars else default + + # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -20,12 +29,12 @@ # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '^##ydkswfu0+=ofw0l#$kv^8n)0$i(qd&d&ol#p9!b$8*5%j1+' +SECRET_KEY = os.environ.get('SECRET_KEY', '^##ydkswfu0+=ofw0l#$kv^8n)0$i(qd&d&ol#p9!b$8*5%j1+') # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = os.environ.get('DEBUG', 'True') == 'True' -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = env_list('ALLOWED_HOSTS', []) # Application definition @@ -78,7 +87,19 @@ 'voting', ] -BASEURL = 'http://localhost:8000' +BASEURL = os.environ.get('BASEURL', 'http://localhost:8000') + +APIS = { + 'authentication': os.environ.get('API_AUTHENTICATION', BASEURL), + 'base': os.environ.get('API_BASE', BASEURL), + 'booth': os.environ.get('API_BOOTH', BASEURL), + 'census': os.environ.get('API_CENSUS', BASEURL), + 'mixnet': os.environ.get('API_MIXNET', BASEURL), + 'postproc': os.environ.get('API_POSTPROC', BASEURL), + 'store': os.environ.get('API_STORE', BASEURL), + 'visualizer': os.environ.get('API_VISUALIZER', BASEURL), + 'voting': os.environ.get('API_VOTING', BASEURL), +} MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', @@ -116,12 +137,12 @@ DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': 'decide', - 'USER': 'decide', - 'PASSWORD': 'decide', - 'HOST': 'localhost', - 'PORT': '5432', + 'ENGINE': os.environ.get('DB_ENGINE', 'django.db.backends.postgresql'), + 'NAME': os.environ.get('DB_NAME', 'decide'), + 'USER': os.environ.get('DB_USER', 'decide'), + 'PASSWORD': os.environ.get('DB_PASSWORD', 'decide'), + 'HOST': os.environ.get('DB_HOST', 'localhost'), + 'PORT': os.environ.get('DB_PORT', '5432'), } } @@ -160,7 +181,16 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = os.environ.get('STATIC_URL', '/static/') +STATIC_ROOT = os.environ.get('STATIC_ROOT', os.path.join(BASE_DIR, 'static')) + +MEDIA_URL = os.environ.get('MEDIA_URL', '/media/') +MEDIA_ROOT = os.environ.get('MEDIA_ROOT', os.path.join(BASE_DIR, 'media')) + +CSRF_TRUSTED_ORIGINS = env_list('CSRF_TRUSTED_ORIGINS', []) + +_secure_proxy_ssl = env_list('SECURE_PROXY_SSL_HEADER') +SECURE_PROXY_SSL_HEADER = tuple(_secure_proxy_ssl) if _secure_proxy_ssl else None # number of bits for the key, all auths should use the same number of bits KEYBITS = 256 diff --git a/docker/.env.decide.example b/docker/.env.decide.example new file mode 100644 index 000000000..9bc81dc1b --- /dev/null +++ b/docker/.env.decide.example @@ -0,0 +1,40 @@ +SECRET_KEY=^##ydkswfu0+=ofw0l#$kv^8n)0$i(qd&d&ol#p9!b$8*5%j1+ +DEBUG=True + +# Database +DB_ENGINE=django.db.backends.postgresql +DB_NAME=postgres +DB_USER=postgres +DB_PASSWORD= +DB_HOST=db +DB_PORT=5432 + +# Hosts +ALLOWED_HOSTS=* +CSRF_TRUSTED_ORIGINS=http://10.5.0.1:8000 +BASEURL=http://10.5.0.1:8000 + +# Static & Media +STATIC_URL=/static/ +STATIC_ROOT=/app/static/ +MEDIA_URL=/media/ +MEDIA_ROOT=/app/static/media/ + +# Gunicorn +GUNICORN_WORKERS=5 +GUNICORN_BIND=0.0.0.0:5000 +GUNICORN_TIMEOUT=500 + +# SSL Proxy (uncomment and set if behind a reverse proxy with SSL) +#SECURE_PROXY_SSL_HEADER=HTTP_X_FORWARDED_PROTO,https + +# APIs (default to BASEURL if not set) +#API_AUTHENTICATION=http://10.5.0.1:8000 +#API_BASE=http://10.5.0.1:8000 +#API_BOOTH=http://10.5.0.1:8000 +#API_CENSUS=http://10.5.0.1:8000 +#API_MIXNET=http://10.5.0.1:8000 +#API_POSTPROC=http://10.5.0.1:8000 +#API_STORE=http://10.5.0.1:8000 +#API_VISUALIZER=http://10.5.0.1:8000 +#API_VOTING=http://10.5.0.1:8000 diff --git a/docker/.env.postgres.example b/docker/.env.postgres.example new file mode 100644 index 000000000..c14aa52f7 --- /dev/null +++ b/docker/.env.postgres.example @@ -0,0 +1 @@ +POSTGRES_PASSWORD= diff --git a/docker/Dockerfile b/docker/Dockerfile index 827b1ca2f..bbec6fd63 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -7,14 +7,13 @@ RUN pip install gunicorn==25.0.3 WORKDIR /app -RUN git clone https://github.com/decide-update-4-1/decide-update-4.1.git . +COPY docker/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +COPY requirements.txt . RUN pip install -r requirements.txt +COPY . . WORKDIR /app/decide -# local settings.py -ADD docker-settings.py /app/decide/local_settings.py - -RUN ./manage.py collectstatic - -#CMD ["gunicorn", "-w 5", "decide.wsgi", "--timeout=500", "-b 0.0.0.0:5000"] +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 867a1b90c..f917ee261 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -9,14 +9,15 @@ services: - db:/var/lib/postgresql/data networks: - decide - environment: - - POSTGRES_PASSWORD= + env_file: + - .env.postgres web: restart: always container_name: decide_web image: decide_web:latest - build: . - command: ash -c "python manage.py collectstatic --noinput --clear && python manage.py migrate && gunicorn -w 5 decide.wsgi --timeout=500 -b 0.0.0.0:5000" + build: + context: .. + dockerfile: docker/Dockerfile expose: - "5000" volumes: @@ -25,6 +26,8 @@ services: - db networks: - decide + env_file: + - .env.decide nginx: restart: always container_name: decide_nginx diff --git a/docker/docker-settings.py b/docker/docker-settings.py deleted file mode 100644 index fb0e6b5c6..000000000 --- a/docker/docker-settings.py +++ /dev/null @@ -1,45 +0,0 @@ -DEBUG = True - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': 'postgres', - 'USER': 'postgres', - 'PASSWORD':'', - 'HOST': 'db', - 'PORT': 5432, - } -} - -STATIC_ROOT = '/app/static/' -MEDIA_ROOT = '/app/static/media/' -ALLOWED_HOSTS = ['*'] - -CSRF_TRUSTED_ORIGINS = ['http://10.5.0.1:8000'] - -# Modules in use, commented modules that you won't use -MODULES = [ - 'authentication', - 'base', - 'booth', - 'census', - 'mixnet', - 'postproc', - 'store', - 'visualizer', - 'voting', -] - -BASEURL = 'http://10.5.0.1:8000' - -APIS = { - 'authentication': 'http://10.5.0.1:8000', - 'base': 'http://10.5.0.1:8000', - 'booth': 'http://10.5.0.1:8000', - 'census': 'http://10.5.0.1:8000', - 'mixnet': 'http://10.5.0.1:8000', - 'postproc': 'http://10.5.0.1:8000', - 'store': 'http://10.5.0.1:8000', - 'visualizer': 'http://10.5.0.1:8000', - 'voting': 'http://10.5.0.1:8000', -} diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 000000000..73b8a36f0 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,28 @@ +#!/bin/sh +set -e + +GUNICORN_WORKERS=${GUNICORN_WORKERS:-5} +GUNICORN_BIND=${GUNICORN_BIND:-"0.0.0.0:5000"} +GUNICORN_TIMEOUT=${GUNICORN_TIMEOUT:-500} + +COMMAND=${1:-""} + +case "$COMMAND" in + start) + exec gunicorn -w "$GUNICORN_WORKERS" decide.wsgi --timeout="$GUNICORN_TIMEOUT" -b "$GUNICORN_BIND" + ;; + migrate) + exec python manage.py migrate + ;; + collectstatic) + exec python manage.py collectstatic --noinput + ;; + manage) + shift + exec python manage.py "$@" + ;; + *) + python manage.py migrate + exec gunicorn -w "$GUNICORN_WORKERS" decide.wsgi --timeout="$GUNICORN_TIMEOUT" -b "$GUNICORN_BIND" + ;; +esac