From df2aea59e70a5b228d95d59df0e92e917dce95c1 Mon Sep 17 00:00:00 2001 From: japrogramer Date: Tue, 25 Jul 2017 16:22:39 -0500 Subject: [PATCH 1/6] moved to a pythonic entry point --- .../compose/django/Dockerfile | 10 +-- .../compose/django/Dockerfile-dev | 8 +-- .../compose/django/entrypoint.py | 72 +++++++++++++++++++ .../compose/django/entrypoint.sh | 39 ---------- 4 files changed, 81 insertions(+), 48 deletions(-) create mode 100644 {{cookiecutter.project_slug}}/compose/django/entrypoint.py delete mode 100644 {{cookiecutter.project_slug}}/compose/django/entrypoint.sh 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..4c3147452 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/django/entrypoint.py @@ -0,0 +1,72 @@ +#!/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(list=None): + """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' + pg_pass = os.environ['POSTGRES_PASSWORD'] + + 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 From 8d02940b1f09f6387d0998305442ac73d7417500 Mon Sep 17 00:00:00 2001 From: japrogramer Date: Tue, 25 Jul 2017 16:30:59 -0500 Subject: [PATCH 2/6] retab --- {{cookiecutter.project_slug}}/compose/django/entrypoint.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/{{cookiecutter.project_slug}}/compose/django/entrypoint.py b/{{cookiecutter.project_slug}}/compose/django/entrypoint.py index 4c3147452..2eeab5da0 100644 --- a/{{cookiecutter.project_slug}}/compose/django/entrypoint.py +++ b/{{cookiecutter.project_slug}}/compose/django/entrypoint.py @@ -28,13 +28,13 @@ def pingpost(): """ try: - if os.environ['POSTGRES_USER']: + if os.environ['POSTGRES_USER']: pg_user = os.environ['POSTGRES_USER'] else: os.environ['POSTGRES_USER'] = pg_user = 'postgres' pg_pass = os.environ['POSTGRES_PASSWORD'] - DATABASE_URL='postgres://{username}:{password}@postgres:5432/{username}' + 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 From 68c4fa5f4f6d3e59c74f0519efde81daa1b6ec92 Mon Sep 17 00:00:00 2001 From: japrogramer Date: Tue, 25 Jul 2017 16:48:08 -0500 Subject: [PATCH 3/6] postgress password --- {{cookiecutter.project_slug}}/compose/django/entrypoint.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/{{cookiecutter.project_slug}}/compose/django/entrypoint.py b/{{cookiecutter.project_slug}}/compose/django/entrypoint.py index 2eeab5da0..40dd4251d 100644 --- a/{{cookiecutter.project_slug}}/compose/django/entrypoint.py +++ b/{{cookiecutter.project_slug}}/compose/django/entrypoint.py @@ -32,7 +32,11 @@ def pingpost(): pg_user = os.environ['POSTGRES_USER'] else: os.environ['POSTGRES_USER'] = pg_user = 'postgres' - pg_pass = os.environ['POSTGRES_PASSWORD'] + + 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), From 209afd5c47ff30891dba0bdf361e381bfe94fa3c Mon Sep 17 00:00:00 2001 From: japrogramer Date: Tue, 25 Jul 2017 16:56:47 -0500 Subject: [PATCH 4/6] removed trailing comma --- {{cookiecutter.project_slug}}/compose/django/entrypoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/{{cookiecutter.project_slug}}/compose/django/entrypoint.py b/{{cookiecutter.project_slug}}/compose/django/entrypoint.py index 40dd4251d..de670713b 100644 --- a/{{cookiecutter.project_slug}}/compose/django/entrypoint.py +++ b/{{cookiecutter.project_slug}}/compose/django/entrypoint.py @@ -39,7 +39,7 @@ def pingpost(): pg_pass = '' DATABASE_URL='postgres://{username}:{password}@postgres:5432/{username}' - DATABASE_URL = DATABASE_URL.format(username=pg_user, password=pg_pass), + 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) From 43d8b661b8d600abb6d48486d5d00658e9d11c19 Mon Sep 17 00:00:00 2001 From: japrogramer Date: Wed, 26 Jul 2017 16:11:07 -0500 Subject: [PATCH 5/6] Added tests for entry point --- .../compose/django/entrypoint.py | 3 +- .../compose/django/test_entrypoint.py | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 {{cookiecutter.project_slug}}/compose/django/test_entrypoint.py diff --git a/{{cookiecutter.project_slug}}/compose/django/entrypoint.py b/{{cookiecutter.project_slug}}/compose/django/entrypoint.py index de670713b..ec7870787 100644 --- a/{{cookiecutter.project_slug}}/compose/django/entrypoint.py +++ b/{{cookiecutter.project_slug}}/compose/django/entrypoint.py @@ -14,7 +14,7 @@ import psycopg2 import time -def exports(list=None): +def exports(**kwargs): """Useful environment variables :list: TODO """ @@ -25,7 +25,6 @@ def pingpost(): """ This is the function that actually trys to connect to postgres - """ try: if os.environ['POSTGRES_USER']: 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..35ec583a9 --- /dev/null +++ b/{{cookiecutter.project_slug}}/compose/django/test_entrypoint.py @@ -0,0 +1,33 @@ +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(mockConn.called) + + +if __name__ == '__main__': + unittest.main() From 02f22e728311aea8fa8a2591d93a9190468c2095 Mon Sep 17 00:00:00 2001 From: japrogramer Date: Wed, 26 Jul 2017 16:16:51 -0500 Subject: [PATCH 6/6] Better test of pingpost --- {{cookiecutter.project_slug}}/compose/django/test_entrypoint.py | 1 + 1 file changed, 1 insertion(+) diff --git a/{{cookiecutter.project_slug}}/compose/django/test_entrypoint.py b/{{cookiecutter.project_slug}}/compose/django/test_entrypoint.py index 35ec583a9..95170c59a 100644 --- a/{{cookiecutter.project_slug}}/compose/django/test_entrypoint.py +++ b/{{cookiecutter.project_slug}}/compose/django/test_entrypoint.py @@ -26,6 +26,7 @@ class TestEntrypoint(unittest.TestCase): 'POSTGRES_USER': 'newvalue', 'POSTGRES_PASSWORD': 'test'}, clear=True): entrypoint.main(('dir',)) + self.assertTrue(entrypoint.os.environ['DATABASE_URL']) self.assertTrue(mockConn.called)