Merge pull request #2947 from mitchellh/f-environment-refactor

Core refactor: "Vagrantfile" abstraction
This commit is contained in:
Mitchell Hashimoto 2014-02-07 09:27:32 -08:00
commit 2dac667544
11 changed files with 753 additions and 301 deletions

View File

@ -23,7 +23,8 @@ module Vagrant
@app = app
env["package.files"] ||= {}
env["package.output"] ||= env[:global_config].package.name
env["package.output"] ||= env[:machine].package.name
env["package.output"] ||= "package.box"
end
def call(env)

View File

@ -8,11 +8,13 @@ require 'log4r'
require 'vagrant/util/file_mode'
require 'vagrant/util/platform'
require "vagrant/vagrantfile"
module Vagrant
# Represents a single Vagrant environment. A "Vagrant environment" is
# defined as basically a folder with a "Vagrantfile." This class allows
# access to the VMs, CLI, etc. all in the scope of this environment.
# A "Vagrant environment" represents a configuration of how Vagrant
# should behave: data directories, working directory, UI output,
# etc. In day-to-day usage, every `vagrant` invocation typically
# leads to a single Vagrant environment.
class Environment
# This is the current version that this version of Vagrant is
# compatible with in the home directory.
@ -150,9 +152,24 @@ module Vagrant
"#<#{self.class}: #{@cwd}>"
end
#---------------------------------------------------------------
# Helpers
#---------------------------------------------------------------
# Action runner for executing actions in the context of this environment.
#
# @return [Action::Runner]
def action_runner
@action_runner ||= Action::Runner.new do
{
:action_runner => action_runner,
:box_collection => boxes,
:hook => method(:hook),
:host => host,
:gems_path => gems_path,
:home_path => home_path,
:root_path => root_path,
:tmp_path => tmp_path,
:ui => @ui
}
end
end
# Returns a list of machines that this environment is currently
# managing that physically have been created.
@ -218,6 +235,15 @@ module Vagrant
end
end
# Makes a call to the CLI with the given arguments as if they
# came from the real command line (sometimes they do!). An example:
#
# env.cli("package", "--vagrantfile", "Vagrantfile")
#
def cli(*args)
CLI.new(args.flatten, self).execute
end
# This returns the provider name for the default provider for this
# environment. The provider returned is currently hardcoded to "virtualbox"
# but one day should be a detected valid, best-case provider for this
@ -235,39 +261,25 @@ module Vagrant
@_boxes ||= BoxCollection.new(boxes_path, temp_dir_root: tmp_path)
end
# 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.
# Returns the {Config::Loader} that can be used to load Vagrantflies
# given the settings of this environment.
#
# This is lazy-loaded upon first use.
#
# @return [Object]
def config_global
return @config_global if @config_global
@logger.info("Initializing config...")
# @return [Config::Loader]
def config_loader
return @config_loader if @config_loader
home_vagrantfile = nil
root_vagrantfile = nil
home_vagrantfile = find_vagrantfile(home_path) if home_path
root_vagrantfile = find_vagrantfile(root_path, @vagrantfile_name) if root_path
if root_path
root_vagrantfile = find_vagrantfile(root_path, @vagrantfile_name)
end
# Create the configuration loader and set the sources that are global.
# We use this to load the configuration, and the list of machines we are
# managing. Then, the actual individual configuration is loaded for
# each {#machine} call.
@config_loader = Config::Loader.new(Config::VERSIONS, Config::VERSIONS_ORDER)
@config_loader = Config::Loader.new(
Config::VERSIONS, Config::VERSIONS_ORDER)
@config_loader.set(:home, home_vagrantfile) if home_vagrantfile
@config_loader.set(:root, root_vagrantfile) if root_vagrantfile
# Make the initial call to get the "global" config. This is mostly
# only useful to get the list of machines that we are managing.
# Because of this, we ignore any warnings or errors.
@config_global, _ = @config_loader.load([:home, :root])
# Return the config
@config_global
@config_loader
end
# This defines a hook point where plugin action hooks that are registered
@ -285,201 +297,6 @@ module Vagrant
opts.delete(:runner).run(opts.delete(:callable), opts)
end
# This returns a machine with the proper provider for this environment.
# The machine named by `name` must be in this environment.
#
# @param [Symbol] name Name of the machine (as configured in the
# Vagrantfile).
# @param [Symbol] provider The provider that this machine should be
# backed by.
# @param [Boolean] refresh If true, then if there is a cached version
# it is reloaded.
# @return [Machine]
def machine(name, provider, refresh=false)
@logger.info("Getting machine: #{name} (#{provider})")
# Compose the cache key of the name and provider, and return from
# the cache if we have that.
cache_key = [name, provider]
@machines ||= {}
if refresh
@logger.info("Refreshing machine (busting cache): #{name} (#{provider})")
@machines.delete(cache_key)
end
if @machines.has_key?(cache_key)
@logger.info("Returning cached machine: #{name} (#{provider})")
return @machines[cache_key]
end
@logger.info("Uncached load of machine.")
sub_vm = config_global.vm.defined_vms[name]
if !sub_vm
raise Errors::MachineNotFound, :name => name, :provider => provider
end
provider_plugin = Vagrant.plugin("2").manager.providers[provider]
if !provider_plugin
raise Errors::ProviderNotFound, :machine => name, :provider => provider
end
# Extra the provider class and options from the plugin data
provider_cls = provider_plugin[0]
provider_options = provider_plugin[1]
# 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_warnings, config_errors = \
@config_loader.load([:home, :root, vm_config_key])
# Determine the possible box formats for any boxes and find the box
box_formats = provider_options[:box_format] || provider
box = nil
# Set this variable in order to keep track of if the box changes
# too many times.
original_box = config.vm.box
box_changed = false
load_box_and_overrides = lambda do
box = nil
if config.vm.box
box = boxes.find(
config.vm.box, box_formats, config.vm.box_version)
end
# If a box was found, then we attempt to load the Vagrantfile for
# that box. We don't require a box since we allow providers to download
# boxes and so on.
if box
box_vagrantfile = find_vagrantfile(box.directory)
if box_vagrantfile
# The box has a custom Vagrantfile, so we load that into the config
# as well.
@logger.info("Box exists with Vagrantfile. Reloading machine config.")
box_config_key = "box_#{box.name}_#{box.provider}".to_sym
@config_loader.set(box_config_key, box_vagrantfile)
config, config_warnings, config_errors = \
@config_loader.load([box_config_key, :home, :root, vm_config_key])
end
end
# If there are provider overrides for the machine, then we run
# those as well.
provider_overrides = config.vm.get_provider_overrides(provider)
if provider_overrides.length > 0
@logger.info("Applying #{provider_overrides.length} provider overrides. Reloading config.")
provider_override_key = "vm_#{name}_#{config.vm.box}_#{provider}".to_sym
@config_loader.set(provider_override_key, provider_overrides)
config, config_warnings, config_errors = \
@config_loader.load([box_config_key, :home, :root, vm_config_key, provider_override_key])
end
if config.vm.box && original_box != config.vm.box
if box_changed
# We already changed boxes once, so report an error that a
# box is attempting to change boxes again.
raise Errors::BoxConfigChangingBox
end
# The box changed, probably due to the provider override. Let's
# run the configuration one more time with the new box.
@logger.info("Box changed to: #{config.vm.box}. Reloading configurations.")
original_box = config.vm.box
box_changed = true
# Recurse so that we reload all the configurations
load_box_and_overrides.call
end
end
# Load the box and overrides configuration
load_box_and_overrides.call
# Get the provider configuration from the final loaded configuration
provider_config = config.vm.get_provider_config(provider)
# Determine the machine data directory and pass it to the machine.
# XXX: Permissions error here.
machine_data_path = @local_data_path.join("machines/#{name}/#{provider}")
FileUtils.mkdir_p(machine_data_path)
# If there were warnings or errors we want to output them
if !config_warnings.empty? || !config_errors.empty?
# The color of the output depends on whether we have warnings
# or errors...
level = config_errors.empty? ? :warn : :error
output = Util::TemplateRenderer.render(
"config/messages",
:warnings => config_warnings,
:errors => config_errors).chomp
@ui.send(level, I18n.t("vagrant.general.config_upgrade_messages",
name: name,
:output => output))
# If we had errors, then we bail
raise Errors::ConfigUpgradeErrors if !config_errors.empty?
end
# Create the machine and cache it for future calls. This will also
# return the machine from this method.
@machines[cache_key] = Machine.new(name, provider, provider_cls, provider_config,
provider_options, config, machine_data_path, box, self)
end
# This returns a list of the configured machines for this environment.
# Each of the names returned by this method is valid to be used with
# the {#machine} method.
#
# @return [Array<Symbol>] Configured machine names.
def machine_names
config_global.vm.defined_vm_keys.dup
end
# This returns the name of the machine that is the "primary." In the
# case of a single-machine environment, this is just the single machine
# name. In the case of a multi-machine environment, then this can
# potentially be nil if no primary machine is specified.
#
# @return [Symbol]
def primary_machine_name
# If it is a single machine environment, then return the name
return machine_names.first if machine_names.length == 1
# If it is a multi-machine environment, then return the primary
config_global.vm.defined_vms.each do |name, subvm|
return name if subvm.options[:primary]
end
# If no primary was specified, nil it is
nil
end
# Unload the environment, running completion hooks. The environment
# should not be used after this (but CAN be, technically). It is
# recommended to always immediately set the variable to `nil` after
# running this so you can't accidentally run any more methods. Example:
#
# env.unload
# env = nil
#
def unload
hook(:environment_unload)
end
# Makes a call to the CLI with the given arguments as if they
# came from the real command line (sometimes they do!). An example:
#
# env.cli("package", "--vagrantfile", "Vagrantfile")
#
def cli(*args)
CLI.new(args.flatten, self).execute
end
# Returns the host object associated with this environment.
#
# @return [Class]
@ -490,7 +307,7 @@ module Vagrant
# 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
host_klass = vagrantfile.config.vagrant.host
host_klass = nil if host_klass == :detect
begin
@ -516,46 +333,6 @@ module Vagrant
end
end
# Action runner for executing actions in the context of this environment.
#
# @return [Action::Runner]
def action_runner
@action_runner ||= Action::Runner.new do
{
:action_runner => action_runner,
:box_collection => boxes,
:global_config => config_global,
:hook => method(:hook),
:host => host,
:gems_path => gems_path,
:home_path => home_path,
:root_path => root_path,
:tmp_path => tmp_path,
:ui => @ui
}
end
end
# The root path is the path where the top-most (loaded last)
# Vagrantfile resides. It can be considered the project root for
# this environment.
#
# @return [String]
def root_path
return @root_path if defined?(@root_path)
root_finder = lambda do |path|
# Note: To remain compatible with Ruby 1.8, we have to use
# a `find` here instead of an `each`.
vf = find_vagrantfile(path, @vagrantfile_name)
return path if vf
return nil if path.root? || !File.exist?(path)
root_finder.call(path.parent)
end
@root_path = root_finder.call(cwd)
end
# This returns the path which Vagrant uses to determine the location
# of the file lock. This is specific to each operating system.
def lock_path
@ -589,6 +366,116 @@ module Vagrant
end
end
# This returns a machine with the proper provider for this environment.
# The machine named by `name` must be in this environment.
#
# @param [Symbol] name Name of the machine (as configured in the
# Vagrantfile).
# @param [Symbol] provider The provider that this machine should be
# backed by.
# @param [Boolean] refresh If true, then if there is a cached version
# it is reloaded.
# @return [Machine]
def machine(name, provider, refresh=false)
@logger.info("Getting machine: #{name} (#{provider})")
# Compose the cache key of the name and provider, and return from
# the cache if we have that.
cache_key = [name, provider]
@machines ||= {}
if refresh
@logger.info("Refreshing machine (busting cache): #{name} (#{provider})")
@machines.delete(cache_key)
end
if @machines.has_key?(cache_key)
@logger.info("Returning cached machine: #{name} (#{provider})")
return @machines[cache_key]
end
@logger.info("Uncached load of machine.")
# Determine the machine data directory and pass it to the machine.
# XXX: Permissions error here.
machine_data_path = @local_data_path.join(
"machines/#{name}/#{provider}")
FileUtils.mkdir_p(machine_data_path)
# Create the machine and cache it for future calls. This will also
# return the machine from this method.
@machines[cache_key] = vagrantfile.machine(
name, provider, boxes, machine_data_path, self)
end
# This returns a list of the configured machines for this environment.
# Each of the names returned by this method is valid to be used with
# the {#machine} method.
#
# @return [Array<Symbol>] Configured machine names.
def machine_names
vagrantfile.machine_names
end
# This returns the name of the machine that is the "primary." In the
# case of a single-machine environment, this is just the single machine
# name. In the case of a multi-machine environment, then this can
# potentially be nil if no primary machine is specified.
#
# @return [Symbol]
def primary_machine_name
vagrantfile.primary_machine_name
end
# The root path is the path where the top-most (loaded last)
# Vagrantfile resides. It can be considered the project root for
# this environment.
#
# @return [String]
def root_path
return @root_path if defined?(@root_path)
root_finder = lambda do |path|
# Note: To remain compatible with Ruby 1.8, we have to use
# a `find` here instead of an `each`.
vf = find_vagrantfile(path, @vagrantfile_name)
return path if vf
return nil if path.root? || !File.exist?(path)
root_finder.call(path.parent)
end
@root_path = root_finder.call(cwd)
end
# Unload the environment, running completion hooks. The environment
# should not be used after this (but CAN be, technically). It is
# recommended to always immediately set the variable to `nil` after
# running this so you can't accidentally run any more methods. Example:
#
# env.unload
# env = nil
#
def unload
hook(:environment_unload)
end
# Represents the default Vagrantfile, or the Vagrantfile that is
# in the working directory or a parent of the working directory
# of this environment.
#
# The existence of this function is primarily a convenience. There
# is nothing stopping you from instantiating your own {Vagrantfile}
# and loading machines in any way you see fit. Typical behavior of
# Vagrant, however, loads this Vagrantfile.
#
# This Vagrantfile is comprised of two major sources: the Vagrantfile
# in the user's home directory as well as the "root" Vagrantfile or
# the Vagrantfile in the working directory (or parent).
#
# @return [Vagrantfile]
def vagrantfile
@vagrantfile ||= Vagrantfile.new(config_loader, [:home, :root])
end
#---------------------------------------------------------------
# Load Methods
#---------------------------------------------------------------

