Smart Vagrantfiles

Vagrant and chef are two amazing programs. Since chef’s DSL is just ruby, you have the ability to make very intelligent recipes. One thing few people realize, is that Vagrant can do the same thing. Rather than just working through a static configuration, you can actually make a the Vagrantfile do a lot of the work for you using functions. It also has the ability to react to your system however you’d like (such as potentially running the machines in different profiles based on system conditions or environment variables).

Below is an example from the chef-solo-infrastructure project I’ve been working on. It shows some of the different things that can be done such as defining defaults, dynamically building boxes. The case here shows a way to build a test machine based on the exact same file that would be used for real provisioning.

rootdir = File.dirname(__FILE__)

def getProp(hash, val, default)
  unless hash.nil? or hash[val].nil?
    return hash[val]
  else
    return default
  end
end

basename = File.basename(rootdir)

Vagrant.configure("2") do |config|
  config.vm.box = "precise32"
  config.vm.box_url = "http://files.vagrantup.com/precise32.box"

  Dir[rootdir + "/config/**.json"].each do |cfgfile|
    boxname = File.basename(cfgfile).gsub(/.json/,"")
    unless boxname == "example"
      config.vm.define boxname do |box|
        displayname=basename+"_"+boxname
        cfg = JSON.parse(IO.read(cfgfile))
        vboxcfg = cfg["virtualbox"]

        config.vm.provider :virtualbox do |vb|
          vb.name = displayname
          vb.customize ["modifyvm", :id, "--memory", getProp(vboxcfg,"memory","512")]
          vb.customize ["modifyvm", :id, "--cpus", getProp(vboxcfg,"cpus","1")]
          vb.customize ["modifyvm", :id, "--cpuexecutioncap", getProp(vboxcfg,"cpucap","100")]
        end

        config.vm.provision :chef_solo do |chef|
          chef.cookbooks_path = rootdir+"/cookbooks"
          chef.roles_path = rootdir+"/roles"
          chef.json = cfg
          cfg["run_list"].each do |runme|
            match = runme.match(/(.*)[(.*)]/)

            if (match.nil?)
              chef.add_recipe runme
            elsif (match[1] == "recipe")
              chef.add_recipe match[2]
            elsif (match[1] == "role")
              chef.add_role match[2]
            else
              puts "unknown type: " + runme 
            end
          end
        end
      end
    end
  end 
end

Below is an example of just a little of what you can do. I hope to start putting this stuff together.

It may be a short post, but hopefully it’s content dense.