Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

Vagrant & Chef

         ... for Drupal developers

Alex Dergachev | Co-founder of Evolving Web

@dergachev on github | @dergachev on twitter

Vagrant is...

Installing Vagrant

Run these GUI installers:

Both are simple, reliable, and work on Windows/Mac/Linux

Vagrant Docs

Base boxes

Just a VM image with:

Where to find base boxes

Base box docs

Vagrant workflow

vagrant box add precise64
  http://files.vagrantup.com/precise64.box
vagrant init
vagrant up # --no provision
vagrant reload
vagrant provision
vagrant destroy --force && vagrant up
vagrant ssh  # -p -- -l alex
ls /vagrant # inside the VM
vagrant package # outputs ./package.box
vagrant snap take # requires vagrant-snap plugin

Vagrantfile config

config.vm.forward_port 80, 4567
config.vm.box = "precise64"
config.vm.share_folder "foo", "/guest/path", 
  "/host/path", :nfs => true
config.vm.network :hostonly, "10.11.12.13"
 # config.vm.network :bridged
config.vm.customize 
  ["modifyvm", :id, "--memory", 1024]
config.vm.provision :shell, 
  :inline => "echo foo > /vagrant/test"
 # support for multiple VMs

Provisioning

Installing software on the VM

Supported provisioners

Shell provisioner

config.vm.provision :shell, 
  :inline => "apt-get -y update"

config.vm.provision :shell, 
  :inline => "sudo apt-get -y vim git"

Chef-solo provisioner

config.vm.provision :chef_solo do |chef|
  chef.cookbooks_path = 
    ["cookbooks", "~/company/cookbooks"]
  chef.add_recipe("apache")
  chef.add_recipe("php")
  chef.json = {
    :load_limit => 42,
    :chunky_bacon => true
  }
end

See Vagrant chef_solo docs and iterative chef tutorial.

Ruby for Chef

Just enough not to freak out

Methods

def double(num)
  num * 2
end

double 33 # => 66

Ruby Strings

:bob == 'bob'.to_sym

%w{git vim mysql} == ['git', 'vim', 'mysql']

"head #{var} tail" == "head "+var+" tail"

Ruby dictionaries

options = {
  :deploy_drupal => { 
    :apache_group => 'vagrant', 
    :sql_load_file => '/vagrant/db/fga.sql.gz',
    :source =>  "/vagrant/public/fga/www",
  },
  :mysql => {
    :server_root_password => "root",
  },
}

DSL... wtf?

template "/etc/mysql/grants.sql" do
  source "grants.sql.erb"
  owner "root"
  group "root"
end
template("/etc/mysql/grants.sql", function() {  
  this.source("grants.sql.erb");
  this.owner("root");
  this.group("root");
});

Writing a cookbook

Cookbooks and recipes

A cookbooks contains recipes, which are very simple ruby scripts that install software or otherwise configure the VM.

Recipes read data from attributes, and call built-in or custom resources, which are helper methods that do all the work.

Cookbook structure

Example Cookbook

Adapted from @opscode/getting-started:

Resources

Install a template

template '/var/www/sites/default/settings.php' do
  # action :create
  # action :create_if_missing
  source "settings.php.erb"
  variables ( {
    :user => 'root',
    :pass => node[:mysql]['root_password'],
    :name => 'drupalsite_v1',
  })
end

Install packages

package "tar" do
  action :install
end

package %w{vim git}

php_pear "uploadprogress"

gem_package "syntax"

Execute bash

execute "download drupal" do
  cwd '/var/www'
  command "drush dl drupal-7.20"
  creates '/var/www/drupal-7.20/index.php'
end

creates tells chef not to re-execute the resource if index.php is already in place.

Deploy from git

deploy "/var/www" do 
  repo "git@github.com/mycompany/project"
  user "www-data"
  group "www-data"
  revision "master"
  action :sync # default value, also :checkout
end

Controlling services

 # runs /etc/init.d/apache2 start|stop|...
service "apache2" do
  supports :status => true, 
    :restart => true, :reload => true
  action [ :enable, :start ]
end

Install a user

user "sam" do
     home "/home/sam"
     shell "/bin/zsh"
     comment "Sam loves DevOps"
     action :create
     password :$1$pfFfDG3M$22vsMsPnn93ZnuodI86Ec0
end

Add a vhost config

web_app 'drupalcampottawa' do
  template "web_app.conf.erb"
  port 80
  server_name 'drupalcampottawa.com'
  docroot '/var/www/dcampottawa'
  notifies :restart, 
    resources("service[apache2]"), 
    :delayed
end

web_app is custom resource defined in the apache2 cookbook

Example recipe

Putting it all together, you should be able to understand this example recipe

More about recipes and resources

Additional stuff

Useful tools and extensions to Vagrant and Chef.

Testing

Cookbook managers

These tools automatically download dependent cookbooks:

See this berkshelf tutorial

Vagrant Plugins

More Vagrant info

Chef-server

Chef links

My vagrant/chef contribs

Drupal and Redmine

User provisioning with agent forwarding