View File

@ -34,7 +34,7 @@ module Vagrant
# Name of the machine. This is assigned by the Vagrantfile.
#
# @return [String]
# @return [Symbol]
attr_reader :name
# The provider backing this machine.
@ -62,6 +62,11 @@ module Vagrant
# @return [UI]
attr_reader :ui
# The Vagrantfile that this machine is attached to.
#
# @return [Vagrantfile]
attr_reader :vagrantfile
# Initialize a new machine.
#
# @param [String] name Name of the virtual machine.
@ -77,7 +82,7 @@ module Vagrant
# @param [Box] box The box that is backing this virtual machine.
# @param [Environment] env The environment that this machine is a
# part of.
def initialize(name, provider_name, provider_cls, provider_config, provider_options, config, data_dir, box, env, base=false)
def initialize(name, provider_name, provider_cls, provider_config, provider_options, config, data_dir, box, env, vagrantfile, base=false)
@logger = Log4r::Logger.new("vagrant::machine")
@logger.info("Initializing machine: #{name}")
@logger.info(" - Provider: #{provider_cls}")
@ -88,6 +93,7 @@ module Vagrant
@config = config
@data_dir = data_dir
@env = env
@vagrantfile = vagrantfile
@guest = Guest.new(
self,
Vagrant.plugin("2").manager.guests,

233
lib/vagrant/vagrantfile.rb Normal file
View File

@ -0,0 +1,233 @@
require "vagrant/util/template_renderer"
module Vagrant
# This class provides a way to load and access the contents
# of a Vagrantfile.
#
# This class doesn't actually load Vagrantfiles, parse them,
# merge them, etc. That is the job of {Config::Loader}. This
# class, on the other hand, has higher-level operations on
# a loaded Vagrantfile such as looking up the defined machines,
# loading the configuration of a specific machine/provider combo,
# etc.
class Vagrantfile
# This is the configuration loaded as-is given the loader and
# keys to #initialize.
attr_reader :config
# Initializes by loading a Vagrantfile.
#
# @param [Config::Loader] loader Configuration loader that should
# already be configured with the proper Vagrantflie locations.
# This usually comes from {Vagrant::Environment}
# @param [Array<Symbol>] keys The Vagrantfiles to load and the
# order to load them in (keys within the loader).
def initialize(loader, keys)
@keys = keys
@loader = loader
@config, _ = loader.load(keys)
end
# Returns a {Machine} for the given name and provider that
# is represented by this Vagrantfile.
#
# @param [Symbol] name Name of the machine.
# @param [Symbol] provider The provider the machine should
# be backed by (required for provider overrides).
# @param [BoxCollection] boxes BoxCollection to look up the
# box Vagrantfile.
# @param [Pathname] data_path Path where local machine data
# can be stored.
# @param [Environment] env The environment running this machine
# @return [Machine]
def machine(name, provider, boxes, data_path, env)
# Load the configuration for the machine
results = machine_config(name, provider, boxes)
box = results[:box]
config = results[:config]
config_errors = results[:config_errors]
config_warnings = results[:config_warnings]
provider_cls = results[:provider_cls]
provider_options = results[:provider_options]
# If there were warnings or errors we want to output them
if !config_warnings.empty? || !config_errors.empty?
# The color of the output depends on whether we have warnings
# or errors...
level = config_errors.empty? ? :warn : :error
output = Util::TemplateRenderer.render(
"config/messages",
:warnings => config_warnings,
:errors => config_errors).chomp
env.ui.send(level, I18n.t("vagrant.general.config_upgrade_messages",
name: name,
output: output))
# If we had errors, then we bail
raise Errors::ConfigUpgradeErrors if !config_errors.empty?
end
# Get the provider configuration from the final loaded configuration
provider_config = config.vm.get_provider_config(provider)
# Create the machine and cache it for future calls. This will also
# return the machine from this method.
return Machine.new(name, provider, provider_cls, provider_config,
provider_options, config, data_path, box, env, self)
end
# Returns the configuration for a single machine.
#
# When loading a box Vagrantfile, it will be prepended to the
# key order specified when initializing this class. Sub-machine
# and provider-specific overrides are appended at the end. The
# actual order is:
#
# - box
# - keys specified for #initialize
# - sub-machine
# - provider
#
# The return value is a hash with the following keys (symbols)
# and values:
#
# - box: the {Box} backing the machine
# - config: the actual configuration
# - config_errors: list of errors, if any
# - config_warnings: list of warnings, if any
# - provider_cls: class of the provider backing the machine
# - provider_options: options for the provider
#
# @param [Symbol] name Name of the machine.
# @param [Symbol] provider The provider the machine should
# be backed by (required for provider overrides).
# @param [BoxCollection] boxes BoxCollection to look up the
# box Vagrantfile.
# @return [Hash<Symbol, Object>] Various configuration parameters for a
# machine. See the main documentation body for more info.
def machine_config(name, provider, boxes)
keys = @keys.dup
sub_machine = @config.vm.defined_vms[name]
if !sub_machine
raise Errors::MachineNotFound,
:name => name, :provider => provider
end
provider_plugin = Vagrant.plugin("2").manager.providers[provider]
if !provider_plugin
raise Errors::ProviderNotFound,
:machine => name, :provider => provider
end
provider_cls = provider_plugin[0]
provider_options = provider_plugin[1]
box_formats = provider_options[:box_format] || provider
# Add the sub-machine configuration to the loader and keys
vm_config_key = "#{object_id}_machine_#{name}"
@loader.set(vm_config_key, sub_machine.config_procs)
keys << vm_config_key
# Load once so that we can get the proper box value
config, config_warnings, config_errors = @loader.load(keys)
# Track the original box so we know if we changed
box = nil
original_box = config.vm.box
# The proc below loads the box and provider overrides. This is
# in a proc because it may have to recurse if the provider override
# changes the box.
load_box_proc = lambda do
local_keys = keys.dup
# Load the box Vagrantfile, if there is one
if config.vm.box
box = boxes.find(config.vm.box, box_formats, config.vm.box_version)
if box
box_vagrantfile = find_vagrantfile(box.directory)
if box_vagrantfile
box_config_key =
"#{boxes.object_id}_#{box.name}_#{box.provider}".to_sym
@loader.set(box_config_key, box_vagrantfile)
local_keys.unshift(box_config_key)
config, config_warnings, config_errors = @loader.load(local_keys)
end
end
end
# Load provider overrides
provider_overrides = config.vm.get_provider_overrides(provider)
if !provider_overrides.empty?
config_key =
"#{object_id}_vm_#{name}_#{config.vm.box}_#{provider}".to_sym
@loader.set(config_key, provider_overrides)
local_keys << config_key
config, config_warnings, config_errors = @loader.load(local_keys)
end
# If the box changed, then we need to reload
if original_box != config.vm.box
# TODO: infinite loop protection?
original_box = config.vm.box
load_box_proc.call
end
end
# Load the box and provider overrides
load_box_proc.call
return {
box: box,
provider_cls: provider_cls,
provider_options: provider_options,
config: config,
config_warnings: config_warnings,
config_errors: config_errors,
}
end
# Returns a list of the machines that are defined within this
# Vagrantfile.
#
# @return [Array<Symbol>]
def machine_names
@config.vm.defined_vm_keys.dup
end
# Returns the name of the machine that is designated as the
# "primary."
#
# In the case of a single-machine environment, this is just the
# single machine name. In the case of a multi-machine environment,
# then this is the machine that is marked as primary, or nil if
# no primary machine was specified.
#
# @return [Symbol]
def primary_machine_name
# If it is a single machine environment, then return the name
return machine_names.first if machine_names.length == 1
# If it is a multi-machine environment, then return the primary
@config.vm.defined_vms.each do |name, subvm|
return name if subvm.options[:primary]
end
# If no primary was specified, nil it is
nil
end
protected
def find_vagrantfile(search_path)
["Vagrantfile", "vagrantfile"].each do |vagrantfile|
current_path = search_path.join(vagrantfile)
return current_path if current_path.file?
end
nil
end
end
end

View File

@ -31,7 +31,7 @@ module VagrantPlugins
raise Vagrant::Errors::BoxNotFound, :name => box_name, :provider => box_provider if !box
# Repackage the box
output_name = @env.config_global.package.name || "package.box"
output_name = @env.vagrantfile.config.package.name || "package.box"
output_path = Pathname.new(File.expand_path(output_name, FileUtils.pwd))
box.repackage(output_path)

View File

@ -59,9 +59,9 @@ module VagrantPlugins
vm = Vagrant::Machine.new(
options[:base],
:virtualbox, provider[0], nil, provider[1],
@env.config_global,
@env.vagrantfile.config,
nil, nil,
@env, true)
@env, @env.vagrantfile, true)
@logger.debug("Packaging base VM: #{vm.name}")
package_vm(vm, options)
end

