diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index 9db88ceb6..b90d4277e 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -227,6 +227,14 @@ module Vagrant error_key(:port_collision_resume) end + class GuestCapabilityInvalid < VagrantError + error_key(:guest_capability_invalid) + end + + class GuestCapabilityNotFound < VagrantError + error_key(:guest_capability_not_found) + end + class GuestNotDetected < VagrantError error_key(:guest_not_detected) end diff --git a/lib/vagrant/guest.rb b/lib/vagrant/guest.rb index 2a36ea3e6..f13dc4885 100644 --- a/lib/vagrant/guest.rb +++ b/lib/vagrant/guest.rb @@ -104,12 +104,30 @@ module Vagrant # # @return [Boolean] def capability?(cap_name) - @chain.each do |guest_name, guest| - caps = @capabilities[guest_name] - return true if caps && caps.has_key?(cap_name) + !capability_module(cap_name).nil? + end + + # Executes the capability with the given name, optionally passing + # more arguments onwards to the capability. + def capability(cap_name) + @logger.info("Execute capability: #{cap_name} (#{@chain[0][0]})") + cap_mod = capability_module(cap_name) + if !cap_mod + raise Errors::GuestCapabilityNotFound, + :cap => cap_name.to_s, + :guest => @chain[0][0].to_s end - false + 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 end # This returns whether the guest is ready to work. If this returns @@ -120,5 +138,20 @@ module Vagrant 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) + @chain.each do |guest_name, guest| + caps = @capabilities[guest_name] + return caps[cap_name] if caps && caps.has_key?(cap_name) + end + + nil + end end end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index ea5ae4d1d..9a70947e0 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -173,6 +173,17 @@ en: of the official installers or another gem is wrongly attempting to use Vagrant internals directly. Please properly install Vagrant to fix this. If this error persists, please contact support. + guest_capability_invalid: |- + The registered guest capability '%{cap}' for the + detected guest OS '%{guest}' is invalid. The capability does + not implement the proper method. This is a bug with Vagrant or the + plugin that implements this capability. Please report a bug. + guest_capability_not_found: |- + Vagrant attempted to execute the capability '%{cap}' + on the detect guest OS '%{guest}', but the guest doesn't + 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_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 228c19360..f530e7046 100644 --- a/test/unit/vagrant/guest_test.rb +++ b/test/unit/vagrant/guest_test.rb @@ -12,9 +12,14 @@ describe Vagrant::Guest do subject { described_class.new(machine, guests, capabilities) } # This registers a capability with a specific guest - def register_capability(guest, capability) + def register_capability(guest, capability, options=nil) + options ||= {} + cap = Class.new do - define_method(capability) do + if !options[:corrupt] + define_method(capability) do + raise "cap: #{capability}" + end end end @@ -41,6 +46,34 @@ describe Vagrant::Guest do guests[name] = [guest, parent] end + describe "#capability" do + before :each do + register_guest(:foo, nil, true) + register_guest(:bar, :foo, true) + + subject.detect! + end + + it "executes the capability" do + register_capability(:bar, :test) + + expect { subject.capability(:test) }. + to raise_error(RuntimeError, "cap: test") + end + + it "raises an exception if the capability doesn't exist" do + expect { subject.capability(:what_is_this_i_dont_even) }. + to raise_error(Vagrant::Errors::GuestCapabilityNotFound) + end + + it "raises an exception if the method doesn't exist on the module" do + register_capability(:bar, :test_is_corrupt, corrupt: true) + + expect { subject.capability(:test_is_corrupt) }. + to raise_error(Vagrant::Errors::GuestCapabilityInvalid) + end + end + describe "#capability?" do before :each do register_guest(:foo, nil, true)