{% load djangofloor %}Installation
============
{% block introduction %}
Here is a simple tutorial to install {{ DF_PROJECT_NAME }} on a basic Debian/Linux installation.
You should easily adapt it on a different Linux or Unix flavor.

Like many Python packages, you can use several methods to install {{ DF_PROJECT_NAME }}.
Of course you can install it from source, but the preferred way is to install it as a standard Python package, via pip.
{% endblock %}

Upgrading
---------

{% block upgrading %}If you want to upgrade an existing installation, just install the new version (with the `--upgrade` flag for `pip`) and run
the `collectstatic` and `migrate` commands (for updating both static files and the database).{% endblock %}

{% block pre_install_step %}{% endblock %}

{% block environment %}Preparing the environment
-------------------------

.. code-block:: bash

{% block pre_environment %}{% endblock %}    sudo adduser --disabled-password {{ DF_MODULE_NAME }}
    sudo chown {{ DF_MODULE_NAME }}:www-data {{ LOCAL_PATH }}
    sudo apt-get install virtualenvwrapper {{ python_version }} {{ python_version }}-dev build-essential postgresql-client libpq-dev
    sudo -u {{ DF_MODULE_NAME }} -H -i
    mkvirtualenv {{ DF_MODULE_NAME }} -p `which {{ python_version }}`
    workon {{ DF_MODULE_NAME }}{% block post_environment %}{% endblock %}

{% endblock %}
{% block database %}Database
--------

PostgreSQL is often a good choice for Django sites:

.. code-block:: bash

{% block pre_database %}{% endblock %}   sudo apt-get install postgresql
   echo "CREATE USER {{ DF_MODULE_NAME }}" | sudo -u postgres psql -d postgres
   echo "ALTER USER {{ DF_MODULE_NAME }} WITH ENCRYPTED PASSWORD '5trongp4ssw0rd'" | sudo -u postgres psql -d postgres
   echo "ALTER ROLE {{ DF_MODULE_NAME }} CREATEDB" | sudo -u postgres psql -d postgres
   echo "CREATE DATABASE {{ DF_MODULE_NAME }} OWNER {{ DF_MODULE_NAME }}" | sudo -u postgres psql -d postgres


{% if USE_CELERY %}{{ DF_PROJECT_NAME }} also requires Redis for websockets, background tasks, caching pages and storing sessions:{% else %}{{ DF_PROJECT_NAME }} can use Redis for caching pages and storing sessions:{% endif %}

.. code-block:: bash

    sudo apt-get install redis-server

{% block post_database %}{% endblock %}
{% endblock %}

{% block webserver %}
Apache
------

Only the Apache installation is presented, but an installation behind nginx should be similar,
you can look at the `Gunicorn doc <http://docs.gunicorn.org/en/stable/deploy.html>`_.
Only the chosen server name (like `{{ DF_MODULE_NAME }}.example.org`) can be used for accessing your site. For example, you cannot use its IP address.
{% block pre_webserver_no_ssl %}
{% endblock %}

