diff --git a/{{cookiecutter.project_slug}}/compose/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/django/Dockerfile index 618533f7d..15190fef1 100644 --- a/{{cookiecutter.project_slug}}/compose/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/django/Dockerfile @@ -10,11 +10,11 @@ COPY ./requirements /requirements RUN pip install --no-cache-dir -r /requirements/production.txt \ && rm -rf /requirements -COPY ./compose/django/gunicorn.sh ./compose/django/entrypoint.sh / -RUN sed -i 's/\r//' /entrypoint.sh \ +COPY ./compose/django/gunicorn.sh ./compose/django/entrypoint.py / +RUN sed -i 's/\r//' /entrypoint.py \ && sed -i 's/\r//' /gunicorn.sh \ - && chmod +x /entrypoint.sh \ - && chown django /entrypoint.sh \ + && chmod +x /entrypoint.py \ + && chown django /entrypoint.py \ && chmod +x /gunicorn.sh \ && chown django /gunicorn.sh @@ -26,4 +26,4 @@ USER django WORKDIR /app -ENTRYPOINT ["/entrypoint.sh"] +ENTRYPOINT ["/entrypoint.py"] diff --git a/{{cookiecutter.project_slug}}/compose/django/Dockerfile-dev b/{{cookiecutter.project_slug}}/compose/django/Dockerfile-dev index 673a06400..68f582f77 100644 --- a/{{cookiecutter.project_slug}}/compose/django/Dockerfile-dev +++ b/{{cookiecutter.project_slug}}/compose/django/Dockerfile-dev @@ -5,9 +5,9 @@ ENV PYTHONUNBUFFERED 1 COPY ./requirements /requirements RUN pip install -r /requirements/local.txt -COPY ./compose/django/entrypoint.sh /entrypoint.sh -RUN sed -i 's/\r//' /entrypoint.sh -RUN chmod +x /entrypoint.sh +COPY ./compose/django/entrypoint.py /entrypoint.py +RUN sed -i 's/\r//' /entrypoint.py +RUN chmod +x /entrypoint.py COPY ./compose/django/start-dev.sh /start-dev.sh RUN sed -i 's/\r//' /start-dev.sh @@ -15,4 +15,4 @@ RUN chmod +x /start-dev.sh WORKDIR /app -ENTRYPOINT ["/entrypoint.sh"] +ENTRYPOINT ["/entrypoint.py"] diff --git a/{{cookiecutter.project_slug}}/compose/django/entrypoint.py b/{{cookiecutter.project_slug}}/compose/django/entrypoint.py new file mode 100644 index 000000000..ec7870787 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/django/entrypoint.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python + +""" +# This entrypoint is used to play nicely with the current cookiecutter configuration. +# Since docker-compose relies heavily on environment variables itself for configuration, we'd have to define multiple +# environment variables just to support cookiecutter out of the box. That makes no sense, so this little entrypoint +# does all this for us. +""" + +from __future__ import print_function +import os +import sys +import psycopg2 +import time + + +def exports(**kwargs): + """Useful environment variables + :list: TODO + """ + os.environ['REDIS_URL'] = 'redis://redis:6379' + os.environ['CELERY_BROKER_URL'] = os.environ['REDIS_URL'] + '/0' + +def pingpost(): + """ + This is the function that actually trys to connect + to postgres + """ + try: + if os.environ['POSTGRES_USER']: + pg_user = os.environ['POSTGRES_USER'] + else: + os.environ['POSTGRES_USER'] = pg_user = 'postgres' + + try: + pg_pass = os.environ['POSTGRES_PASSWORD'] + except Exception as e: + pg_pass = '' + + DATABASE_URL='postgres://{username}:{password}@postgres:5432/{username}' + DATABASE_URL = DATABASE_URL.format(username=pg_user, password=pg_pass) + os.environ['DATABASE_URL'] = DATABASE_URL + + conn = psycopg2.connect(DATABASE_URL, connect_timeout=3) + + except psycopg2.OperationalError as error: + print(error) + return False + return True + +def main(arguments): + """ + call exports + and check to see if postgres is ready + """ + exports() + + while True: + if pingpost(): + break + print('Postgres is unavailable - sleeping') + time.sleep(1) + + import subprocess + try: + hold = subprocess.check_call(arguments) + except subprocess.CalledProcessError as cpe: + print("something went wrong") + raise Exception(cpe) + + print('exiting: ...') + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/{{cookiecutter.project_slug}}/compose/django/entrypoint.sh b/{{cookiecutter.project_slug}}/compose/django/entrypoint.sh deleted file mode 100644 index 9fe7f5c88..000000000 --- a/{{cookiecutter.project_slug}}/compose/django/entrypoint.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -set -e -cmd="$@" - -# This entrypoint is used to play nicely with the current cookiecutter configuration. -# Since docker-compose relies heavily on environment variables itself for configuration, we'd have to define multiple -# environment variables just to support cookiecutter out of the box. That makes no sense, so this little entrypoint -# does all this for us. -export REDIS_URL=redis://redis:6379 - -# the official postgres image uses 'postgres' as default user if not set explictly. -if [ -z "$POSTGRES_USER" ]; then - export POSTGRES_USER=postgres -fi - -export DATABASE_URL=postgres://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres:5432/$POSTGRES_USER -{% if cookiecutter.use_celery == 'y' %} -export CELERY_BROKER_URL=$REDIS_URL/0 -{% endif %} - -function postgres_ready(){ -python << END -import sys -import psycopg2 -try: - conn = psycopg2.connect(dbname="$POSTGRES_USER", user="$POSTGRES_USER", password="$POSTGRES_PASSWORD", host="postgres") -except psycopg2.OperationalError: - sys.exit(-1) -sys.exit(0) -END -} - -until postgres_ready; do - >&2 echo "Postgres is unavailable - sleeping" - sleep 1 -done - ->&2 echo "Postgres is up - continuing..." -exec $cmd diff --git a/{{cookiecutter.project_slug}}/compose/django/test_entrypoint.py b/{{cookiecutter.project_slug}}/compose/django/test_entrypoint.py new file mode 100644 index 000000000..95170c59a --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/django/test_entrypoint.py @@ -0,0 +1,34 @@ +from unittest.mock import patch, PropertyMock +import unittest +import entrypoint + +class TestEntrypoint(unittest.TestCase): + + """ + We test that the entry point script works. + """ + def setUp(self): + self.prop = 'test' + + def test_exports(self): + with patch.dict(entrypoint.os.environ, {'newkey': 'newvalue'}, clear=True): + entrypoint.exports() + self.assertTrue(entrypoint.os.environ['REDIS_URL']) + self.assertTrue(entrypoint.os.environ['CELERY_BROKER_URL']) + self.assertTrue(entrypoint.os.environ['newkey']) + + @patch('entrypoint.psycopg2.connect') + def test_pingpost(self, mockConn): + """ + We must assert that psycopg2 conn is called + """ + with patch.dict(entrypoint.os.environ, { + 'POSTGRES_USER': 'newvalue', + 'POSTGRES_PASSWORD': 'test'}, clear=True): + entrypoint.main(('dir',)) + self.assertTrue(entrypoint.os.environ['DATABASE_URL']) + self.assertTrue(mockConn.called) + + +if __name__ == '__main__': + unittest.main()