From ba18d98d458f3450107b9278036277b62059b765 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 8 Dec 2013 11:14:18 -0800 Subject: [PATCH] core: improve guest type detection /cc @fgrehm - I figured you might be interested in this. :) --- lib/vagrant/errors.rb | 4 + lib/vagrant/guest.rb | 147 ++++++++++++++++++-------------- templates/locales/en.yml | 5 ++ test/unit/vagrant/guest_test.rb | 9 ++ 4 files changed, 101 insertions(+), 64 deletions(-) diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index a921ac3c0..82c3d32c8 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -300,6 +300,10 @@ module Vagrant error_key(:guest_capability_not_found) end + class GuestExplicitNotDetected < VagrantError + error_key(:guest_explicit_not_detected) + end + class GuestNotDetected < VagrantError error_key(:guest_not_detected) end diff --git a/lib/vagrant/guest.rb b/lib/vagrant/guest.rb index d31b8fb0b..4cd719400 100644 --- a/lib/vagrant/guest.rb +++ b/lib/vagrant/guest.rb @@ -39,74 +39,23 @@ module Vagrant def detect! @logger.info("Detect guest for machine: #{@machine}") - # Get the mapping of guests with the most parents. We start searching - # with the guests with the most parents first. - parent_count = {} - @guests.each do |name, parts| - parent_count[name] = 0 - - parent = parts[1] - while parent - parent_count[name] += 1 - parent = @guests[parent] - parent = parent[1] if parent - end + guest_name = @machine.config.vm.guest + if guest_name + @logger.info("Using explicit config.vm.guest value: #{guest_name}") + else + # No explicit guest was specified, so autodetect it. + guest_name = autodetect_guest + raise Errors::GuestNotDetected if !guest_name end - # Now swap around the mapping so that it is a mapping of - # count to the actual list of guest names - parent_count_to_guests = {} - parent_count.each do |name, count| - parent_count_to_guests[count] ||= [] - parent_count_to_guests[count] << name + if !@guests[guest_name] + # This can happen if config.vm.guest was specified with a value + # that doesn't exist. + raise Errors::GuestExplicitNotDetected, value: guest_name.to_s end - catch(:guest_os) do - sorted_counts = parent_count_to_guests.keys.sort.reverse - sorted_counts.each do |count| - parent_count_to_guests[count].each do |name| - @logger.debug("Trying: #{name}") - guest_info = @guests[name] - guest = guest_info[0].new - - # If a specific guest was specified, then attempt to use that - # guest no matter what. Otherwise, only use it if it was detected. - use_this_guest = false - if @machine.config.vm.guest.nil? - use_this_guest = guest.detect?(@machine) - else - use_this_guest = @machine.config.vm.guest.to_sym == name.to_sym - end - - if use_this_guest - @logger.info("Detected: #{name}!") - @chain << [name, guest] - @name = name - - # Build the proper chain of parents if there are any. - # This allows us to do "inheritance" of capabilities later - if guest_info[1] - parent_name = guest_info[1] - parent_info = @guests[parent_name] - while parent_info - @chain << [parent_name, parent_info[0].new] - parent_name = parent_info[1] - parent_info = @guests[parent_name] - end - end - - @logger.info("Full guest chain: #{@chain.inspect}") - - # Exit the search - throw :guest_os - end - end - end - end - - # We shouldn't reach this point. Ideally we would detect - # all operating systems. - raise Errors::GuestNotDetected if @chain.empty? + @name = guest_name + @chain = guest_chain(@name) end # Tests whether the guest has the named capability. @@ -168,5 +117,75 @@ module Vagrant nil end + + # This autodetects the guest to use and returns the name of the guest. + # This returns nil if the guest type could not be autodetected. + # + # @return [Symbol] + def autodetect_guest + @logger.info("Autodetecting guest for machine: #{@machine}") + + # Get the mapping of guests with the most parents. We start searching + # with the guests with the most parents first. + parent_count = {} + @guests.each do |name, parts| + parent_count[name] = 0 + + parent = parts[1] + while parent + parent_count[name] += 1 + parent = @guests[parent] + parent = parent[1] if parent + end + end + + # Now swap around the mapping so that it is a mapping of + # count to the actual list of guest names + parent_count_to_guests = {} + parent_count.each do |name, count| + parent_count_to_guests[count] ||= [] + parent_count_to_guests[count] << name + end + + sorted_counts = parent_count_to_guests.keys.sort.reverse + sorted_counts.each do |count| + parent_count_to_guests[count].each do |name| + @logger.debug("Trying: #{name}") + guest_info = @guests[name] + guest = guest_info[0].new + + if guest.detect?(@machine) + @logger.info("Detected: #{name}!") + return name + end + end + end + + return nil + end + + # This returns the complete chain for the given guest. + # + # @return [Array] + def guest_chain(name) + guest_info = @guests[name] + guest = guest_info[0].new + chain = [] + chain << [name, guest] + + # Build the proper chain of parents if there are any. + # This allows us to do "inheritance" of capabilities later + if guest_info[1] + parent_name = guest_info[1] + parent_info = @guests[parent_name] + while parent_info + chain << [parent_name, parent_info[0].new] + parent_name = parent_info[1] + parent_info = @guests[parent_name] + end + end + + return chain + end end end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 4094907f8..87bfcdc11 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -366,6 +366,11 @@ en: support that capability. This capability is required for your configuration of Vagrant. Please either reconfigure Vagrant to avoid this capability or fix the issue by creating the capability. + guest_explicit_not_detected: |- + The guest implementation explicitly specified in your Vagrantfile + ("%{value}") could not be found. Please verify that the plugin is + installed which implements this guest and that the value you + used for `config.vm.guest` is correct. guest_not_detected: |- The guest operating system of the machine could not be detected! Vagrant requires this knowledge to perform specific tasks such diff --git a/test/unit/vagrant/guest_test.rb b/test/unit/vagrant/guest_test.rb index b66ccc708..4dcaab414 100644 --- a/test/unit/vagrant/guest_test.rb +++ b/test/unit/vagrant/guest_test.rb @@ -156,6 +156,15 @@ describe Vagrant::Guest do subject.name.should == :bar end + it "raises an exception if the forced guest can't be found" do + register_guest(:foo, nil, true) + + machine.config.vm.stub(:guest => :bar) + + expect { subject.detect! }. + to raise_error(Vagrant::Errors::GuestExplicitNotDetected) + end + it "raises an exception if no guest can be detected" do expect { subject.detect! }. to raise_error(Vagrant::Errors::GuestNotDetected)