After installing Vagrant, create and boot the VM:
vagrant up
SSH to the VM:
vagrant ssh
Run your app:
fab app.run
After initial boot, you should freeze the newly-installed pip packages at their versions:
pip freeze > requirements.txt
*~ | |
*.pyc | |
.vagrant | |
venv |
from flask import Flask | |
app = Flask(__name__) | |
app.config.from_pyfile('%s/config/default.cfg' % app.root_path) | |
app.config.from_envvar('FLASK_CONFIG') |
from fabric.decorators import task | |
import app | |
import db | |
import puppet | |
import virtualenv | |
@task | |
def build(): | |
"""Execute build tasks for all components.""" | |
virtualenv.build() | |
db.build() |
from fabric.decorators import task | |
from fabric.context_managers import settings | |
from utils import do | |
@task | |
def run(): | |
"""Start app in debug mode (for development).""" | |
do('export FLASK_CONFIG=$PWD/app/config/dev.cfg && venv/bin/python ./run.py') |
remote_path = '' | |
branches = { | |
'staging': { | |
'hosts': '' | |
}, | |
'uat': { | |
'hosts': '' | |
}, | |
'production': { | |
'hosts': '' | |
}, | |
} |
from fabric.decorators import task | |
from fabric.context_managers import settings, hide | |
from fabric.colors import cyan | |
from utils import do | |
config_file_path = 'db/alembic.ini' | |
@task | |
def build(): | |
"""Initialise and migrate database to latest version.""" | |
print(cyan('\nUpdating database...')) | |
with settings(hide('warnings'), warn_only=True): | |
do('venv/bin/alembic -c %s init db/postgresql' % config_file_path) |
SECRET_KEY = '' |
DEBUG = True |
class fabric { | |
package { 'Fabric': | |
provider => 'pip', | |
ensure => 'present', | |
} | |
} |
class git { | |
package { 'git': | |
ensure => 'installed', | |
} | |
} |
class postgresql { | |
# postgresql-dev required for Python's psycopg2 | |
package { [ 'postgresql', 'postgresql-server-dev-all' ]: | |
ensure => 'installed', | |
} | |
service { 'postgresql': | |
ensure => running, | |
require => Package[postgresql], | |
} | |
} |
class python { | |
include python::modules | |
package { 'python': | |
ensure => installed, | |
} | |
} |
class vagrant { | |
line { 'line-venv-activate': | |
ensure => present, | |
file => '/home/vagrant/.bashrc', | |
line => 'cd /vagrant && . venv/bin/activate', | |
} | |
} |
define line($file, $line, $ensure = 'present') { | |
case $ensure { | |
default : { err ( "unknown ensure value ${ensure}" ) } | |
present: { | |
exec { "/bin/echo '${line}' >> '${file}'": | |
unless => "/bin/grep -qFx '${line}' '${file}'" | |
} | |
} | |
absent: { | |
exec { "/bin/grep -vFx '${line}' '${file}' | /usr/bin/tee '${file}' >/dev/null 2>&1": | |
onlyif => "/bin/grep -qFx '${line}' '${file}'" | |
} | |
} | |
uncomment: { | |
exec { "/bin/sed -i -e'/${line}/s/#\\+//' '${file}'": | |
onlyif => "/bin/grep '${line}' '${file}' | /bin/grep '^#' | /usr/bin/wc -l" | |
} | |
} | |
comment: { | |
exec { "/bin/sed -i -e'/${line}/s/\\(.\\+\\)$/#\\1/' '${file}'": | |
onlyif => "/usr/bin/test `/bin/grep '${line}' '${file}' | /bin/grep -v '^#' | /usr/bin/wc -l` -ne 0" | |
} | |
} | |
} | |
} |
class python::modules { | |
package { [ 'python-virtualenv', 'python-dev', ]: | |
ensure => 'installed', | |
} | |
} |
from fabric.api import env | |
from fabric.decorators import task | |
from fabric.colors import cyan | |
from utils import do | |
@task | |
def check(): | |
"""Syntax check on Puppet config.""" | |
print(cyan('\nChecking puppet syntax...')) | |
do('find puppet -type f -name \'*.pp\' |xargs puppet parser validate') | |
@task | |
def apply(): | |
"""Apply Puppet manifest.""" | |
print(cyan('\nApplying puppet manifest...')) | |
do('sudo puppet apply --modulepath=puppet/modules/ puppet/manifests/standalone.pp' % env) |
alembic | |
Flask | |
Flask-SQLAlchemy | |
Flask-Assets | |
psycopg2 |
#!/usr/bin/env python | |
from app import app | |
app.run(host='0.0.0.0') |
# | |
# Standalone manifest - for dev Vagrant box. | |
# | |
import 'lib/*.pp' | |
include fabric | |
include git | |
include postgresql | |
include python | |
include vagrant |
from fabric.api import require, env, local, run as fab_run | |
from fabric.utils import abort | |
import config | |
def do(*args, **kwargs): | |
""" | |
Runs command locally or remotely depending on whether a remote host has | |
been specified. | |
""" | |
if env.host_string: | |
with settings(cd(config.remote_path)): | |
return fab_run(*args, **kwargs) | |
else: | |
return local(*args, **kwargs) | |
def require_host(): | |
""" | |
Forces a remote host to be set, automatically detecting it from the current | |
git branch if possible. | |
""" | |
if not env.host_string: | |
# Detect current branch from git, based on config | |
branch = local('git symbolic-ref -q HEAD', capture=True).split('/')[2] | |
# Ensure the current branch matches up to a known branch | |
if branch in config.branches: | |
env.branch = branch | |
require('branch') | |
# Manually set host_string variable | |
env.host_string = env.branches[branch]['hosts'] | |
require('host_string') |
# -*- mode: ruby -*- | |
# vi: set ft=ruby : | |
Vagrant::Config.run do |config| | |
# All Vagrant configuration is done here. The most common configuration | |
# options are documented and commented below. For a complete reference, | |
# please see the online documentation at vagrantup.com. | |
# Every Vagrant virtual environment requires a box to build off of. | |
config.vm.box = "precise64" | |
# The url from where the 'config.vm.box' box will be fetched if it | |
# doesn't already exist on the user's system. | |
config.vm.box_url = "http://files.vagrantup.com/precise64.box" | |
# Boot with a GUI so you can see the screen. (Default is headless) | |
# config.vm.boot_mode = :gui | |
# Assign this VM to a host-only network IP, allowing you to access it | |
# via the IP. Host-only networks can talk to the host machine as well as | |
# any other machines on the same network, but cannot be accessed (through this | |
# network interface) by any external networks. | |
config.vm.network :hostonly, "172.16.1.2" | |
# Assign this VM to a bridged network, allowing you to connect directly to a | |
# network using the host's network device. This makes the VM appear as another | |
# physical device on your network. | |
# config.vm.network :bridged | |
# Forward a port from the guest to the host, which allows for outside | |
# computers to access the VM, whereas host only networking does not. | |
config.vm.forward_port 5000, 5000 | |
# Share an additional folder to the guest VM. The first argument is | |
# an identifier, the second is the path on the guest to mount the | |
# folder, and the third is the path on the host to the actual folder. | |
config.vm.share_folder("v-root", "/vagrant", ".", :nfs => true) | |
# Update apt | |
config.vm.provision :shell, :inline => "aptitude -q2 update" | |
# Enable provisioning with Puppet stand alone. Puppet manifests | |
# are contained in a directory path relative to this Vagrantfile. | |
# You will need to create the manifests directory and a manifest in | |
# the file base.pp in the manifests_path directory. | |
config.vm.provision :puppet do |puppet| | |
puppet.manifests_path = "puppet/manifests" | |
puppet.module_path = "puppet/modules" | |
puppet.manifest_file = "standalone.pp" | |
end | |
# Application provision | |
config.vm.provision :shell, :inline => "cd /vagrant && stdbuf -o0 fab build; exit 0" | |
end |
from fabric.decorators import task | |
from fabric.context_managers import settings, hide | |
from fabric.colors import cyan, red | |
from fabric.utils import abort | |
from utils import do | |
@task | |
def build(): | |
"""Build or update the virtualenv.""" | |
with settings(hide('stdout')): | |
print(cyan('\nUpdating venv, installing packages...')) | |
do('[ -e venv ] || virtualenv venv --no-site-packages') | |
# annoyingly, pip prints errors to stdout (instead of stderr), so we | |
# have to check the return code and output only if there's an error. | |
with settings(warn_only=True): | |
pip = do('venv/bin/pip install -r requirements.txt', capture=True) | |
if pip.failed: | |
print(red(pip)) | |
abort("pip exited with return code %i" % pip.return_code) |
I think fabfile/app.py should be...
I could be very wrong though.