View File

@ -1,30 +1,25 @@
require_relative "../base"
describe VagrantPlugins::ProviderVirtualBox::Action::PrepareNFSValidIds do
include_context "unit"
include_context "virtualbox"
let(:machine) {
environment = Vagrant::Environment.new
provider = :virtualbox
provider_cls, provider_options = Vagrant.plugin("2").manager.providers[provider]
provider_config = Vagrant.plugin("2").manager.provider_configs[provider]
let(:iso_env) do
# We have to create a Vagrantfile so there is a root path
env = isolated_environment
env.vagrantfile("")
env.create_vagrant_env
end
Vagrant::Machine.new(
'test_machine',
provider,
provider_cls,
provider_config,
provider_options,
environment.config_global,
Pathname('data_dir'),
double('box'),
environment
)
}
let(:machine) do
iso_env.machine(iso_env.machine_names[0], :dummy).tap do |m|
m.provider.stub(driver: driver)
end
end
let(:env) {{ machine: machine }}
let(:app) { lambda { |*args| }}
let(:driver) { env[:machine].provider.driver }
let(:driver) { double("driver") }
subject { described_class.new(app, env) }

View File

@ -833,7 +833,7 @@ VF
end
env = environment.create_vagrant_env
env.config_global.ssh.port.should == 200
env.vagrantfile.config.ssh.port.should == 200
end
it "should load from a custom Vagrantfile" do
@ -846,7 +846,7 @@ VF
end
env = environment.create_vagrant_env(:vagrantfile_name => "non_standard_name")
env.config_global.ssh.port.should == 200
env.vagrantfile.config.ssh.port.should == 200
end
it "should load from a custom Vagrantfile specified by env var" do
@ -862,7 +862,7 @@ VF
environment.create_vagrant_env
end
env.config_global.ssh.port.should == 400
env.vagrantfile.config.ssh.port.should == 400
end
end

