Merge branch 'master' of github.com:glic3rinu/django-orchestra

This commit is contained in:
Marc Aymerich 2015-05-04 12:58:28 +00:00
commit 1cc29eb7d8
8 changed files with 209 additions and 147 deletions

View File

@ -38,22 +38,38 @@ Django-orchestra can be installed on any Linux system, however it is **strongly
5. Create and configure a Postgres database 5. Create and configure a Postgres database
```bash ```bash
sudo apt-get install python3-psycopg2 postgresql
sudo python3 manage.py setuppostgres --db_password <password> sudo python3 manage.py setuppostgres --db_password <password>
# admin_tools needs accounts and does not have migrations # admin_tools needs accounts and does not have migrations
python3 manage.py migrate accounts python3 manage.py migrate accounts
python3 manage.py migrate python3 manage.py migrate
``` ```
7. Configure celeryd
6. See the Django deployment checklist
```bash ```bash
sudo python3 manage.py setupcelery --username orchestra python3 panel/manage.py check --deploy
``` ```
6. Configure periodic execution of tasks (choose one)
1. Use cron
```bash
sudo python3 manage.py setupcronbeat
```
2. Use celeryd
```bash
sudo apt-get install rabbitmq
sudo python3 manage.py setupcelery --username orchestra
```
8. Configure the web server: 8. Configure the web server:
```bash ```bash
python3 manage.py collectstatic --noinput python3 manage.py collectstatic --noinput
sudo apt-get install nginx-full uwsgi uwsgi-plugin-python3 sudo apt-get install nginx-full uwsgi uwsgi-plugin-python3
sudo python3 manage.py setupnginx sudo python3 manage.py setupnginx --user orchestra
``` ```
9. Start all services: 9. Start all services:

43
TODO.md
View File

@ -361,9 +361,10 @@ Collecting lxml==3.3.5 (from -r re (line 22))
# project settings modified copy of django's default project settings # project settings modified copy of django's default project settings
# migrate accounts break on superuser insert because of orders signals: read() + db_ready() # migrate accounts break on superuser insert because of orders signals: ready() + db_ready()
# if backend.async: don't join # if backend.async: don't join.
# RELATED: domains.sync to ns3 make it async
# ngnix setup certificate # ngnix setup certificate
from orchestra.contrib.tasks import task from orchestra.contrib.tasks import task
@ -377,46 +378,18 @@ Collecting lxml==3.3.5 (from -r re (line 22))
time.sleep(1) time.sleep(1)
counter.apply_async(10, '/tmp/kakas') counter.apply_async(10, '/tmp/kakas')
# standard django deployment pracices (run checks)
# setup main systemuser on post_migrate SystemUser # setup main systemuser on post_migrate SystemUser
# Provide some fixtures with mocked data # Provide some fixtures with mocked data
# don't make hard dependencies strict dependencies, fail when needed. don't make hard dependencies strict dependencies, fail when needed.
# on project_settings add debug settings but commented # on project_settings add debug settings but commented
# rename context processes varbailes to its original name # rename context processes varbailes to its original name
# TODO http://wiki2.dovecot.org/HowTo/SimpleVirtualInstall TODO http://wiki2.dovecot.org/HowTo/SimpleVirtualInstall
# TODO http://wiki2.dovecot.org/HowTo/VirtualUserFlatFilesPostfix TODO http://wiki2.dovecot.org/HowTo/VirtualUserFlatFilesPostfix
# TODO mount the filesystem with "nosuid" option TODO mount the filesystem with "nosuid" option
# execute Make after postfix update # execute Make after postfix update
# setupuwsgi + setupnginx # wkhtmltopdf -> reportlab
if not cert:
sudo mkdir /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt
if --noinput
openssl req \
-new \
-newkey rsa:4096 \
-days 365 \
-nodes \
-x509 \
-subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" \
-keyout www.example.com.key \
-out www.example.com.cert
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
if server_name:
at sites-enabled
server_name your_domain.com;
else conf-enabled

View File

