Initial commit, with 'host' server example.

Even that isn't quite complete, but I'm anxious to test other things..
This commit is contained in:
Lance Edgar 2015-11-15 20:22:01 -06:00
commit cf06a1987d
17 changed files with 571 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
rattail_fabdemo.egg-info/
servers/*/fabenv.py
servers/*/.vagrant/

3
MANIFEST.in Normal file
View file

@ -0,0 +1,3 @@
# -*- mode: conf -*-
recursive-include servers *

12
README.rst Normal file
View file

@ -0,0 +1,12 @@
.. -*- coding: utf-8 -*-
Rattail Fabric Demo
===================
This project serves as a working demo, to illustrate various concepts of the
Rattail server deployment framework. See the `Rattail Wiki`_ for more info.
Note that it also aims to be usable as a starting point for your own deployment
project(s), should you need one.
.. _`Rattail Wiki`: https://rattailproject.org/moin/Deployment

6
fabdemo/__init__.py Normal file
View file

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
"""
Rattail Fabric Demo
"""
from ._version import __version__

3
fabdemo/_version.py Normal file
View file

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
__version__ = u'0.1.0'

10
servers/host/README.rst Normal file
View file

@ -0,0 +1,10 @@
.. -*- coding: utf-8 -*-
Bundle for 'host' Server
========================
This is a fictitious "host" server, with no special requirements other than
general bundle prep (fabenv only, no secure files needed for this one). Please
see the `Rattail Wiki`_ for instructions.
.. _`Rattail Wiki`: https://rattailproject.org/moin/Deployment/ServerBundlePrep

7
servers/host/Vagrantfile vendored Normal file
View file

@ -0,0 +1,7 @@
# -*- mode: ruby -*-
Vagrant.configure("2") do |config|
config.vm.box = "debian/jessie64"
config.vm.network "forwarded_port", guest: 80, host: 9080
config.vm.network "forwarded_port", guest: 443, host: 9443
end

View file

@ -0,0 +1,3 @@
# -*- mode: apache -*-
WSGIPythonHome /srv/envs/BASELINE

View file

@ -0,0 +1,49 @@
# -*- mode: apache -*-
<VirtualHost *:80>
ServerName pod.localhost
DocumentRoot /srv/pod/
<Directory /srv/pod/>
Options +Indexes
Require all granted
</Directory>
# Forbid directory browsing of POD images; there are just too many.
<DirectoryMatch "^/srv/pod/pictures/gtin/gtin-\d{3}">
Options -Indexes
</DirectoryMatch>
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<VirtualHost *:443>
ServerName pod.localhost
DocumentRoot /srv/pod/
<Directory /srv/pod>
Options +Indexes
Require all granted
</Directory>
# Forbid directory browsing of POD images; there are just too many.
<DirectoryMatch "^/srv/pod/pictures/gtin/gtin-\d{3}">
Options -Indexes
</DirectoryMatch>
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/ssl_access.log combined
BrowserMatch "MSIE [2-6]" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
</VirtualHost>

View file

@ -0,0 +1,17 @@
#!/bin/bash
# This hook is run after a new virtualenv is created and before it is activated.
cat >$1/pip.conf <<EOF
[global]
allow-external = PIL
allow-unverified = PIL
log-file = $WORKON_HOME/$1/pip.log
EOF
cat >$1/bin/postactivate <<EOF
export PIP_CONFIG_FILE=$WORKON_HOME/$1/pip.conf
EOF
cat >$1/bin/postdeactivate <<EOF
unset PIP_CONFIG_FILE
EOF

View file

@ -0,0 +1,120 @@
######################################################################
#
# Rattail config for 'host' server
#
######################################################################
##############################
# Rattail
##############################
[rattail]
timezone.default = America/Chicago
[rattail.config]
configure_logging = true
[rattail.db]
default.url = postgresql://rattail:%(password_postgresql_rattail)s@localhost/rattail
[rattail.mail]
smtp.server = localhost
templates = rattail:templates/mail
default.from = rattail@localhost
default.to = root@localhost
default.subject = [Rattail] Automated email
[rattail.pod]
pictures.gtin.root_url = http://pod.localhost/pictures/gtin
pictures.gtin.root_path = /srv/pod/pictures/gtin
####################
# alembic
####################
[alembic]
script_location = rattail.db:alembic
version_locations = rattail.db:alembic/versions
##############################
# Logging
##############################
[loggers]
keys = root, exc_logger, beaker, txn, sqlalchemy, django_db, flufl_bounce
[handlers]
keys = file, console, email
[formatters]
keys = generic, console
[logger_root]
handlers = file, console
level = DEBUG
[logger_exc_logger]
qualname = exc_logger
# Set this to 'email' in order to enable the Pyramid exception logger.
handlers =
level = ERROR
[logger_beaker]
qualname = beaker
handlers =
# By default the 'beaker' logger is a bit noisy, this helps.
level = INFO
[logger_txn]
qualname = txn
handlers =
# By default the 'txn' logger is a bit noisy, this helps.
level = INFO
[logger_sqlalchemy]
qualname = sqlalchemy.engine
handlers =
# Uncomment this to make SQLAlchemy log the SQL as it is executed.
#level = INFO
[logger_django_db]
qualname = django.db.backends
handlers =
# If you use the Django ORM at all, this will help to cut down on logging
# noise. Set this to DEBUG if you do wish Django to log the SQL as it is
# executed.
level = INFO
[logger_flufl_bounce]
qualname = flufl.bounce
handlers =
# By default the 'flufl.bounce' logger is a bit noisy, this helps.
level = WARNING
[handler_file]
class = handlers.RotatingFileHandler
args = ('/var/log/rattail/rattail.log', 'a', 1000000, 20, 'utf_8')
formatter = generic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
formatter = console
[handler_email]
class = handlers.SMTPHandler
args = ('localhost', 'rattail@localhost', ['root@localhost'], "[Rattail] Logging")
formatter = generic
level = ERROR
[formatter_generic]
format = %%(asctime)s %%(levelname)-5.5s [%%(name)s][%%(threadName)s] %%(funcName)s: %%(message)s
datefmt = %%Y-%%m-%%d %%H:%%M:%%S
[formatter_console]
format = %%(levelname)-5.5s [%%(name)s][%%(threadName)s] %%(message)s

