diff --git a/.s2i/bin/assemble b/.s2i/bin/assemble old mode 100644 new mode 100755 index 86e18b8b..3ecb6fcf --- a/.s2i/bin/assemble +++ b/.s2i/bin/assemble @@ -1,9 +1,80 @@ #!/bin/bash +function is_django_installed() { + python -c "import django" &>/dev/null +} + +function should_collectstatic() { + is_django_installed && [[ -z "$DISABLE_COLLECTSTATIC" ]] +} + +# Install pipenv to the separate virtualenv to isolate it +# from system Python packages and packages in the main +# virtualenv. Executable is simlinked into ~/.local/bin +# to be accessible. This approach is inspired by pipsi +# (pip script installer). +function install_pipenv() { + echo "---> Installing pipenv packaging tool ..." + VENV_DIR=$HOME/.local/venvs/pipenv + virtualenv $VENV_DIR + $VENV_DIR/bin/pip --isolated install -U pipenv + mkdir -p $HOME/.local/bin + ln -s $VENV_DIR/bin/pipenv $HOME/.local/bin/pipenv +} + +set -e + shopt -s dotglob +echo "---> Installing application source ..." +mv /tmp/src/* ./ + +if [[ ! -z "$UPGRADE_PIP_TO_LATEST" || ! -z "$ENABLE_PIPENV" ]]; then + echo "---> Upgrading pip to latest version ..." + pip install -U pip setuptools wheel +fi + +if [[ ! -z "$ENABLE_PIPENV" ]]; then + install_pipenv + echo "---> Installing dependencies via pipenv ..." + if [[ -f Pipfile ]]; then + pipenv install --deploy + elif [[ -f requirements.txt ]]; then + pipenv install -r requirements.txt + fi + pipenv check +elif [[ -f requirements.txt ]]; then + echo "---> Installing dependencies ..." + pip install -r requirements.txt +elif [[ -f setup.py ]]; then + echo "---> Installing application ..." + python setup.py develop +fi + +if should_collectstatic; then + ( + echo "---> Collecting Django static files ..." + + + APP_HOME=${APP_HOME:-.} + # Look for 'manage.py' in the directory specified by APP_HOME, or the current directory + manage_file=$APP_HOME/manage.py + + if [[ ! -f "$manage_file" ]]; then + echo "WARNING: seems that you're using Django, but we could not find a 'manage.py' file." + echo "'manage.py collectstatic' ignored." + exit + fi + + if ! python $manage_file collectstatic --dry-run --noinput; then + echo "WARNING: could not run 'manage.py collectstatic'. To debug, run:" + echo " $ python $manage_file collectstatic --noinput" + echo "Ignore this warning if you're not serving static files with Django." + exit + fi -pip install --upgrade pip -pip install --upgrade setuptools -pip install -r requirements.txt + python $manage_file collectstatic --noinput + ) +fi -#python manage.py migrate --noinput +# set permissions for any installed artifacts +fix-permissions /opt/app-root diff --git a/fabfile.py b/fabfile.py deleted file mode 100644 index 8bfcb47c..00000000 --- a/fabfile.py +++ /dev/null @@ -1,182 +0,0 @@ -from confy import read_environment_file, env -import os -from fabric.api import cd, run, local, get, settings -from fabric.contrib.files import exists, upload_template - -read_environment_file() -DEPLOY_REPO_URL = env('DEPLOY_REPO_URL', '') -DEPLOY_TARGET = env('DEPLOY_TARGET', '') -DEPLOY_VENV_PATH = env('DEPLOY_VENV_PATH', '') -DEPLOY_VENV_NAME = env('DEPLOY_VENV_NAME', '') -DEPLOY_DEBUG = env('DEPLOY_DEBUG', '') -DEPLOY_DATABASE_URL = env('DEPLOY_DATABASE_URL', '') -DEPLOY_SECRET_KEY = env('DEPLOY_SECRET_KEY', '') -DEPLOY_CSRF_COOKIE_SECURE = env('DEPLOY_CSRF_COOKIE_SECURE', '') -DEPLOY_SESSION_COOKIE_SECURE = env('DEPLOY_SESSION_COOKIE_SECURE', '') -DEPLOY_USER = env('DEPLOY_USER', '') -DEPLOY_DB_NAME = env('DEPLOY_DB_NAME', 'db') -DEPLOY_DB_USER = env('DEPLOY_DB_USER', 'dbuser') -DEPLOY_SUPERUSER_USERNAME = env('DEPLOY_SUPERUSER_USERNAME', 'superuser') -DEPLOY_SUPERUSER_EMAIL = env('DEPLOY_SUPERUSER_EMAIL', 'test@email.com') -DEPLOY_SUPERUSER_PASSWORD = env('DEPLOY_SUPERUSER_PASSWORD', 'pass') -DEPLOY_SUPERVISOR_NAME = env('DEPLOY_SUPERVISOR_NAME', 'sv') -DEPLOY_EMAIL_HOST = env('DEPLOY_EMAIL_HOST', 'email.host') -DEPLOY_EMAIL_PORT = env('DEPLOY_EMAIL_PORT', '25') -DEPLOY_SITE_URL = env('SITE_URL', 'url') -GEOSERVER_WMS_URL = env('GEOSERVER_WMS_URL', 'url') -GEOSERVER_WFS_URL = env('GEOSERVER_WFS_URL', 'url') -BORGCOLLECTOR_API = env('BORGCOLLECTOR_API', 'url') -SSO_COOKIE_NAME = env('SSO_COOKIE_NAME', 'oim_dpaw_wa_gov_au_sessionid') -PRS_USER_GROUP = env('PRS_USER_GROUP', 'PRS user') -PRS_PWUSER_GROUP = env('PRS_PWUSER_GROUP', 'PRS power user') - - -def _get_latest_source(): - """Creates target directory, either clones repo or pulls changes from master branch. - """ - run('mkdir -p {}'.format(DEPLOY_TARGET)) - if exists(os.path.join(DEPLOY_TARGET, '.git')): - run('cd {} && git pull'.format(DEPLOY_TARGET)) - else: - run('git clone {} {}'.format(DEPLOY_REPO_URL, DEPLOY_TARGET)) - run('cd {} && git checkout master'.format(DEPLOY_TARGET)) - - -def _create_dirs(): - """Ensure that required directories exist. - """ - with cd(DEPLOY_TARGET): - run('mkdir -p log && mkdir -p media') - - -def _update_venv(req='requirements.txt'): - """Creates a virtualenv, installs requirements. - """ - with cd(DEPLOY_VENV_PATH): - if not exists('{}/bin/pip'.format(DEPLOY_VENV_NAME)): - run('virtualenv {}'.format(DEPLOY_VENV_NAME)) - run('{}/bin/pip install -r {}'.format(DEPLOY_VENV_NAME, req)) - - -def _setup_env(): - """Creates a .env file in the deployment directory. - """ - with cd(DEPLOY_TARGET): - context = { - 'DEPLOY_DEBUG': DEPLOY_DEBUG, - 'DEPLOY_DATABASE_URL': DEPLOY_DATABASE_URL, - 'DEPLOY_SECRET_KEY': DEPLOY_SECRET_KEY, - 'DEPLOY_CSRF_COOKIE_SECURE': DEPLOY_CSRF_COOKIE_SECURE, - 'DEPLOY_SESSION_COOKIE_SECURE': DEPLOY_SESSION_COOKIE_SECURE, - 'DEPLOY_EMAIL_HOST': DEPLOY_EMAIL_HOST, - 'DEPLOY_EMAIL_PORT': DEPLOY_EMAIL_PORT, - 'DEPLOY_SITE_URL': DEPLOY_SITE_URL, - 'GEOSERVER_WMS_URL': GEOSERVER_WMS_URL, - 'GEOSERVER_WFS_URL': GEOSERVER_WFS_URL, - 'BORGCOLLECTOR_API': BORGCOLLECTOR_API, - 'SSO_COOKIE_NAME': SSO_COOKIE_NAME, - 'PRS_USER_GROUP': PRS_USER_GROUP, - 'PRS_PWUSER_GROUP': PRS_PWUSER_GROUP, - } - upload_template('prs2/templates/env.jinja', '.env', context, use_jinja=True, backup=False) - - -def _chown(): - """Assumes that the DEPLOY_USER user exists on the target server. - """ - run('chown -R {0}:{0} {1}'.format(DEPLOY_USER, DEPLOY_TARGET)) - - -def _collectstatic(): - """Runs the Django collectstatic management command. - """ - with cd(DEPLOY_TARGET): - run_str = 'source {}/{}/bin/activate && python manage.py collectstatic --noinput' - run(run_str.format(DEPLOY_VENV_PATH, DEPLOY_VENV_NAME), shell='/bin/bash') - - -def _create_db(): - """Creates a database on the deploy target. Assumes that PGHOST and PGUSER are set. - """ - db = { - 'NAME': DEPLOY_DB_NAME, - 'USER': DEPLOY_DB_USER, - } - sql = '''CREATE DATABASE {NAME} OWNER {USER}; - \c {NAME}'''.format(**db) - run('echo "{}" | psql -d postgres'.format(sql)) - - -def _migrate(): - """Runs the Django migrate management command. - """ - with cd(DEPLOY_TARGET): - run_str = 'source {}/{}/bin/activate && python manage.py migrate' - run(run_str.format(DEPLOY_VENV_PATH, DEPLOY_VENV_NAME), shell='/bin/bash') - - -def _create_superuser(): - script = """from django.contrib.auth.models import User; -User.objects.create_superuser('{}', '{}', '{}')""".format(DEPLOY_SUPERUSER_USERNAME, DEPLOY_SUPERUSER_EMAIL, DEPLOY_SUPERUSER_PASSWORD) - with cd(DEPLOY_TARGET): - run_str = 'source {}/{}/bin/activate && echo "{}" | python manage.py shell' - run(run_str.format(DEPLOY_VENV_PATH, DEPLOY_VENV_NAME, script), shell='/bin/bash') - - -def deploy_env(): - """Normally used to deploy a new environment (idempotent). - """ - _get_latest_source() - _create_dirs() - _update_venv() - _setup_env() - _chown() - _collectstatic() - - -def deploy_db(): - """Normally used to deploy a new database (idempotent). - """ - _create_db() - _migrate() - _create_superuser() - - -def deploy_all(): - """Deploy to a new environment in one step. Non-destructive, but will - raise lots of errors for an existing environment. - """ - deploy_env() - deploy_db() - - -def update_repo(): - """Update only: pulls repo changes, runs migrations, runs collectstatic. - """ - _get_latest_source() - _migrate() - _collectstatic() - - -def test(): - """Locally runs unit tests for the referral application. - """ - local('python manage.py test referral -v 2 -k', shell='/bin/bash') - - -def test_coverage(): - """Locally runs code coverage report for the referral application. - """ - local('coverage run --source="." manage.py test referral -v 0 -k && coverage report -m', shell='/bin/bash') - - -def test_func(): - """Runs (Selenium) functional unit tests for the prs2 application. - """ - cmds = """\ -python manage.py test prs2.referral.test_functional.PrsSeleniumNormalUserTests.test_login -k -python manage.py test prs2.referral.test_functional.PrsSeleniumNormalUserTests.test_login -k -python manage.py test prs2.referral.test_functional.PrsSeleniumReadOnlyUserTests.test_login -k - """.split("\n") - with settings(warn_only=True): - map(local, cmds) diff --git a/prs2/wsgi.py b/prs2/wsgi.py index ccb44aba..bea522ab 100644 --- a/prs2/wsgi.py +++ b/prs2/wsgi.py @@ -3,10 +3,11 @@ It exposes the WSGI callable as a module-level variable named ``application``. """ import confy -confy.read_environment_file('.env') # Must precede dj_static imports. +confy.read_environment_file() # Must precede dj_static imports. from django.core.wsgi import get_wsgi_application from dj_static import Cling, MediaCling import os + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "prs2.settings") application = Cling(MediaCling(get_wsgi_application())) diff --git a/requirements.txt b/requirements.txt index c1aba95f..5523abf2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ Django==1.11.7 django-extensions==1.9.7 git+https://github.com/parksandwildlife/dpaw-utils.git@0.3a16#egg=dpaw-utils +uWSGI==2.0.15 django-reversion==2.0.10 django-debug-toolbar==1.8 django-tastypie==0.14.0 @@ -14,8 +15,7 @@ django-storages==1.5.2 django-bootstrap-pagination==1.6.2 dj-static==0.0.6 Unipath==1.1 -Unidecode==0.04.20 -Fabric==1.10.1 +Unidecode==1.0.22 jinja2==2.9.6 Pillow==3.4.2 coverage==3.7.1 @@ -25,6 +25,5 @@ python-magic==0.4.13 lxml==3.7.3 django-downloadview==1.9 webtemplate-dpaw==0.4.8 -selenium==2.46.1 openpyxl==2.2.6 xmltodict==0.10.2 diff --git a/run_uwsgi.py b/run_uwsgi.py new file mode 100755 index 00000000..c38fc814 --- /dev/null +++ b/run_uwsgi.py @@ -0,0 +1,2 @@ +#!/bin/bash +exec uwsgi --ini uwsgi.ini --module prs2.wsgi diff --git a/uwsgi.ini b/uwsgi.ini new file mode 100644 index 00000000..fc0a3146 --- /dev/null +++ b/uwsgi.ini @@ -0,0 +1,19 @@ +[uwsgi] +# Sensible defaults for an uWSGI application +processes = 4 +max-requests = 100 +cache2 = name=default,bitmap=1,items=10000,blocksize=1000,blocks=200000 +vacuum = true +logdate = %%Y/%%m/%%d %%H:%%M:%%S + +# Process-related settings +master = true +http-socket = :8080 +die-on-term = true +touch-reload = uwsgi.ini + +# Django static files +static-map = /static=staticfiles + +# Media uploads +static-map = /media=media