Prior to this commit, if a user set the env var VAGRANT_HOME to be the same directory where the project home is, Vagrant would load that file twice and merge its config. This caused various provisioner and other provider blocks to unexpectedly run twice. This commit updates the config loader to look and see if the `:root` and `:home` procs are equal, and if so, removes the `:home` object so that it isn't loaded and duplicated. This commit however does not prevent duplicate loading if an identical Vagrantfile exists in the home and project dir if those locations are different.
236 lines
6.3 KiB
Ruby
236 lines
6.3 KiB
Ruby
require File.expand_path("../../../base", __FILE__)
|
|
|
|
require "vagrant/registry"
|
|
|
|
describe Vagrant::Config::Loader do
|
|
include_context "unit"
|
|
|
|
# This is the current version of configuration for the tests.
|
|
let(:current_version) { version_order.last }
|
|
|
|
# This is just a dummy implementation of a configuraiton loader which
|
|
# simply acts on hashes.
|
|
let(:test_loader) do
|
|
Class.new(Vagrant::Config::VersionBase) do
|
|
def self.init
|
|
{}
|
|
end
|
|
|
|
def self.load(proc)
|
|
init.tap do |obj|
|
|
proc.call(obj)
|
|
end
|
|
end
|
|
|
|
def self.merge(old, new)
|
|
old.merge(new) {|key, oldval, newval| oldval.concat(newval)}
|
|
end
|
|
end
|
|
end
|
|
|
|
let(:versions) do
|
|
Vagrant::Registry.new.tap do |r|
|
|
r.register("1") { test_loader }
|
|
end
|
|
end
|
|
|
|
let(:version_order) { ["1"] }
|
|
|
|
let(:instance) { described_class.new(versions, version_order) }
|
|
|
|
describe "#set" do
|
|
context "with an object that cannot be inspected" do
|
|
|
|
# This represents the euro symbol in UTF-16LE. pack("c*") returns an ASCII
|
|
# string and so we have to force the encoding
|
|
UTF_16LE_STRING_THAT_CANNOT_BE_DOWNCAST_TO_ASCII = [0x20, 0xAC].pack("c*").force_encoding("UTF-16LE")
|
|
|
|
|
|
let(:klass_with_bad_inspect_string) do
|
|
Class.new do
|
|
def inspect
|
|
UTF_16LE_STRING_THAT_CANNOT_BE_DOWNCAST_TO_ASCII
|
|
end
|
|
end
|
|
end
|
|
|
|
let(:test_source) {
|
|
Class.new do
|
|
def initialize(collaborator)
|
|
@foo = collaborator.new
|
|
end
|
|
end.new(klass_with_bad_inspect_string)
|
|
}
|
|
|
|
it "does not raise the ascii encoding exception" do
|
|
expect {
|
|
instance.set(:arbitrary, test_source)
|
|
}.to raise_error(ArgumentError, /Unknown configuration source/)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "basic loading" do
|
|
it "should ignore non-existent load order keys" do
|
|
instance.load([:foo])
|
|
end
|
|
|
|
it "should load and return the configuration" do
|
|
proc = Proc.new do |config|
|
|
config[:foo] = "yep"
|
|
end
|
|
|
|
instance.set(:proc, [[current_version, proc]])
|
|
config, warnings, errors = instance.load([:proc])
|
|
|
|
expect(config[:foo]).to eq("yep")
|
|
expect(warnings).to eq([])
|
|
expect(errors).to eq([])
|
|
end
|
|
end
|
|
|
|
describe "finalization" do
|
|
it "should finalize the configuration" do
|
|
# Create the finalize method on our loader
|
|
def test_loader.finalize(obj)
|
|
obj[:finalized] = true
|
|
obj
|
|
end
|
|
|
|
# Basic configuration proc
|
|
proc = lambda do |config|
|
|
config[:foo] = "yep"
|
|
end
|
|
|
|
# Run the actual configuration and assert that we get the proper result
|
|
instance.set(:proc, [[current_version, proc]])
|
|
config, _ = instance.load([:proc])
|
|
expect(config[:foo]).to eq("yep")
|
|
expect(config[:finalized]).to eq(true)
|
|
end
|
|
end
|
|
|
|
describe "upgrading" do
|
|
it "should do an upgrade to the latest version" do
|
|
test_loader_v2 = Class.new(test_loader) do
|
|
def self.upgrade(old)
|
|
new = old.dup
|
|
new[:v2] = true
|
|
|
|
[new, [], []]
|
|
end
|
|
end
|
|
|
|
versions.register("2") { test_loader_v2 }
|
|
version_order << "2"
|
|
|
|
# Load a version 1 proc, and verify it is upgraded to version 2
|
|
proc = lambda { |config| config[:foo] = "yep" }
|
|
instance.set(:proc, [["1", proc]])
|
|
config, _ = instance.load([:proc])
|
|
expect(config[:foo]).to eq("yep")
|
|
expect(config[:v2]).to eq(true)
|
|
end
|
|
|
|
it "should keep track of warnings and errors" do
|
|
test_loader_v2 = Class.new(test_loader) do
|
|
def self.upgrade(old)
|
|
new = old.dup
|
|
new[:v2] = true
|
|
|
|
[new, ["foo!"], ["bar!"]]
|
|
end
|
|
end
|
|
|
|
versions.register("2") { test_loader_v2 }
|
|
version_order << "2"
|
|
|
|
# Load a version 1 proc, and verify it is upgraded to version 2
|
|
proc = lambda { |config| config[:foo] = "yep" }
|
|
instance.set(:proc, [["1", proc]])
|
|
config, warnings, errors = instance.load([:proc])
|
|
expect(config[:foo]).to eq("yep")
|
|
expect(config[:v2]).to eq(true)
|
|
expect(warnings).to eq(["foo!"])
|
|
expect(errors).to eq(["bar!"])
|
|
end
|
|
end
|
|
|
|
describe "loading edge cases" do
|
|
it "should only run the same proc once" do
|
|
count = 0
|
|
proc = Proc.new do |config|
|
|
config[:foo] = "yep"
|
|
count += 1
|
|
end
|
|
|
|
instance.set(:proc, [[current_version, proc]])
|
|
|
|
5.times do
|
|
result, _ = instance.load([:proc])
|
|
|
|
# Verify the config result
|
|
expect(result[:foo]).to eq("yep")
|
|
|
|
# Verify the count is only one
|
|
expect(count).to eq(1)
|
|
end
|
|
end
|
|
|
|
it "should discard duplicate configs if :home and :root are the same" do
|
|
proc = Proc.new do |config|
|
|
config[:foo] = ["yep"]
|
|
end
|
|
|
|
order = [:root, :home]
|
|
|
|
instance.set(:root, [[current_version, proc]])
|
|
instance.set(:home, [[current_version, proc]])
|
|
|
|
result, warnings, errors = instance.load(order)
|
|
|
|
# Verify the config result
|
|
expect(result[:foo]).to eq(["yep"])
|
|
expect(result[:foo].size).to eq(1)
|
|
expect(warnings).to eq([])
|
|
expect(errors).to eq([])
|
|
end
|
|
|
|
it "should only load configuration files once" do
|
|
$_config_data = 0
|
|
|
|
# We test both setting a file multiple times as well as multiple
|
|
# loads, since both should not cache the data.
|
|
file = temporary_file("$_config_data += 1")
|
|
5.times { instance.set(:file, file) }
|
|
5.times { instance.load([:file]) }
|
|
|
|
expect($_config_data).to eq(1)
|
|
end
|
|
|
|
it "should not clear the cache if setting to the same value multiple times" do
|
|
$_config_data = 0
|
|
|
|
file = temporary_file("$_config_data += 1")
|
|
|
|
instance.set(:proc, file)
|
|
5.times { instance.load([:proc]) }
|
|
|
|
instance.set(:proc, file)
|
|
5.times { instance.load([:proc]) }
|
|
|
|
expect($_config_data).to eq(1)
|
|
end
|
|
|
|
it "should raise proper error if there is a syntax error in a Vagrantfile" do
|
|
expect { instance.set(:file, temporary_file("Vagrant:^Config")) }.
|
|
to raise_exception(Vagrant::Errors::VagrantfileSyntaxError)
|
|
end
|
|
|
|
it "should raise a proper error if there is a problem with the Vagrantfile" do
|
|
expect { instance.set(:file, temporary_file("foo")) }.
|
|
to raise_exception(Vagrant::Errors::VagrantfileLoadError)
|
|
end
|
|
end
|
|
end
|