diff --git a/lib/vagrant/guest.rb b/lib/vagrant/guest.rb index 4cd719400..85a6eb8da 100644 --- a/lib/vagrant/guest.rb +++ b/lib/vagrant/guest.rb @@ -1,5 +1,7 @@ require "log4r" +require "vagrant/capability_host" + module Vagrant # This class handles guest-OS specific interactions with a machine. # It is primarily responsible for detecting the proper guest OS @@ -17,21 +19,13 @@ module Vagrant # This system allows for maximum flexibility and pluginability for doing # guest OS specific operations. class Guest - attr_reader :chain - - # The name of the guest OS. This is available after {#detect!} is - # called. - # - # @return [Symbol] - attr_reader :name + include CapabilityHost def initialize(machine, guests, capabilities) @logger = Log4r::Logger.new("vagrant::guest") @capabilities = capabilities - @chain = [] @guests = guests @machine = machine - @name = nil end # This will detect the proper guest OS for the machine and set up @@ -40,52 +34,7 @@ module Vagrant @logger.info("Detect guest for machine: #{@machine}") 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 - - 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 - - @name = guest_name - @chain = guest_chain(@name) - end - - # Tests whether the guest has the named capability. - # - # @return [Boolean] - def capability?(cap_name) - !capability_module(cap_name.to_sym).nil? - end - - # Executes the capability with the given name, optionally passing - # more arguments onwards to the capability. - def capability(cap_name, *args) - @logger.info("Execute capability: #{cap_name} (#{@chain[0][0]})") - cap_mod = capability_module(cap_name.to_sym) - if !cap_mod - raise Errors::GuestCapabilityNotFound, - :cap => cap_name.to_s, - :guest => @chain[0][0].to_s - end - - cap_method = nil - begin - cap_method = cap_mod.method(cap_name) - rescue NameError - raise Errors::GuestCapabilityInvalid, - :cap => cap_name.to_s, - :guest => @chain[0][0].to_s - end - - cap_method.call(@machine, *args) + initialize_capabilities!(guest_name, @guests, @capabilities, @machine) end # This returns whether the guest is ready to work. If this returns @@ -94,98 +43,7 @@ module Vagrant # # @return [Boolean] def ready? - !@chain.empty? - end - - protected - - # Returns the registered module for a capability with the given name. - # - # @param [Symbol] cap_name - # @return [Module] - def capability_module(cap_name) - @logger.debug("Searching for cap: #{cap_name}") - @chain.each do |guest_name, guest| - @logger.debug("Checking in: #{guest_name}") - caps = @capabilities[guest_name] - - if caps && caps.has_key?(cap_name) - @logger.debug("Found cap: #{cap_name} in #{guest_name}") - return caps[cap_name] - end - end - - 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 + !!capability_host_chain end end end diff --git a/test/unit/vagrant/guest_test.rb b/test/unit/vagrant/guest_test.rb index 6dd9cbf8c..0a7d76001 100644 --- a/test/unit/vagrant/guest_test.rb +++ b/test/unit/vagrant/guest_test.rb @@ -3,7 +3,7 @@ require "pathname" require File.expand_path("../../base", __FILE__) describe Vagrant::Guest do - include_context "unit" + include_context "capability_helpers" let(:capabilities) { {} } let(:guests) { {} } @@ -18,44 +18,27 @@ describe Vagrant::Guest do subject { described_class.new(machine, guests, capabilities) } - # This registers a capability with a specific guest - def register_capability(guest, capability, options=nil) - options ||= {} + describe "#detect!" do + it "auto-detects if no explicit guest name given" do + machine.config.vm.stub(guest: nil) + subject.should_receive(:initialize_capabilities!). + with(nil, guests, capabilities, machine) - cap = Class.new do - if !options[:corrupt] - define_method(capability) do |*args| - raise "cap: #{capability} #{args.inspect}" - end - end + subject.detect! end - capabilities[guest] ||= {} - capabilities[guest][capability] = cap.new - end + it "uses the explicit guest name if specified" do + machine.config.vm.stub(guest: :foo) + subject.should_receive(:initialize_capabilities!). + with(:foo, guests, capabilities, machine) - # This registers a guest with the class. - # - # @param [Symbol] name Name of the guest - # @param [Symbol] parent Name of the parent - # @param [Boolean] detect Whether or not to detect properly - def register_guest(name, parent, detect) - guest = Class.new(Vagrant.plugin("2", "guest")) do - define_method(:name) do - name - end - - define_method(:detect?) do |m| - detect - end + subject.detect! end - - guests[name] = [guest, parent] end describe "#ready?" do before(:each) do - register_guest(:foo, nil, true) + guests[:foo] = [detect_class(true), nil] end it "should not be ready by default" do