.. code-block:: bash

    SERVICE_NAME={{ DF_MODULE_NAME }}.example.org
    sudo apt-get install apache2 libapache2-mod-xsendfile
    sudo a2enmod headers proxy proxy_http xsendfile{% if WEBSOCKET_URL %} proxy_wstunnel{% endif %}
    sudo a2dissite 000-default.conf
    # sudo a2dissite 000-default on Debian7
    cat << EOF | sudo tee /etc/apache2/sites-available/{{ DF_MODULE_NAME }}.conf
    <VirtualHost *:80>
        ServerName $SERVICE_NAME
{% block webserver_static %}        Alias {{ STATIC_URL }} {{ STATIC_ROOT }}
        ProxyPass {{ STATIC_URL }} !
        <Location {{ STATIC_URL }}>
            Order deny,allow
            Allow from all
            Satisfy any
        </Location>
{% endblock %}{% block webserver_media %}        # CAUTION: THE FOLLOWING LINES ALLOW PUBLIC ACCESS TO ANY UPLOADED CONTENT
        Alias {{ MEDIA_URL }} {{ MEDIA_ROOT }}
        # the right value is provided by "{{ control_command }} config python | grep MEDIA_ROOT"
        ProxyPass {{ MEDIA_URL }} !
        <Location {{ MEDIA_URL }}>
            Order deny,allow
            Allow from all
            Satisfy any
        </Location>
{% endblock %}{% if WEBSOCKET_URL %}        ProxyPass {{ WEBSOCKET_URL }} ws://{{ LISTEN_ADDRESS }}{{ WEBSOCKET_URL }}  DisableReuse=on
        ProxyPassReverse {{ WEBSOCKET_URL }} ws://{{ LISTEN_ADDRESS }}{{ WEBSOCKET_URL }}
{% endif %}        ProxyPass / http://{{ LISTEN_ADDRESS }}/ DisableReuse=on
        # https://github.com/aio-libs/aiohttp/issues/2687
        ProxyPassReverse / http://{{ LISTEN_ADDRESS }}/
        DocumentRoot {{ STATIC_ROOT }}
        # the right value is provided by "{{ control_command }} config python | grep STATIC_ROOT"
        ServerSignature off
{% block webserver_xsendfilepath %}        # the optional two following lines are useful
        # for keeping uploaded content  private with good performance
        XSendFile on
        XSendFilePath {{ MEDIA_ROOT }}
        # the right value is provided by "{{ control_command }} config python | grep MEDIA_ROOT"
        # in older versions of XSendFile (<= 0.9), use XSendFileAllowAbove On
{% endblock %}{% block webserver_extra %}{% endblock %}    </VirtualHost>
    EOF
    sudo mkdir {{ LOCAL_PATH }}
    sudo chown -R www-data:www-data {{ LOCAL_PATH }}
    sudo a2ensite {{ DF_MODULE_NAME }}.conf
    sudo apachectl -t
    sudo apachectl restart
{% block post_webserver_no_ssl %}
{% endblock %}

{% block pre_webserver_ssl %}
{% endblock %}
{% block webserver_ssl %}
If you want to use SSL:

