From d81e5dc28d4530178296db90f97c71439bdc2c64 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 10 Mar 2010 14:27:02 -0800 Subject: [PATCH] Remove all "chef" configuration and moved chef solo provisioning to its own class --- config/default.rb | 7 +- lib/vagrant.rb | 2 +- lib/vagrant/actions/vm/provision.rb | 63 ----------- lib/vagrant/actions/vm/reload.rb | 2 +- lib/vagrant/actions/vm/up.rb | 2 +- lib/vagrant/config.rb | 25 +---- lib/vagrant/provisioners/base.rb | 8 +- lib/vagrant/provisioners/chef_solo.rb | 81 ++++++++++++++ test/test_helper.rb | 7 +- test/vagrant/actions/vm/provision_test.rb | 98 +---------------- test/vagrant/actions/vm/reload_test.rb | 3 +- test/vagrant/actions/vm/up_test.rb | 2 +- test/vagrant/config_test.rb | 12 -- test/vagrant/provisioners/base_test.rb | 7 ++ test/vagrant/provisioners/chef_solo_test.rb | 116 ++++++++++++++++++++ 15 files changed, 228 insertions(+), 207 deletions(-) create mode 100644 test/vagrant/provisioners/chef_solo_test.rb diff --git a/config/default.rb b/config/default.rb index e925c15c0..6e69f1206 100644 --- a/config/default.rb +++ b/config/default.rb @@ -20,10 +20,9 @@ Vagrant::Config.run do |config| config.package.name = 'vagrant' config.package.extension = '.box' - config.chef.enabled = false - config.chef.cookbooks_path = "cookbooks" - config.chef.provisioning_path = "/tmp/vagrant-chef" - config.chef.json = { + config.chef_solo.cookbooks_path = "cookbooks" + config.chef_solo.provisioning_path = "/tmp/vagrant-chef" + config.chef_solo.json = { :instance_role => "vagrant", :recipes => ["vagrant_main"] } diff --git a/lib/vagrant.rb b/lib/vagrant.rb index 1c2f38080..284f1ed4a 100644 --- a/lib/vagrant.rb +++ b/lib/vagrant.rb @@ -5,7 +5,7 @@ PROJECT_ROOT = File.join(libdir, '..') unless defined?(PROJECT_ROOT) # The libs which must be loaded prior to the rest %w{tempfile open-uri json pathname logger uri net/http virtualbox net/ssh archive/tar/minitar net/scp fileutils vagrant/util vagrant/actions/base vagrant/downloaders/base vagrant/actions/runner - vagrant/provisioners/base}.each do |f| + vagrant/config vagrant/provisioners/base}.each do |f| require f end diff --git a/lib/vagrant/actions/vm/provision.rb b/lib/vagrant/actions/vm/provision.rb index 3750fb21a..9a42d87b1 100644 --- a/lib/vagrant/actions/vm/provision.rb +++ b/lib/vagrant/actions/vm/provision.rb @@ -2,69 +2,6 @@ module Vagrant module Actions module VM class Provision < Base - def prepare - Vagrant.config.vm.share_folder("vagrant-provisioning", cookbooks_path, File.expand_path(Vagrant.config.chef.cookbooks_path, Env.root_path)) - end - - def execute! - chown_provisioning_folder - setup_json - setup_solo_config - run_chef_solo - end - - def chown_provisioning_folder - logger.info "Setting permissions on provisioning folder..." - SSH.execute do |ssh| - ssh.exec!("sudo chown #{Vagrant.config.ssh.username} #{Vagrant.config.chef.provisioning_path}") - end - end - - def setup_json - logger.info "Generating JSON and uploading..." - - # Set up initial configuration - data = { - :config => Vagrant.config, - :directory => Vagrant.config.vm.project_directory, - } - - # And wrap it under the "vagrant" namespace - data = { :vagrant => data } - - # Merge with the "extra data" which isn't put under the - # vagrant namespace by default - data.merge!(Vagrant.config.chef.json) - - json = data.to_json - - SSH.upload!(StringIO.new(json), File.join(Vagrant.config.chef.provisioning_path, "dna.json")) - end - - def setup_solo_config - solo_file = <<-solo -file_cache_path "#{Vagrant.config.chef.provisioning_path}" -cookbook_path "#{cookbooks_path}" -solo - - logger.info "Uploading chef-solo configuration script..." - SSH.upload!(StringIO.new(solo_file), File.join(Vagrant.config.chef.provisioning_path, "solo.rb")) - end - - def run_chef_solo - logger.info "Running chef recipes..." - SSH.execute do |ssh| - ssh.exec!("cd #{Vagrant.config.chef.provisioning_path} && sudo chef-solo -c solo.rb -j dna.json") do |channel, data, stream| - # TODO: Very verbose. It would be easier to save the data and only show it during - # an error, or when verbosity level is set high - logger.info("#{stream}: #{data}") - end - end - end - - def cookbooks_path - File.join(Vagrant.config.chef.provisioning_path, "cookbooks") - end end end end diff --git a/lib/vagrant/actions/vm/reload.rb b/lib/vagrant/actions/vm/reload.rb index d699e0ff5..940cf183b 100644 --- a/lib/vagrant/actions/vm/reload.rb +++ b/lib/vagrant/actions/vm/reload.rb @@ -5,7 +5,7 @@ module Vagrant def prepare steps = [ForwardPorts, SharedFolders, Boot] steps.unshift(Halt) if @runner.vm.running? - steps << Provision if Vagrant.config.chef.enabled + steps << Provision if !Vagrant.config.vm.provisioner.nil? steps.each do |action_klass| @runner.add_action(action_klass) diff --git a/lib/vagrant/actions/vm/up.rb b/lib/vagrant/actions/vm/up.rb index 190fc64f1..6556e4863 100644 --- a/lib/vagrant/actions/vm/up.rb +++ b/lib/vagrant/actions/vm/up.rb @@ -17,7 +17,7 @@ msg # Up is a "meta-action" so it really just queues up a bunch # of other actions in its place: steps = [Import, ForwardPorts, SharedFolders, Boot] - steps << Provision if Vagrant.config.chef.enabled + steps << Provision if !Vagrant.config.vm.provisioner.nil? steps.insert(0, MoveHardDrive) if Vagrant.config.vm.hd_location steps.each do |action_klass| diff --git a/lib/vagrant/config.rb b/lib/vagrant/config.rb index 0b6459536..afeaa78e4 100644 --- a/lib/vagrant/config.rb +++ b/lib/vagrant/config.rb @@ -14,7 +14,7 @@ module Vagrant end def configures(key, klass) - @@config.class.configures(key, klass) + config.class.configures(key, klass) end def config @@ -75,11 +75,12 @@ module Vagrant attr_reader :shared_folders attr_accessor :hd_location attr_accessor :disk_image_format - + attr_accessor :provisioner def initialize @forwarded_ports = {} @shared_folders = {} + @provisioner = nil end def forward_port(name, guestport, hostport, protocol="TCP") @@ -112,25 +113,6 @@ module Vagrant attr_accessor :extension end - class ChefConfig < Base - attr_accessor :cookbooks_path - attr_accessor :provisioning_path - attr_accessor :json - attr_accessor :enabled - - def initialize - @enabled = false - end - - def to_json - # Overridden so that the 'json' key could be removed, since its just - # merged into the config anyways - data = instance_variables_hash - data.delete(:json) - data.to_json - end - end - class VagrantConfig < Base attr_accessor :dotfile_name attr_accessor :log_output @@ -159,7 +141,6 @@ module Vagrant configures :package, PackageConfig configures :ssh, SSHConfig configures :vm, VMConfig - configures :chef, ChefConfig configures :vagrant, VagrantConfig def initialize diff --git a/lib/vagrant/provisioners/base.rb b/lib/vagrant/provisioners/base.rb index 26394086c..cb7a650cf 100644 --- a/lib/vagrant/provisioners/base.rb +++ b/lib/vagrant/provisioners/base.rb @@ -7,7 +7,13 @@ module Vagrant class Base include Vagrant::Util - # This is the single method called to provision the system. This method + # This is the method called to "prepare" the provisioner. This is called + # before any actions are run by the action runner (see {Vagrant::Actions::Runner}). + # This can be used to setup shared folders, forward ports, etc. Whatever is + # necessary on a "meta" level. + def prepare; end + + # This is the method called to provision the system. This method # is expected to do whatever necessary to provision the system (create files, # SSH, etc.) def provision!; end diff --git a/lib/vagrant/provisioners/chef_solo.rb b/lib/vagrant/provisioners/chef_solo.rb index d47e6bc54..180257db6 100644 --- a/lib/vagrant/provisioners/chef_solo.rb +++ b/lib/vagrant/provisioners/chef_solo.rb @@ -2,6 +2,87 @@ module Vagrant module Provisioners # This class implements provisioning via chef-solo. class ChefSolo < Base + # This is the configuration which is available through `config.chef_solo` + class CustomConfig < Vagrant::Config::Base + attr_accessor :cookbooks_path + attr_accessor :provisioning_path + attr_accessor :json + + def to_json + # Overridden so that the 'json' key could be removed, since its just + # merged into the config anyways + data = instance_variables_hash + data.delete(:json) + data.to_json + end + end + + # Tell the Vagrant configure class about our custom configuration + Config.configures :chef_solo, CustomConfig + + def prepare + Vagrant.config.vm.share_folder("vagrant-chef-solo", cookbooks_path, File.expand_path(Vagrant.config.chef_solo.cookbooks_path, Env.root_path)) + end + + def provision! + chown_provisioning_folder + setup_json + setup_solo_config + run_chef_solo + end + + def chown_provisioning_folder + logger.info "Setting permissions on chef solo provisioning folder..." + SSH.execute do |ssh| + ssh.exec!("sudo chown #{Vagrant.config.ssh.username} #{Vagrant.config.chef_solo.provisioning_path}") + end + end + + def setup_json + logger.info "Generating chef solo JSON and uploading..." + + # Set up initial configuration + data = { + :config => Vagrant.config, + :directory => Vagrant.config.vm.project_directory, + } + + # And wrap it under the "vagrant" namespace + data = { :vagrant => data } + + # Merge with the "extra data" which isn't put under the + # vagrant namespace by default + data.merge!(Vagrant.config.chef_solo.json) + + json = data.to_json + + SSH.upload!(StringIO.new(json), File.join(Vagrant.config.chef_solo.provisioning_path, "dna.json")) + end + + def setup_solo_config + solo_file = <<-solo +file_cache_path "#{Vagrant.config.chef_solo.provisioning_path}" +cookbook_path "#{cookbooks_path}" +solo + + logger.info "Uploading chef-solo configuration script..." + SSH.upload!(StringIO.new(solo_file), File.join(Vagrant.config.chef_solo.provisioning_path, "solo.rb")) + end + + def run_chef_solo + logger.info "Running chef-solo..." + SSH.execute do |ssh| + ssh.exec!("cd #{Vagrant.config.chef_solo.provisioning_path} && sudo chef-solo -c solo.rb -j dna.json") do |channel, data, stream| + # TODO: Very verbose. It would be easier to save the data and only show it during + # an error, or when verbosity level is set high + logger.info("#{stream}: #{data}") + end + end + end + + def cookbooks_path + File.join(Vagrant.config.chef_solo.provisioning_path, "cookbooks") + end end end end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 4c14f3b43..77642a96d 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -42,9 +42,10 @@ class Test::Unit::TestCase config.package.name = 'vagrant' config.package.extension = '.box' - config.chef.cookbooks_path = "cookbooks" - config.chef.provisioning_path = "/tmp/hobo-chef" - config.chef.json = { + # Chef solo + config.chef_solo.cookbooks_path = "cookbooks" + config.chef_solo.provisioning_path = "/tmp/hobo-chef" + config.chef_solo.json = { :recipes => ["hobo_main"] } diff --git a/test/vagrant/actions/vm/provision_test.rb b/test/vagrant/actions/vm/provision_test.rb index 64e6b808c..70f90afcc 100644 --- a/test/vagrant/actions/vm/provision_test.rb +++ b/test/vagrant/actions/vm/provision_test.rb @@ -2,103 +2,7 @@ require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') class ProvisionActionTest < Test::Unit::TestCase setup do - @mock_vm, @vm, @action = mock_action(Vagrant::Actions::VM::Provision) - - Vagrant::SSH.stubs(:execute) - Vagrant::SSH.stubs(:upload!) - + @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Provision) mock_config end - - context "shared folders" do - should "setup shared folder on VM for the cookbooks" do - File.expects(:expand_path).with(Vagrant.config.chef.cookbooks_path, Vagrant::Env.root_path).returns("foo") - @action.expects(:cookbooks_path).returns("bar") - Vagrant.config.vm.expects(:share_folder).with("vagrant-provisioning", "bar", "foo").once - @action.prepare - end - end - - context "cookbooks path" do - should "return the proper cookbook path" do - cookbooks_path = File.join(Vagrant.config.chef.provisioning_path, "cookbooks") - assert_equal cookbooks_path, @action.cookbooks_path - end - end - - context "permissions on provisioning folder" do - should "chown the folder to the ssh user" do - ssh = mock("ssh") - ssh.expects(:exec!).with("sudo chown #{Vagrant.config.ssh.username} #{Vagrant.config.chef.provisioning_path}") - Vagrant::SSH.expects(:execute).yields(ssh) - @action.chown_provisioning_folder - end - end - - context "generating and uploading json" do - def assert_json - Vagrant::SSH.expects(:upload!).with do |json, path| - data = JSON.parse(json.read) - yield data - true - end - - @action.setup_json - end - - should "merge in the extra json specified in the config" do - Vagrant.config.chef.json = { :foo => "BAR" } - assert_json do |data| - assert_equal "BAR", data["foo"] - end - end - - should "add the directory as a special case to the JSON" do - assert_json do |data| - assert_equal Vagrant.config.vm.project_directory, data["vagrant"]["directory"] - end - end - - should "add the config to the JSON" do - assert_json do |data| - assert_equal Vagrant.config.vm.project_directory, data["vagrant"]["config"]["vm"]["project_directory"] - end - end - - should "upload a StringIO to dna.json" do - StringIO.expects(:new).with(anything).returns("bar") - File.expects(:join).with(Vagrant.config.chef.provisioning_path, "dna.json").once.returns("baz") - Vagrant::SSH.expects(:upload!).with("bar", "baz").once - @action.setup_json - end - end - - context "generating and uploading chef solo configuration file" do - should "upload properly generate the configuration file using configuration data" do - expected_config = <<-config -file_cache_path "#{Vagrant.config.chef.provisioning_path}" -cookbook_path "#{@action.cookbooks_path}" -config - - StringIO.expects(:new).with(expected_config).once - @action.setup_solo_config - end - - should "upload this file as solo.rb to the provisioning folder" do - @action.expects(:cookbooks_path).returns("cookbooks") - StringIO.expects(:new).returns("foo") - File.expects(:join).with(Vagrant.config.chef.provisioning_path, "solo.rb").once.returns("bar") - Vagrant::SSH.expects(:upload!).with("foo", "bar").once - @action.setup_solo_config - end - end - - context "running chef solo" do - should "cd into the provisioning directory and run chef solo" do - ssh = mock("ssh") - ssh.expects(:exec!).with("cd #{Vagrant.config.chef.provisioning_path} && sudo chef-solo -c solo.rb -j dna.json").once - Vagrant::SSH.expects(:execute).yields(ssh) - @action.run_chef_solo - end - end end diff --git a/test/vagrant/actions/vm/reload_test.rb b/test/vagrant/actions/vm/reload_test.rb index 9218390ff..4bd912d71 100644 --- a/test/vagrant/actions/vm/reload_test.rb +++ b/test/vagrant/actions/vm/reload_test.rb @@ -33,7 +33,8 @@ class ReloadActionTest < Test::Unit::TestCase should "add in the provisioning step if enabled" do mock_config do |config| - config.chef.enabled = true + # Dummy provisioner to test + config.vm.provisioner = "foo" end @default_order.push(Vagrant::Actions::VM::Provision) diff --git a/test/vagrant/actions/vm/up_test.rb b/test/vagrant/actions/vm/up_test.rb index 520c70194..c39dfedca 100644 --- a/test/vagrant/actions/vm/up_test.rb +++ b/test/vagrant/actions/vm/up_test.rb @@ -47,7 +47,7 @@ class UpActionTest < Test::Unit::TestCase should "add in the provisioning step if enabled" do mock_config do |config| - config.chef.enabled = true + config.vm.provisioner = "foo" end @default_order.push(Vagrant::Actions::VM::Provision) diff --git a/test/vagrant/config_test.rb b/test/vagrant/config_test.rb index 311296e3e..547e07cb3 100644 --- a/test/vagrant/config_test.rb +++ b/test/vagrant/config_test.rb @@ -117,18 +117,6 @@ class ConfigTest < Test::Unit::TestCase end end - context "chef config" do - setup do - @config = Vagrant::Config::ChefConfig.new - @config.json = "HEY" - end - - should "not include the 'json' key in the config dump" do - result = JSON.parse(@config.to_json) - assert !result.has_key?("json") - end - end - context "top config class" do setup do @configures_list = [] diff --git a/test/vagrant/provisioners/base_test.rb b/test/vagrant/provisioners/base_test.rb index 6473fcf77..403665cc1 100644 --- a/test/vagrant/provisioners/base_test.rb +++ b/test/vagrant/provisioners/base_test.rb @@ -16,5 +16,12 @@ class BaseProvisionerTest < Test::Unit::TestCase @base.provision! end end + + should "implement prepare which does nothing" do + assert_nothing_raised do + assert @base.respond_to?(:prepare) + @base.prepare + end + end end end diff --git a/test/vagrant/provisioners/chef_solo_test.rb b/test/vagrant/provisioners/chef_solo_test.rb new file mode 100644 index 000000000..31822a893 --- /dev/null +++ b/test/vagrant/provisioners/chef_solo_test.rb @@ -0,0 +1,116 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') + +class ChefSoloProvisionerTest < Test::Unit::TestCase + setup do + @action = Vagrant::Provisioners::ChefSolo.new + + Vagrant::SSH.stubs(:execute) + Vagrant::SSH.stubs(:upload!) + + mock_config + end + + context "config" do + setup do + @config = Vagrant::Provisioners::ChefSolo::CustomConfig.new + @config.json = "HEY" + end + + should "not include the 'json' key in the config dump" do + result = JSON.parse(@config.to_json) + assert !result.has_key?("json") + end + end + + context "shared folders" do + should "setup shared folder on VM for the cookbooks" do + File.expects(:expand_path).with(Vagrant.config.chef_solo.cookbooks_path, Vagrant::Env.root_path).returns("foo") + @action.expects(:cookbooks_path).returns("bar") + Vagrant.config.vm.expects(:share_folder).with("vagrant-chef-solo", "bar", "foo").once + @action.prepare + end + end + + context "cookbooks path" do + should "return the proper cookbook path" do + cookbooks_path = File.join(Vagrant.config.chef_solo.provisioning_path, "cookbooks") + assert_equal cookbooks_path, @action.cookbooks_path + end + end + + context "permissions on provisioning folder" do + should "chown the folder to the ssh user" do + ssh = mock("ssh") + ssh.expects(:exec!).with("sudo chown #{Vagrant.config.ssh.username} #{Vagrant.config.chef_solo.provisioning_path}") + Vagrant::SSH.expects(:execute).yields(ssh) + @action.chown_provisioning_folder + end + end + + context "generating and uploading json" do + def assert_json + Vagrant::SSH.expects(:upload!).with do |json, path| + data = JSON.parse(json.read) + yield data + true + end + + @action.setup_json + end + + should "merge in the extra json specified in the config" do + Vagrant.config.chef_solo.json = { :foo => "BAR" } + assert_json do |data| + assert_equal "BAR", data["foo"] + end + end + + should "add the directory as a special case to the JSON" do + assert_json do |data| + assert_equal Vagrant.config.vm.project_directory, data["vagrant"]["directory"] + end + end + + should "add the config to the JSON" do + assert_json do |data| + assert_equal Vagrant.config.vm.project_directory, data["vagrant"]["config"]["vm"]["project_directory"] + end + end + + should "upload a StringIO to dna.json" do + StringIO.expects(:new).with(anything).returns("bar") + File.expects(:join).with(Vagrant.config.chef_solo.provisioning_path, "dna.json").once.returns("baz") + Vagrant::SSH.expects(:upload!).with("bar", "baz").once + @action.setup_json + end + end + + context "generating and uploading chef solo configuration file" do + should "upload properly generate the configuration file using configuration data" do + expected_config = <<-config +file_cache_path "#{Vagrant.config.chef_solo.provisioning_path}" +cookbook_path "#{@action.cookbooks_path}" +config + + StringIO.expects(:new).with(expected_config).once + @action.setup_solo_config + end + + should "upload this file as solo.rb to the provisioning folder" do + @action.expects(:cookbooks_path).returns("cookbooks") + StringIO.expects(:new).returns("foo") + File.expects(:join).with(Vagrant.config.chef_solo.provisioning_path, "solo.rb").once.returns("bar") + Vagrant::SSH.expects(:upload!).with("foo", "bar").once + @action.setup_solo_config + end + end + + context "running chef solo" do + should "cd into the provisioning directory and run chef solo" do + ssh = mock("ssh") + ssh.expects(:exec!).with("cd #{Vagrant.config.chef_solo.provisioning_path} && sudo chef-solo -c solo.rb -j dna.json").once + Vagrant::SSH.expects(:execute).yields(ssh) + @action.run_chef_solo + end + end +end