View File

@ -21,7 +21,7 @@ describe Vagrant::Machine do
let(:provider_name) { :test }
let(:provider_options) { {} }
let(:box) { Object.new }
let(:config) { env.config_global }
let(:config) { env.vagrantfile.config }
let(:data_dir) { Pathname.new(Dir.mktmpdir("vagrant")) }
let(:env) do
# We need to create a Vagrantfile so that this test environment
@ -41,7 +41,8 @@ describe Vagrant::Machine do
# Returns a new instance with the test data
def new_instance
described_class.new(name, provider_name, provider_cls, provider_config,
provider_options, config, data_dir, box, env)
provider_options, config, data_dir, box,
env, env.vagrantfile)
end
describe "initialization" do
@ -77,7 +78,8 @@ describe Vagrant::Machine do
# Initialize a new machine and verify that we properly receive
# the machine we expect.
instance = described_class.new(name, provider_name, provider_cls, provider_config,
provider_options, config, data_dir, box, env)
provider_options, config, data_dir, box,
env, env.vagrantfile)
received_machine.should eql(instance)
end
@ -110,6 +112,12 @@ describe Vagrant::Machine do
end
end
it "should have the vagrantfile" do
provider_init_test do |machine|
expect(machine.vagrantfile).to equal(env.vagrantfile)
end
end
it "should have access to the ID" do
# Stub this because #id= calls it.
provider.stub(:machine_id_changed)

