jbbarth's

vagrant is a wonderful tool when you want to manage virtual machines in a development context. It even supports chef-solo provisioning.

Today I wanted to run a vagrant VM with the same chef-solo configuration I have on the host. So here’s the first Vagrantfile I wrote:

Vagrant::Config.run do |config|
  config.vm.box = "debian_squeeze_32"
  config.vm.share_folder "chef-cookbooks", "/var/chef", "/var/chef"
  config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = "/var/chef/cookbooks"
    chef.json.merge!(JSON.parse(File.read("/etc/chef/dna.json")))
  end
end

But it ended up with:

% vagrant up     
There was a problem with the configuration of Vagrant. The error message(s)
are printed below:

vm:
* Run list must not be empty.

When you read the documentation, it seems vagrant assumes you add some recipes explicitly through the add_recipe method:

dna = JSON.parse(File.read("/etc/chef/dna.json"))
dna["recipes"].each do |recipe|
  chef.add_recipe(recipe)
end
chef.json.merge!(dna)

…which ends with a Chef error:

[default] /usr/lib/ruby/1.8/chef/node.rb:382:in `consume_run_list': stderr
[default] : please set the node's run list using the 'run_list' attribute only. (Chef::Exceptions::AmbiguousRunlistSpecification)

Ok, let’s remove the recipes key in our json file:

dna = JSON.parse(File.read("/etc/chef/dna.json"))
dna.delete("recipes").each do |recipe|
  chef.add_recipe(recipe)
end
chef.json.merge!(dna)

It works. But it’s not really clean. After a research in the vagrant gem source code, I found that json[:run_list] does exactly what I want, so here’s the final Vagrantfile:

Vagrant::Config.run do |config|
  config.vm.box = "debian_squeeze_32"
  config.vm.share_folder "chef-cookbooks", "/var/chef", "/var/chef"
  config.vm.provision :chef_solo do |chef|
    dna = JSON.parse(File.read("/etc/chef/dna.json"))
    dna[:run_list] = dna.delete("recipes")
    chef.cookbooks_path = "/var/chef/cookbooks"
    chef.json.merge!(dna)
  end
end

Next!