Load the configuration per machine, so that provider boxes work

Boxes are provider-specific, and we don't know the provider until
Environment#machine is called, so we need to build up the machine
configuration during this time.
This commit is contained in:
Mitchell Hashimoto 2012-11-14 16:07:46 -08:00
parent 0180ed849d
commit f2b91d26fd
2 changed files with 141 additions and 206 deletions

View File

@ -16,6 +16,12 @@ module Vagrant
DEFAULT_HOME = "~/.vagrant.d" DEFAULT_HOME = "~/.vagrant.d"
DEFAULT_RC = "~/.vagrantrc" DEFAULT_RC = "~/.vagrantrc"
# This is the global config, comprised of loading configuration from
# the default, home, and root Vagrantfiles. This configuration is only
# really useful for reading the list of virtual machines, since each
# individual VM can override _most_ settings.
attr_reader :config_global
# The `cwd` that this environment represents # The `cwd` that this environment represents
attr_reader :cwd attr_reader :cwd
@ -99,6 +105,9 @@ module Vagrant
# Load the plugins # Load the plugins
load_plugins load_plugins
# Initialize the configuration. This will load our global configuration.
initialize_config
end end
# Return a human-friendly string for pretty printed or inspected # Return a human-friendly string for pretty printed or inspected
@ -119,7 +128,7 @@ module Vagrant
# @return [Pathname] # @return [Pathname]
def dotfile_path def dotfile_path
return nil if !root_path return nil if !root_path
root_path.join(config.global.vagrant.dotfile_name) root_path.join(config_global.vagrant.dotfile_name)
end end
# Returns the collection of boxes for the environment. # Returns the collection of boxes for the environment.
@ -144,8 +153,8 @@ module Vagrant
@machines ||= {} @machines ||= {}
return @machines[cache_key] if @machines.has_key?(cache_key) return @machines[cache_key] if @machines.has_key?(cache_key)
vm_config = config.for_vm(name) sub_vm = config_global.vm.defined_vms[name]
if !vm_config if !sub_vm
raise Errors::MachineNotFound, :name => name, :provider => provider raise Errors::MachineNotFound, :name => name, :provider => provider
end end
@ -154,11 +163,36 @@ module Vagrant
raise Errors::ProviderNotFound, :machine => name, :provider => provider raise Errors::ProviderNotFound, :machine => name, :provider => provider
end end
box = boxes.find(vm_config.vm.box, provider) # Build the machine configuration. This requires two passes: The first pass
# loads in the machine sub-configuration. Since this can potentially
# define a new box to base the machine from, we then make a second pass
# with the box Vagrantfile (if it has one).
vm_config_key = "vm_#{name}".to_sym
@config_loader.set(vm_config_key, sub_vm.config_procs)
config = @config_loader.load([:default, :home, :root, vm_config_key])
box = nil
begin
box = boxes.find(config.vm.box, provider)
rescue Errors::BoxUpgradeRequired
# Upgrade the box if we must
@logger.info("Upgrading box during config load: #{config.vm.box}")
boxes.upgrade(config.vm.box)
retry
end
box_vagrantfile = find_vagrantfile(box.directory)
if box_vagrantfile
# The box has a custom Vagrantfile, so we load that into the config
# as well.
box_config_key = "box_#{box.name}_#{box.provider}".to_sym
@config_loader.set(box_config_key, box_vagrantfile)
config = @config_loader.load([:default, box_config_key, :home, :root, vm_config_key])
end
# Create the machine and cache it for future calls. This will also # Create the machine and cache it for future calls. This will also
# return the machine from this method. # return the machine from this method.
@machines[cache_key] = Machine.new(name, provider_cls, vm_config, box, self) @machines[cache_key] = Machine.new(name, provider_cls, config, box, self)
end end
# This returns a list of the configured machines for this environment. # This returns a list of the configured machines for this environment.
@ -167,7 +201,7 @@ module Vagrant
# #
# @return [Array<Symbol>] Configured machine names. # @return [Array<Symbol>] Configured machine names.
def machine_names def machine_names
config.vms config_global.vm.defined_vm_keys.dup
end end
# Returns the primary VM associated with this environment. This # Returns the primary VM associated with this environment. This
@ -180,7 +214,7 @@ module Vagrant
return machine(machine_names[0], :virtualbox) return machine(machine_names[0], :virtualbox)
end end
config.global.vm.defined_vms.each do |name, subvm| config_global.vm.defined_vms.each do |name, subvm|
return machine(name, :virtualbox) if subvm.options[:primary] return machine(name, :virtualbox) if subvm.options[:primary]
end end
@ -206,7 +240,7 @@ module Vagrant
# matters here, so please don't touch. Specifically: The symbol # matters here, so please don't touch. Specifically: The symbol
# check is done after the detect check because the symbol check # check is done after the detect check because the symbol check
# will return nil, and we don't want to trigger a detect load. # will return nil, and we don't want to trigger a detect load.
host_klass = config.global.vagrant.host host_klass = config_global.vagrant.host
if host_klass.nil? || host_klass == :detect if host_klass.nil? || host_klass == :detect
hosts = Vagrant.plugin("2").manager.hosts hosts = Vagrant.plugin("2").manager.hosts
@ -228,7 +262,7 @@ module Vagrant
{ {
:action_runner => action_runner, :action_runner => action_runner,
:box_collection => boxes, :box_collection => boxes,
:global_config => config.global, :global_config => config_global,
:host => host, :host => host,
:root_path => root_path, :root_path => root_path,
:tmp_path => tmp_path, :tmp_path => tmp_path,
@ -314,138 +348,33 @@ module Vagrant
end end
end end
#---------------------------------------------------------------
# Config Methods
#---------------------------------------------------------------
# The configuration object represented by this environment. This
# will trigger the environment to load if it hasn't loaded yet (see
# {#load!}).
#
# @return [Config::Container]
def config
load! if !loaded?
@config
end
#--------------------------------------------------------------- #---------------------------------------------------------------
# Load Methods # Load Methods
#--------------------------------------------------------------- #---------------------------------------------------------------
# Returns a boolean representing if the environment has been # This initializes the config loader for this environment. The config
# loaded or not. # loader is cached so that prior Vagrantfiles aren't loaded multiple
# # times.
# @return [Bool] def initialize_config
def loaded? home_vagrantfile = nil
!!@loaded root_vagrantfile = nil
end home_vagrantfile = find_vagrantfile(home_path) if home_path
root_vagrantfile = find_vagrantfile(root_path) if root_path
# Loads this entire environment, setting up the instance variables # Create the configuration loader and set the sources that are global.
# such as `vm`, `config`, etc. on this environment. The order this # We use this to load the configuration, and the list of machines we are
# method calls its other methods is very particular. # managing. Then, the actual individual configuration is loaded for
def load! # each {#machine} call.
if !loaded? @config_loader = Config::Loader.new(Config::VERSIONS, Config::VERSIONS_ORDER)
@loaded = true @config_loader.set(:default, File.expand_path("config/default.rb", Vagrant.source_root))
@logger.info("Loading configuration...") @config_loader.set(:home, home_vagrantfile) if home_vagrantfile
load_config! @config_loader.set(:root, root_vagrantfile) if root_vagrantfile
end
self # Make the initial call to get the "global" config. This is mostly
end # only useful to get the list of machines that we are managing.
@config_global = @config_loader.load([:default, :home, :root])
# Reloads the configuration of this environment. # Old order: default, box, home, root, vm
def reload!
# Reload the configuration
load_config!
# Clear the VMs because this can now be diferent due to configuration
@vms = nil
end
# Loads this environment's configuration and stores it in the {#config}
# variable. The configuration loaded by this method is specified to
# this environment, meaning that it will use the given root directory
# to load the Vagrantfile into that context.
def load_config!
# Initialize the config loader
config_loader = Config::Loader.new(Config::VERSIONS, Config::VERSIONS_ORDER)
config_loader.load_order = [:default, :box, :home, :root, :vm]
inner_load = lambda do |*args|
# This is for Ruby 1.8.7 compatibility. Ruby 1.8.7 doesn't allow
# default arguments for lambdas, so we get around by doing a *args
# and setting the args here.
subvm = args[0]
box = args[1]
# Default Vagrantfile first. This is the Vagrantfile that ships
# with Vagrant.
config_loader.set(:default, File.expand_path("config/default.rb", Vagrant.source_root))
if box
# We load the box Vagrantfile
box_vagrantfile = find_vagrantfile(box.directory)
config_loader.set(:box, box_vagrantfile) if box_vagrantfile
end
if home_path
# Load the home Vagrantfile
home_vagrantfile = find_vagrantfile(home_path)
config_loader.set(:home, home_vagrantfile) if home_vagrantfile
end
if root_path
# Load the Vagrantfile in this directory
root_vagrantfile = find_vagrantfile(root_path)
config_loader.set(:root, root_vagrantfile) if root_vagrantfile
end
if subvm
# We have subvm configuration, so set that up as well.
config_loader.set(:vm, subvm.config_procs)
end
# Execute the configuration stack and store the result as the final
# value in the config ivar.
config_loader.load
end
# For the global configuration, we only need to load the configuration
# in a single pass, since nothing is conditional on the configuration.
global = inner_load.call
# For each virtual machine represented by this environment, we have
# to load the configuration in two-passes. We do this because the
# first pass is used to determine the box for the VM. The second pass
# is used to also load the box Vagrantfile.
defined_vm_keys = global.vm.defined_vm_keys.dup
defined_vms = global.vm.defined_vms.dup
vm_configs = defined_vm_keys.map do |vm_name|
@logger.debug("Loading configuration for VM: #{vm_name}")
subvm = defined_vms[vm_name]
# First pass, first run.
config = inner_load[subvm]
# Second pass, with the box
box = nil
begin
box = boxes.find(config.vm.box, :virtualbox) if config.vm.box
rescue Errors::BoxUpgradeRequired
# Upgrade the box if we must.
@logger.info("Upgrading box during config load: #{config.vm.box}")
boxes.upgrade(config.vm.box)
retry
end
inner_load[subvm, box]
end
# Finally, we have our configuration. Set it and forget it.
@config = Config::Container.new(global, vm_configs)
end end
# This sets the `@home_path` variable properly. # This sets the `@home_path` variable properly.

View File

@ -117,6 +117,8 @@ Vagrant.configure("2") do |config|
config.vm.define :bar, :primary => true config.vm.define :bar, :primary => true
end end
VF VF
env.box2("base", :virtualbox)
end end
env = environment.create_vagrant_env env = environment.create_vagrant_env
@ -135,7 +137,7 @@ VF
end end
env = environment.create_vagrant_env env = environment.create_vagrant_env
env.config.global.vagrant.dotfile_name.should == "foo" env.config_global.vagrant.dotfile_name.should == "foo"
end end
it "should load from a custom Vagrantfile" do it "should load from a custom Vagrantfile" do
@ -148,79 +150,9 @@ VF
end end
env = environment.create_vagrant_env(:vagrantfile_name => "non_standard_name") env = environment.create_vagrant_env(:vagrantfile_name => "non_standard_name")
env.config.global.vagrant.dotfile_name.should == "custom" env.config_global.vagrant.dotfile_name.should == "custom"
end end
it "should load VM configuration" do
environment = isolated_environment do |env|
env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
config.vagrant.dotfile_name = "foo"
end
VF
end
env = environment.create_vagrant_env
env.config.for_vm(:default).vm.name.should == :default
end
it "should load VM configuration with multiple VMs" do
environment = isolated_environment do |env|
env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
config.vm.define :foo do |vm|
vm.ssh.port = 100
end
config.vm.define :bar do |vm|
vm.ssh.port = 200
end
end
VF
end
env = environment.create_vagrant_env
env.config.for_vm(:foo).ssh.port.should == 100
env.config.for_vm(:bar).ssh.port.should == 200
end
it "should load a V1 vagrant box" do
environment = isolated_environment do |env|
env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
config.vm.box = "base"
end
VF
env.box("base", <<-VF)
Vagrant.configure("2") do |config|
config.ssh.port = 100
end
VF
end
env = environment.create_vagrant_env
env.config.for_vm(:default).ssh.port.should == 100
end
it "should load box configuration" do
environment = isolated_environment do |env|
env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
config.vm.box = "base"
end
VF
env.box2("base", :virtualbox, :vagrantfile => <<-VF)
Vagrant.configure("2") do |config|
config.ssh.port = 100
end
VF
end
env = environment.create_vagrant_env
env.config.for_vm(:default).ssh.port.should == 100
end
end end
describe "ui" do describe "ui" do
@ -261,6 +193,8 @@ Vagrant.configure("2") do |config|
config.vm.define "foo" config.vm.define "foo"
end end
VF VF
e.box2("base", :foo)
end end
# Verify that we can get the machine # Verify that we can get the machine
@ -285,6 +219,9 @@ Vagrant.configure("2") do |config|
config.vm.define "vm2" config.vm.define "vm2"
end end
VF VF
e.box2("base", :foo)
e.box2("base", :bar)
end end
env = isolated_env.create_vagrant_env env = isolated_env.create_vagrant_env
@ -298,6 +235,75 @@ VF
vm2_foo.should eql(env.machine(:vm2, :foo)) vm2_foo.should eql(env.machine(:vm2, :foo))
end end
it "should load the machine configuration" do
register_provider("foo")
environment = isolated_environment do |env|
env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
config.ssh.port = 1
config.vagrant.dotfile_name = "foo"
config.vm.box = "base"
config.vm.define "vm1" do |inner|
inner.ssh.port = 100
end
end
VF
env.box2("base", :foo)
end
env = environment.create_vagrant_env
machine = env.machine(:vm1, :foo)
machine.config.ssh.port.should == 100
machine.config.vagrant.dotfile_name.should == "foo"
end
it "should load a machine configured with a V1 box" do
register_provider("foo")
environment = isolated_environment do |env|
env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
config.vm.box = "base"
end
VF
env.box("base", <<-VF)
Vagrant.configure("2") do |config|
config.ssh.port = 100
end
VF
end
env = environment.create_vagrant_env
machine = env.machine(:default, :virtualbox)
machine.config.ssh.port.should == 100
end
it "should load the box configuration for a V2 box" do
register_provider("foo")
environment = isolated_environment do |env|
env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
config.vm.box = "base"
end
VF
env.box2("base", :foo, :vagrantfile => <<-VF)
Vagrant.configure("2") do |config|
config.ssh.port = 100
end
VF
end
env = environment.create_vagrant_env
machine = env.machine(:default, :foo)
machine.config.ssh.port.should == 100
end
it "should raise an error if the VM is not found" do it "should raise an error if the VM is not found" do
expect { instance.machine("i-definitely-dont-exist", :virtualbox) }. expect { instance.machine("i-definitely-dont-exist", :virtualbox) }.
to raise_error(Vagrant::Errors::MachineNotFound) to raise_error(Vagrant::Errors::MachineNotFound)