.. code-block:: bash

    sudo apt-get install apache2 libapache2-mod-xsendfile
    PEM=/etc/apache2/`hostname -f`.pem
    # ok, I assume that you already have your certificate
    sudo a2enmod headers proxy proxy_http ssl{% if WEBSOCKET_URL %} proxy_wstunnel{% endif %}
    openssl x509 -text -noout < $PEM
    sudo chown www-data $PEM
    sudo chmod 0400 $PEM
{% block webserver_ssl_keytab %}
    sudo apt-get install libapache2-mod-auth-kerb
    KEYTAB=/etc/apache2/http.`hostname -f`.keytab
    # ok, I assume that you already have your keytab
    sudo a2enmod auth_kerb
    cat << EOF | sudo ktutil
    rkt $KEYTAB
    list
    quit
    EOF
    sudo chown www-data $KEYTAB
    sudo chmod 0400 $KEYTAB
{% endblock %}
    SERVICE_NAME={{ DF_MODULE_NAME }}.example.org
    cat << EOF | sudo tee /etc/apache2/sites-available/{{ DF_MODULE_NAME }}.conf
    <VirtualHost *:80>
        ServerName $SERVICE_NAME
        RedirectPermanent / https://$SERVICE_NAME/
    </VirtualHost>
    <VirtualHost *:443>
        ServerName $SERVICE_NAME
        SSLCertificateFile $PEM
        SSLEngine on
{% block webserver_ssl_static %}        Alias {{ STATIC_URL }} {{ STATIC_ROOT }}
        ProxyPass {{ STATIC_URL }} !
        <Location {{ STATIC_URL }}>
            Order deny,allow
            Allow from all
            Satisfy any
        </Location>
{% endblock %}{% block webserver_ssl_media %}        # CAUTION: THE FOLLOWING LINES ALLOW PUBLIC ACCESS TO ANY UPLOADED CONTENT
        Alias {{ MEDIA_URL }} {{ MEDIA_ROOT }}
        # the right value is provided by "{{ control_command }} config python | grep MEDIA_ROOT"
        ProxyPass {{ MEDIA_URL }} !
        <Location {{ MEDIA_URL }}>
            Order deny,allow
            Allow from all
            Satisfy any
        </Location>
{% endblock %}{% if WEBSOCKET_URL %}        ProxyPass {{ WEBSOCKET_URL }} ws://{{ LISTEN_ADDRESS }}{{ WEBSOCKET_URL }}  DisableReuse=on
        ProxyPassReverse {{ WEBSOCKET_URL }} ws://{{ LISTEN_ADDRESS }}{{ WEBSOCKET_URL }}
{% endif %}        ProxyPass / http://{{ LISTEN_ADDRESS }}/ DisableReuse=on
        ProxyPassReverse / http://{{ LISTEN_ADDRESS }}/
        DocumentRoot {{ STATIC_ROOT }}
        # the right value is provided by "{{ control_command }} config python | grep STATIC_ROOT"
        ServerSignature off
        RequestHeader set X_FORWARDED_PROTO https
{% block webserver_ssl_auth %}        <Location />
            AuthType Kerberos
            AuthName "{{ DF_PROJECT_NAME }}"
            KrbAuthRealms EXAMPLE.ORG example.org
            Krb5Keytab $KEYTAB
            KrbLocalUserMapping On
            KrbServiceName HTTP
            KrbMethodK5Passwd Off
            KrbMethodNegotiate On
            KrbSaveCredentials On
            Require valid-user
            RequestHeader set REMOTE_USER %{REMOTE_USER}s
        </Location>
{% endblock %}{% block webserver_ssl_xsendfilepath %}        # the optional two following lines are useful
        # for private uploaded content and good performance
        XSendFile on
        XSendFilePath {{ MEDIA_ROOT }}
        # the right value is provided by "{{ control_command }} config python | grep MEDIA_ROOT"
        # in older versions of XSendFile (<= 0.9), use XSendFileAllowAbove On
{% endblock %}{% block webserver_ssl_extra %}{% endblock %}    </VirtualHost>
    EOF
    sudo mkdir {{ LOCAL_PATH }}
    sudo chown -R www-data:www-data {{ LOCAL_PATH }}
    sudo a2ensite {{ DF_MODULE_NAME }}.conf
    sudo apachectl -t
    sudo apachectl restart
{% endblock %}
{% endblock %}

{% block other_application %}{% endblock %}
{% block application %}Application
-----------

Now, it's time to install {{ DF_PROJECT_NAME }}:

.. code-block:: bash

{% block pre_application %}{% endblock %}    pip install setuptools --upgrade
    pip install pip --upgrade
    pip install {{ DF_PIP_NAME }} psycopg2-binary
    mkdir -p $VIRTUAL_ENV/etc/{{ DF_MODULE_NAME }}
    cat << EOF > $VIRTUAL_ENV/etc/{{ DF_MODULE_NAME }}/settings.ini
{% block ini_configuration %}    [global]
    data = $HOME/{{ DF_MODULE_NAME }}
    [database]
    db = {{ DF_MODULE_NAME }}
    engine = postgresql
    host = localhost
    password = 5trongp4ssw0rd
    port = 5432
    user = {{ DF_MODULE_NAME }}
{% endblock %}    EOF
    chmod 0400 $VIRTUAL_ENV/etc/{{ DF_MODULE_NAME }}/settings.ini
    # protect passwords in the config files from by being readable by everyone
    {{ control_command }} collectstatic --noinput
    {{ control_command }} migrate
{% block post_application %}    {{ control_command }} createsuperuser
{% endblock %}
{% block post_webserver_ssl %}
{% endblock %}
{% endblock %}

{% block supervisor %}supervisor
----------

Supervisor can be used to automatically launch {{ DF_MODULE_NAME }}:

