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) |