diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index 1365ebc83..9b4d086ed 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -487,22 +487,33 @@ module Vagrant def host return @host if defined?(@host) - # Attempt to figure out the host class. Note that the order - # matters here, so please don't touch. Specifically: The symbol - # check is done after the detect check because the symbol check - # will return nil, and we don't want to trigger a detect load. + # Determine the host class to use. ":detect" is an old Vagrant config + # that shouldn't be valid anymore, but we respect it here by assuming + # its old behavior. No need to deprecate this because I thin it is + # fairly harmless. host_klass = config_global.vagrant.host - if host_klass.nil? || host_klass == :detect - hosts = Vagrant.plugin("2").manager.hosts.to_hash + host_klass = nil if host_klass == :detect - # Get the flattened list of available hosts - host_klass = Hosts.detect(hosts) + begin + @host = Host.new( + host_klass, + Vagrant.plugin("2").manager.hosts, + Vagrant.plugin("2").manager.host_capabilities) + rescue Errors::CapabilityHostNotDetected + # If the auto-detect failed, then we create a brand new host + # with no capabilities and use that. This should almost never happen + # since Vagrant works on most host OS's now, so this is a "slow path" + klass = Class.new(Vagrant.plugin("2", :host)) do + def detect?; true; end + end + + hosts = { generic: [klass, nil] } + host_caps = {} + + @host = Host.new(:generic, hosts, host_caps) + rescue Errors::CapabilityHostExplicitNotDetected => e + raise Errors::HostExplicitNotDetected, e.extra_data end - - # If no host class is detected, we use the base class. - host_klass ||= Vagrant.plugin("2", :host) - - @host ||= host_klass.new(@ui) end # Action runner for executing actions in the context of this environment. diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index 8654ac752..40777581f 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -333,6 +333,10 @@ module Vagrant error_key(:guest_not_detected) end + class HostExplicitNotDetected < VagrantError + error_key(:host_explicit_not_detected) + end + class LinuxMountFailed < VagrantError error_key(:linux_mount_failed) end diff --git a/lib/vagrant/plugin/v2/host.rb b/lib/vagrant/plugin/v2/host.rb index 51e5ae3f1..815ac0c8f 100644 --- a/lib/vagrant/plugin/v2/host.rb +++ b/lib/vagrant/plugin/v2/host.rb @@ -14,16 +14,6 @@ module Vagrant false end - # Initializes a new host class. - # - # The only required parameter is a UI object so that the host - # objects have some way to communicate with the outside world. - # - # @param [UI] ui UI for the hosts to output to. - def initialize(ui) - @ui = ui - end - # Returns true of false denoting whether or not this host supports # NFS shared folder setup. This method ideally should verify that # NFS is installed. diff --git a/plugins/kernel_v2/config/vagrant.rb b/plugins/kernel_v2/config/vagrant.rb index 5e049fe60..0cad409cb 100644 --- a/plugins/kernel_v2/config/vagrant.rb +++ b/plugins/kernel_v2/config/vagrant.rb @@ -5,6 +5,15 @@ module VagrantPlugins class VagrantConfig < Vagrant.plugin("2", :config) attr_accessor :host + def initialize + @host = UNSET_VALUE + end + + def finalize! + @host = nil if @host == UNSET_VALUE + @host = @host.to_sym if @host + end + def to_s "Vagrant" end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 6ca7b8e0b..52579da36 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -412,6 +412,11 @@ en: directory that Vagrant uses must be both readable and writable. You specified: %{home_path} + host_explicit_not_detected: |- + The host implementation explicitly specified in your Vagrantfile + ("%{value}") could not be found. Please verify that the plugin is + installed which implements this host and that the value you used + for `config.vagrant.host` is correct. interrupted: |- Vagrant exited after cleanup due to external interrupt. local_data_dir_not_accessible: |- diff --git a/test/unit/vagrant/environment_test.rb b/test/unit/vagrant/environment_test.rb index a3b914eb9..5c120a9a3 100644 --- a/test/unit/vagrant/environment_test.rb +++ b/test/unit/vagrant/environment_test.rb @@ -8,6 +8,7 @@ require "vagrant/util/file_mode" describe Vagrant::Environment do include_context "unit" + include_context "capability_helpers" let(:env) do isolated_environment.tap do |e| @@ -21,9 +22,83 @@ describe Vagrant::Environment do end let(:instance) { env.create_vagrant_env } - subject { instance } + describe "#host" do + let(:plugin_hosts) { {} } + let(:plugin_host_caps) { {} } + + before do + m = Vagrant.plugin("2").manager + m.stub(hosts: plugin_hosts) + m.stub(host_capabilities: plugin_host_caps) + end + + it "should default to some host even if there are none" do + env.vagrantfile <<-VF + Vagrant.configure("2") do |config| + config.vagrant.host = nil + end + VF + + expect(subject.host).to be + end + + it "should attempt to detect a host if no host is set" do + env.vagrantfile <<-VF + Vagrant.configure("2") do |config| + config.vagrant.host = nil + end + VF + + plugin_hosts[:foo] = [detect_class(true), nil] + plugin_host_caps[:foo] = { bar: Class } + + result = subject.host + expect(result.capability?(:bar)).to be_true + end + + it "should attempt to detect a host if host is :detect" do + env.vagrantfile <<-VF + Vagrant.configure("2") do |config| + config.vagrant.host = :detect + end + VF + + plugin_hosts[:foo] = [detect_class(true), nil] + plugin_host_caps[:foo] = { bar: Class } + + result = subject.host + expect(result.capability?(:bar)).to be_true + end + + it "should use an exact host if specified" do + env.vagrantfile <<-VF + Vagrant.configure("2") do |config| + config.vagrant.host = "foo" + end + VF + + plugin_hosts[:foo] = [detect_class(false), nil] + plugin_hosts[:bar] = [detect_class(true), nil] + plugin_host_caps[:foo] = { bar: Class } + + result = subject.host + expect(result.capability?(:bar)).to be_true + end + + it "should raise an error if an exact match was specified but not found" do + env.vagrantfile <<-VF + Vagrant.configure("2") do |config| + config.vagrant.host = "bar" + end + VF + + expect { subject.host }. + to raise_error(Vagrant::Errors::HostExplicitNotDetected) + end + end + describe "active machines" do it "should be empty if the machines folder doesn't exist" do folder = instance.local_data_path.join("machines")