diff --git a/lib/vagrant/machine.rb b/lib/vagrant/machine.rb index 43abe1b5e..f2c74fd57 100644 --- a/lib/vagrant/machine.rb +++ b/lib/vagrant/machine.rb @@ -129,8 +129,21 @@ module Vagrant # # @return [Object] def guest - raise Errors::MachineGuestNotReady if !communicator.ready? - # XXX: Todo + raise Errors::MachineGuestNotReady if !communicate.ready? + + # Load the initial guest. + guest = load_guest(config.vm.guest) + + # Loop and distro dispatch while there are distros. + while true + distro = guest.distro_dispatch + break if !distro + + guest = load_guest(distro) + end + + # Return the result + guest end # This sets the unique ID associated with this machine. This will @@ -230,5 +243,32 @@ module Vagrant def state @provider.state end + + protected + + # Given a guest name (such as `:windows`), this will load the associated + # guest implementation and return an instance. + # + # @param [Symbol] guest The name of the guest implementation. + # @return [Object] + def load_guest(guest) + @logger.info("Loading guest: #{guest}") + + klass = nil + Vagrant.plugin("1").registered.each do |plugin| + if plugin.guest.has_key?(guest) + klass = plugin.guest[guest] + break + end + end + + if klass.nil? + raise Errors::VMGuestError, + :_key => :unknown_type, + :guest => guest.to_s + end + + return klass.new(self) + end end end diff --git a/lib/vagrant/plugin/v1/guest.rb b/lib/vagrant/plugin/v1/guest.rb index 86e96c249..a62cbbce6 100644 --- a/lib/vagrant/plugin/v1/guest.rb +++ b/lib/vagrant/plugin/v1/guest.rb @@ -27,9 +27,8 @@ module Vagrant # Vagrant can successfully SSH into the machine) to give the system a chance # to determine the distro and return a distro-specific system. # - # **Warning:** If a return value which subclasses from {Base} is - # returned, Vagrant will use it as the new system instance for the - # class. + # If this method returns nil, then this instance is assumed to be + # the most specific guest implementation. def distro_dispatch end diff --git a/plugins/providers/virtualbox/action/halt.rb b/plugins/providers/virtualbox/action/halt.rb index b4f2d8bba..5f339de8e 100644 --- a/plugins/providers/virtualbox/action/halt.rb +++ b/plugins/providers/virtualbox/action/halt.rb @@ -13,13 +13,13 @@ module VagrantPlugins # attempt a graceful shutdown if current_state == :running && !env["force"] env[:ui].info I18n.t("vagrant.actions.vm.halt.graceful") - env[:vm].guest.halt + env[:machine].guest.halt end # If we're not powered off now, then force it - if env[:vm].state != :poweroff + if env[:machine].state != :poweroff env[:ui].info I18n.t("vagrant.actions.vm.halt.force") - env[:vm].driver.halt + env[:machine].driver.halt end # Sleep for a second to verify that the VM properly diff --git a/test/unit/support/shared/base_context.rb b/test/unit/support/shared/base_context.rb index 947fd01ed..734a9c5d8 100644 --- a/test/unit/support/shared/base_context.rb +++ b/test/unit/support/shared/base_context.rb @@ -5,11 +5,23 @@ require "unit/support/isolated_environment" shared_context "unit" do before(:each) do + # State to store the list of registered plugins that we have to + # unregister later. + @_plugins = [] + # Create a thing to store our temporary files so that they aren't # unlinked right away. @_temp_files = [] end + after(:each) do + # Unregister each of the plugins we have may have temporarily + # registered for the duration of this test. + @_plugins.each do |plugin| + plugin.unregister! + end + end + # This creates an isolated environment so that Vagrant doesn't # muck around with your real system during unit tests. # @@ -22,6 +34,20 @@ shared_context "unit" do env end + # This registers a Vagrant plugin for the duration of a single test. + # This will yield a new plugin class that you can then call the + # public plugin methods on. + # + # @yield [plugin] Yields the plugin class for you to call the public + # API that you need to. + def register_plugin + plugin = Class.new(Vagrant.plugin("1")) + plugin.name("Test Plugin #{plugin.inspect}") + yield plugin if block_given? + @_plugins << plugin + plugin + end + # This helper creates a temporary file and returns a Pathname # object pointed to it. # diff --git a/test/unit/vagrant/machine_test.rb b/test/unit/vagrant/machine_test.rb index a8ccc5ab8..7cc7eb03f 100644 --- a/test/unit/vagrant/machine_test.rb +++ b/test/unit/vagrant/machine_test.rb @@ -199,7 +199,7 @@ describe Vagrant::Machine do end before(:each) do - instance.stub(:communicator).and_return(communicator) + instance.stub(:communicate).and_return(communicator) end it "should raise an exception if communication is not ready" do @@ -208,6 +208,47 @@ describe Vagrant::Machine do expect { instance.guest }. to raise_error(Vagrant::Errors::MachineGuestNotReady) end + + it "should return the configured guest" do + test_guest = Class.new(Vagrant.plugin("1", :guest)) + + register_plugin do |p| + p.guest(:test) { test_guest } + end + + config.vm.guest = :test + + result = instance.guest + result.should be_kind_of(test_guest) + end + + it "should raise an exception if it can't find the configured guest" do + config.vm.guest = :bad + + expect { instance.guest }. + to raise_error(Vagrant::Errors::VMGuestError) + end + + it "should distro dispatch to the most specific guest" do + # Create the classes and dispatch the parent into the child + guest_parent = Class.new(Vagrant.plugin("1", :guest)) do + def distro_dispatch + :child + end + end + + guest_child = Class.new(Vagrant.plugin("1", :guest)) + + # Register the classes + register_plugin do |p| + p.guest(:parent) { guest_parent } + p.guest(:child) { guest_child } + end + + # Test that the result is the child + config.vm.guest = :parent + instance.guest.should be_kind_of(guest_child) + end end describe "setting the ID" do