Support loading plugin information from nested Vagrantfiles

Since plugin installation happens when the environment is first
initialized, attempt to determine the provider in use and load
any box provided Vagrantfiles to include any plugin configuration
they may include.
This commit is contained in:
Chris Roberts 2019-06-04 10:07:02 -07:00
parent b3877f4b2c
commit 50c4464d44
3 changed files with 151 additions and 9 deletions

View File

@ -175,9 +175,7 @@ module Vagrant
# Load any global plugins
Vagrant::Plugin::Manager.instance.load_plugins(plugins)
if !vagrantfile.config.vagrant.plugins.empty?
plugins = process_configured_plugins
end
plugins = process_configured_plugins
# Call the hooks that does not require configurations to be loaded
# by using a "clean" action runner
@ -922,6 +920,49 @@ module Vagrant
protected
# Attempt to guess the configured provider in use. Will fallback
# to the default provider if an explicit provider name is not
# provided. This can be pretty error prone, but is used during
# initial environment setup to allow loading plugins so it doesn't
# need to be perfect
#
# @return [String]
def guess_provider
gp = nil
ARGV.each_with_index do |val, idx|
if val.start_with?("--provider=")
gp = val.split("=", 2).last
break
elsif val == "--provider"
gp = ARGV[idx+1]
break
end
end
return gp if gp
begin
default_provider
rescue Errors::NoDefaultProvider
# if a provider cannot be determined just return nil
nil
end
end
# Load any configuration provided by guests defined within
# the Vagrantfile to pull plugin information they may have
# defined.
def find_configured_plugins
plugins = []
provider = guess_provider
vagrantfile.machine_names.each do |mname|
ldp = @local_data_path.join("machines/#{mname}/#{provider}") if @local_data_path
plugins << vagrantfile.machine_config(mname, guess_provider, boxes, ldp)[:config]
end
result = plugins.reverse.inject(Vagrant::Util::HashWithIndifferentAccess.new) do |memo, val|
Vagrant::Util::DeepMerge.deep_merge(memo, val.vagrant.plugins)
end
Vagrant::Util::DeepMerge.deep_merge(result, vagrantfile.config.vagrant.plugins)
end
# Check for any local plugins defined within the Vagrantfile. If
# found, validate they are available. If they are not available,
# request to install them, or raise an exception
@ -939,7 +980,7 @@ module Vagrant
# Check if defined plugins are installed
installed = Plugin::Manager.instance.installed_plugins
needs_install = []
config_plugins = vagrantfile.config.vagrant.plugins
config_plugins = find_configured_plugins
config_plugins.each do |name, info|
if !installed[name]
needs_install << name

View File

@ -165,7 +165,7 @@ describe Vagrant::Environment do
collection = double("collection")
expect(Vagrant::BoxCollection).to receive(:new).with(
env.homedir.join("boxes"), anything).and_return(collection)
env.homedir.join("boxes"), anything).twice.and_return(collection)
expect(collection).to receive(:upgrade_v1_1_v1_5).once
subject
end
@ -761,6 +761,7 @@ VF
before do
m = Vagrant.plugin("2").manager
allow(m).to receive(:providers).and_return(plugin_providers)
allow_any_instance_of(described_class).to receive(:process_configured_plugins)
end
it "is the highest matching usable provider" do
@ -1431,6 +1432,108 @@ VF
end
end
describe "guess_provider" do
before { allow_any_instance_of(described_class).to receive(:process_configured_plugins) }
it "should return the default provider by default" do
expect(subject).to receive(:default_provider).and_return("default_provider")
expect(subject.send(:guess_provider)).to eq("default_provider")
end
context "when provider is defined via command line argument" do
before { stub_const("ARGV", argv) }
context "when provider is given as single argument" do
let(:argv) { ["--provider=single_arg"] }
it "should return the provider name" do
expect(subject.send(:guess_provider)).to eq("single_arg")
end
end
context "when provider is given as two arguments" do
let(:argv) { ["--provider", "double_arg"] }
it "should return the provider name" do
expect(subject.send(:guess_provider)).to eq("double_arg")
end
end
end
context "when no default provider is available" do
before {
expect(subject).to receive(:default_provider).
and_raise(Vagrant::Errors::NoDefaultProvider) }
it "should return a nil value" do
expect(subject.send(:guess_provider)).to be_nil
end
end
end
describe "#find_configured_plugins" do
before do
allow_any_instance_of(described_class).to receive(:guess_provider).and_return(:dummy)
allow_any_instance_of(described_class).to receive(:process_configured_plugins)
end
it "should find no plugins when no plugins are configured" do
expect(subject.send(:find_configured_plugins)).to be_empty
end
context "when plugins are defined in the Vagrantfile" do
before do
env.vagrantfile <<-VF
Vagrant.configure("2") do |config|
config.vagrant.plugins = "vagrant-plugin"
end
VF
end
it "should return the vagrant-plugin" do
expect(subject.send(:find_configured_plugins).keys).to include("vagrant-plugin")
end
end
context "when plugins are defined in the Vagrantfile of a box" do
before do
env.box3("foo", "1.0", :dummy, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
config.vagrant.plugins = "vagrant-plugin"
end
VF
env.vagrantfile <<-VF
Vagrant.configure("2") do |config|
config.vm.box = "foo"
end
VF
end
it "should return the vagrant-plugin" do
expect(subject.send(:find_configured_plugins).keys).to include("vagrant-plugin")
end
end
context "when the box does not match the provider" do
before do
env.box3("foo", "1.0", :other, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
config.vagrant.plugins = "vagrant-plugin"
end
VF
env.vagrantfile <<-VF
Vagrant.configure("2") do |config|
config.vm.box = "foo"
end
VF
end
it "should not return the vagrant-plugin" do
expect(subject.send(:find_configured_plugins).keys).not_to include("vagrant-plugin")
end
end
end
describe "#process_configured_plugins" do
let(:env) do
isolated_environment.tap do |e|

View File

@ -39,7 +39,7 @@ describe Vagrant::Util::SSH do
dsa_authentication: true
}}
let(:ssh_path) { "/usr/bin/ssh" }
let(:ssh_path) { /.*ssh/ }
it "searches original PATH for executable" do
expect(Vagrant::Util::Which).to receive(:which).with("ssh", original_path: true).and_return("valid-return")
@ -97,8 +97,6 @@ describe Vagrant::Util::SSH do
dsa_authentication: true
}}
let(:ssh_path) { "/usr/bin/ssh" }
it "uses the IdentityFile argument and escapes the '%' character" do
allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil)
described_class.exec(ssh_info)
@ -247,7 +245,7 @@ describe Vagrant::Util::SSH do
it "enables ssh config loading" do
allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil)
expect(Vagrant::Util::SafeExec).to receive(:exec) do |exe_path, *args|
expect(exe_path).to eq(ssh_path)
expect(exe_path).to match(ssh_path)
config_options = ["-F", "/path/to/config"]
expect(args & config_options).to eq(config_options)
end