View File

@ -1,10 +1,8 @@
require File.expand_path("../../../../base", __FILE__)
describe Vagrant::Plugin::V2::Plugin do
after(:each) do
# We want to make sure that the registered plugins remains empty
# after each test.
described_class.manager.reset!
before do
described_class.stub(manager: Vagrant::Plugin::V2::Manager.new)
end
it "should be able to set and get the name" do

View File

@ -0,0 +1,324 @@
require File.expand_path("../../base", __FILE__)
require "pathname"
require "tmpdir"
require "vagrant/vagrantfile"
describe Vagrant::Vagrantfile do
include_context "unit"
let(:keys) { [] }
let(:loader) {
Vagrant::Config::Loader.new(
Vagrant::Config::VERSIONS, Vagrant::Config::VERSIONS_ORDER)
}
subject { described_class.new(loader, keys) }
before do
keys << :test
end
def configure(&block)
loader.set(:test, [["2", block]])
end
# A helper to register a provider for use in tests.
def register_provider(name, config_class=nil, options=nil)
provider_cls = Class.new(Vagrant.plugin("2", :provider))
register_plugin("2") do |p|
p.provider(name, options) { provider_cls }
if config_class
p.config(name, :provider) { config_class }
end
end
provider_cls
end
describe "#config" do
it "exposes the global configuration" do
configure do |config|
config.vm.box = "what"
end
expect(subject.config.vm.box).to eq("what")
end
end
describe "#machine" do
let(:boxes) { Vagrant::BoxCollection.new(iso_env.boxes_dir) }
let(:data_path) { Pathname.new(Dir.mktmpdir) }
let(:env) { iso_env.create_vagrant_env }
let(:iso_env) { isolated_environment }
let(:vagrantfile) { described_class.new(loader, keys) }
subject { vagrantfile.machine(:default, :foo, boxes, data_path, env) }
before do
@foo_config_cls = Class.new(Vagrant.plugin("2", "config")) do
attr_accessor :value
end
@provider_cls = register_provider("foo", @foo_config_cls)
configure do |config|
config.vm.box = "foo"
config.vm.provider "foo" do |p|
p.value = "rawr"
end
end
iso_env.box3("foo", "1.0", :foo, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
config.ssh.port = 123
end
VF
end
its(:data_dir) { should eq(data_path) }
its(:env) { should equal(env) }
its(:name) { should eq(:default) }
its(:provider) { should be_kind_of(@provider_cls) }
its(:provider_name) { should eq(:foo) }
its(:vagrantfile) { should equal(vagrantfile) }
it "has the proper box" do
expect(subject.box.name).to eq("foo")
end
it "has the valid configuration" do
expect(subject.config.vm.box).to eq("foo")
end
it "loads the provider-specific configuration" do
expect(subject.provider_config).to be_kind_of(@foo_config_cls)
expect(subject.provider_config.value).to eq("rawr")
end
end
describe "#machine_config" do
let(:iso_env) { isolated_environment }
let(:boxes) { Vagrant::BoxCollection.new(iso_env.boxes_dir) }
it "should return a basic configured machine" do
provider_cls = register_provider("foo")
configure do |config|
config.vm.box = "foo"
end
results = subject.machine_config(:default, :foo, boxes)
box = results[:box]
config = results[:config]
expect(config.vm.box).to eq("foo")
expect(box).to be_nil
expect(results[:provider_cls]).to equal(provider_cls)
end
it "configures with sub-machine config" do
register_provider("foo")
configure do |config|
config.ssh.port = "1"
config.vm.box = "base"
config.vm.define "foo" do |f|
f.ssh.port = 100
end
end
results = subject.machine_config(:foo, :foo, boxes)
config = results[:config]
expect(config.vm.box).to eq("base")
expect(config.ssh.port).to eq(100)
end
it "configures with box configuration if it exists" do
register_provider("foo")
configure do |config|
config.vm.box = "base"
end
iso_env.box3("base", "1.0", :foo, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
config.ssh.port = 123
end
VF
results = subject.machine_config(:default, :foo, boxes)
box = results[:box]
config = results[:config]
expect(config.vm.box).to eq("base")
expect(config.ssh.port).to eq(123)
expect(box).to_not be_nil
expect(box.name).to eq("base")
end
it "configures with the proper box version" do
register_provider("foo")
configure do |config|
config.vm.box = "base"
config.vm.box_version = "~> 1.2"
end
iso_env.box3("base", "1.0", :foo, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
config.ssh.port = 123
end
VF
iso_env.box3("base", "1.3", :foo, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
config.ssh.port = 245
end
VF
results = subject.machine_config(:default, :foo, boxes)
box = results[:box]
config = results[:config]
expect(config.vm.box).to eq("base")
expect(config.ssh.port).to eq(245)
expect(box).to_not be_nil
expect(box.name).to eq("base")
expect(box.version).to eq("1.3")
end
it "configures with box config of other supported formats" do
register_provider("foo", nil, box_format: "bar")
configure do |config|
config.vm.box = "base"
end
iso_env.box3("base", "1.0", :bar, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
config.ssh.port = 123
end
VF
results = subject.machine_config(:default, :foo, boxes)
config = results[:config]
expect(config.vm.box).to eq("base")
expect(config.ssh.port).to eq(123)
end
it "loads provider overrides if set" do
register_provider("foo")
register_provider("bar")
configure do |config|
config.ssh.port = 1
config.vm.box = "base"
config.vm.provider "foo" do |_, c|
c.ssh.port = 100
end
end
# Test with the override
results = subject.machine_config(:default, :foo, boxes)
config = results[:config]
expect(config.vm.box).to eq("base")
expect(config.ssh.port).to eq(100)
# Test without the override
results = subject.machine_config(:default, :bar, boxes)
config = results[:config]
expect(config.vm.box).to eq("base")
expect(config.ssh.port).to eq(1)
end
it "loads the proper box if in a provider override" do
register_provider("foo")
configure do |config|
config.vm.box = "base"
config.vm.provider "foo" do |_, c|
c.vm.box = "foobox"
end
end
iso_env.box3("base", "1.0", :foo, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
config.ssh.port = 123
end
VF
iso_env.box3("foobox", "1.0", :foo, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
config.ssh.port = 234
end
VF
results = subject.machine_config(:default, :foo, boxes)
config = results[:config]
box = results[:box]
expect(config.vm.box).to eq("foobox")
expect(config.ssh.port).to eq(234)
expect(box).to_not be_nil
expect(box.name).to eq("foobox")
end
it "raises an error if the machine is not found" do
expect { subject.machine_config(:foo, :foo, boxes) }.
to raise_error(Vagrant::Errors::MachineNotFound)
end
it "raises an error if the provider is not found" do
expect { subject.machine_config(:default, :foo, boxes) }.
to raise_error(Vagrant::Errors::ProviderNotFound)
end
end
describe "#machine_names" do
it "returns the default name when single-VM" do
configure { |config| }
expect(subject.machine_names).to eq([:default])
end
it "returns all of the names in a multi-VM" do
configure do |config|
config.vm.define "foo"
config.vm.define "bar"
end
expect(subject.machine_names).to eq(
[:foo, :bar])
end
end
describe "#primary_machine_name" do
it "returns the default name when single-VM" do
configure { |config| }
expect(subject.primary_machine_name).to eq(:default)
end
it "returns the designated machine in multi-VM" do
configure do |config|
config.vm.define "foo"
config.vm.define "bar", primary: true
config.vm.define "baz"
end
expect(subject.primary_machine_name).to eq(:bar)
end
it "returns nil if no designation in multi-VM" do
configure do |config|
config.vm.define "foo"
config.vm.define "baz"
end
expect(subject.primary_machine_name).to be_nil
end
end
end