-
Notifications
You must be signed in to change notification settings - Fork 0
Docker infra for local development #224
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
Changes from all commits
621ed6d
1a0c2b4
d6cba09
f594725
259a652
2be4fe2
0b66e96
9f6bca1
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 |
|---|---|---|
|
|
@@ -115,4 +115,8 @@ venv.bak/ | |
| # vagrant | ||
| .vagrant/ | ||
|
|
||
| .DS_Store | ||
| .DS_Store | ||
|
|
||
| .env.dev | ||
|
|
||
| db_dump.sql | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| FROM python:3.12-slim | ||
|
Contributor
Author
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'd rather use this base image and try to bump django versions than use a docker image for an older python version. |
||
|
|
||
| # Prevent Python from writing .pyc files and enable unbuffered stdout/stderr | ||
| ENV PYTHONDONTWRITEBYTECODE=1 | ||
| ENV PYTHONUNBUFFERED=1 | ||
| ENV PIP_NO_CACHE_DIR=1 | ||
|
|
||
| # Set working directory | ||
| WORKDIR /app | ||
|
|
||
| # Install system dependencies | ||
| RUN apt-get update && apt-get install -y --no-install-recommends \ | ||
| postgresql-client \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| # Copy system requirements | ||
| COPY requirements.txt /app/ | ||
|
|
||
| # Upgrade pip and install dependencies | ||
| RUN pip install --upgrade pip setuptools wheel | ||
|
|
||
| # Install dependencies | ||
| RUN pip install --upgrade pip && \ | ||
| pip install -r requirements.txt | ||
|
|
||
| # Copy application code | ||
| COPY . /app/ | ||
|
|
||
| COPY portal/entrypoint.sh /entrypoint.sh | ||
| RUN chmod +x /entrypoint.sh | ||
|
|
||
| EXPOSE 8000 | ||
|
|
||
| ENV DJANGO_SETTINGS_MODULE=portal.settings | ||
|
|
||
| ENTRYPOINT ["/entrypoint.sh"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| #!/bin/sh | ||
|
|
||
| # Exit on errors | ||
| set -e | ||
|
|
||
| # If a SQL_HOST is provided, wait for Postgres to become available before running | ||
| # migrations. This prevents race conditions when using docker-compose where the | ||
| # web container starts before the DB is ready. | ||
| if [ -n "$SQL_HOST" ]; then | ||
| echo "Waiting for database at ${SQL_HOST}:${SQL_PORT:-5432}..." | ||
| # pg_isready is available after installing postgresql-client in the image | ||
| until pg_isready -h "$SQL_HOST" -p "${SQL_PORT:-5432}" >/dev/null 2>&1; do | ||
| echo "Postgres is unavailable - sleeping" | ||
| sleep 1 | ||
| done | ||
| echo "Postgres is up" | ||
| fi | ||
|
|
||
| echo "Applying database migrations..." | ||
| python manage.py migrate --noinput | ||
| echo "Collecting static files..." | ||
| python manage.py collectstatic --noinput | ||
|
paigewilliams marked this conversation as resolved.
|
||
|
|
||
| echo "Checking for existing providers..." | ||
| if [ "$(python manage.py shell -c 'from providers.models import PoliticalRegion; print(PoliticalRegion.objects.count())' 2>/dev/null | tail -1)" = "0" ]; then | ||
| echo "No providers found, loading default providers fixture..." | ||
| python manage.py loaddata fixtures/providers_20210524.json | ||
| else | ||
| echo "number of political regions: $(python manage.py shell -c 'from providers.models import PoliticalRegion; print(PoliticalRegion.objects.count())' 2>/dev/null | tail -1)" | ||
| echo "Providers content already exist, skipping fixture load." | ||
| fi | ||
|
|
||
| echo "Starting python development server on :8000" | ||
| python manage.py runserver 0.0.0.0:8000 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,12 +32,15 @@ | |
| # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ | ||
|
|
||
| # SECURITY WARNING: keep the secret key used in production secret! | ||
| SECRET_KEY = 't8(b^k=eh4pu6o6to8px!7pmcf)@x#p$&nyp&ksm!oc00s2s-(' | ||
| SECRET_KEY = os.environ.get('SECRET_KEY', default='set in .env file') | ||
|
|
||
| # 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 = os.environ.get("ALLOWED_HOSTS") | ||
| if ALLOWED_HOSTS_ENV: | ||
| ALLOWED_HOSTS.extend(ALLOWED_HOSTS_ENV.split(",")) | ||
|
Member
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. +1 |
||
|
|
||
|
|
||
| # Application definition | ||
|
|
@@ -112,9 +115,12 @@ | |
|
|
||
| DATABASES = { | ||
| 'default': { | ||
| 'ENGINE': 'django.db.backends.postgresql', | ||
| 'NAME': 'set_in_local_settings.py', | ||
| 'USER': 'set_in_local_settings.py' | ||
| 'ENGINE': os.environ.get('SQL_ENGINE', default='django.db.backends.postgresql'), | ||
| 'NAME': os.environ.get('SQL_DATABASE', default='postgres'), | ||
| 'USER': os.environ.get('SQL_USER', default='postgres'), | ||
| 'PASSWORD': os.environ.get('SQL_PASSWORD', default=None), | ||
|
Member
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.
Explicitly creating a new PostgreSQL user with a password and making them the owner of the tables is MUCH more secure, and I like that you are re-investing some of the gains from the streamlining gains of dockerizing this tool into making deployments follow best practices! |
||
| 'HOST': os.environ.get('SQL_HOST', default='db'), | ||
| 'PORT': os.environ.get('SQL_PORT', default='5432'), | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -175,5 +181,9 @@ | |
|
|
||
| MIN_SEARCH_RANK=0.1 | ||
| MIN_SEARCH_SIMILARITY=0.25 | ||
| MAPBOX_TOKEN = os.environ.get('MAPBOX_TOKEN', default='') | ||
|
Member
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. What are we using MapBox for? GeoLocation?
Member
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. |
||
|
|
||
| from .local_settings import * | ||
| try: | ||
|
Member
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. +1 |
||
| from .local_settings import * | ||
| except ImportError: | ||
| pass | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,7 +39,6 @@ pexpect==4.6.0 | |
| phonenumbers==8.10.4 | ||
| pickleshare==0.7.5 | ||
| prompt-toolkit>=3.0.30 | ||
| psycopg2==2.9.10 | ||
| psycopg2-binary==2.9.10 | ||
|
Member
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 thought the pendulum had swung and
Member
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 could be 100% wrong, too. |
||
| ptyprocess==0.6.0 | ||
| py-moneyed==0.8.0 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| DEBUG= | ||
| SECRET_KEY= | ||
| ALLOWED_HOSTS= | ||
| SQL_ENGINE=django.db.backends.postgresql | ||
| SQL_DATABASE= | ||
| SQL_USER= | ||
| SQL_PASSWORD= | ||
| SQL_HOST=db | ||
| SQL_PORT=5432 | ||
| MAPBOX_TOKEN= |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| services: | ||
| db: | ||
| image: postgis/postgis:16-3.5-alpine | ||
| restart: always | ||
| platform: linux/amd64 | ||
| environment: | ||
| POSTGRES_DB: ${SQL_DATABASE} | ||
| POSTGRES_USER: ${SQL_USER} | ||
| POSTGRES_PASSWORD: ${SQL_PASSWORD} | ||
| volumes: | ||
| - oh4s:/var/lib/postgresql/data | ||
| - ./db_dump.sql:/tmp/db_dump.sql:ro | ||
| - ./init-db.sh:/docker-entrypoint-initdb.d/init-db.sh | ||
| ports: | ||
| - "5432:5432" | ||
| healthcheck: | ||
| test: ["CMD-SHELL", "pg_isready -U ${SQL_USER} -d ${SQL_DATABASE} -h localhost -p ${SQL_PORT:-5432}"] | ||
| interval: 10s | ||
| timeout: 5s | ||
| retries: 5 | ||
|
|
||
| web: | ||
| build: | ||
| context: ../app/portal | ||
| dockerfile: Dockerfile | ||
| restart: unless-stopped | ||
| env_file: | ||
| - path: .env | ||
| required: true | ||
| ports: | ||
| - "8000:8000" | ||
| healthcheck: | ||
| test: ["CMD-SHELL", "python manage.py check --deploy 2>/dev/null || python -c 'import urllib.request; urllib.request.urlopen(\"http://localhost:8000/\")' 2>/dev/null"] | ||
| interval: 15s | ||
| timeout: 10s | ||
| retries: 5 | ||
| start_period: 30s | ||
| environment: | ||
| ALLOWED_HOSTS: ${ALLOWED_HOSTS} | ||
| DEBUG: ${DEBUG} | ||
| SQL_ENGINE: ${SQL_ENGINE} | ||
| SQL_HOST: ${SQL_HOST} | ||
| SQL_PORT: ${SQL_PORT} | ||
| SQL_DATABASE: ${SQL_DATABASE} | ||
| SQL_USER: ${SQL_USER} | ||
| SQL_PASSWORD: ${SQL_PASSWORD} | ||
| SECRET_KEY: ${SECRET_KEY} | ||
| depends_on: | ||
| db: | ||
| condition: service_healthy | ||
| volumes: | ||
| - ../app/portal:/app | ||
|
|
||
| volumes: | ||
| oh4s: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| #!/bin/sh | ||
| set -e | ||
|
|
||
| # This script runs automatically when the PostgreSQL container is first initialized | ||
| # It will only run if the database is empty (first time setup) | ||
|
|
||
| DB_DUMP_FILE="/tmp/db_dump.sql" | ||
| if [ ! -f "$DB_DUMP_FILE" ]; then | ||
| echo "Error: database dump file not found at $DB_DUMP_FILE. Mount or copy the SQL dump before initializing the database." >&2 | ||
| exit 1 | ||
| fi | ||
| if [ ! -r "$DB_DUMP_FILE" ]; then | ||
| echo "Error: database dump file is not readable at $DB_DUMP_FILE. Check file permissions before initializing the database." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "Importing database dump..." | ||
|
|
||
| # Import the SQL dump, ignoring meta-command errors | ||
| PGPASSWORD="$POSTGRES_PASSWORD" psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v ON_ERROR_STOP=0 < "$DB_DUMP_FILE" | ||
|
|
||
| echo "Database import completed!" |
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.
I removed needs for local_settings.py
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.
Next step: remove the need for Vagrant 😁