.. code-block:: bash

{% block pre_supervisor %}{% endblock %}
    sudo apt-get install supervisor
    cat << EOF | sudo tee /etc/supervisor/conf.d/{{ DF_MODULE_NAME }}.conf
    [program:{{ DF_MODULE_NAME }}_aiohttp]
    command = $VIRTUAL_ENV/bin/{{ control_command }} server
    user = {{ DF_MODULE_NAME }}
{% for queue in required_celery_queues %}    [program:{{ DF_MODULE_NAME }}_celery_{{ queue }}]
    command = $VIRTUAL_ENV/bin/{{ control_command }} worker -Q {{ queue }}
    user = {{ DF_MODULE_NAME }}
{% endfor %}{% block post_supervisor %}{% endblock %}    EOF
    sudo service supervisor stop
    sudo service supervisor start

Now, Supervisor should start {{ DF_MODULE_NAME }} after a reboot.
{% endblock %}

{% block systemd %}systemd
-------

You can also use systemd in most modern Linux distributions to launch {{ DF_MODULE_NAME }}:

.. code-block:: bash
{% block pre_systemd %}{% endblock %}
    cat << EOF | sudo tee /etc/systemd/system/{{ DF_MODULE_NAME }}-web.service
    [Unit]
    Description={{ DF_PROJECT_NAME }} web process
    After=network.target
{% if required_celery_queus %}    After={{ DF_MODULE_NAME }}.service
    PartOf={{ DF_MODULE_NAME }}.service
{% endif %}
    [Service]
    User={{ DF_MODULE_NAME }}
    Group={{ DF_MODULE_NAME }}
    WorkingDirectory={{ LOCAL_PATH }}/
    ExecStart=$VIRTUAL_ENV/bin/{{ control_command }} server
    ExecReload=/bin/kill -s HUP \$MAINPID
    ExecStop=/bin/kill -s TERM \$MAINPID
    Restart=on-failure

    [Install]
    WantedBy={% if required_celery_queus %}{{ DF_MODULE_NAME }}.service{% else %}multi-user.target{% endif %}
    EOF
    systemctl enable {{ DF_MODULE_NAME }}-web.service
    sudo service {{ DF_MODULE_NAME }}-web
{% for queue in required_celery_queues %}    cat << EOF | sudo tee /etc/systemd/system/{{ DF_MODULE_NAME }}-worker-{{ queue }}.service
    [Unit]
    Description={{ DF_PROJECT_NAME }} worker process
    After=network.target
    After={{ DF_MODULE_NAME }}.service
    PartOf={{ DF_MODULE_NAME }}.service

    [Service]
    User={{ DF_MODULE_NAME }}
    Group={{ DF_MODULE_NAME }}
    WorkingDirectory={{ LOCAL_PATH }}/
    ExecStart=$VIRTUAL_ENV/bin/{{ control_command }} worker -Q {{ queue }}
    ExecReload=/bin/kill -s HUP \$MAINPID
    ExecStop=/bin/kill -s TERM \$MAINPID
    Restart=on-failure

    [Install]
    WantedBy={{ DF_MODULE_NAME }}.service
    EOF
    sudo systemctl enable {{ DF_MODULE_NAME }}-worker-{{ queue }}.service
    sudo service {{ DF_MODULE_NAME }}-worker-{{ queue }} start
{% endfor %}{% if required_celery_queues %}    cat << EOF | sudo tee /etc/systemd/system/{{ DF_MODULE_NAME }}.service
    [Unit]
    Description={{ DF_PROJECT_NAME }}

    [Service]
    Type=oneshot
    ExecStart=/bin/true
    RemainAfterExit=yes

    [Install]
    WantedBy=multi-user.target
    EOF
    systemctl enable {{ DF_MODULE_NAME }}.service
    sudo service {{ DF_MODULE_NAME }} start
{% endif %}
{% block post_systemd %}{% endblock %}{% endblock %}
{% block post_install_step %}{% endblock %}
