Skip to content

Commit

Permalink
Build the v2 interface, which is just a copy of V1 for now.
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchellh committed Nov 7, 2012
1 parent efa0a6b commit be294e0
Show file tree
Hide file tree
Showing 17 changed files with 1,270 additions and 1 deletion.
4 changes: 3 additions & 1 deletion lib/vagrant/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module Config
autoload :VersionBase, 'vagrant/config/version_base'

autoload :V1, 'vagrant/config/v1'
autoload :V2, 'vagrant/config/v2'

# This is a mutex used to guarantee that only one thread can load
# procs at any given time.
Expand All @@ -19,12 +20,13 @@ module Config
# `Vagrant.configure` calls.
VERSIONS = Registry.new
VERSIONS.register("1") { V1::Loader }
VERSIONS.register("2") { V2::Loader }

# This is the order of versions. This is used by the loader to figure out
# how to "upgrade" versions up to the desired (current) version. The
# current version is always considered to be the last version in this
# list.
VERSIONS_ORDER = ["1"]
VERSIONS_ORDER = ["1", "2"]
CURRENT_VERSION = VERSIONS_ORDER.last

# This is the method which is called by all Vagrantfiles to configure Vagrant.
Expand Down
8 changes: 8 additions & 0 deletions lib/vagrant/config/v2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Vagrant
module Config
module V2
autoload :Loader, "vagrant/config/v2/loader"
autoload :Root, "vagrant/config/v2/root"
end
end
end
114 changes: 114 additions & 0 deletions lib/vagrant/config/v2/loader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
require "vagrant/config/v2/root"

module Vagrant
module Config
module V2
# This is the loader that handles configuration loading for V2
# configurations.
class Loader < VersionBase
# Returns a bare empty configuration object.
#
# @return [V2::Root]
def self.init
new_root_object
end

# Finalizes the configuration by making sure there is at least
# one VM defined in it.
def self.finalize(config)
# Call the `#finalize` method on each of the configuration keys.
# They're expected to modify themselves in our case.
config.finalize!

# Return the object
config
end

# Loads the configuration for the given proc and returns a configuration
# object.
#
# @param [Proc] config_proc
# @return [Object]
def self.load(config_proc)
# Create a root configuration object
root = new_root_object

# Call the proc with the root
config_proc.call(root)

# Return the root object, which doubles as the configuration object
# we actually use for accessing as well.
root
end

# Merges two configuration objects.
#
# @param [V2::Root] old The older root config.
# @param [V2::Root] new The newer root config.
# @return [V2::Root]
def self.merge(old, new)
# Grab the internal states, we use these heavily throughout the process
old_state = old.__internal_state
new_state = new.__internal_state

# The config map for the new object is the old one merged with the
# new one.
config_map = old_state["config_map"].merge(new_state["config_map"])

# Merge the keys.
old_keys = old_state["keys"]
new_keys = new_state["keys"]
keys = {}
old_keys.each do |key, old_value|
if new_keys.has_key?(key)
# We need to do a merge, which we expect to be available
# on the config class itself.
keys[key] = old_value.merge(new_keys[key])
else
# We just take the old value, but dup it so that we can modify.
keys[key] = old_value.dup
end
end

new_keys.each do |key, new_value|
# Add in the keys that the new class has that we haven't merged.
if !keys.has_key?(key)
keys[key] = new_value.dup
end
end

# Return the final root object
V2::Root.new(config_map, keys)
end

# Upgrade a V1 configuration to a V2 configuration.
#
# @param [V1::Root] old
# @return [Array] A 3-tuple result.
def self.upgrade(old)
# TODO: Actually do an upgrade. For now we just return V1.
[old, [], []]
end

protected

def self.new_root_object
# Get all the registered configuration objects and use them. If
# we're currently on version 1, then we load all the config objects,
# otherwise we load only the upgrade safe ones, since we're
# obviously being loaded for an upgrade.
config_map = nil
plugin_manager = Vagrant.plugin("1").manager
if Config::CURRENT_VERSION == "1"
config_map = plugin_manager.config
else
config_map = plugin_manager.config_upgrade_safe
end

# Create the configuration root object
V2::Root.new(config_map)
end
end
end
end
end
75 changes: 75 additions & 0 deletions lib/vagrant/config/v2/root.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
module Vagrant
module Config
module V2
# This is the root configuration class. An instance of this is what
# is passed into version 1 Vagrant configuration blocks.
class Root
# Initializes a root object that maps the given keys to specific
# configuration classes.
#
# @param [Hash] config_map Map of key to config class.
def initialize(config_map, keys=nil)
@keys = keys || {}
@config_map = config_map
end

# We use method_missing as a way to get the configuration that is
# used for Vagrant and load the proper configuration classes for
# each.
def method_missing(name, *args)
return @keys[name] if @keys.has_key?(name)

config_klass = @config_map[name.to_sym]
if config_klass
# Instantiate the class and return the instance
@keys[name] = config_klass.new
return @keys[name]
else
# Super it up to probably raise a NoMethodError
super
end
end

# Called to finalize this object just prior to it being used by
# the Vagrant system. The "!" signifies that this is expected to
# mutate itself.
def finalize!
@keys.each do |_key, instance|
instance.finalize!
end
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!(env)
# Validate each of the configured classes and store the results into
# a hash.
errors = @keys.inject({}) do |container, data|
key, instance = data
recorder = ErrorRecorder.new
instance.validate(env, 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

# Returns the internal state of the root object. This is used
# by outside classes when merging, and shouldn't be called directly.
# Note the strange method name is to attempt to avoid any name
# clashes with potential configuration keys.
def __internal_state
{
"config_map" => @config_map,
"keys" => @keys
}
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/vagrant/plugin.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Vagrant
module Plugin
autoload :V1, "vagrant/plugin/v1"
autoload :V2, "vagrant/plugin/v2"
end
end
19 changes: 19 additions & 0 deletions lib/vagrant/plugin/v2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require "log4r"

require "vagrant/plugin/v2/errors"

module Vagrant
module Plugin
module V2
autoload :Command, "vagrant/plugin/v2/command"
autoload :Communicator, "vagrant/plugin/v2/communicator"
autoload :Config, "vagrant/plugin/v2/config"
autoload :Guest, "vagrant/plugin/v2/guest"
autoload :Host, "vagrant/plugin/v2/host"
autoload :Manager, "vagrant/plugin/v2/manager"
autoload :Plugin, "vagrant/plugin/v2/plugin"
autoload :Provider, "vagrant/plugin/v2/provider"
autoload :Provisioner, "vagrant/plugin/v2/provisioner"
end
end
end
Loading

0 comments on commit be294e0

Please sign in to comment.