Enhance install process by generating config files from templates (#275)

* Add a command to generate configuration examples

Config files are generated from templates (which remplace previous example files).

- solve the issue of hard-to-explain configuration examples
- ease pkg path seeking (avoid it, actually)
- add working defaults for sqlite and unix socket paths (instead of
  /replace/me/path/example)
- move settings comments from default_settings.py to ihatemoney.cfg.j2, as it is
  the one that will be facing user.

* Use generate-config command  in install doc

Also follow the new working defaults of templates for socket and db path.

* Fix doc settings table

On the long term, plaintext tables might destroy humanity.

* Mention templates dir URL in documentation

As requested by @almet
This commit is contained in:
JocelynDelalande 2017-10-23 18:05:50 +02:00 committed by Alexis Metaireau
parent efd2b66ac6
commit e3285bef36
12 changed files with 193 additions and 115 deletions

View file

@ -17,10 +17,12 @@ Changed
======= =======
- Logged admin can see any project (#262) - Logged admin can see any project (#262)
- Better install doc (#275)
Added Added
===== =====
- ``ihatemoney generate-config`` to give working examples of config files (#275)
- Statistics tab (#257) - Statistics tab (#257)
- Python3.6 support (#259) - Python3.6 support (#259)
- ALLOW_PUBLIC_PROJECT_CREATION setting (#262) - ALLOW_PUBLIC_PROJECT_CREATION setting (#262)

View file

@ -1,18 +0,0 @@
<VirtualHost *:80>
ServerAdmin admin@example.com
ServerName ihatemoney.example.com
# Uncomment the python-home option if you use a virtualenv
WSGIDaemonProcess ihatemoney user=www-data group=www-data threads=5 python-path=/path/to/ihatemoney/ihatemoney # python-home=/path/to/your/venv
WSGIScriptAlias / /path/to/ihatemoney/ihatemoney/wsgi.py
ErrorLog /var/log/apache2/ihatemoney.example.com_error.log
CustomLog /var/log/apache2/ihatemoney.example.com_access.log combined
<Directory /path/to/ihatemoney>
WSGIProcessGroup ihatemoney
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
# Alias value may be some messy path, within python libs.
# You may want to use "find $VIRTUAL_ENV -path */ihatemoney*/static" to find it.
Alias /static/ /path/to/ihatemoney/ihatemoney/static/
</VirtualHost>

View file

@ -1,7 +0,0 @@
backlog = 2048
daemon = False
debug = True
workers = 3
logfile = "/path/to/your/app/ihatemoney.gunicorn.log"
loglevel = "info"
bind = "unix:/path/to/your/app/ihatemoney.gunicorn.sock"

View file

@ -1,6 +0,0 @@
[program:ihatemoney]
command=/path/to/your/app/venv/bin/gunicorn -c /etc/ihatemoney/gunicorn.conf.py ihatemoney.wsgi:application
user=www
autostart=true
autorestart=true
redirect_stderr=True

View file

@ -18,6 +18,8 @@ it will work without if you prefer.
If wondering about the backend, SQLite is the simplest and will work fine for If wondering about the backend, SQLite is the simplest and will work fine for
most small to medium setups. most small to medium setups.
.. note:: If curious, source config templates can be found in the `project git repository <https://github.com/spiral-project/ihatemoney/tree/master/ihatemoney/conf-templates>`_.
Prepare virtualenv (recommended) Prepare virtualenv (recommended)
================================ ================================
@ -57,28 +59,56 @@ Now, if you want to deploy it on your own server, you have many options.
Two of them are documented at the moment. Two of them are documented at the moment.
*Of course, if you want to contribute another configuration, feel free to open a *Of course, if you want to contribute another configuration, feel free to open a
pull-request against this repository!*. pull-request against this repository!*
Whatever your installation option is…
--------------------------------------
1. Initialize the ihatemoney directories::
mkdir /etc/ihatemoney /var/lib/ihatemoney
2. Generate settings::
ihatemoney generate-config ihatemoney.cfg > /etc/ihatemoney/ihatemoney.cfg
chmod 740 /etc/ihatemoney/ihatemoney.cfg
You probably want to adjust `/etc/ihatemoney/ihatemoney.cfg` contents, you may
do it later, see `Configuration`_.
With Apache and mod_wsgi With Apache and mod_wsgi
------------------------ ------------------------
1. Install Apache and mod_wsgi - libapache2-mod-wsgi(-py3) for Debian based and mod_wsgi for RedHat based distributions - 1. Fix permissions (considering `www-data` is the user running apache)::
2. Create an Apache virtual host based on the sample configuration file in `conf/apache-vhost.conf`
3. Adapt it to your paths and specify your virtualenv path if you use one chgrp www-data /etc/ihatemoney/ihatemoney.cfg
chown www-data /var/lib/ihatemoney
2. Install Apache and mod_wsgi - libapache2-mod-wsgi(-py3) for Debian based and mod_wsgi for RedHat based distributions -
3. Create an Apache virtual host, the command ``ihatemoney generate-config apache-vhost.conf`` will output a good starting point (read and adapt it)
4. Activate the virtual host if needed and restart Apache 4. Activate the virtual host if needed and restart Apache
With Nginx, Gunicorn and Supervisord With Nginx, Gunicorn and Supervisord
------------------------------------ ------------------------------------
.. note:: For the 3 configuration files mentioned below, you will need to fix 1. Create a dedicated unix user (here called `ihatemoney`), required dirs, and fix permissions::
the paths to reflect yours.
1. Copy *conf/gunicorn.conf.py* to */etc/ihatemoney/gunicorn.conf.py* useradd ihatemoney
2. Copy *conf/supervisord.conf* to */etc/supervisor/conf.d/ihatemoney.conf* chown ihatemoney /var/lib/ihatemoney/
3. Copy *conf/nginx.conf* with your nginx vhosts [#nginx-vhosts]_ chgrp ihatemoney /etc/ihatemoney/ihatemoney.cfg
4. Reload both nginx and supervisord. It should be working ;)
Don't forget to set the right permission for your files ! 2. Create gunicorn config file ::
ihatemoney generate-config gunicorn.conf.py > /etc/ihatemoney/gunicorn.conf.py
3. Create supervisor config file ::
ihatemoney generate-config supervisord.conf > /etc/supervisor/conf.d/ihatemoney.conf
4. Copy (and adapt) output of ``ihatemoney generate-config conf/nginx.conf`` with your nginx vhosts [#nginx-vhosts]_
5. Reload both nginx and supervisord. It should be working ;)
.. [#nginx-vhosts] typically, */etc/nginx/conf.d/* or .. [#nginx-vhosts] typically, */etc/nginx/conf.d/* or
*/etc/nginx/sites-available*, depending on your distribution. */etc/nginx/sites-available*, depending on your distribution.
@ -90,48 +120,47 @@ ihatemoney relies on a configuration file. If you run the application for the
first time, you will need to take a few moments to configure the application first time, you will need to take a few moments to configure the application
properly. properly.
.. warning:: You **must** customize the ``SECRET_KEY`` on a production installation. Defaults given here, are those for development mode. To know defaults on your
deployed instance, simply look at your *ihatemoney.cfg*.
+-------------------------------+---------------------------------+----------------------------------------------------------------------------------+
+-------------------------------+---------------------------------+----------------------------------------------------------------------------------------+
| Setting name | Default | What does it do? | | Setting name | Default | What does it do? |
+===============================+=================================+==================================================================================+ +===============================+=================================+========================================================================================+
| SQLALCHEMY_DATABASE_URI | ``sqlite:///tmp/ihatemoney.db`` | Specifies the type of backend to use and its location. More information | | SQLALCHEMY_DATABASE_URI | ``sqlite:///tmp/ihatemoney.db`` | Specifies the type of backend to use and its location. More information on the |
| | | on the format used can be found on `the SQLAlchemy documentation | | | | format used can be found on `the SQLAlchemy documentation`_. |
| | | <http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls>`_. | +-------------------------------+---------------------------------+----------------------------------------------------------------------------------------+
+-------------------------------+---------------------------+----------------------------------------------------------------------------------------+ | SECRET_KEY | ``tralala`` | The secret key used to encrypt the cookies. `ihatemoney conf-example ihatemoney.cfg` |
| SECRET_KEY | ``tralala`` | The secret key used to encrypt the cookies. **This needs to be changed**. | | | | sets it to something random, which is good. |
+-------------------------------+---------------------------+----------------------------------------------------------------------------------------+ +-------------------------------+---------------------------------+----------------------------------------------------------------------------------------+
| MAIL_DEFAULT_SENDER | ``("Budget manager", | A python tuple describing the name and email adress to use when sending | | MAIL_DEFAULT_SENDER | ``("Budget manager", | A python tuple describing the name and email adress to use when sending |
| | "budget@notmyidea.org")`` | emails. | | | "budget@notmyidea.org")`` | emails. |
+-------------------------------+---------------------------+----------------------------------------------------------------------------------------+ +-------------------------------+---------------------------------+----------------------------------------------------------------------------------------+
| ACTIVATE_DEMO_PROJECT | ``True`` | If set to `True`, a demo project will be available on the frontpage. | | ACTIVATE_DEMO_PROJECT | ``True`` | If set to `True`, a demo project will be available on the frontpage. |
+-------------------------------+---------------------------+----------------------------------------------------------------------------------------+ +-------------------------------+---------------------------------+----------------------------------------------------------------------------------------+
| | | Hashed password to access protected endpoints. If left empty, all administrative | | | | Hashed password to access protected endpoints. If left empty, all administrative |
| ADMIN_PASSWORD | ``""`` | tasks are disabled. | | ADMIN_PASSWORD | ``""`` | tasks are disabled. |
| | | To generate the proper password HASH, use ``ihatemoney generate_password_hash`` | | | | To generate the proper password HASH, use ``ihatemoney generate_password_hash`` |
| | | and copy the output into the value of *ADMIN_PASSWORD*. | | | | and copy the output into the value of *ADMIN_PASSWORD*. |
+-------------------------------+---------------------------+----------------------------------------------------------------------------------------+ +-------------------------------+---------------------------------+----------------------------------------------------------------------------------------+
| ALLOW_PUBLIC_PROJECT_CREATION | ``True`` | If set to `True`, everyone can create a project without entering the admin password | | ALLOW_PUBLIC_PROJECT_CREATION | ``True`` | If set to `True`, everyone can create a project without entering the admin password |
| | | If set to `False`, the password needs to be entered (and as such, defined in the | | | | If set to `False`, the password needs to be entered (and as such, defined in the |
| | | settings). | | | | settings). |
+-------------------------------+---------------------------+----------------------------------------------------------------------------------------+ +-------------------------------+---------------------------------+----------------------------------------------------------------------------------------+
| ACTIVATE_ADMIN_DASHBOARD | ``False`` | If set to `True`, the dashboard will become accessible entering the admin password | | ACTIVATE_ADMIN_DASHBOARD | ``False`` | If set to `True`, the dashboard will become accessible entering the admin password |
| | | If set to `True`, a non empty ADMIN_PASSWORD needs to be set | | | | If set to `True`, a non empty ADMIN_PASSWORD needs to be set |
+-------------------------------+---------------------------+----------------------------------------------------------------------------------------+ +-------------------------------+---------------------------------+----------------------------------------------------------------------------------------+
| APPLICATION_ROOT | ``""`` | If empty, ihatemoney will be served at domain root (e.g: *http://domain.tld*), if set | | APPLICATION_ROOT | ``""`` | If empty, ihatemoney will be served at domain root (e.g: *http://domain.tld*), if set |
| | | to ``"foo"``, it will be served from a "folder" (e.g: *http://domain.tld/foo*) | | | | to ``"foo"``, it will be served from a "folder" (e.g: *http://domain.tld/foo*) |
+-------------------------------+---------------------------+----------------------------------------------------------------------------------------+ +-------------------------------+---------------------------------+----------------------------------------------------------------------------------------+
In a production environment .. _the SQLAlchemy documentation: http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls
---------------------------
Make a copy of ``ihatemoney/default_settings.py`` and name it ``ihatemoney.cfg``. Using an alternate settings path
Then adjust the settings to your needs and move this file to --------------------------------
``/etc/ihatemoney/ihatemoney.cfg``.
This is the default path of the settings but you can also place it You can put your settings file where you want, and pass its path to the
elsewhere and pass the configuration file path to the application using application using the ``IHATEMONEY_SETTINGS_FILE_PATH`` environment variable.
the IHATEMONEY_SETTINGS_FILE_PATH environment variable.
e.g.:: e.g.::

View file

@ -0,0 +1,19 @@
<VirtualHost *:80>
ServerAdmin admin@example.com # CUSTOMIZE
ServerName ihatemoney.example.com # CUSTOMIZE
WSGIDaemonProcess ihatemoney user=www-data group=www-data threads=5 python-path={{ pkg_path }} {% if venv_path %}python-home={{ venv_path }}{% endif %}
WSGIScriptAlias / {{ pkg_path }}/wsgi.py
ErrorLog /var/log/apache2/ihatemoney.example.com_error.log
CustomLog /var/log/apache2/ihatemoney.example.com_access.log combined
<Directory {{ pkg_path }}>
WSGIProcessGroup ihatemoney
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
Alias /static/ {{ pkg_path }}/static/
</VirtualHost>

View file

@ -0,0 +1,8 @@
backlog = 2048
daemon = False
debug = True
workers = 3
# log to stdout,
logfile = "-" # Is the default setting for gunicorn>=20
loglevel = "info"
bind = "unix:/tmp/ihatemoney.gunicorn.sock"

View file

@ -0,0 +1,36 @@
# You can find more information about what these settings mean in the
# documentation, available online at
# http://ihatemoney.readthedocs.io/en/latest/installation.html#configuration
# Turn this on if you want to have more output on what's happening under the
# hood. DO NOT TURN IT ON IN PRODUCTION.
DEBUG = False
# The database URI, reprensenting the type of database and how to connect to it.
# Enter an absolute path here.
SQLALCHEMY_DATABASE_URI = 'sqlite:///var/lib/ihatemoney/ihatemoney.sqlite'
SQLACHEMY_ECHO = DEBUG
# Will likely become the default value in flask-sqlalchemy >=3 ; could be removed
# then:
SQLALCHEMY_TRACK_MODIFICATIONS = False
# This secret key is random and auto-generated, it protects cookies and user sessions
SECRET_KEY = "{{ secret_key }}"
# A python tuple describing the name and email adress of the sender of the mails.
MAIL_DEFAULT_SENDER = ("Budget manager", "budget@notmyidea.org") # CUSTOMIZE
# If set to True, a demonstration project will be activated.
ACTIVATE_DEMO_PROJECT = True
# If not empty, the specified password must be entered to create new projects.
# DO NOT enter the password in cleartext. Generate a password hash with
# "ihatemoney generate_password_hash" instead.
ADMIN_PASSWORD = ""
# If set to True (default value) anyone can create a new project.
ALLOW_PUBLIC_PROJECT_CREATION = True
# If set to True, an administration dashboard is available.
ACTIVATE_ADMIN_DASHBOARD = False

View file

@ -1,11 +1,9 @@
server { server {
server_name yourur; server_name ihatemoney.example.com; # CUSTOMIZE
keepalive_timeout 5; keepalive_timeout 5;
location /static/ { location /static/ {
# Alias value may be some messy path, within python libs. alias {{ pkg_path }}/static/;
# You may want to use "find $VIRTUAL_ENV -path */ihatemoney*/static" to find it.
alias /path/to/app/ihatemoney/static/;
} }
location / { location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@ -26,5 +24,5 @@ server {
} }
upstream ihatemoney_backend { upstream ihatemoney_backend {
server unix:/path/to/app/ihatemoney.gunicorn.sock; server unix:/tmp/ihatemoney.gunicorn.sock;
} }

View file

@ -0,0 +1,6 @@
[program:ihatemoney]
command={{ venv_path }}/bin/gunicorn -c /etc/ihatemoney/gunicorn.conf.py ihatemoney.wsgi:application
user=ihatemoney
autostart=true
autorestart=true
redirect_stderr=true

View file

@ -1,37 +1,10 @@
# You can find more information about what these settings mean in the # Verbose and documented settings are in conf-templates/ihatemoney.cfg.j2
# documentation, available online at DEBUG = SQLACHEMY_ECHO = False
# http://ihatemoney.readthedocs.io/en/latest/installation.html#configuration
# Turn this on if you want to have more output on what's happening under the
# hood.
DEBUG = False
# The database URI, reprensenting the type of database and how to connect to it.
# Enter an absolute path here.
SQLALCHEMY_DATABASE_URI = 'sqlite:////tmp/ihatemoney.db' SQLALCHEMY_DATABASE_URI = 'sqlite:////tmp/ihatemoney.db'
SQLACHEMY_ECHO = DEBUG
# Will likely become the default value in flask-sqlalchemy >=3 ; could be removed
# then:
SQLALCHEMY_TRACK_MODIFICATIONS = False SQLALCHEMY_TRACK_MODIFICATIONS = False
# You need to change this secret key, otherwise bad things might happen to your
# users.
SECRET_KEY = "tralala" SECRET_KEY = "tralala"
# A python tuple describing the name and email adress of the sender of the mails.
MAIL_DEFAULT_SENDER = ("Budget manager", "budget@notmyidea.org") MAIL_DEFAULT_SENDER = ("Budget manager", "budget@notmyidea.org")
# If set to True, a demonstration project will be activated.
ACTIVATE_DEMO_PROJECT = True ACTIVATE_DEMO_PROJECT = True
# If not empty, the specified password must be entered to create new projects.
# DO NOT enter the password in cleartext. Generate a password hash with
# "ihatemoney generate_password_hash" instead.
ADMIN_PASSWORD = "" ADMIN_PASSWORD = ""
# If set to True (default value) anyone can create a new project.
ALLOW_PUBLIC_PROJECT_CREATION = True ALLOW_PUBLIC_PROJECT_CREATION = True
# If set to True, an administration dashboard is available.
ACTIVATE_ADMIN_DASHBOARD = False ACTIVATE_ADMIN_DASHBOARD = False

View file

@ -1,8 +1,13 @@
#!/usr/bin/env python #!/usr/bin/env python
import os
import pkgutil
import random
from getpass import getpass from getpass import getpass
from flask_script import Manager, Command
from flask_script import Manager, Command, Option
from flask_migrate import Migrate, MigrateCommand from flask_migrate import Migrate, MigrateCommand
from jinja2 import Template
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
from ihatemoney.run import create_app from ihatemoney.run import create_app
@ -18,6 +23,38 @@ class GeneratePasswordHash(Command):
print(generate_password_hash(password)) print(generate_password_hash(password))
class ConfigTemplate(Command):
def get_options(self):
return [
Option('config_file', choices=[
'ihatemoney.cfg',
'apache-vhost.conf',
'gunicorn.conf.py',
'supervisord.conf',
'nginx.conf',
]),
]
@staticmethod
def gen_secret_key():
return ''.join([
random.SystemRandom().choice(
'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)')
for i in range(50)])
def run(self, config_file):
template_content = pkgutil.get_data(
'ihatemoney',
os.path.join('conf-templates/', config_file) + '.j2'
).decode('utf-8')
print(Template(template_content).render(
pkg_path=os.path.abspath(os.path.dirname(__file__)),
venv_path=os.environ.get('VIRTUAL_ENV'),
secret_key=self.gen_secret_key(),
))
def main(): def main():
app = create_app() app = create_app()
Migrate(app, db) Migrate(app, db)
@ -25,6 +62,7 @@ def main():
manager = Manager(app) manager = Manager(app)
manager.add_command('db', MigrateCommand) manager.add_command('db', MigrateCommand)
manager.add_command('generate_password_hash', GeneratePasswordHash) manager.add_command('generate_password_hash', GeneratePasswordHash)
manager.add_command('generate-config', ConfigTemplate)
manager.run() manager.run()