hashicorp / vagrant

Vagrant is a tool for building and distributing development environments.
https://www.vagrantup.com
Other
26.27k stars 4.43k forks source link

Issue with chef.json in multi-vm configurations #1070

Closed samhendley closed 12 years ago

samhendley commented 12 years ago
boxes = [{:role => "db_master", :host_name => "node1"},{ :role => "db_slave", :host_name => "node2"}]

Vagrant::Config.run do |config|
  boxes.each do |box_config|
    config.vm.define box_config[:host_name] do |db_config|
      db_config.vm.box = "CentOS6.3"
      config.vm.provision :chef_solo do |chef|
        chef.cookbooks_path = ['cookbooks']
        chef.add_recipe 'htop'
        chef.json = {
          :instance_role => "#{box_config[:role]}"
        }
      end
    end
  end
end

With the above Vagrantfile after doing vagrant up both nodes will have the same value for :instance_role in the /tmp/vagrant-chef-solo-1/dna.json file on both nodes. In my experiments it is always the first file in the list.

I believe this must be related to "shared config" object. In particular this line drew my attention:

https://github.com/mitchellh/vagrant/blob/master/plugins/provisioners/chef/provisioner/base.rb#L140

There appears to be some ruby magic in this class but the comments about "instance variables" "shared config" make me believe there is something in the chef provisioner that assumes only one config would be created.

There may be a better way to build multiple VMs that bypasses this issue but it did bite me pretty hard today, any advice would be appreciated.

EDIT: This was using vagrant 1.0.3 on windows (from MSI)

samhendley commented 12 years ago

I thought this issue was related only to the json field but it also appears to affect the roles field as well.

boxes = [
  {:role => "db_master", :host_name => "node1"},
  {:role => "db_slave", :host_name => "node2"}]

Vagrant::Config.run do |config|
  boxes.each do |box_config|
    config.vm.define box_config[:host_name] do |db_config|
      db_config.vm.box = "CentOS6.3"
      config.vm.provision :chef_solo do |chef|
        chef.cookbooks_path = ['cookbooks']
        chef.add_recipe 'htop'
        chef.add_role box_config[:role]
      end
    end
  end
end
vagrant ssh node1 -c 'cat /tmp/vagrant-chef-1/dna.json | (grep db_master && echo "db_master")' 
=> db_master
vagrant ssh node2 -c 'cat /tmp/vagrant-chef-1/dna.json | (grep db_master && echo "db_master")' 
=> db_master !! (should be db_slave)
samhendley commented 12 years ago

After some digging it looks like this issue is actually with the command "vagrant provision node2". In that case it puts the wrong file on server. "vagrant provision" works correctly.

samhendley commented 12 years ago

In my previous tests I found that running just "vagrant provision" works but that is because it actually runs the provisioning step twice for the second node, 3 times for the 3rd node etc. In my real tests I had so much output I wasn't able to see that it was actually running twice.

I am now suspicious of this line. It looks like it may be attaching each provisioner to multiple configurations somehow. https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/action/vm/provision.rb#L41

samhendley commented 12 years ago

Nevermind. I am an idiot and was using db_config for the box configuration and config for the global config and attaching the provisioner to the global context.