View file

@ -0,0 +1,40 @@
# -*- mode: apache -*-
WSGIDaemonProcess rattail user=rattail group=rattail
<VirtualHost *:80>
ServerName rattail.localhost
WSGIScriptAlias / /srv/envs/rattail/app/rattail.wsgi
<Directory /srv/envs/rattail/app/>
WSGIProcessGroup rattail
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<VirtualHost *:443>
ServerName rattail.localhost
WSGIScriptAlias / /srv/envs/rattail/app/rattail.wsgi
<Directory /srv/envs/rattail/app/>
WSGIProcessGroup rattail
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/ssl_access.log combined
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
BrowserMatch "MSIE [2-6]" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
</VirtualHost>

View file

@ -0,0 +1,9 @@
# -*- coding: utf-8; mode: python -*-
from __future__ import unicode_literals
import site
site.addsitedir('/srv/envs/rattail/lib/python2.7/site-packages')
from pyramid.paster import get_app
application = get_app('/srv/envs/rattail/app/web.conf')

View file

@ -0,0 +1,44 @@
######################################################################
#
# Tailbone Website
#
######################################################################
##############################
# Rattail
##############################
[rattail.config]
include = %(here)s/rattail.conf
####################
# Pyramid
####################
[app:main]
use = egg:Tailbone
pyramid.reload_templates = true
pyramid.debug_all = true
pyramid.default_locale_name = en
pyramid.includes = pyramid_debugtoolbar
beaker.session.type = file
beaker.session.data_dir = %(here)s/sessions/data
beaker.session.lock_dir = %(here)s/sessions/lock
beaker.session.secret = some-gobbledy-gook
beaker.session.key = rattail
# Hack so rattail can find this file from within WSGI app.
edbob.config = %(here)s/web.conf
##############################
# Logging
##############################
[handler_file]
args = ('/srv/envs/rattail/app/log/web.log', 'a', 1000000, 20, 'utf_8')

View file

@ -0,0 +1,25 @@
# -*- coding: utf-8; mode: python -*-
"""
Fabric environment tweaks.
You must save this file as 'fabenv.py' within the current directory, then edit
the settings however you need/like to manage a particular target server.
"""
from __future__ import unicode_literals
from fabric.api import env
# Whether or not the target server should be considered "live". This may
# control various things, such as whether certain automated tasks are enabled.
env.server_is_live = False
# Whether or not Product Open Data (POD) files should be downloaded.
env.rattail_download_pod = False
# Alternate download URL for POD source file.
#env.setting_pod_download_url = 'https://rattailproject.org/downloads/POD/pod_pictures_gtin_2013.08.29_01.zip'
# Password for 'rattail' PostgreSQL user; used to access the Rattail database.
env.password_postgresql_rattail = 'password'

144
servers/host/fabfile.py vendored Normal file
View file