@ -122,56 +122,23 @@ function install_requirements () {
check_root || true check_root || true
ORCHESTRA_PATH=$(get_orchestra_dir) || true ORCHESTRA_PATH=$(get_orchestra_dir) || true
# TODO reduce this list to 0
# include /usr/sbin/named-checkzone
# wkhtmltopdf -> reportlab
# remove rabbit, postgres
# uwsgi py-autoreload for devel
APT="python3 \ APT="python3 \
python3-pip \ python3-pip \
python3-psycopg2 \
python3-lxml \
python3-dev \ python3-dev \
bind9utils \ libxml2-dev \
python3-cracklib \ libxslt1-dev \
libz-dev \
wkhtmltopdf \ wkhtmltopdf \
xvfb \ xvfb \
ca-certificates \ ca-certificates \
gettext" gettext"
# TODO remove celery deps, django 1.8.1, glic3rinu fork, celery email PIP=$(wget https://raw.githubusercontent.com/glic3rinu/django-orchestra/master/requirements.txt -q -O - | tr '\n' ' ')
PIP="django==1.8.1 \
django-celery-email==1.0.4 \
https://github.com/glic3rinu/django-fluent-dashboard/archive/master.zip \
https://bitbucket.org/izi/django-admin-tools/get/a0abfffd76a0.zip \
IPy==0.81 \
django-extensions==1.5.2 \
django-transaction-signals==1.0.0 \
django-celery==3.1.16 \
celery==3.1.16 \
kombu==3.0.23 \
billiard==3.3.0.18 \
Markdown==2.4 \
djangorestframework==3.1.1 \
paramiko==1.15.1 \
ecdsa==0.11 \
Pygments==1.6 \
django-filter==0.9.2 \
https://github.com/glic3rinu/passlib/archive/master.zip \
jsonfield==0.9.22 \
python-dateutil==2.4.2 \
django-iban==0.3.0 \
requests \
phonenumbers \
django-countries \
django-localflavor \
pip==6.0.8"
if $testing; then if $testing; then
APT="${APT} \ APT="${APT} \
iceweasel \ iceweasel \
dnsutils" dnsutils"
PIP="${PIP} \ PIP="${PIP} \
selenium \ selenium \
xvfbwrapper \ xvfbwrapper \
@ -205,13 +172,6 @@ function install_requirements () {
fi fi
run pip3 install $PIP run pip3 install $PIP
# Patch passlib
# IMPORT="from django.contrib.auth.hashers import mask_hash, _"
# COLLECTIONS="from collections import OrderedDict"
# PASSLIB_PATH=$(python3 -c "from passlib.ext.django import utils; print(utils.__file__)")
# sed -i "s/${IMPORT}, SortedDict/${IMPORT}\n ${COLLECTIONS}/" $PASSLIB_PATH
# sed -i "s/SortedDict/OrderedDict/g" $PASSLIB_PATH
} }
export -f install_requirements export -f install_requirements

Binary file not shown.

View File

