Skip to content

Commit

Permalink
Merge pull request #3 from willianantunes/release/heroku-qa
Browse files Browse the repository at this point in the history
Fresh new release with Heroku support
  • Loading branch information
willianantunes authored Jul 7, 2019
2 parents 49105f6 + 152039c commit 4d8608c
Show file tree
Hide file tree
Showing 13 changed files with 325 additions and 145 deletions.
3 changes: 1 addition & 2 deletions .env
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
##################
#### HTTP
DJANGO_BIND_ADDRESS=0.0.0.0
DJANGO_BIND_PORT=8000
PORT=8000

##################
#### Database engine
Expand Down
11 changes: 10 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
FROM python:3.7.3-slim-stretch

# In order to have OUTPUT from Heroku
RUN apt-get update && apt-get install curl -y

RUN groupadd --system app-user && adduser --system --ingroup app-user app-user

WORKDIR /app

COPY . /app
COPY --chown=app-user:app-user . /app

RUN python -m pip install --upgrade pip && \
pip install pipenv && \
pipenv install --system --deploy --ignore-pipfile

USER app-user

CMD gunicorn -cfile:gunicorn_config.ini -b 0.0.0.0:${PORT} django_graphql_playground.wsgi
3 changes: 3 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ drf-extensions = "*"
graphene-django = "*"
# Database driver
psycopg2-binary = "*"
### WSGI
gunicorn = "*"
gevent = "*"
### GraphQL clients
graphqlclient = "*"
gql = "*"
Expand Down
336 changes: 201 additions & 135 deletions Pipfile.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ Useful links:

- https://docs.graphene-python.org/en/latest/testing/#testing-tools
- https://pytest-django.readthedocs.io/en/latest/helpers.html
- https://gist.github.com/JamesMGreene/cdd0ac49f90c987e45ac

## Issues

- [Pipeline for Django fails with: No module named '_sqlite3'](https://developercommunity.visualstudio.com/content/problem/574733/pipeline-for-django-fails-with-no-module-named-sql.html)
- [GitHubInstallationTokenSignatureSecret](https://developercommunity.visualstudio.com/content/problem/564582/githubinstallationtokensignaturesecret-does-not-ex.html)
20 changes: 19 additions & 1 deletion django_graphql_playground/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
from enum import Enum

from django_graphql_playground.support.utils import eval_env_as_boolean
from django_graphql_playground.support.utils import extract_db_properties_from_url

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
Expand Down Expand Up @@ -92,6 +93,23 @@
}
}

# https://devcenter.heroku.com/changelog-items/438
DATABASE_URL = os.getenv("DATABASE_URL")

if DATABASE_URL:
properties = extract_db_properties_from_url(DATABASE_URL)
if properties.target == "postgres":
DATABASES["default"] = {
"ENGINE": "django.db.backends.postgresql",
"NAME": properties.database_name,
"USER": properties.user,
"HOST": properties.hostname,
"PORT": properties.port,
"PASSWORD": properties.password,
}
else:
raise NotImplementedError(f"The following DB is not supported: {properties.target}")

DATABASES["default"]["CONN_MAX_AGE"] = int(os.getenv("DB_CONN_MAX_AGE", 0)) # type: ignore

# Password validation
Expand Down
35 changes: 35 additions & 0 deletions django_graphql_playground/support/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,45 @@
import os
import re
from dataclasses import dataclass
from datetime import date
from datetime import timedelta

from dateutil import relativedelta


@dataclass(frozen=True)
class _DatabaseProperties:
target: str
user: str
password: str
hostname: str
port: int
database_name: str


_TARGET = 1
_USERNAME = 2
_PASSWORD = 3
_HOSTNAME = 4
_PORT = 5
_DB_NAME = 6
_pattern = r"^([a-z]*):\/\/([a-zA-Z0-9]*):([a-zA-Z0-9]*)@([a-zA-Z0-9.-]*):([0-9]{4,5})\/([a-zA-Z0-9]*)"
_compiled_pattern = re.compile(_pattern, re.IGNORECASE)


def extract_db_properties_from_url(connection_url):
matches = _compiled_pattern.search(connection_url)

return _DatabaseProperties(
matches.group(_TARGET),
matches.group(_USERNAME),
matches.group(_PASSWORD),
matches.group(_HOSTNAME),
matches.group(_PORT),
matches.group(_DB_NAME),
)


def retrieve_ip_address(request):
"""
Makes the best attempt to get the client's real IP or return the loopback
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ services:
- db
- broker
ports:
- "8000:${DJANGO_BIND_PORT}"
- "8000:${PORT}"
mem_limit: 256mb
command:
[
Expand Down
16 changes: 16 additions & 0 deletions gunicorn_config.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
############
###### http://docs.gunicorn.org/en/stable/settings.html#worker-processes

workers = 5
worker_class = "gevent"
worker_connections = 1000
timeout = 30
keepalive = 2

############
###### http://docs.gunicorn.org/en/stable/settings.html#logging

errorlog = "-"
loglevel = "info"
accesslog = "-"
access_log_format = '{"message": "%(r)s", "http_status": %(s)s, "ip_address": "%(h)s", "response_length": "%(b)s", "referer": "%(f)s", "user_agent": "%(a)s", "request_time": %(L)s, "date": "%(t)s"}'
7 changes: 7 additions & 0 deletions heroku.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
build:
docker:
web: Dockerfile
release:
image: web
command:
- ./release-task.sh
4 changes: 4 additions & 0 deletions release-task.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
python manage.py makemigrations
python manage.py migrate
python manage.py seed_db --create-super-user
10 changes: 5 additions & 5 deletions start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ python manage.py makemigrations
python manage.py migrate
python manage.py seed_db --create-super-user

if [[ ${DJANGO_BIND_ADDRESS+x} ]] && [[ ${DJANGO_BIND_PORT+x} ]];
if [[ ${PORT+x} ]];
then
echo "OK! Using custom ADRESSS $DJANGO_BIND_ADDRESS and PORT $DJANGO_BIND_PORT to set Django runserver command"
python manage.py runserver ${DJANGO_BIND_ADDRESS}:${DJANGO_BIND_PORT}
echo "OK! Using custom PORT $PORT to set Django runserver command"
python manage.py runserver 0.0.0.0:${PORT}
else
echo "Using 0.0.0.0:80 as parameter for Django runserver command"
python manage.py runserver 0.0.0.0:80
echo "Using 0.0.0.0:8000 as parameter for Django runserver command"
python manage.py runserver 0.0.0.0:8000
fi
21 changes: 21 additions & 0 deletions tests/unit/support/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os
import re
from dataclasses import dataclass
from datetime import date
from datetime import timedelta

from dateutil import relativedelta

from django_graphql_playground.support import utils


def test_should_extract_db_properties_given_url():
result = utils.extract_db_properties_from_url(
"postgres://czazgirubdxrxw:8d5d635df6f2a226b1a1f81541cfcd20fadf8617fff383f553e39330392de901@ec2-54-243-47-196.compute-1.amazonaws.com:5432/dau6saplk4ri6e"
)
assert result.target == "postgres"
assert result.user == "czazgirubdxrxw"
assert result.password == "8d5d635df6f2a226b1a1f81541cfcd20fadf8617fff383f553e39330392de901"
assert result.hostname == "ec2-54-243-47-196.compute-1.amazonaws.com"
assert result.port == "5432"
assert result.database_name == "dau6saplk4ri6e"

0 comments on commit 4d8608c

Please sign in to comment.