vagrant/lib/vagrant/config.rb

172 lines
5.8 KiB
Ruby

require 'vagrant/config/base'
require 'vagrant/config/error_recorder'
module Vagrant
# The config class is responsible for loading Vagrant configurations, which
# are usually found in Vagrantfiles but may also be procs. The loading is done
# by specifying a queue of files or procs that are for configuration, and then
# executing them. The config loader will run each item in the queue, so that
# configuration from later items overwrite that from earlier items. This is how
# Vagrant "scoping" of Vagranfiles is implemented.
#
# If you're looking to create your own configuration classes, see {Base}.
#
# # Loading Configuration Files
#
# If you are in fact looking to load configuration files, then this is the
# class you are looking for. Loading configuration is quite easy. The following
# example assumes `env` is already a loaded instance of {Environment}:
#
# config = Vagrant::Config.new
# config.set(:first, "/path/to/some/Vagrantfile")
# config.set(:second, "/path/to/another/Vagrantfile")
# config.load_order = [:first, :second]
# result = config.load(env)
#
# p "Your box is: #{result.vm.box}"
#
# The load order determines what order the config files specified are loaded.
# If a key is not mentioned (for example if above the load order was set to
# `[:first]`, therefore `:second` was not mentioned), then that config file
# won't be loaded.
class Config
# An array of symbols specifying the load order for the procs.
attr_accessor :load_order
# This is the method which is called by all Vagrantfiles to configure Vagrant.
# This method expects a block which accepts a single argument representing
# an instance of the {Config::Top} class.
#
# Note that the block is not run immediately. Instead, it's proc is stored
# away for execution later.
def self.run(&block)
# Store it for later
@last_proc = block
end
# Returns the last proc which was activated for the class via {run}. This
# also sets the last proc to `nil` so that calling this method multiple times
# will not return duplicates.
#
# @return [Proc]
def self.last_proc
value = @last_proc
@last_proc = nil
value
end
def initialize
@procs = {}
@load_order = []
end
# Adds a Vagrantfile to be loaded to the queue of config procs. Note
# that this causes the Vagrantfile file to be loaded at this point,
# and it will never be loaded again.
def set(key, path)
@procs[key] = [path].flatten.map(&method(:proc_for))
end
# Loads the added procs using the set `load_order` attribute and returns
# the {Config::Top} object result. The configuration is loaded for the
# given {Environment} object.
#
# @param [Environment] env
def load(env)
config = Top.new(env)
# Only run the procs specified in the load order, in the order
# specified.
load_order.each do |key|
if @procs[key]
@procs[key].each do |proc|
proc.call(config) if proc
end
end
end
config
end
protected
def proc_for(path)
return nil if !path
return path if path.is_a?(Proc)
begin
Kernel.load path if File.exist?(path)
return self.class.last_proc
rescue SyntaxError => e
# Report syntax errors in a nice way for Vagrantfiles
raise Errors::VagrantfileSyntaxError, :file => e.message
end
end
end
class Config
# This class is the "top" configure class, which handles registering
# other configuration classes as well as validation of all configured
# classes. This is the object which is returned by {Environment#config}
# and has accessors to all other configuration classes.
#
# If you're looking to create your own configuration class, see {Base}.
class Top < Base
@@configures = {} if !defined?(@@configures)
class << self
# The list of registered configuration classes as well as the key
# they're registered under.
def configures_list
@@configures ||= {}
end
# Registers a configuration class with the given key. This method shouldn't
# be called. Instead, inherit from {Base} and call {Base.configures}.
def configures(key, klass)
configures_list[key] = klass
attr_reader key.to_sym
end
end
def initialize(env=nil)
self.class.configures_list.each do |key, klass|
config = klass.new
config.env = env
config.top = self
instance_variable_set("@#{key}".to_sym, config)
end
@env = env
end
# Validates the configuration classes of this instance and raises an
# exception if they are invalid. If you are implementing a custom configuration
# class, the method you want to implement is {Base#validate}. This is
# the method that checks all the validation, not one which defines
# validation rules.
def validate!
# Validate each of the configured classes and store the results into
# a hash.
errors = self.class.configures_list.inject({}) do |container, data|
key, _ = data
recorder = ErrorRecorder.new
send(key.to_sym).validate(recorder)
container[key.to_sym] = recorder if !recorder.errors.empty?
container
end
return if errors.empty?
raise Errors::ConfigValidationFailed, :messages => Util::TemplateRenderer.render("config/validation_failed", :errors => errors)
end
end
end
end
# The built-in configuration classes
require 'vagrant/config/vagrant'
require 'vagrant/config/ssh'
require 'vagrant/config/nfs'
require 'vagrant/config/vm'
require 'vagrant/config/package'