@ -63,7 +63,7 @@ DOMAINS_SLAVES_PATH = Setting('DOMAINS_SLAVES_PATH',
DOMAINS_CHECKZONE_BIN_PATH = Setting('DOMAINS_CHECKZONE_BIN_PATH', DOMAINS_CHECKZONE_BIN_PATH = Setting('DOMAINS_CHECKZONE_BIN_PATH',
'/usr/sbin/named-checkzone -i local -k fail -n fail', 'named-checkzone -i local -k fail -n fail',
) )

View File

@ -1,3 +1,4 @@
import os
import textwrap import textwrap
from optparse import make_option from optparse import make_option
from os.path import expanduser from os.path import expanduser
@ -17,6 +18,29 @@ class Command(BaseCommand):
help='Nginx SSL certificate, one will be created by default.'), help='Nginx SSL certificate, one will be created by default.'),
make_option('--cert-key', dest='cert_key', default='', make_option('--cert-key', dest='cert_key', default='',
help='Nginx SSL certificate key.'), help='Nginx SSL certificate key.'),
make_option('--cert-path', dest='cert_path', default='/etc/nginx/ssl/orchestra.crt',
help='Nginx SSL certificate, one will be created by default.'),
make_option('--cert-key-path', dest='cert_key_path', default='/etc/nginx/ssl/orchestra.key',
help='Nginx SSL certificate key.'),
# Cert options
make_option('--cert-override', dest='cert_override', action='store_true',
default=False, help='Force override cert and keys if exists.'),
make_option('--cert-country', dest='cert_country', default='ES',
help='Certificate Distinguished Name Country.'),
make_option('--cert-state', dest='cert_state', default='Spain',
help='Certificate Distinguished Name STATE.'),
make_option('--cert-locality', dest='cert_locality', default='Barcelona',
help='Certificate Distinguished Name Country.'),
make_option('--cert-org_name', dest='cert_org_name', default='Orchestra',
help='Certificate Distinguished Name Organization Name.'),
make_option('--cert-org_unit', dest='cert_org_unit', default='DevOps',
help='Certificate Distinguished Name Organization Unity.'),
make_option('--cert-email', dest='cert_email', default='orchestra@orchestra.lan',
help='Certificate Distinguished Name Email Address.'),
make_option('--cert-common_name', dest='cert_common_name', default=None,
help='Certificate Distinguished Name Common Name.'),
make_option('--server-name', dest='server_name', default='', make_option('--server-name', dest='server_name', default='',
help='Nginx SSL certificate key.'), help='Nginx SSL certificate key.'),
make_option('--user', dest='user', default='', make_option('--user', dest='user', default='',
@ -34,36 +58,121 @@ class Command(BaseCommand):
option_list = BaseCommand.option_list option_list = BaseCommand.option_list
help = 'Configures nginx + uwsgi to run with your Orchestra instance.' help = 'Configures nginx + uwsgi to run with your Orchestra instance.'
@check_root def generate_certificate(self, **options):
def handle(self, *args, **options): override = options.get('cert_override')
interactive = options.get('interactive') interactive = options.get('interactive')
cert = options.get('cert') cert = options.get('cert')
cert_key = options.get('cert_key') key = options.get('cert_key')
if bool(cert) != bool(cert_key): if bool(cert) != bool(key):
raise CommandError("--cert and --cert-key go in tandem") raise CommandError("--cert and --cert-key go in tandem")
if not cert: cert_path = options.get('cert_path')
run("mkdir -p /etc/nginx/ssl") key_path = options.get('cert_key_path')
if interactive:
run("openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt") run('mkdir -p %s' % os.path.basename(cert_path))
exists = os.path.isfile(cert_path)
if not override and exists:
self.stdout.write('Your cert and keys are already in place.')
self.stdout.write('Use --override in order to regenerate them.')
return cert_path, key_path
common_name = options.get('cert_common_name') or options.get('server_name') or 'orchestra.lan'
country = options.get('cert_country')
state = options.get('cert_state')
locality = options.get('cert_locality')
org_name = options.get('cert_org_name')
org_unit = options.get('cert_org_unit')
email = options.get('cert_email')
if interactive:
msg = ('-----\n'
'You are about to be asked to enter information that\n'
'will be incorporated\n'
'into your certificate request.\n'
'What you are about to enter is what is called a\n'
'Distinguished Name or a DN.\n'
'There are quite a few fields but you can leave some blank\n'
'-----\n')
self.stdout.write(msg)
msg = 'Country Name (2 letter code) [%s]: ' % country
country = input(msg) or country
msg = 'State or Province Name (full name) [%s]: ' % state
state = input(msg) or state
msg = 'Locality Name (eg, city) [%s]: ' % locality
locality = input(msg) or locality
msg = 'Organization Name (eg, company) [%s]: ' % org_name
org_name = input(msg) or org_name
msg = 'Organizational Unit Name (eg, section) [%s]: ' % org_unit
org_unit = input(msg) or org_unit
msg = 'Email Address [%s]: ' % email
email = input(msg) or email
self.stdout.write('Common Name: %s' % common_name)
subject = {
'C': country,
'S': state,
'L': locality,
'O': org_name,
'OU': org_unit,
'Email': email,
'CN': common_name,
}
context = {
'subject': ''.join(('/%s=%s' % (k,v) for k,v in subject.items())),
'key_path': key_path,
'cert_path': cert_path,
}
self.stdout.write('writing new cert to \'%s\'' % cert_path)
self.stdout.write('writing new cert key to \'%s\'' % key_path)
run('openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout %(key_path)s -out %(cert_path)s -subj "%(subject)s"' % context, display=True)
return cert_path, key_path
@check_root
def handle(self, *args, **options):
user = options.get('user')
if not user:
raise CommandError("System user for running uwsgi must be provided.")
cert_path, key_path = self.generate_certificate(**options)
server_name = options.get('server_name')
context = { context = {
'cert_path': cert_path,
'key_path': key_path,
'project_name': paths.get_project_name(), 'project_name': paths.get_project_name(),
'project_dir': paths.get_project_dir(), 'project_dir': paths.get_project_dir(),
'site_dir': paths.get_site_dir(), 'site_dir': paths.get_site_dir(),
'static_root': settings.STATIC_ROOT, 'static_root': settings.STATIC_ROOT,
'user': options.get('user'), 'user': user,
'group': options.get('group') or options.get('user'), 'group': options.get('group') or user,
'home': expanduser("~%s" % options.get('user')), 'home': expanduser("~%s" % options.get('user')),
'processes': int(options.get('processes')),} 'processes': int(options.get('processes')),
'server_name': 'server_name %s' % server_name if server_name else ''
}
nginx_conf = textwrap.dedent("""\ nginx_conf = textwrap.dedent("""\
server { server {
listen 80; listen 80;
listen [::]:80 ipv6only=on; listen [::]:80 ipv6only=on;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
# listen [::]:443 ssl; # add SSL support to IPv6 address
%(server_name)s
ssl_certificate %(cert_path)s;
ssl_certificate_key %(key_path)s;
rewrite ^/$ /admin/; rewrite ^/$ /admin/;
client_max_body_size 500m; client_max_body_size 16m;
location / { location / {
uwsgi_pass unix:///var/run/uwsgi/app/%(project_name)s/socket; uwsgi_pass unix:///var/run/uwsgi/app/%(project_name)s/socket;
include uwsgi_params; include uwsgi_params;
@ -78,34 +187,42 @@ class Command(BaseCommand):
uwsgi_conf = textwrap.dedent("""\ uwsgi_conf = textwrap.dedent("""\
[uwsgi] [uwsgi]
plugins = python plugins = python
chdir = %(site_dir)s chdir = %(site_dir)s
module = %(project_name)s.wsgi module = %(project_name)s.wsgi
master = true master = true
processes = %(processes)d processes = %(processes)d
chmod-socket = 664 chmod-socket = 664
stats = /run/uwsgi/%%(deb-confnamespace)/%%(deb-confname)/statsocket stats = /run/uwsgi/%%(deb-confnamespace)/%%(deb-confname)/statsocket
vacuum = true vacuum = true
uid = %(user)s uid = %(user)s
gid = %(group)s gid = %(group)s
env = HOME=%(home)s env = HOME=%(home)s
touch-reload = %(project_dir)s/wsgi.py touch-reload = %(project_dir)s/wsgi.py
enable-threads = true
max-requests = 500
""" """
) % context ) % context
nginx_file = '/etc/nginx/conf.d/%(project_name)s.conf' % context
if server_name:
context['server_name'] = server_name
nginx_file = '/etc/nginx/sites-available/%(server_name)s.conf' % context
nginx = { nginx = {
'file': '/etc/nginx/conf.d/%(project_name)s.conf' % context, 'file': nginx_file,
'conf': nginx_conf } 'conf': nginx_conf
}
uwsgi = { uwsgi = {
'file': '/etc/uwsgi/apps-available/%(project_name)s.ini' % context, 'file': '/etc/uwsgi/apps-available/%(project_name)s.ini' % context,
'conf': uwsgi_conf } 'conf': uwsgi_conf
}
for extra_context in (nginx, uwsgi): for extra_context in (nginx, uwsgi):
context.update(extra_context) context.update(extra_context)
diff = run("echo '%(conf)s'|diff - %(file)s" % context, error_codes=[0,1,2]) diff = run("echo '%(conf)s' | diff - %(file)s" % context, error_codes=[0,1,2])
if diff.return_code == 2: if diff.return_code == 2:
# File does not exist # File does not exist
run("echo '%(conf)s' > %(file)s" % context) run("echo '%(conf)s' > %(file)s" % context, display=True)
elif diff.return_code == 1: elif diff.return_code == 1:
# File is different, save the old one # File is different, save the old one
if interactive: if interactive:
@ -119,16 +236,14 @@ class Command(BaseCommand):
if confirm == 'no': if confirm == 'no':
return return
break break
run("cp %(file)s %(file)s.save" % context) run("cp %(file)s %(file)s.save" % context, display=True)
run("echo '%(conf)s' > %(file)s" % context) run("echo '%(conf)s' > %(file)s" % context, display=True)
self.stdout.write("\033[1;31mA new version of %(file)s has been installed.\n " self.stdout.write("\033[1;31mA new version of %(file)s has been installed.\n "
"The old version has been placed at %(file)s.save\033[m" % context) "The old version has been placed at %(file)s.save\033[m" % context)
run('ln -s /etc/uwsgi/apps-available/%(project_name)s.ini /etc/uwsgi/apps-enabled/' % context, error_codes=[0,1]) if server_name:
run('ln -s /etc/nginx/sites-available/%(server_name)s.conf /etc/nginx/sites-enabled/' % context, error_codes=[0,1], display=True)
# nginx should start after tincd run('ln -s /etc/uwsgi/apps-available/%(project_name)s.ini /etc/uwsgi/apps-enabled/' % context, error_codes=[0,1], display=True)
current = "\$local_fs \$remote_fs \$network \$syslog"
run('sed -i "s/ %s$/ %s \$named/g" /etc/init.d/nginx' % (current, current))
rotate = textwrap.dedent("""\ rotate = textwrap.dedent("""\
/var/log/nginx/*.log { /var/log/nginx/*.log {
@ -145,7 +260,7 @@ class Command(BaseCommand):
endscript endscript
}""" }"""
) )
run("echo '%s' > /etc/logrotate.d/nginx" % rotate) run("echo '%s' > /etc/logrotate.d/nginx" % rotate, display=True)
# Allow nginx to write to uwsgi socket # Allow nginx to write to uwsgi socket
run('adduser www-data %(group)s' % context) run('adduser www-data %(group)s' % context, display=True)

View File

@ -1,7 +1,7 @@
cracklib cracklib
django==1.8.1 django==1.8.1
django-celery-email==1.0.4 django-celery-email==1.0.4
https://github.com/glic3rinu/django-fluent-dashboard/archive/master.zip django-fluent-dashboard==0.5
https://bitbucket.org/izi/django-admin-tools/get/a0abfffd76a0.zip https://bitbucket.org/izi/django-admin-tools/get/a0abfffd76a0.zip
IPy==0.81 IPy==0.81
django-extensions==1.5.2 django-extensions==1.5.2
@ -25,6 +25,3 @@ requests
phonenumbers phonenumbers
django-countries django-countries
django-localflavor django-localflavor
###development
django-debug-toolbar
django-nose

View File

@ -30,6 +30,7 @@ setup(
scripts=[ scripts=[
'orchestra/bin/orchestra-admin', 'orchestra/bin/orchestra-admin',
'orchestra/contrib/tasks/bin/orchestra-beat', 'orchestra/contrib/tasks/bin/orchestra-beat',
'orchestra/contrib/domains/bin/named-checkzone',
], ],
packages = packages, packages = packages,
classifiers = [ classifiers = [