@ -0,0 +1,144 @@
# -*- coding: utf-8 -*-
"""
Fabric script for the 'host' server
Please see the accompanying README for full instructions.
"""
from __future__ import unicode_literals
import os
from fabric.api import *
from fabric.utils import warn, puts
from fabric.contrib.files import append, exists
from rattail.fablib import make_deploy, mkdir, make_system_user, cdvirtualenv, workon
from rattail.fablib import apt, pod, postgresql, python
__all__ = ['bootstrap_all', 'bootstrap_system', 'bootstrap_rattail', 'bootstrap_pod']
# Set the 'live' role to the canonical hostname for this server.
#env.roledefs = {'live': ['host.example.com']}
deploy = make_deploy(__file__)
try:
import fabenv
except ImportError as error:
warn("Couldn't import fabenv: {0}".format(error))
@task
def bootstrap_all():
"""
Bootstrap all aspects of the server.
"""
bootstrap_system()
bootstrap_rattail()
bootstrap_pod()
@task
def bootstrap_system():
"""
Bootstrap the base system.
"""
apt.dist_upgrade()
# postgresql
apt.install('postgresql')
# python
apt.install('libpython-dev', 'libpq-dev')
python.install_pip()
python.install_virtualenvwrapper()
deploy('python/premkvirtualenv', '/srv/envs/premkvirtualenv')
sudo('mkvirtualenv BASELINE')
# apache
apt.install('apache2', 'libapache2-mod-wsgi')
deploy('apache/wsgi.conf', '/etc/apache2/conf-available/wsgi.conf')
sudo('a2enconf wsgi')
sudo('a2enmod ssl')
sudo('service apache2 restart')
# misc
apt.install('git', 'emacs-nox')
@task
def bootstrap_rattail():
"""
Bootstrap the Rattail software.
"""
from rattail.fablib.rattail import bootstrap_rattail
# rattail user and core files
bootstrap_rattail()
# virtual environment
python.mkvirtualenv('rattail')
with cdvirtualenv('rattail'):
mkdir('src')
with workon('rattail'):
python.pip('psycopg2')
# rattail source
with cdvirtualenv('rattail', 'src'):
if not exists('rattail'):
sudo('git clone https://rattailproject.org/git/rattail.git')
with cdvirtualenv('rattail', 'src/rattail'):
sudo('git pull')
sudo('pip install --editable .')
with workon('rattail'):
sudo('pip install --upgrade rattail[db,auth]')
# tailbone source
with cdvirtualenv('rattail', 'src'):
if not exists('tailbone'):
sudo('git clone https://rattailproject.org/git/tailbone.git')
with cdvirtualenv('rattail', 'src/tailbone'):
sudo('git pull')
sudo('pip install --upgrade --editable .')
# config
with cdvirtualenv('rattail', 'app'):
deploy('rattail/rattail.conf.template', 'rattail.conf')
# database
postgresql.create_user('rattail', password=env.password_postgresql_rattail)
postgresql.create_db('rattail', owner='rattail')
with cd('/srv/envs/rattail'):
sudo('bin/alembic --config=app/rattail.conf upgrade heads', user='rattail')
sudo('bin/rattail --config app/rattail.conf initdb --with-admin', user='rattail')
# website
with cdvirtualenv('rattail', 'app'):
mkdir('sessions', owner='rattail:rattail')
deploy('rattail/web.conf', 'web.conf')
deploy('rattail/rattail.wsgi', 'rattail.wsgi')
append('/etc/hosts', '127.0.0.1 rattail.localhost', use_sudo=True)
deploy('rattail/rattail.localhost.conf', '/etc/apache2/sites-available/rattail.localhost.conf')
sudo('a2ensite rattail.localhost')
sudo('service apache2 restart')
@task
def bootstrap_pod():
"""
Bootstrap the POD images website.
"""
# images
mkdir('/srv/pod')
if getattr(env, 'rattail_download_pod', False):
pod.install_pod()
else:
puts("Skipping POD download, per 'fabenv' settings.")
# website
append('/etc/hosts', '127.0.0.1 pod.localhost', use_sudo=True)
deploy('pod/pod.localhost.conf', '/etc/apache2/sites-available/pod.localhost.conf')
sudo('a2ensite pod.localhost')
sudo('service apache2 restart')

76
setup.py Normal file
View file

@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
"""
Setup script for Rattail Fabric Demo
"""
from __future__ import unicode_literals
import os
from setuptools import setup, find_packages
here = os.path.abspath(os.path.dirname(__file__))
execfile(os.path.join(here, 'fabdemo', '_version.py'))
README = open(os.path.join(here, 'README.rst')).read()
requires = [
#
# Version numbers within comments below have specific meanings.
# Basically the 'low' value is a "soft low," and 'high' a "soft high."
# In other words:
#
# If either a 'low' or 'high' value exists, the primary point to be
# made about the value is that it represents the most current (stable)
# version available for the package (assuming typical public access
# methods) whenever this project was started and/or documented.
# Therefore:
#
# If a 'low' version is present, you should know that attempts to use
# versions of the package significantly older than the 'low' version
# may not yield happy results. (A "hard" high limit may or may not be
# indicated by a true version requirement.)
#
# Similarly, if a 'high' version is present, and especially if this
# project has laid dormant for a while, you may need to refactor a bit
# when attempting to support a more recent version of the package. (A
# "hard" low limit should be indicated by a true version requirement
# when a 'high' version is present.)
#
# In any case, developers and other users are encouraged to play
# outside the lines with regard to these soft limits. If bugs are
# encountered then they should be filed as such.
#
# package # low high
'Fabric', # 1.10.2
'rattail', # 0.5.9
]
setup(
name = "rattail-fabdemo",
version = __version__,
author = "Lance Edgar",
author_email = "lance@edbob.org",
url = "https://rattailproject.org/",
description = "Rattail Fabric Deployment Demo",
long_description = README,
classifiers = [
'Private :: Do No Upload',
'Development Status :: 3 - Alpha',
'Environment :: Console',
'Intended Audience :: Developers',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Topic :: Office/Business',
'Topic :: Software Development :: Libraries :: Python Modules',
],
install_requires = requires,
packages = find_packages(),
)