diff --git a/lib/vagrant.rb b/lib/vagrant.rb index 61759eca6..790c2b94d 100644 --- a/lib/vagrant.rb +++ b/lib/vagrant.rb @@ -12,5 +12,8 @@ require File.expand_path("util/glob_loader", libdir) # Load them up Vagrant::GlobLoader.glob_require(libdir, %w{util/stacked_proc_runner - actions/base downloaders/base actions/collection actions/runner config - provisioners/base provisioners/chef systems/base commands/base commands/box}) + downloaders/base config provisioners/base provisioners/chef systems/base + commands/base commands/box action/exception_catcher}) + +# Initialize the built-in actions +Vagrant::Action.builtin! diff --git a/lib/vagrant/action.rb b/lib/vagrant/action.rb new file mode 100644 index 000000000..3ee4b3308 --- /dev/null +++ b/lib/vagrant/action.rb @@ -0,0 +1,62 @@ +module Vagrant + # Manages action running and registration. Every Vagrant environment + # has an instance of {Action} to allow for running in the context of + # the environment. + class Action + include Util + + class << self + # Returns the list of registered actions. + def actions + @actions ||= {} + end + + # Registers an action and associates it with a symbol. This + # symbol can then be referenced in other action builds and + # callbacks can be registered on that symbol. + # + # @param [Symbol] key + def register(key, callable) + actions[key] = callable + end + + # Retrieves a registered action by key. + def [](key) + actions[key] + end + end + + # The environment to run the actions in. + attr_reader :env + + # Initializes the action with the given environment which the actions + # will be run in. + # + # @param [Environment] env + def initialize(env) + @env = env + end + + # Runs the given callable object in the context of the environment. + # If a symbol is given as the `callable` parameter, then it is looked + # up in the registered actions list which are registered with {register}. + # + # @param [Object] callable An object which responds to `call`. + def run(callable, options=nil) + callable = Builder.new.use(callable) if callable.kind_of?(Class) + callable = self.class.actions[callable] if callable.kind_of?(Symbol) + + action_environment = Action::Environment.new(env) + action_environment.merge!(options || {}) + callable.call(action_environment) + + if action_environment.error? + # Erroneous environment resulted. Properly display error + # message. + key, options = action_environment.error + error_and_exit(key, options) + return false + end + end + end +end diff --git a/lib/vagrant/action/action_exception.rb b/lib/vagrant/action/action_exception.rb new file mode 100644 index 000000000..3d5299215 --- /dev/null +++ b/lib/vagrant/action/action_exception.rb @@ -0,0 +1,16 @@ +module Vagrant + class Action + class ActionException < Exception + attr_reader :key + attr_reader :data + + def initialize(key, data = {}) + @key = key + @data = data + + message = Vagrant::Util::Translator.t(key, data) + super(message) + end + end + end +end diff --git a/lib/vagrant/action/box/destroy.rb b/lib/vagrant/action/box/destroy.rb new file mode 100644 index 000000000..36cdacafd --- /dev/null +++ b/lib/vagrant/action/box/destroy.rb @@ -0,0 +1,19 @@ +module Vagrant + class Action + module Box + class Destroy + def initialize(app, env) + @app = app + @env = env + end + + def call(env) + env.logger.info "Deleting box directory..." + FileUtils.rm_rf(env["box"].directory) + + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant/action/box/download.rb b/lib/vagrant/action/box/download.rb new file mode 100644 index 000000000..c1abe50a2 --- /dev/null +++ b/lib/vagrant/action/box/download.rb @@ -0,0 +1,82 @@ +module Vagrant + class Action + module Box + class Download + BASENAME = "box" + + include Util + include ExceptionCatcher + + attr_reader :temp_path + + def initialize(app, env) + @app = app + @env = env + @env["download.classes"] ||= [] + @env["download.classes"] << [Downloaders::HTTP, Downloaders::File] + @env["download.classes"].flatten! + end + + def call(env) + @env = env + + catch_action_exception(env) do + download if instantiate_downloader + end + + return if env.error? + @app.call(@env) + + cleanup + end + + def instantiate_downloader + @env["download.classes"].each do |klass| + if klass.match?(@env["box"].uri) + @env.logger.info "Downloading with #{klass}..." + @downloader = klass.new(@env["box"].uri) + end + end + + if !@downloader + @env.error!(:box_download_unknown_type) + return false + end + + @downloader.prepare(@env["box"].uri) + true + end + + def download + with_tempfile do |tempfile| + download_to(tempfile) + @temp_path = @env["download.temp_path"] = tempfile.path + end + end + + def cleanup + if temp_path && File.exist?(temp_path) + @env.logger.info "Cleaning up downloaded box..." + File.unlink(temp_path) + end + end + + def with_tempfile + @env.logger.info "Creating tempfile for storing box file..." + File.open(box_temp_path, Platform.tar_file_options) do |tempfile| + yield tempfile + end + end + + def box_temp_path + File.join(@env.env.tmp_path, BASENAME + Time.now.to_i.to_s) + end + + def download_to(f) + @env.logger.info "Copying box to temporary location..." + @downloader.download!(@env["box"].uri, f) + end + end + end + end +end diff --git a/lib/vagrant/action/box/unpackage.rb b/lib/vagrant/action/box/unpackage.rb new file mode 100644 index 000000000..79f832ab5 --- /dev/null +++ b/lib/vagrant/action/box/unpackage.rb @@ -0,0 +1,58 @@ +module Vagrant + class Action + module Box + # Unpackages a downloaded box to a given directory with a given + # name. + # + # # Required Variables + # + # * `download.temp_path` - A location for the downloaded box. This is + # set by the {Download} action. + # * `box` - A {Vagrant::Box} object. + # + class Unpackage + attr_reader :box_directory + + def initialize(app, env) + @app = app + @env = env + end + + def call(env) + @env = env + + return if !setup_box_directory + decompress + + @app.call(@env) + + cleanup if @env.error? + end + + def cleanup + if File.directory?(box_directory) + FileUtils.rm_rf(box_directory) + end + end + + def setup_box_directory + if File.directory?(@env["box"].directory) + @env.error!(:box_already_exists, :box_name => @env["box"].name) + return false + end + + FileUtils.mkdir_p(@env["box"].directory) + @box_directory = @env["box"].directory + true + end + + def decompress + Dir.chdir(@env["box"].directory) do + @env.logger.info "Extracting box to #{@env["box"].directory}..." + Archive::Tar::Minitar.unpack(@env["download.temp_path"], @env["box"].directory) + end + end + end + end + end +end diff --git a/lib/vagrant/action/box/verify.rb b/lib/vagrant/action/box/verify.rb new file mode 100644 index 000000000..285b92ba5 --- /dev/null +++ b/lib/vagrant/action/box/verify.rb @@ -0,0 +1,23 @@ +module Vagrant + class Action + module Box + class Verify + def initialize(app, env) + @app = app + @env = env + end + + def call(env) + begin + env.logger.info "Verifying box..." + VirtualBox::Appliance.new(env["box"].ovf_file) + rescue Exception + return env.error!(:box_verification_failed) + end + + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant/action/builder.rb b/lib/vagrant/action/builder.rb new file mode 100644 index 000000000..7873e9ecc --- /dev/null +++ b/lib/vagrant/action/builder.rb @@ -0,0 +1,148 @@ +module Vagrant + class Action + # Action builder which provides a nice DSL for building up + # a middleware sequence for Vagrant actions. This code is based + # heavily off of `Rack::Builder` and `ActionDispatch::MiddlewareStack` + # in Rack and Rails, respectively. + # + # Usage + # + # Building an action sequence is very easy: + # + # app = Vagrant::Action::Builder.new do + # use MiddlewareA + # use MiddlewareB + # end + # + # Vagrant::Action.run(app) + # + class Builder + # Initializes the builder. An optional block can be passed which + # will be evaluated in the context of the instance. + def initialize(&block) + instance_eval(&block) if block_given? + end + + # Returns the current stack of middlewares. You probably won't + # need to use this directly, and its recommended that you don't. + # + # @return [Array] + def stack + @stack ||= [] + end + + # Returns a mergeable version of the builder. If `use` is called with + # the return value of this method, then the stack will merge, instead + # of being treated as a separate single middleware. + def flatten + lambda do |env| + self.call(env) + end + end + + # Adds a middleware class to the middleware stack. Any additional + # args and a block, if given, are saved and passed to the initializer + # of the middleware. + # + # @param [Class] middleware The middleware class + def use(middleware, *args, &block) + if middleware.kind_of?(Builder) + # Merge in the other builder's stack into our own + self.stack.concat(middleware.stack) + else + self.stack << [middleware, args, block] + end + + self + end + + # Inserts a middleware at the given index or directly before the + # given middleware object. + def insert(index, middleware, *args, &block) + index = self.index(index) unless index.is_a?(Integer) + stack.insert(index, [middleware, args, block]) + end + + alias_method :insert_before, :insert + + # Inserts a middleware after the given index or middleware object. + def insert_after(index, middleware, *args, &block) + index = self.index(index) unless index.is_a?(Integer) + raise "no such middleware to insert after: #{index.inspect}" unless index + insert(index + 1, middleware, *args, &block) + end + + # Swaps out the given middlware object or index with the new + # middleware. + def swap(index, middleware, *args, &block) + if index.is_a?(Integer) + delete(index) + insert(index, middleware, *args, &block) + else + insert_before(index, middleware, *args, &block) + delete(index) + end + end + + # Deletes the given middleware object or index + def delete(index) + index = self.index(index) unless index.is_a?(Integer) + stack.delete_at(index) + end + + # Returns the numeric index for the given middleware object. + # + # @param [Object] object The item to find the index for + # @return [Integer] + def index(object) + stack.each_with_index do |item, i| + return i if item[0] == object + end + + nil + end + + # Converts the builder stack to a runnable action sequence. + # + # @param [Vagrant::Action::Environment] env The action environment + # @return [Object] A callable object + def to_app(env) + # Prepend the error halt task so errneous environments are halted + # before the chain even begins. + items = stack.dup.unshift([ErrorHalt, [], nil]) + + # Convert each middleware into a lambda which takes the next + # middleware. + items = items.collect do |item| + klass, args, block = item + + lambda do |app| + if klass.is_a?(Class) + # A middleware klass which is to be instantiated with the + # app, env, and any arguments given + klass.new(app, env, *args, &block) + elsif klass.respond_to?(:call) + # Make it a lambda which calls the item then forwards + # up the chain + lambda do |e| + klass.call(e) + app.call(e) + end + else + raise "Invalid middleware: #{item.inspect}" + end + end + end + + # Append the final step and convert into flattened call chain. + items << lambda { |env| } + items[0...-1].reverse.inject(items.last) { |a,e| e.call(a) } + end + + # Runs the builder stack with the given environment. + def call(env) + to_app(env).call(env) + end + end + end +end diff --git a/lib/vagrant/action/builtin.rb b/lib/vagrant/action/builtin.rb new file mode 100644 index 000000000..af30dd8c0 --- /dev/null +++ b/lib/vagrant/action/builtin.rb @@ -0,0 +1,104 @@ +module Vagrant + class Action + # Registers the builtin actions. These are locked away in a + # method so that their definition can be deferred until after + # all the necessary Vagrant libraries are loaded. Hopefully + # in the future this will no longer be necessary with autoloading. + def self.builtin! + # provision - Provisions a running VM + provision = Builder.new do + use VM::Provision + end + + register :provision, provision + + # start - Starts a VM, assuming it already exists on the + # environment. + start = Builder.new do + use VM::Customize + use VM::ForwardPorts + use VM::Provision + use VM::ShareFolders + use VM::Network + use VM::Boot + end + + register :start, start + + # halt - Halts the VM, attempting gracefully but then forcing + # a restart if fails. + halt = Builder.new do + use VM::Halt + end + + register :halt, halt + + # suspend - Suspends the VM + suspend = Builder.new do + use VM::Suspend + end + + register :suspend, suspend + + # resume - Resume a VM + resume = Builder.new do + use VM::Resume + end + + register :resume, resume + + # reload - Halts then restarts the VM + reload = Builder.new do + use Action[:halt] + use Action[:start] + end + + register :reload, reload + + # up - Imports, prepares, then starts a fresh VM. + up = Builder.new do + use VM::Import + use VM::Persist + use VM::MatchMACAddress + use VM::CheckGuestAdditions + use Action[:start] + end + + register :up, up + + # destroy - Halts, cleans up, and destroys an existing VM + destroy = Builder.new do + use Action[:halt] + use VM::DestroyUnusedNetworkInterfaces + use VM::Destroy + end + + register :destroy, destroy + + # package - Export and package the VM + package = Builder.new do + use Action[:halt] + use VM::Export + use VM::Package + end + + register :package, package + + # box_add - Download and add a box. + box_add = Builder.new do + use Box::Download + use Box::Unpackage + use Box::Verify + end + + register :box_add, box_add + + # box_remove - Removes/deletes a box. + box_remove = Builder.new do + use Box::Destroy + end + + register :box_remove, box_remove + end + end +end diff --git a/lib/vagrant/action/environment.rb b/lib/vagrant/action/environment.rb new file mode 100644 index 000000000..cf756516f --- /dev/null +++ b/lib/vagrant/action/environment.rb @@ -0,0 +1,54 @@ +module Vagrant + class Action + # Represents an action environment which is what is passed + # to the `call` method of each action. This environment contains + # some helper methods for accessing the environment as well + # as being a hash, to store any additional options. + class Environment < Hash + # The {Vagrant::Environment} object represented by this + # action environment. + attr_reader :env + + # If nonnil, the error associated with this environment. Set + # using {#error!} + attr_reader :error + + def initialize(env) + super() do |h,k| + # By default, try to find the key as a method on the + # environment. Gross eval use here. + begin + value = eval("h.env.#{k}") + h[k] = value + rescue Exception + nil + end + end + + @env = env + @error = nil + end + + # Returns a logger associated with the environment. + def logger + env.logger + end + + # Flags the environment as erroneous. Stores the given key + # and options until the end of the action sequence. + # + # @param [Symbol] key Key to translation to display error message. + # @param [Hash] options Variables to pass to the translation + def error!(key, options=nil) + @error = [key, (options || {})] + end + + # Returns boolean denoting if environment is in erroneous state. + # + # @return [Boolean] + def error? + !error.nil? + end + end + end +end diff --git a/lib/vagrant/action/error_halt.rb b/lib/vagrant/action/error_halt.rb new file mode 100644 index 000000000..2c4411659 --- /dev/null +++ b/lib/vagrant/action/error_halt.rb @@ -0,0 +1,14 @@ +module Vagrant + class Action + # A middleware which simply halts if the environment is erroneous. + class ErrorHalt + def initialize(app,env) + @app = app + end + + def call(env) + @app.call(env) if !env.error? + end + end + end +end diff --git a/lib/vagrant/action/exception_catcher.rb b/lib/vagrant/action/exception_catcher.rb new file mode 100644 index 000000000..35fe78547 --- /dev/null +++ b/lib/vagrant/action/exception_catcher.rb @@ -0,0 +1,14 @@ +module Vagrant + class Action + # A helper to catch any ActionExceptions raised and to + # apply the error to the environment. + module ExceptionCatcher + def catch_action_exception(env) + yield env + rescue ActionException => e + env.error!(e.key, e.data) + false + end + end + end +end diff --git a/lib/vagrant/action/vm/boot.rb b/lib/vagrant/action/vm/boot.rb new file mode 100644 index 000000000..383913e8b --- /dev/null +++ b/lib/vagrant/action/vm/boot.rb @@ -0,0 +1,46 @@ +module Vagrant + class Action + module VM + class Boot + def initialize(app, env) + @app = app + @env = env + end + + def call(env) + @env = env + + # Start up the VM and wait for it to boot. + boot + return env.error!(:vm_failed_to_boot) if !wait_for_boot + + @app.call(env) + end + + def boot + @env.logger.info "Booting VM..." + @env["vm"].vm.start(@env.env.config.vm.boot_mode) + end + + def wait_for_boot(sleeptime=5) + @env.logger.info "Waiting for VM to boot..." + + @env.env.config.ssh.max_tries.to_i.times do |i| + @env.logger.info "Trying to connect (attempt ##{i+1} of #{@env.env.config[:ssh][:max_tries]})..." + + if @env["vm"].ssh.up? + @env.logger.info "VM booted and ready for use!" + return true + end + + sleep sleeptime + end + + @env.logger.info "Failed to connect to VM! Failed to boot?" + false + end + end + end + end +end + diff --git a/lib/vagrant/action/vm/check_guest_additions.rb b/lib/vagrant/action/vm/check_guest_additions.rb new file mode 100644 index 000000000..4782c051b --- /dev/null +++ b/lib/vagrant/action/vm/check_guest_additions.rb @@ -0,0 +1,32 @@ +module Vagrant + class Action + module VM + # Middleware which verifies that the VM has the proper guest additions + # installed and prints a warning if they're not detected or if the + # version does not match the installed VirtualBox version. + class CheckGuestAdditions + include Util + + def initialize(app, env) + @app = app + end + + def call(env) + # Use the raw interface for now, while the virtualbox gem + # doesn't support guest properties (due to cross platform issues) + version = env["vm"].vm.interface.get_guest_property_value("/VirtualBox/GuestAdd/Version") + if version.empty? + env.logger.error Translator.t(:vm_additions_not_detected) + elsif version != VirtualBox.version + env.logger.error Translator.t(:vm_additions_version_mismatch, + :guest_additions_version => version, + :virtualbox_version => VirtualBox.version) + end + + # Continue + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant/action/vm/customize.rb b/lib/vagrant/action/vm/customize.rb new file mode 100644 index 000000000..505dd665a --- /dev/null +++ b/lib/vagrant/action/vm/customize.rb @@ -0,0 +1,21 @@ +module Vagrant + class Action + module VM + class Customize + def initialize(app, env) + @app = app + end + + def call(env) + if !env.env.config.vm.proc_stack.empty? + env.logger.info "Running any VM customizations..." + env.env.config.vm.run_procs!(env["vm"].vm) + env["vm"].vm.save + end + + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant/action/vm/destroy.rb b/lib/vagrant/action/vm/destroy.rb new file mode 100644 index 000000000..4aa5a6109 --- /dev/null +++ b/lib/vagrant/action/vm/destroy.rb @@ -0,0 +1,20 @@ +module Vagrant + class Action + module VM + class Destroy + def initialize(app, env) + @app = app + end + + def call(env) + env.logger.info "Destroying VM and associated drives..." + env["vm"].vm.destroy(:destroy_medium => :delete) + env["vm"].vm = nil + env.env.update_dotfile + + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant/action/vm/destroy_unused_network_interfaces.rb b/lib/vagrant/action/vm/destroy_unused_network_interfaces.rb new file mode 100644 index 000000000..43febf259 --- /dev/null +++ b/lib/vagrant/action/vm/destroy_unused_network_interfaces.rb @@ -0,0 +1,35 @@ +module Vagrant + class Action + module VM + # Destroys the unused host only interfaces. This middleware cleans + # up any created host only networks. + class DestroyUnusedNetworkInterfaces + def initialize(app, env) + @app = app + end + + def call(env) + # We need to check if the host only network specified by any + # of the adapters would not have any more clients if it was + # destroyed. And if so, then destroy the host only network + # itself. + interfaces = env["vm"].vm.network_adapters.collect do |adapter| + adapter.host_interface_object + end + + interfaces.compact.uniq.each do |interface| + # Destroy the network interface if there is only one + # attached VM (which must be this VM) + if interface.attached_vms.length == 1 + env.logger.info "Destroying unused network interface..." + interface.destroy + end + end + + # Continue along + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant/action/vm/export.rb b/lib/vagrant/action/vm/export.rb new file mode 100644 index 000000000..a4ff8c90e --- /dev/null +++ b/lib/vagrant/action/vm/export.rb @@ -0,0 +1,42 @@ +module Vagrant + class Action + module VM + class Export + def initialize(app, env) + @app = app + @env = env + end + + def call(env) + @env = env + + return env.error!(:vm_power_off_to_package) if !@env["vm"].vm.powered_off? + + setup_temp_dir + export + + @app.call(env) + end + + def setup_temp_dir + @env.logger.info "Creating temporary directory for export..." + @env["export.temp_dir"] = File.join(@env.env.tmp_path, Time.now.to_i.to_s) + FileUtils.mkpath(@env["export.temp_dir"]) + end + + def export + @env.logger.info "Exporting VM to #{ovf_path}..." + @env["vm"].vm.export(ovf_path) do |progress| + @env.logger.report_progress(progress.percent, 100, false) + end + ensure + @env.logger.clear_progress + end + + def ovf_path + File.join(@env["export.temp_dir"], @env.env.config.vm.box_ovf) + end + end + end + end +end diff --git a/lib/vagrant/actions/vm/forward_ports.rb b/lib/vagrant/action/vm/forward_ports.rb similarity index 59% rename from lib/vagrant/actions/vm/forward_ports.rb rename to lib/vagrant/action/vm/forward_ports.rb index c5f5eadc0..804803bdb 100644 --- a/lib/vagrant/actions/vm/forward_ports.rb +++ b/lib/vagrant/action/vm/forward_ports.rb @@ -1,18 +1,26 @@ module Vagrant - module Actions + class Action module VM - class ForwardPorts < Base - def prepare + class ForwardPorts + def initialize(app,env) + @app = app + @env = env + external_collision_check end + #-------------------------------------------------------------- + # Prepare Helpers - These functions are not involved in actually + # executing the action + #-------------------------------------------------------------- + # This method checks for any port collisions with any VMs # which are already created (by Vagrant or otherwise). # report the collisions detected or will attempt to fix them # automatically if the port is configured to do so. def external_collision_check existing = used_ports - runner.env.config.vm.forwarded_ports.each do |name, options| + @env.env.config.vm.forwarded_ports.each do |name, options| if existing.include?(options[:hostport].to_s) handle_collision(name, options, existing) end @@ -26,18 +34,18 @@ module Vagrant if !options[:auto] # Auto fixing is disabled for this port forward, so we # must throw an error so the user can fix it. - raise ActionException.new(:vm_port_collision, :name => name, :hostport => options[:hostport].to_s, :guestport => options[:guestport].to_s, :adapter => options[:adapter]) + return @env.error!(:vm_port_collision, :name => name, :hostport => options[:hostport].to_s, :guestport => options[:guestport].to_s, :adapter => options[:adapter]) end # Get the auto port range and get rid of the used ports and # ports which are being used in other forwards so we're just # left with available ports. - range = runner.env.config.vm.auto_port_range.to_a - range -= runner.env.config.vm.forwarded_ports.collect { |n, o| o[:hostport].to_i } + range = @env.env.config.vm.auto_port_range.to_a + range -= @env.env.config.vm.forwarded_ports.collect { |n, o| o[:hostport].to_i } range -= existing_ports if range.empty? - raise ActionException.new(:vm_port_auto_empty, :vm_name => @runner.name, :name => name, :options => options) + return @env.error!(:vm_port_auto_empty, :vm_name => @env["vm"].name, :name => name, :options => options) end # Set the port up to be the first one and add that port to @@ -46,51 +54,54 @@ module Vagrant existing_ports << options[:hostport] # Notify the user - logger.info "Fixed port collision: #{name} now on port #{options[:hostport]}" + @env.logger.info "Fixed port collision: #{name} now on port #{options[:hostport]}" end - def execute! + #-------------------------------------------------------------- + # Execution + #-------------------------------------------------------------- + def call(env) + @env = env + clear forward_ports + + @app.call(env) end def clear if used_ports.length > 0 - logger.info "Deleting any previously set forwarded ports..." + @env.logger.info "Deleting any previously set forwarded ports..." clear_ports - runner.reload! + @env["vm"].reload! end end def forward_ports - logger.info "Forwarding ports..." + @env.logger.info "Forwarding ports..." - runner.env.config.vm.forwarded_ports.each do |name, options| + @env.env.config.vm.forwarded_ports.each do |name, options| adapter = options[:adapter] # Assuming the only reason to establish port forwarding is because the VM is using Virtualbox NAT networking. # Host-only or Bridged networking don't require port-forwarding and establishing forwarded ports on these # attachment types has uncertain behaviour. - if @runner.vm.network_adapters[adapter].attachment_type == :nat - logger.info "Forwarding \"#{name}\": #{options[:guestport]} on adapter \##{adapter+1} => #{options[:hostport]}" + if @env["vm"].vm.network_adapters[adapter].attachment_type == :nat + @env.logger.info "Forwarding \"#{name}\": #{options[:guestport]} on adapter \##{adapter+1} => #{options[:hostport]}" forward_port(name, options) else - logger.info "VirtualBox adapter \##{adapter+1} not configured as \"NAT\"." - logger.info "Skipped port forwarding \"#{name}\": #{options[:guestport]} on adapter\##{adapter+1} => #{options[:hostport]}" + @env.logger.info "VirtualBox adapter \##{adapter+1} not configured as \"NAT\"." + @env.logger.info "Skipped port forwarding \"#{name}\": #{options[:guestport]} on adapter\##{adapter+1} => #{options[:hostport]}" end end - runner.vm.save - runner.reload! + @env["vm"].vm.save + @env["vm"].reload! end - #------------------------------------------------------------ - # VirtualBox version-specific helpers below. VirtualBox 3.2 - # introduced a breaking change into the way that forwarded - # ports are handled. For backwards compatability with 3.1, we - # need this trickery. - #------------------------------------------------------------ - # TODO In a future version, fix this. + #-------------------------------------------------------------- + # General Helpers + #-------------------------------------------------------------- # Returns an array of used ports. This method is implemented # differently depending on the VirtualBox version, but the @@ -99,7 +110,7 @@ module Vagrant # @return [Array] def used_ports result = VirtualBox::VM.all.collect do |vm| - if vm.running? && vm.uuid != runner.uuid + if vm.running? && vm.uuid != @env["vm"].uuid vm.network_adapters.collect do |na| na.nat_driver.forwarded_ports.collect do |fp| fp.hostport.to_s @@ -113,7 +124,7 @@ module Vagrant # Deletes existing forwarded ports. def clear_ports - runner.vm.network_adapters.each do |na| + @env["vm"].vm.network_adapters.each do |na| na.nat_driver.forwarded_ports.dup.each do |fp| fp.destroy end @@ -126,7 +137,7 @@ module Vagrant port.name = name port.guestport = options[:guestport] port.hostport = options[:hostport] - runner.vm.network_adapters[options[:adapter]].nat_driver.forwarded_ports << port + @env["vm"].vm.network_adapters[options[:adapter]].nat_driver.forwarded_ports << port end end end diff --git a/lib/vagrant/action/vm/halt.rb b/lib/vagrant/action/vm/halt.rb new file mode 100644 index 000000000..4615c49be --- /dev/null +++ b/lib/vagrant/action/vm/halt.rb @@ -0,0 +1,29 @@ +module Vagrant + class Action + module VM + class Halt + include ExceptionCatcher + + def initialize(app, env) + @app = app + end + + def call(env) + if env["vm"].vm.running? + if !env["force"] + catch_action_exception(env) { env["vm"].system.halt } + return if env.error? + end + + if env["vm"].vm.state(true) != :powered_off + env.logger.info "Forcing shutdown of VM..." + env["vm"].vm.stop + end + end + + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant/action/vm/import.rb b/lib/vagrant/action/vm/import.rb new file mode 100644 index 000000000..b544819cf --- /dev/null +++ b/lib/vagrant/action/vm/import.rb @@ -0,0 +1,30 @@ +module Vagrant + class Action + module VM + class Import + def initialize(app, env) + @app = app + end + + def call(env) + env.logger.info "Importing base VM (#{env.env.box.ovf_file})" + + begin + # Import the virtual machine + env.env.vm.vm = VirtualBox::VM.import(env.env.box.ovf_file) do |progress| + env.logger.report_progress(progress.percent, 100, false) + end + + # Flag as erroneous and return if import failed + return env.error!(:virtualbox_import_failure) if !env['vm'].vm + ensure + env.logger.clear_progress + end + + # Import completed successfully. Continue the chain + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant/action/vm/match_mac_address.rb b/lib/vagrant/action/vm/match_mac_address.rb new file mode 100644 index 000000000..93b27005d --- /dev/null +++ b/lib/vagrant/action/vm/match_mac_address.rb @@ -0,0 +1,19 @@ +module Vagrant + class Action + module VM + class MatchMACAddress + def initialize(app, env) + @app = app + end + + def call(env) + env.logger.info "Matching MAC addresses..." + env["vm"].vm.network_adapters.first.mac_address = env.env.config.vm.base_mac + env["vm"].vm.save + + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant/actions/vm/network.rb b/lib/vagrant/action/vm/network.rb similarity index 63% rename from lib/vagrant/actions/vm/network.rb rename to lib/vagrant/action/vm/network.rb index 13b928a92..f04e3917d 100644 --- a/lib/vagrant/actions/vm/network.rb +++ b/lib/vagrant/action/vm/network.rb @@ -1,71 +1,43 @@ module Vagrant - module Actions + class Action module VM - class Network < Base - def prepare - # Verify that the given network options are valid - runner.env.config.vm.network_options.compact.each do |network_options| - verify_no_bridge_collision(network_options) - end - end + # Networking middleware for Vagrant. This enables host only + # networking on VMs if configured as such. + class Network + include ExceptionCatcher - def before_destroy - # We need to check if the host only network specified by any - # of the adapters would not have any more clients if it was - # destroyed. And if so, then destroy the host only network - # itself. - interfaces = runner.vm.network_adapters.collect do |adapter| - adapter.host_interface_object - end + def initialize(app, env) + @app = app + @env = env - interfaces.compact.uniq.each do |interface| - # Destroy the network interface if there is only one - # attached VM (which must be this VM) - if interface.attached_vms.length == 1 - logger.info "Destroying unused network interface..." - interface.destroy + env["config"].vm.network_options.compact.each do |network_options| + if !verify_no_bridge_collision(network_options) + env.error!(:network_collides) + return end end end - def before_boot + def call(env) + @env = env assign_network if enable_network? - end - def after_boot - if enable_network? - logger.info "Enabling host only network..." + @app.call(env) - runner.system.prepare_host_only_network - - runner.env.config.vm.network_options.compact.each do |network_options| - runner.system.enable_host_only_network(network_options) + if !env.error? && enable_network? + catch_action_exception(env) do + @env.logger.info "Enabling host only network..." + @env["vm"].system.prepare_host_only_network + @env.env.config.vm.network_options.compact.each do |network_options| + @env["vm"].system.enable_host_only_network(network_options) + end end end end - def enable_network? - !runner.env.config.vm.network_options.compact.empty? - end - - # Enables and assigns the host only network to the proper - # adapter on the VM, and saves the adapter. - def assign_network - logger.info "Preparing host only network..." - - runner.env.config.vm.network_options.compact.each do |network_options| - adapter = runner.vm.network_adapters[network_options[:adapter]] - adapter.enabled = true - adapter.attachment_type = :host_only - adapter.host_interface = network_name(network_options) - adapter.save - end - end - # Verifies that there is no collision with a bridged network interface # for the given network options. def verify_no_bridge_collision(net_options) - # First try to find a matching network interfaces = VirtualBox::Global.global.host.network_interfaces interfaces.each do |ni| next if ni.interface_type == :host_only @@ -76,7 +48,27 @@ module Vagrant true if matching_network?(ni, net_options) end - raise ActionException.new(:network_collides) if result + return false if result + end + + true + end + + def enable_network? + !@env.env.config.vm.network_options.compact.empty? + end + + # Enables and assigns the host only network to the proper + # adapter on the VM, and saves the adapter. + def assign_network + @env.logger.info "Preparing host only network..." + + @env.env.config.vm.network_options.compact.each do |network_options| + adapter = @env["vm"].vm.network_adapters[network_options[:adapter]] + adapter.enabled = true + adapter.attachment_type = :host_only + adapter.host_interface = network_name(network_options) + adapter.save end end @@ -98,10 +90,10 @@ module Vagrant end end - raise ActionException.new(:network_not_found, :name => net_options[:name]) if net_options[:name] + return @env.error!(:network_not_found, :name => net_options[:name]) if net_options[:name] # One doesn't exist, create it. - logger.info "Creating new host only network for environment..." + @env.logger.info "Creating new host only network for environment..." ni = interfaces.create ni.enable_static(network_ip(net_options[:ip], net_options[:netmask]), diff --git a/lib/vagrant/action/vm/package.rb b/lib/vagrant/action/vm/package.rb new file mode 100644 index 000000000..3647890f0 --- /dev/null +++ b/lib/vagrant/action/vm/package.rb @@ -0,0 +1,92 @@ +module Vagrant + class Action + module VM + class Package + include Util + + def initialize(app, env) + @app = app + @env = env + @env["package.output"] ||= "package" + @env["package.include"] ||= [] + + env.error!(:box_file_exists, :output_file => tar_path) if File.exist?(tar_path) + end + + def call(env) + @env = env + + return env.error!(:package_requires_export) if !@env["export.temp_dir"] + return if !verify_included_files + compress + + @app.call(env) + end + + def verify_included_files + @env["package.include"].each do |file| + if !File.exist?(file) + @env.error!(:package_include_file_doesnt_exist, :filename => file) + return false + end + end + + true + end + + # This method copies the include files (passed in via command line) + # to the temporary directory so they are included in a sub-folder within + # the actual box + def copy_include_files + if @env["package.include"].length > 0 + include_dir = File.join(@env["export.temp_dir"], "include") + FileUtils.mkdir_p(include_dir) + + @env["package.include"].each do |f| + @env.logger.info "Packaging additional file: #{f}" + FileUtils.cp(f, include_dir) + end + end + end + + # This method creates the auto-generated Vagrantfile at the root of the + # box. This Vagrantfile contains the MAC address so that the user doesn't + # have to worry about it. + def create_vagrantfile + File.open(File.join(@env["export.temp_dir"], "Vagrantfile"), "w") do |f| + f.write(TemplateRenderer.render("package_Vagrantfile", { + :base_mac => @env["vm"].vm.network_adapters.first.mac_address + })) + end + end + + # Compress the exported file into a package + def compress + @env.logger.info "Packaging VM into #{tar_path}..." + File.open(tar_path, Platform.tar_file_options) do |tar| + Archive::Tar::Minitar::Output.open(tar) do |output| + begin + current_dir = FileUtils.pwd + + copy_include_files + create_vagrantfile + + FileUtils.cd(@env["export.temp_dir"]) + Dir.glob(File.join(".", "**", "*")).each do |entry| + Archive::Tar::Minitar.pack_file(entry, output) + end + ensure + FileUtils.cd(current_dir) + end + end + end + end + + # Path to the final box output file + def tar_path + File.join(FileUtils.pwd, "#{@env["package.output"]}#{@env.env.config.package.extension}") + end + end + end + end +end diff --git a/lib/vagrant/action/vm/persist.rb b/lib/vagrant/action/vm/persist.rb new file mode 100644 index 000000000..73f37852b --- /dev/null +++ b/lib/vagrant/action/vm/persist.rb @@ -0,0 +1,22 @@ +module Vagrant + class Action + module VM + class Persist + def initialize(app, env) + @app = app + + # Error the environment if the dotfile is not valid + env.error!(:dotfile_error, :env => env.env) if File.exist?(env.env.dotfile_path) && + !File.file?(env.env.dotfile_path) + end + + def call(env) + env.logger.info "Persisting the VM UUID (#{env["vm"].uuid})" + env.env.update_dotfile + + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant/action/vm/provision.rb b/lib/vagrant/action/vm/provision.rb new file mode 100644 index 000000000..aa5da22d0 --- /dev/null +++ b/lib/vagrant/action/vm/provision.rb @@ -0,0 +1,50 @@ +module Vagrant + class Action + module VM + class Provision + def initialize(app, env) + @app = app + @env = env + + load_provisioner if provisioning_enabled? + end + + def call(env) + @app.call(env) + + if !env.error? && provisioning_enabled? + @env.logger.info "Beginning provisioning process..." + @provisioner.provision! + end + end + + def provisioning_enabled? + !@env["config"].vm.provisioner.nil? + end + + def load_provisioner + provisioner = @env["config"].vm.provisioner + + if provisioner.is_a?(Class) + @provisioner = provisioner.new(@env) + return @env.error!(:provisioner_invalid_class) unless @provisioner.is_a?(Provisioners::Base) + elsif provisioner.is_a?(Symbol) + # We have a few hard coded provisioners for built-ins + mapping = { + :chef_solo => Provisioners::ChefSolo, + :chef_server => Provisioners::ChefServer + } + + provisioner_klass = mapping[provisioner] + return @env.error!(:provisioner_unknown_type, :provisioner => provisioner.to_s) if provisioner_klass.nil? + @provisioner = provisioner_klass.new(@env) + end + + @env.logger.info "Provisioning enabled with #{@provisioner.class}" + @provisioner.prepare + @provisioner + end + end + end + end +end diff --git a/lib/vagrant/action/vm/resume.rb b/lib/vagrant/action/vm/resume.rb new file mode 100644 index 000000000..97533c011 --- /dev/null +++ b/lib/vagrant/action/vm/resume.rb @@ -0,0 +1,20 @@ +module Vagrant + class Action + module VM + class Resume + def initialize(app, env) + @app = app + end + + def call(env) + if env["vm"].vm.saved? + env.logger.info "Resuming suspended VM..." + env["actions"].run(:start) + end + + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant/actions/vm/shared_folders.rb b/lib/vagrant/action/vm/share_folders.rb similarity index 53% rename from lib/vagrant/actions/vm/shared_folders.rb rename to lib/vagrant/action/vm/share_folders.rb index 774f0618c..d5c1869a4 100644 --- a/lib/vagrant/actions/vm/shared_folders.rb +++ b/lib/vagrant/action/vm/share_folders.rb @@ -1,11 +1,36 @@ module Vagrant - module Actions + class Action module VM - class SharedFolders < Base + class ShareFolders + include ExceptionCatcher + + def initialize(app, env) + @app = app + @env = env + end + + def call(env) + @env = env + + clear_shared_folders + create_metadata + + @app.call(env) + + if !env.error? + catch_action_exception(env) do + # Only mount and setup shared folders in the absense of an + # error + mount_shared_folders + setup_unison + end + end + end + # This method returns an actual list of VirtualBox shared # folders to create and their proper path. def shared_folders - runner.env.config.vm.shared_folders.inject({}) do |acc, data| + @env.env.config.vm.shared_folders.inject({}) do |acc, data| key, value = data # This to prevent overwriting the actual shared folders data @@ -15,7 +40,7 @@ module Vagrant # Syncing this folder. Change the guestpath to reflect # what we're actually mounting. value[:original] = value.dup - value[:guestpath] = "#{value[:guestpath]}#{runner.env.config.unison.folder_suffix}" + value[:guestpath] = "#{value[:guestpath]}#{@env.env.config.unison.folder_suffix}" end acc[key] = value @@ -33,23 +58,39 @@ module Vagrant end end - def before_boot - clear_shared_folders - create_metadata + def clear_shared_folders + if @env["vm"].vm.shared_folders.length > 0 + @env.logger.info "Clearing previously set shared folders..." + + folders = @env["vm"].vm.shared_folders.dup + folders.each do |shared_folder| + shared_folder.destroy + end + + @env["vm"].reload! + end end - def after_boot - mount_shared_folders - setup_unison + def create_metadata + @env.logger.info "Creating shared folders metadata..." + + shared_folders.each do |name, data| + folder = VirtualBox::SharedFolder.new + folder.name = name + folder.host_path = File.expand_path(data[:hostpath], @env.env.root_path) + @env["vm"].vm.shared_folders << folder + end + + @env["vm"].vm.save end def mount_shared_folders - logger.info "Mounting shared folders..." + @env.logger.info "Mounting shared folders..." - @runner.ssh.execute do |ssh| + @env["vm"].ssh.execute do |ssh| shared_folders.each do |name, data| - logger.info "-- #{name}: #{data[:guestpath]}" - @runner.system.mount_shared_folder(ssh, name, data[:guestpath]) + @env.logger.info "-- #{name}: #{data[:guestpath]}" + @env["vm"].system.mount_shared_folder(ssh, name, data[:guestpath]) end end end @@ -57,41 +98,15 @@ module Vagrant def setup_unison return if unison_folders.empty? - runner.ssh.execute do |ssh| - runner.system.prepare_unison(ssh) + @env["vm"].ssh.execute do |ssh| + @env["vm"].system.prepare_unison(ssh) - logger.info "Creating unison crontab entries..." + @env.logger.info "Creating unison crontab entries..." unison_folders.each do |name, data| - runner.system.create_unison(ssh, data) + @env["vm"].system.create_unison(ssh, data) end end end - - def clear_shared_folders - if runner.vm.shared_folders.length > 0 - logger.info "Clearing previously set shared folders..." - - folders = @runner.vm.shared_folders.dup - folders.each do |shared_folder| - shared_folder.destroy - end - - @runner.reload! - end - end - - def create_metadata - logger.info "Creating shared folders metadata..." - - shared_folders.each do |name, data| - folder = VirtualBox::SharedFolder.new - folder.name = name - folder.host_path = File.expand_path(data[:hostpath], runner.env.root_path) - @runner.vm.shared_folders << folder - end - - @runner.vm.save - end end end end diff --git a/lib/vagrant/action/vm/suspend.rb b/lib/vagrant/action/vm/suspend.rb new file mode 100644 index 000000000..abda51e63 --- /dev/null +++ b/lib/vagrant/action/vm/suspend.rb @@ -0,0 +1,20 @@ +module Vagrant + class Action + module VM + class Suspend + def initialize(app, env) + @app = app + end + + def call(env) + if env["vm"].vm.running? + env.logger.info "Saving VM state and suspending execution..." + env["vm"].vm.save_state + end + + @app.call(env) + end + end + end + end +end diff --git a/lib/vagrant/actions/base.rb b/lib/vagrant/actions/base.rb deleted file mode 100644 index 65ad265a4..000000000 --- a/lib/vagrant/actions/base.rb +++ /dev/null @@ -1,130 +0,0 @@ -module Vagrant - module Actions - # Base class for any command actions. - # - # Actions are the smallest unit of functionality found within - # Vagrant. Vagrant composes many actions together to execute - # its complex tasks while keeping the individual pieces of a - # task as discrete reusable actions. Actions are ran exclusively - # by an {Runner action runner} which is simply a subclass of {Runner}. - # - # Actions work by implementing any or all of the following methods - # which a {Runner} executes: - # - # * `prepare` - Called once for each action before any action has `execute!` - # called. This is meant for basic setup. - # * `execute!` - This is where the meat of the action typically goes; - # the main code which executes the action. - # * `cleanup` - This is called exactly once for each action after every - # other action is completed. It is meant for cleaning up any resources. - # * `rescue` - This is called if an exception occurs in _any action_. This - # gives every other action a chance to clean itself up. - # - # For details of each step of an action, read the specific function call - # documentation below. - class Base - # The {Runner runner} which is executing the action - attr_reader :runner - - # Any options which are passed into the initializer as a hash. - attr_reader :options - - # Included so subclasses don't need to include it themselves. - include Vagrant::Util - - # A helper method for logging which simply gets the logger from - # the runner. Since actions tend to log quite a bit, this - # removes the need to prefix `logger` with `@runner` over and - # over. - def logger - runner.env.logger - end - - # Initialization of the action, passing any arguments which may have - # been given to the {Runner runner}. This method can be used by subclasses - # to save any of the configuration options which are passed in. - def initialize(runner, options=nil) - @runner = runner - @options = options || {} - end - - # This method is called once per action, allowing the action - # to setup any callbacks, add more events, etc. Prepare is - # called in the order the actions are defined, and the action - # itself has no control over this. - # - # Examples of its usage: - # - # Perhaps we need an additional action only if a configuration is set: - # - # def prepare - # @vm.actions << FooAction if Vagrant.config[:foo] == :bar - # end - # - def prepare; end - - # This method is called once, after preparing, to execute the - # actual task. This method is responsible for calling any - # callbacks. Adding new actions here will have unpredictable - # effects and should never be done. - # - # Examples of its usage: - # - # def execute! - # @vm.invoke_callback(:before_oven, "cookies") - # # Do lots of stuff here - # @vm.invoke_callback(:after_oven, "more", "than", "one", "option") - # end - # - def execute!; end - - # This method is called after all actions have finished executing. - # It is meant as a place where final cleanup code can be done, knowing - # that all other actions are finished using your data. - def cleanup; end - - # This method is only called if some exception occurs in the chain - # of actions. If an exception is raised in any action in the current - # chain, then every action part of that chain has {#rescue} called - # before raising the exception further. This method should be used to - # perform any cleanup necessary in the face of errors. - # - # **Warning:** Since this method is called when an exception is already - # raised, be _extra careful_ when implementing this method to handle - # all your own exceptions, otherwise it'll mask the initially raised - # exception. - def rescue(exception); end - - # The following two methods are used for declaring action dependencies. - # For example, you require that the reload action be in place before - # a your new FooAction you might do the following - # - # def follows; [Reload] end - - # This method is called when the runner is determining the actions that - # must precede a given action. You would say "This action follows [Action1, Action2]" - def follows; [] end - - # This method is called when the runner is determining the actions that - # must follow a given action. You would say "This action precedes [Action3, Action4] - def precedes; [] end - end - - # An exception which occured within an action. This should be used instead of - # {Vagrant::Util#error_and_exit error_and_exit}, since it allows the {Runner} to call - # {Base#rescue rescue} on all the actions and properly exit. Any message - # passed into the {ActionException} is then shown and and vagrant exits. - class ActionException < Exception - attr_reader :key - attr_reader :data - - def initialize(key, data = {}) - @key = key - @data = data - - message = Vagrant::Util::Translator.t(key, data) - super(message) - end - end - end -end diff --git a/lib/vagrant/actions/box/add.rb b/lib/vagrant/actions/box/add.rb deleted file mode 100644 index fbce7fb40..000000000 --- a/lib/vagrant/actions/box/add.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Vagrant - module Actions - module Box - # A meta-action which adds a box by downloading and unpackaging it. - # This action downloads and unpackages a box with a given URI. This - # is a _meta action_, meaning it simply adds more actions to the - # action chain, and those actions do the work. - # - # This is the action called by {Box#add}. - class Add < Base - def prepare - if File.exists?(@runner.directory) - raise ActionException.new(:box_add_already_exists, :box_name => @runner.name) - end - - @runner.add_action(Download) - @runner.add_action(Unpackage) - @runner.add_action(Verify) - end - end - end - end -end diff --git a/lib/vagrant/actions/box/destroy.rb b/lib/vagrant/actions/box/destroy.rb deleted file mode 100644 index ff9d6bd04..000000000 --- a/lib/vagrant/actions/box/destroy.rb +++ /dev/null @@ -1,14 +0,0 @@ -module Vagrant - module Actions - module Box - # Action to destroy a box. This action is not reversible and expects - # to be called by a {Box} object. - class Destroy < Base - def execute! - logger.info "Deleting box directory..." - FileUtils.rm_rf(@runner.directory) - end - end - end - end -end \ No newline at end of file diff --git a/lib/vagrant/actions/box/download.rb b/lib/vagrant/actions/box/download.rb deleted file mode 100644 index f066b2946..000000000 --- a/lib/vagrant/actions/box/download.rb +++ /dev/null @@ -1,67 +0,0 @@ -module Vagrant - module Actions - module Box - # An action which acts on a box by downloading the box file from - # the given URI into a temporary location. This action parses a - # given URI and handles downloading it via one of the many Vagrant - # downloads (such as {Vagrant::Downloaders::File}). - # - # This action cleans itself up by removing the downloaded box file. - class Download < Base - BASENAME = "box" - BUFFERSIZE = 1048576 # 1 MB - - attr_reader :downloader - - def prepare - # Check the URI given and prepare a downloader - [Downloaders::HTTP, Downloaders::File].each do |dler| - if dler.match?(@runner.uri) - logger.info "Downloading via #{dler}..." - @downloader = dler.new(@runner.env) - end - end - - raise ActionException.new(:box_download_unknown_type) unless @downloader - - # Prepare the downloader - @downloader.prepare(@runner.uri) - end - - def execute! - with_tempfile do |tempfile| - download_to(tempfile) - @runner.temp_path = tempfile.path - end - end - - def cleanup - if @runner.temp_path && File.exist?(@runner.temp_path) - logger.info "Cleaning up downloaded box..." - File.unlink(@runner.temp_path) - end - end - - def rescue(exception) - cleanup - end - - def with_tempfile - logger.info "Creating tempfile for storing box file..." - File.open(box_temp_path, Platform.tar_file_options) do |tempfile| - yield tempfile - end - end - - def box_temp_path - File.join(@runner.env.tmp_path, BASENAME + Time.now.to_i.to_s) - end - - def download_to(f) - logger.info "Copying box to temporary location..." - downloader.download!(@runner.uri, f) - end - end - end - end -end diff --git a/lib/vagrant/actions/box/unpackage.rb b/lib/vagrant/actions/box/unpackage.rb deleted file mode 100644 index 89df68fb4..000000000 --- a/lib/vagrant/actions/box/unpackage.rb +++ /dev/null @@ -1,42 +0,0 @@ -module Vagrant - module Actions - module Box - # This action unpackages a downloaded box file into its final - # box destination within the vagrant home folder. - class Unpackage < Base - def execute! - @runner.invoke_around_callback(:unpackage) do - setup_box_dir - decompress - end - end - - def rescue(exception) - if File.directory?(box_dir) - logger.info "An error occurred, rolling back box unpackaging..." - FileUtils.rm_rf(box_dir) - end - end - - def setup_box_dir - if File.directory?(box_dir) - error_and_exit(:box_already_exists, :box_name => @runner.name) - end - - FileUtils.mkdir_p(box_dir) - end - - def box_dir - @runner.directory - end - - def decompress - Dir.chdir(box_dir) do - logger.info "Extracting box to #{box_dir}..." - Archive::Tar::Minitar.unpack(@runner.temp_path, box_dir) - end - end - end - end - end -end diff --git a/lib/vagrant/actions/box/verify.rb b/lib/vagrant/actions/box/verify.rb deleted file mode 100644 index 8cc74fd6e..000000000 --- a/lib/vagrant/actions/box/verify.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Vagrant - module Actions - module Box - # This action verifies that a given box is valid. This works by attempting - # to read/interpret the appliance file (OVF). If the reading succeeds, then - # the box is assumed to be valid. - class Verify < Base - def execute! - logger.info "Verifying box..." - reload_configuration - verify_appliance - end - - def reload_configuration - # We have to reload the environment config since we _just_ added the - # box. We do this by setting the current box to the recently added box, - # then reloading - @runner.env.config.vm.box = @runner.name - @runner.env.load_box! - @runner.env.load_config! - end - - def verify_appliance - # We now try to read the applince. If it succeeds, we return true. - VirtualBox::Appliance.new(@runner.ovf_file) - rescue Exception - raise ActionException.new(:box_verification_failed) - end - end - end - end -end diff --git a/lib/vagrant/actions/collection.rb b/lib/vagrant/actions/collection.rb deleted file mode 100644 index 805fe4203..000000000 --- a/lib/vagrant/actions/collection.rb +++ /dev/null @@ -1,36 +0,0 @@ -module Vagrant - module Actions - class Collection < Array - def dependencies! - each_with_index do |action, i| - action.follows.each do |klass| - unless self[0..i].klasses.include?(klass) - raise DependencyNotSatisfiedException.new("#{action.class} action must follow #{klass}") - end - end - - action.precedes.each do |klass| - unless self[i..length].klasses.include?(klass) - raise DependencyNotSatisfiedException.new("#{action.class} action must precede #{klass}") - end - end - end - end - - def duplicates? - klasses.uniq.size != size - end - - def duplicates! - raise DuplicateActionException.new if duplicates? - end - - def klasses - map { |o| o.class } - end - end - - class DuplicateActionException < Exception; end - class DependencyNotSatisfiedException < Exception; end - end -end diff --git a/lib/vagrant/actions/runner.rb b/lib/vagrant/actions/runner.rb deleted file mode 100644 index 22a29e42f..000000000 --- a/lib/vagrant/actions/runner.rb +++ /dev/null @@ -1,131 +0,0 @@ -module Vagrant - module Actions - # Base class for any class which will act as a runner - # for actions. A runner handles queueing up and executing actions, - # and executing the methods of an action in the proper order. The - # action runner also handles invoking callbacks that actions may - # request. - # - # # Executing Actions - # - # Actions can be executed by adding them and executing them all - # at once: - # - # runner = Vagrant::Actions::Runner.new - # runner.add_action(FooAction) - # runner.add_action(BarAction) - # runner.add_action(BazAction) - # runner.execute! - # - # Single actions have a shorthand to be executed: - # - # Vagrant::Actions::Runner.execute!(FooAction) - # - # Arguments may be passed into added actions by adding them after - # the action class: - # - # runner.add_action(FooAction, "many", "arguments", "may", "follow") - # - class Runner - include Vagrant::Util - - class << self - # Executes a specific action, optionally passing in any arguments to that - # action. This method is shorthand to initializing a runner, adding a single - # action, and executing it. - def execute!(action_klass, *args) - runner = new - runner.add_action(action_klass, *args) - runner.execute! - end - end - - # Returns an array of all the actions in queue. Because this - # will persist accross calls (calling {#actions} twice will yield - # exactly the same object), to clear or modify it, use the ruby - # array methods which act on `self`, such as `Array#clear`. - # - # @return [Array] - def actions - @actions ||= Actions::Collection.new - end - - # Returns the first action instance which matches the given class. - # - # @param [Class] action_klass The action to search for in the queue - # @return [Object] - def find_action(action_klass) - actions.find { |a| a.is_a?(action_klass) } - end - - # Add an action to the list of queued actions to execute. This method - # appends the given action class to the end of the queue. Any arguments - # given after the class are passed into the class constructor. - def add_action(action_klass, *args) - actions << action_klass.new(self, *args) - end - - # Execute the actions in queue. This method can also optionally be used - # to execute a single action on an instance. The syntax for executing a - # single method on an instance is the same as the {execute!} class method. - def execute!(single_action=nil, *args) - if single_action - actions.clear - add_action(single_action, *args) - end - - actions.duplicates! - actions.dependencies! - - # Call the prepare method on each once its - # initialized, then call the execute! method - begin - [:prepare, :execute!, :cleanup].each do |method| - actions.each do |action| - action.send(method) - end - end - rescue Exception => e - # Run the rescue code to do any emergency cleanup - actions.each do |action| - action.rescue(e) - end - - # If its an ActionException, error and exit the message - if e.is_a?(ActionException) - error_and_exit(e.key, e.data) - return - end - - # Finally, reraise the exception - raise - end - - # Clear the actions - actions.clear - end - - # Invokes an "around callback" which invokes before_name and - # after_name for the given callback name, yielding a block between - # callback invokations. - def invoke_around_callback(name, *args) - invoke_callback("before_#{name}".to_sym, *args) - yield - invoke_callback("after_#{name}".to_sym, *args) - end - - # Invokes a single callback. This method will go through each action - # and call the method given in the parameter `name` if the action - # responds to it. - def invoke_callback(name, *args) - # Attempt to call the method for the callback on each of the - # actions - results = [] - actions.each do |action| - results << action.send(name, *args) if action.respond_to?(name) - end - results - end - end - end -end diff --git a/lib/vagrant/actions/vm/boot.rb b/lib/vagrant/actions/vm/boot.rb deleted file mode 100644 index a60a1c268..000000000 --- a/lib/vagrant/actions/vm/boot.rb +++ /dev/null @@ -1,43 +0,0 @@ -module Vagrant - module Actions - module VM - class Boot < Base - def execute! - @runner.invoke_around_callback(:boot) do - # Startup the VM - boot - - # Wait for it to complete booting, or error if we could - # never detect it booted up successfully - if !wait_for_boot - error_and_exit(:vm_failed_to_boot) - end - end - end - - def boot - logger.info "Booting VM..." - @runner.vm.start(@runner.env.config.vm.boot_mode) - end - - def wait_for_boot(sleeptime=5) - logger.info "Waiting for VM to boot..." - - @runner.env.config.ssh.max_tries.to_i.times do |i| - logger.info "Trying to connect (attempt ##{i+1} of #{@runner.env.config[:ssh][:max_tries]})..." - - if @runner.ssh.up? - logger.info "VM booted and ready for use!" - return true - end - - sleep sleeptime - end - - logger.info "Failed to connect to VM! Failed to boot?" - false - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/customize.rb b/lib/vagrant/actions/vm/customize.rb deleted file mode 100644 index c3e5fe816..000000000 --- a/lib/vagrant/actions/vm/customize.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Vagrant - module Actions - module VM - class Customize < Base - def execute! - if !runner.env.config.vm.proc_stack.empty? - logger.info "Running any VM customizations..." - - # Run the customization procs over the VM - runner.env.config.vm.run_procs!(@runner.vm) - - # Save the vm - runner.vm.save - end - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/destroy.rb b/lib/vagrant/actions/vm/destroy.rb deleted file mode 100644 index b65f883de..000000000 --- a/lib/vagrant/actions/vm/destroy.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Vagrant - module Actions - module VM - class Destroy < Base - def execute! - @runner.invoke_around_callback(:destroy) do - destroy_vm - update_dotfile - end - end - - def destroy_vm - logger.info "Destroying VM and associated drives..." - @runner.vm.destroy(:destroy_medium => :delete) - @runner.vm = nil - end - - def update_dotfile - @runner.env.update_dotfile - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/down.rb b/lib/vagrant/actions/vm/down.rb deleted file mode 100644 index 53142f545..000000000 --- a/lib/vagrant/actions/vm/down.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Vagrant - module Actions - module VM - class Down < Base - def prepare - # The true as the 2nd parameter always forces the shutdown so its - # fast (since we're destroying anyways) - @runner.add_action(Halt, :force => true) if @runner.vm.running? - @runner.add_action(Network) - @runner.add_action(Destroy) - end - - def after_halt - # This sleep is necessary to wait for the VM to clean itself up. - # There appears to be nothing in the API that does this "wait" - # for us. - Kernel.sleep(1) - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/export.rb b/lib/vagrant/actions/vm/export.rb deleted file mode 100644 index 7993dc285..000000000 --- a/lib/vagrant/actions/vm/export.rb +++ /dev/null @@ -1,45 +0,0 @@ -module Vagrant - module Actions - module VM - class Export < Base - attr_reader :temp_dir - - def execute! - setup_temp_dir - export - end - - def cleanup - if temp_dir - logger.info "Removing temporary export directory..." - FileUtils.rm_r(temp_dir) - end - end - - def rescue(exception) - cleanup - end - - def setup_temp_dir - @temp_dir = File.join(@runner.env.tmp_path, Time.now.to_i.to_s) - - logger.info "Creating temporary directory for export..." - FileUtils.mkpath(temp_dir) - end - - def ovf_path - File.join(temp_dir, @runner.env.config.vm.box_ovf) - end - - def export - logger.info "Exporting VM to #{ovf_path}..." - @runner.vm.export(ovf_path) do |progress| - logger.report_progress(progress.percent, 100, false) - end - - logger.clear_progress - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/halt.rb b/lib/vagrant/actions/vm/halt.rb deleted file mode 100644 index ca5c9abab..000000000 --- a/lib/vagrant/actions/vm/halt.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Vagrant - module Actions - module VM - class Halt < Base - def execute! - raise ActionException.new(:vm_not_running) unless @runner.vm.running? - - @runner.invoke_around_callback(:halt) do - @runner.system.halt if !options[:force] - - if @runner.vm.state(true) != :powered_off - logger.info "Forcing shutdown of VM..." - @runner.vm.stop - end - end - end - - def force? - !!options[:force] - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/import.rb b/lib/vagrant/actions/vm/import.rb deleted file mode 100644 index 4a545c152..000000000 --- a/lib/vagrant/actions/vm/import.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Vagrant - module Actions - module VM - class Import < Base - def execute! - @runner.invoke_around_callback(:import) do - Busy.busy do - logger.info "Importing base VM (#{@runner.env.box.ovf_file})..." - # Use the first argument passed to the action - @runner.vm = VirtualBox::VM.import(@runner.env.box.ovf_file) do |progress| - logger.report_progress(progress.percent, 100, false) - end - - logger.clear_progress - - raise ActionException.new(:virtualbox_import_failure) unless @runner.vm - end - end - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/move_hard_drive.rb b/lib/vagrant/actions/vm/move_hard_drive.rb deleted file mode 100644 index 65caadb1d..000000000 --- a/lib/vagrant/actions/vm/move_hard_drive.rb +++ /dev/null @@ -1,51 +0,0 @@ -module Vagrant - module Actions - module VM - class MoveHardDrive < Base - def execute! - unless @runner.powered_off? - error_and_exit(:vm_power_off_to_move_hd) - return - end - - destroy_drive_after { clone_and_attach } - end - - def hard_drive - @hard_drive ||= find_hard_drive - end - - # TODO won't work if the first disk is not the boot disk or even if there are multiple disks - def find_hard_drive - @runner.vm.storage_controllers.each do |sc| - sc.devices.each do |d| - return d if d.image.is_a?(VirtualBox::HardDrive) - end - end - end - - def clone_and_attach - logger.info "Cloning current VM Disk to new location (#{new_image_path})..." - hard_drive.image = hard_drive.image.clone(new_image_path, @runner.env.config.vm.disk_image_format, true) - - logger.info "Attaching new disk to VM ..." - @runner.vm.save - end - - def destroy_drive_after - old_image = hard_drive.image - - yield - - logger.info "Destroying old VM Disk (#{old_image.filename})..." - old_image.destroy(true) - end - - # Returns the path to the new location for the hard drive - def new_image_path - File.join(@runner.env.config.vm.hd_location, hard_drive.image.filename) - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/package.rb b/lib/vagrant/actions/vm/package.rb deleted file mode 100644 index f4f47de2d..000000000 --- a/lib/vagrant/actions/vm/package.rb +++ /dev/null @@ -1,94 +0,0 @@ -module Vagrant - module Actions - module VM - class Package < Base - attr_reader :export_action - - def initialize(*args) - super - @temp_path = nil - end - - def prepare - raise ActionException.new(:box_file_exists, :output_file => tar_path) if File.exist?(tar_path) - - # Verify the existance of all the additional files, if any - include_files.each do |file| - raise ActionException.new(:package_include_file_doesnt_exist, :filename => file) unless File.exists?(file) - end - - # Get the export action and store a reference to it - @export_action = @runner.find_action(Export) - raise ActionException.new(:packaged_requires_export) unless @export_action - end - - def execute! - compress - end - - def out_path - options[:output] || "package" - end - - def include_files - options[:include] || [] - end - - def tar_path - File.join(FileUtils.pwd, "#{out_path}#{@runner.env.config.package.extension}") - end - - def temp_path - export_action.temp_dir - end - - # This method copies the include files (passed in via command line) - # to the temporary directory so they are included in a sub-folder within - # the actual box - def copy_include_files - if include_files.length > 0 - include_dir = File.join(temp_path, "include") - FileUtils.mkdir_p(include_dir) - - include_files.each do |f| - logger.info "Packaging additional file: #{f}" - FileUtils.cp(f, include_dir) - end - end - end - - # This method creates the auto-generated Vagrantfile at the root of the - # box. This Vagrantfile contains the MAC address so that the user doesn't - # have to worry about it. - def create_vagrantfile - File.open(File.join(temp_path, "Vagrantfile"), "w") do |f| - f.write(TemplateRenderer.render("package_Vagrantfile", { - :base_mac => @runner.vm.network_adapters.first.mac_address - })) - end - end - - def compress - logger.info "Packaging VM into #{tar_path}..." - File.open(tar_path, Platform.tar_file_options) do |tar| - Archive::Tar::Minitar::Output.open(tar) do |output| - begin - current_dir = FileUtils.pwd - - copy_include_files - create_vagrantfile - - FileUtils.cd(temp_path) - Dir.glob(File.join(".", "**", "*")).each do |entry| - Archive::Tar::Minitar.pack_file(entry, output) - end - ensure - FileUtils.cd(current_dir) - end - end - end - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/provision.rb b/lib/vagrant/actions/vm/provision.rb deleted file mode 100644 index dea5760ca..000000000 --- a/lib/vagrant/actions/vm/provision.rb +++ /dev/null @@ -1,49 +0,0 @@ -module Vagrant - module Actions - module VM - class Provision < Base - attr_reader :provisioner - - def intialize(*args) - super - - @provisioner = nil - end - - def prepare - provisioner = @runner.env.config.vm.provisioner - - if provisioner.nil? - logger.info("Provisioning not enabled, ignoring this step") - return - end - - if provisioner.is_a?(Class) - @provisioner = provisioner.new(@runner) - raise ActionException.new(:provisioner_invalid_class) unless @provisioner.is_a?(Provisioners::Base) - elsif provisioner.is_a?(Symbol) - # We have a few hard coded provisioners for built-ins - mapping = { - :chef_solo => Provisioners::ChefSolo, - :chef_server => Provisioners::ChefServer - } - - provisioner_klass = mapping[provisioner] - raise ActionException.new(:provisioner_unknown_type, :provisioner => provisioner.to_s) if provisioner_klass.nil? - @provisioner = provisioner_klass.new(@runner) - end - - logger.info "Provisioning enabled with #{@provisioner.class}" - @provisioner.prepare - end - - def execute! - if provisioner - logger.info "Beginning provisioning process..." - provisioner.provision! - end - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/reload.rb b/lib/vagrant/actions/vm/reload.rb deleted file mode 100644 index c3dad1640..000000000 --- a/lib/vagrant/actions/vm/reload.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Vagrant - module Actions - module VM - class Reload < Base - def prepare - steps = [Customize, ForwardPorts, SharedFolders, Network, Boot] - steps.unshift(Halt) if @runner.vm.running? - steps << Provision if !@runner.env.config.vm.provisioner.nil? - - steps.each do |action_klass| - @runner.add_action(action_klass) - end - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/resume.rb b/lib/vagrant/actions/vm/resume.rb deleted file mode 100644 index 118b9ca8e..000000000 --- a/lib/vagrant/actions/vm/resume.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Vagrant - module Actions - module VM - class Resume < Base - def execute! - if !@runner.vm.saved? - raise ActionException.new(:vm_not_suspended) - end - - logger.info "Resuming suspended VM..." - @runner.start - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/start.rb b/lib/vagrant/actions/vm/start.rb deleted file mode 100644 index 069f1d0b3..000000000 --- a/lib/vagrant/actions/vm/start.rb +++ /dev/null @@ -1,26 +0,0 @@ -module Vagrant - module Actions - module VM - class Start < Base - def prepare - # Start is a "meta-action" so it really just queues up a bunch - # of other actions in its place: - steps = [Boot] - if !@runner.vm || !@runner.vm.saved? - steps.unshift([Customize, ForwardPorts, SharedFolders, Network]) - steps << Provision if provision? - end - - steps.flatten.each do |action_klass| - @runner.add_action(action_klass, options) - end - end - - def provision? - enabled = options[:provision].nil? ? true : options[:provision] - !@runner.env.config.vm.provisioner.nil? && enabled - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/suspend.rb b/lib/vagrant/actions/vm/suspend.rb deleted file mode 100644 index 2fb75b174..000000000 --- a/lib/vagrant/actions/vm/suspend.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Vagrant - module Actions - module VM - class Suspend < Base - def execute! - if !@runner.vm.running? - raise ActionException.new(:vm_not_running_for_suspend) - end - - logger.info "Saving VM state and suspending execution..." - @runner.vm.save_state - end - end - end - end -end diff --git a/lib/vagrant/actions/vm/up.rb b/lib/vagrant/actions/vm/up.rb deleted file mode 100644 index 69091c1a9..000000000 --- a/lib/vagrant/actions/vm/up.rb +++ /dev/null @@ -1,53 +0,0 @@ -module Vagrant - module Actions - module VM - class Up < Base - def prepare - # If the dotfile is not a file, raise error - if File.exist?(@runner.env.dotfile_path) && !File.file?(@runner.env.dotfile_path) - raise ActionException.new(:dotfile_error, :env => @runner.env) - end - - # Up is a "meta-action" so it really just queues up a bunch - # of other actions in its place: - steps = [Import, Start] - steps.insert(0, MoveHardDrive) if @runner.env.config.vm.hd_location - - steps.each do |action_klass| - @runner.add_action(action_klass, options) - end - end - - def after_import - update_dotfile - setup_mac_address - check_guest_additions - end - - def update_dotfile - logger.info "Persisting the VM UUID (#{@runner.uuid})..." - @runner.env.update_dotfile - end - - def setup_mac_address - logger.info "Matching MAC addresses..." - @runner.vm.network_adapters.first.mac_address = @runner.env.config.vm.base_mac - @runner.vm.save - end - - def check_guest_additions - # Use the raw interface for now, while the virtualbox gem - # doesn't support guest properties (due to cross platform issues) - version = @runner.vm.interface.get_guest_property_value("/VirtualBox/GuestAdd/Version") - if version.empty? - logger.error Translator.t(:vm_additions_not_detected) - elsif version != VirtualBox.version - logger.error Translator.t(:vm_additions_version_mismatch, - :guest_additions_version => version, - :virtualbox_version => VirtualBox.version) - end - end - end - end - end -end diff --git a/lib/vagrant/box.rb b/lib/vagrant/box.rb index 723fa2c74..857d0d953 100644 --- a/lib/vagrant/box.rb +++ b/lib/vagrant/box.rb @@ -40,7 +40,7 @@ module Vagrant # box = Vagrant::Box.find("foo") # box.destroy # - class Box < Actions::Runner + class Box # The name of the box. attr_accessor :name @@ -133,12 +133,12 @@ module Vagrant # method requires that `name` and `uri` be set. The logic of this method # is kicked out to the {Actions::Box::Add add box} action. def add - execute!(Actions::Box::Add) + env.actions.run(:box_add, { "box" => self }) end # Beings the process of destroying this box. def destroy - execute!(Actions::Box::Destroy) + env.actions.run(:box_remove, { "box" => self }) end # Returns the directory to the location of this boxes content in the local diff --git a/lib/vagrant/commands/package.rb b/lib/vagrant/commands/package.rb index f934840f0..bdca30f97 100644 --- a/lib/vagrant/commands/package.rb +++ b/lib/vagrant/commands/package.rb @@ -53,11 +53,6 @@ module Vagrant end def package_vm(vm) - if !vm.powered_off? - error_and_exit(:vm_power_off_to_package) - return # for tests - end - vm.package(options) end diff --git a/lib/vagrant/downloaders/file.rb b/lib/vagrant/downloaders/file.rb index 86369201c..e0adf2ee9 100644 --- a/lib/vagrant/downloaders/file.rb +++ b/lib/vagrant/downloaders/file.rb @@ -9,7 +9,7 @@ module Vagrant def prepare(source_url) if !::File.file?(source_url) - raise Actions::ActionException.new(:downloader_file_doesnt_exist, :source_url => source_url) + raise Action::ActionException.new(:downloader_file_doesnt_exist, :source_url => source_url) end end diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index f58608e1c..0ec2ff2c8 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -21,6 +21,7 @@ module Vagrant attr_reader :active_list attr_reader :commands attr_reader :logger + attr_reader :actions #--------------------------------------------------------------- # Class Methods @@ -142,6 +143,7 @@ module Vagrant load_vm! load_active_list! load_commands! + load_actions! self end @@ -301,6 +303,13 @@ module Vagrant @commands = Command.new(self) end + # Loads the instance of {Action} for this environment. This allows + # users of the instance to run action sequences in the context of + # this environment. + def load_actions! + @actions = Action.new(self) + end + #--------------------------------------------------------------- # Methods to manage VM #--------------------------------------------------------------- diff --git a/lib/vagrant/provisioners/base.rb b/lib/vagrant/provisioners/base.rb index f393e2152..be2d450f0 100644 --- a/lib/vagrant/provisioners/base.rb +++ b/lib/vagrant/provisioners/base.rb @@ -7,19 +7,27 @@ module Vagrant class Base include Vagrant::Util - # The VM which this is being provisioned for - attr_reader :vm + # The environment which provisioner is running in. This is a + # {Vagrant::Action::Environment} + attr_reader :action_env - def initialize(vm) - @vm = vm + def initialize(env) + @action_env = env end - # This method returns the environment which the provisioner is working - # on. This is also the environment of the VM. This method is provided - # as a simple helper since the environment is often used throughout the - # provisioner. + # Returns the actual {Vagrant::Environment} which this provisioner + # represents. + # + # @return [Vagrant::Environment] def env - @vm.env + action_env.env + end + + # Returns the VM which this provisioner is working on. + # + # @return [Vagrant::VM] + def vm + env.vm end # This method returns the environment's logger as a convenience diff --git a/lib/vagrant/provisioners/chef.rb b/lib/vagrant/provisioners/chef.rb index 2b958c329..22996e51d 100644 --- a/lib/vagrant/provisioners/chef.rb +++ b/lib/vagrant/provisioners/chef.rb @@ -72,7 +72,7 @@ module Vagrant Config.configures :chef, ChefConfig def prepare - raise Actions::ActionException.new(:chef_base_invalid_provisioner) + action_env.error!(:chef_base_invalid_provisioner) end def verify_binary(binary) diff --git a/lib/vagrant/provisioners/chef_server.rb b/lib/vagrant/provisioners/chef_server.rb index 2eba7e3d6..a4b1ee7f6 100644 --- a/lib/vagrant/provisioners/chef_server.rb +++ b/lib/vagrant/provisioners/chef_server.rb @@ -5,13 +5,11 @@ module Vagrant class ChefServer < Chef def prepare if env.config.chef.validation_key_path.nil? - raise Actions::ActionException.new(:chef_server_validation_key_required) + action_env.error!(:chef_server_validation_key_required) elsif !File.file?(validation_key_path) - raise Actions::ActionException.new(:chef_server_validation_key_doesnt_exist) - end - - if env.config.chef.chef_server_url.nil? - raise Actions::ActionException.new(:chef_server_url_required) + action_env.error!(:chef_server_validation_key_doesnt_exist) + elsif env.config.chef.chef_server_url.nil? + action_env.error!(:chef_server_url_required) end end diff --git a/lib/vagrant/ssh.rb b/lib/vagrant/ssh.rb index d78c92b0c..f2e226b34 100644 --- a/lib/vagrant/ssh.rb +++ b/lib/vagrant/ssh.rb @@ -219,7 +219,7 @@ module Vagrant } }.merge(options || {}) - raise Actions::ActionException.new(options[:error_key], options[:error_data]) + raise Action::ActionException.new(options[:error_key], options[:error_data]) end end end diff --git a/lib/vagrant/systems/linux.rb b/lib/vagrant/systems/linux.rb index 2b0d1bb7c..305b6b3b1 100644 --- a/lib/vagrant/systems/linux.rb +++ b/lib/vagrant/systems/linux.rb @@ -119,7 +119,7 @@ module Vagrant break unless result attempts += 1 - raise Actions::ActionException.new(:vm_mount_fail) if attempts >= 10 + raise Action::ActionException.new(:vm_mount_fail) if attempts >= 10 sleep sleeptime end end diff --git a/lib/vagrant/vm.rb b/lib/vagrant/vm.rb index 4ab57930b..d9a00043d 100644 --- a/lib/vagrant/vm.rb +++ b/lib/vagrant/vm.rb @@ -1,5 +1,5 @@ module Vagrant - class VM < Actions::Runner + class VM include Vagrant::Util attr_reader :env @@ -94,43 +94,41 @@ module Vagrant end def package(options=nil) - add_action(Actions::VM::Export, options) - add_action(Actions::VM::Package, options) - execute! + env.actions.run(:package, options) end def up(options=nil) - execute!(Actions::VM::Up, options) + env.actions.run(:up, options) end def start return if @vm.running? - execute!(Actions::VM::Start) + env.actions.run(:start) end def halt(options=nil) - execute!(Actions::VM::Halt, options) + env.actions.run(:halt, options) end def reload - execute!(Actions::VM::Reload) + env.actions.run(:reload) end def provision - execute!(Actions::VM::Provision) + env.actions.run(:provision) end def destroy - execute!(Actions::VM::Down) + env.actions.run(:destroy) end def suspend - execute!(Actions::VM::Suspend) + env.actions.run(:suspend) end def resume - execute!(Actions::VM::Resume) + env.actions.run(:resume) end def saved? diff --git a/templates/strings.yml b/templates/strings.yml index 674b5c6cf..f2fc149c8 100644 --- a/templates/strings.yml +++ b/templates/strings.yml @@ -250,8 +250,6 @@ Failed to connect to VM! Failed to boot? :vm_base_not_found: |- The specified base VM "<%= name %>" was not found. -:vm_not_running: |- - VM is not running! Nothing to shut down! :vm_not_running_for_suspend: |- The vagrant virtual environment you are trying to suspend must be running to be suspended. :vm_not_suspended: |- diff --git a/test/test_helper.rb b/test/test_helper.rb index 33fb1f575..751b53394 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -94,30 +94,10 @@ class Test::Unit::TestCase vm end - # Sets up the mocks and instantiates an action for testing - def mock_action(action_klass, *args) - vm = mock("vboxvm") - mock_vm = mock("vm") - action = action_klass.new(mock_vm, *args) - stub_default_action_dependecies(action) - - mock_vm.stubs(:name).returns("foo") - mock_vm.stubs(:vm).returns(vm) - mock_vm.stubs(:vm=) - mock_vm.stubs(:invoke_callback) - mock_vm.stubs(:invoke_around_callback).yields - mock_vm.stubs(:actions).returns([action]) - mock_vm.stubs(:env).returns(mock_environment) - mock_vm.env.stubs(:logger).returns(quiet_logger("mock")) - - mock_ssh = Vagrant::SSH.new(mock_vm.env) - mock_ssh.stubs(:execute) - - mock_vm.stubs(:ssh).returns(mock_ssh) - - vm.stubs(:env).returns(mock_vm.env) - - [mock_vm, vm, action] + def mock_action_data + app = lambda { |env| } + env = Vagrant::Action::Environment.new(mock_environment) + [app, env] end # Returns a resource logger which is safe for tests diff --git a/test/vagrant/action/box/destroy_test.rb b/test/vagrant/action/box/destroy_test.rb new file mode 100644 index 000000000..72c44b00d --- /dev/null +++ b/test/vagrant/action/box/destroy_test.rb @@ -0,0 +1,30 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class DestroyBoxActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::Box::Destroy + @app, @env = mock_action_data + + @vm = mock("vm") + @env["vm"] = @vm + @env["box"] = Vagrant::Box.new(mock_environment, "foo") + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + context "calling" do + setup do + @env.logger.stubs(:info) + end + + should "delete the box directory" do + seq = sequence("seq") + FileUtils.expects(:rm_rf).with(@env["box"].directory).in_sequence(seq) + @app.expects(:call).with(@env).once.in_sequence(seq) + @instance.call(@env) + end + end +end diff --git a/test/vagrant/action/box/download_test.rb b/test/vagrant/action/box/download_test.rb new file mode 100644 index 000000000..cd283fa9b --- /dev/null +++ b/test/vagrant/action/box/download_test.rb @@ -0,0 +1,141 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class DownloadBoxActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::Box::Download + @app, @env = mock_action_data + + @vm = mock("vm") + @env["vm"] = @vm + @env["box"] = Vagrant::Box.new(mock_environment, "foo") + @env["box"].uri = "http://google.com" + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + end + + context "initializing" do + should "initialize download classes" do + @klass.new(@app, @env) + assert_equal [Vagrant::Downloaders::HTTP, Vagrant::Downloaders::File], @env["download.classes"] + end + end + + context "with an instance" do + setup do + @instance = @klass.new(@app, @env) + end + + context "calling" do + should "call the proper methods in sequence" do + seq = sequence("seq") + @instance.expects(:instantiate_downloader).in_sequence(seq).returns(true) + @instance.expects(:download).in_sequence(seq) + @app.expects(:call).with(@env).in_sequence(seq) + @instance.expects(:cleanup).in_sequence(seq) + @instance.call(@env) + end + + should "halt the chain if downloader instantiation fails" do + seq = sequence("seq") + @env.error!(:foo) + @instance.expects(:instantiate_downloader).in_sequence(seq).returns(false) + @instance.expects(:download).never + @app.expects(:call).with(@env).never + @instance.expects(:cleanup).never + @instance.call(@env) + end + end + + context "instantiating downloader" do + should "instantiate the proper class" do + instance = mock("instance") + Vagrant::Downloaders::HTTP.expects(:new).with(@env["box"].uri).returns(instance) + instance.expects(:prepare).with(@env["box"].uri).once + assert @instance.instantiate_downloader + end + + should "error environment if URI is invalid for any downloaders" do + @env["box"].uri = "foobar" + assert !@instance.instantiate_downloader + assert @env.error? + assert_equal :box_download_unknown_type, @env.error.first + end + end + + context "downloading" do + setup do + @path = "foo" + + @tempfile = mock("tempfile") + @tempfile.stubs(:path).returns(@path) + + @instance.stubs(:with_tempfile).yields(@tempfile) + @instance.stubs(:download_to) + end + + should "make a tempfile and copy the URI contents to it" do + @instance.expects(:with_tempfile).yields(@tempfile) + @instance.expects(:download_to).with(@tempfile) + @instance.download + end + + should "save the tempfile path" do + @instance.download + assert @env.has_key?("download.temp_path") + assert_equal @tempfile.path, @env["download.temp_path"] + assert_equal @tempfile.path, @instance.temp_path + end + end + + context "tempfile" do + should "create a tempfile in the vagrant tmp directory" do + File.expects(:open).with { |name, bitmask| + name =~ /#{Vagrant::Action::Box::Download::BASENAME}/ && name =~ /#{@env.env.tmp_path}/ + }.once + @instance.with_tempfile + end + + should "yield the tempfile object" do + @tempfile = mock("tempfile") + File.expects(:open).yields(@tempfile) + + @instance.with_tempfile do |otherfile| + assert @tempfile.equal?(otherfile) + end + end + end + + context "cleaning up" do + setup do + @temp_path = "foo" + @instance.stubs(:temp_path).returns(@temp_path) + File.stubs(:exist?).returns(true) + end + + should "delete the temporary file if it exists" do + File.expects(:unlink).with(@temp_path).once + @instance.cleanup + end + + should "not delete anything if it doesn't exist" do + File.stubs(:exist?).returns(false) + File.expects(:unlink).never + @instance.cleanup + end + end + + context "downloading to" do + setup do + @downloader = mock("downloader") + @instance.instance_variable_set(:@downloader, @downloader) + end + + should "call download! on the download with the URI and tempfile" do + tempfile = "foo" + @downloader.expects(:download!).with(@env["box"].uri, tempfile) + @instance.download_to(tempfile) + end + end + end +end diff --git a/test/vagrant/action/box/unpackage_test.rb b/test/vagrant/action/box/unpackage_test.rb new file mode 100644 index 000000000..685b67059 --- /dev/null +++ b/test/vagrant/action/box/unpackage_test.rb @@ -0,0 +1,103 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class UnpackageBoxActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::Box::Unpackage + @app, @env = mock_action_data + + @vm = mock("vm") + @env["vm"] = @vm + @env["box"] = Vagrant::Box.new(mock_environment, "foo") + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + context "calling" do + should "call the proper chain" do + seq = sequence("sequence") + @instance.expects(:setup_box_directory).in_sequence(seq).returns(true) + @instance.expects(:decompress).in_sequence(seq) + @app.expects(:call).with(@env) + @instance.expects(:cleanup).never + @instance.call(@env) + end + + should "halt the chain if setting up the box directory fails" do + @instance.expects(:setup_box_directory).returns(false) + @instance.expects(:decompress).never + @app.expects(:call).never + @instance.expects(:cleanup).never + @instance.call(@env) + end + + should "cleanup if there was an error" do + @env.error!(:foo) + + seq = sequence("sequence") + @instance.expects(:setup_box_directory).in_sequence(seq).returns(true) + @instance.expects(:decompress).in_sequence(seq) + @app.expects(:call).with(@env) + @instance.expects(:cleanup).once + @instance.call(@env) + end + end + + context "cleaning up" do + setup do + @instance.stubs(:box_directory).returns("foo") + File.stubs(:directory?).returns(false) + FileUtils.stubs(:rm_rf) + end + + should "do nothing if not a directory" do + FileUtils.expects(:rm_rf).never + @instance.cleanup + end + + should "remove the directory if exists" do + File.expects(:directory?).with(@instance.box_directory).once.returns(true) + FileUtils.expects(:rm_rf).with(@instance.box_directory).once + @instance.cleanup + end + end + + context "setting up the box directory" do + setup do + File.stubs(:directory?).returns(false) + FileUtils.stubs(:mkdir_p) + end + + should "error the environment if the box already exists" do + File.expects(:directory?).returns(true) + assert !@instance.setup_box_directory + assert @env.error? + assert_equal :box_already_exists, @env.error.first + end + + should "create the directory" do + FileUtils.expects(:mkdir_p).with(@env["box"].directory).once + @instance.setup_box_directory + end + end + + context "decompressing" do + setup do + @env["download.temp_path"] = "bar" + + Dir.stubs(:chdir).yields + end + + should "change to the box directory" do + Dir.expects(:chdir).with(@env["box"].directory) + @instance.decompress + end + + should "open the tar file within the new directory, and extract it all" do + Archive::Tar::Minitar.expects(:unpack).with(@env["download.temp_path"], @env["box"].directory).once + @instance.decompress + end + end +end diff --git a/test/vagrant/action/box/verify_test.rb b/test/vagrant/action/box/verify_test.rb new file mode 100644 index 000000000..82fe97083 --- /dev/null +++ b/test/vagrant/action/box/verify_test.rb @@ -0,0 +1,39 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class VerifyBoxActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::Box::Verify + @app, @env = mock_action_data + + @vm = mock("vm") + @env["vm"] = @vm + @env["box"] = Vagrant::Box.new(mock_environment, "foo") + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + context "calling" do + setup do + @env.logger.stubs(:info) + end + + should "continue fine if verification succeeds" do + seq = sequence("seq") + VirtualBox::Appliance.expects(:new).with(@env["box"].ovf_file).in_sequence(seq) + @app.expects(:call).with(@env).once.in_sequence(seq) + @instance.call(@env) + assert !@env.error? + end + + should "halt chain if verification fails" do + VirtualBox::Appliance.expects(:new).with(@env["box"].ovf_file).raises(Exception) + @app.expects(:call).with(@env).never + @instance.call(@env) + assert @env.error? + assert_equal :box_verification_failed, @env.error.first + end + end +end diff --git a/test/vagrant/action/builder_test.rb b/test/vagrant/action/builder_test.rb new file mode 100644 index 000000000..e4c1dc17c --- /dev/null +++ b/test/vagrant/action/builder_test.rb @@ -0,0 +1,207 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') + +class ActionBuilderTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::Builder + end + + context "initializing" do + should "setup empty middleware stack" do + builder = @klass.new + assert builder.stack.empty? + end + + should "take block to setup stack" do + builder = @klass.new do + use Hash + use lambda { |i| i } + end + + assert !builder.stack.empty? + assert_equal 2, builder.stack.length + end + end + + context "with an instance" do + setup do + @instance = @klass.new + end + + context "adding to the stack" do + should "return self" do + assert @instance.equal?(@instance.use(1)) + end + + should "add to the end" do + @instance.use 1 + @instance.use 2 + assert_equal [2, [], nil], @instance.stack.last + end + + should "merge in other builder's stack" do + other = @klass.new do + use 2 + use 3 + end + + @instance.use 1 + @instance.use other + assert_equal 3, @instance.stack.length + end + end + + context "flatten" do + should "return the flattened format of the builder" do + env = Vagrant::Action::Environment.new(nil) + env.expects(:foo).once + + func = lambda { |x| x.foo } + @instance.use func + proc = @instance.flatten + assert proc.respond_to?(:call) + proc.call(env) + end + end + + context "inserting" do + setup do + @instance.use "1" + @instance.use "2" + end + + should "insert at the proper numeric index" do + @instance.insert(1, "3") + assert_equal "3", @instance.stack[1].first + end + + should "insert next to the proper object if given" do + @instance.insert("2", "3") + assert_equal "3", @instance.stack[1].first + end + + should "be able to call insert_before as well" do + @instance.insert_before("1", "0") + assert_equal "0", @instance.stack.first.first + end + + should "be able to insert_after" do + @instance.insert_after("1", "0") + assert_equal "0", @instance.stack[1].first + end + + should "be able to insert_after using numeric index" do + @instance.insert_after(1, "0") + assert_equal "0", @instance.stack[2].first + end + + should "raise an exception if invalid index" do + assert_raises(RuntimeError) { + @instance.insert_after("15", "0") + } + end + end + + context "swapping" do + setup do + @instance.use "1" + @instance.use "2" + end + + should "be able to swap using the object" do + @instance.swap "1", "3" + assert_equal "3", @instance.stack.first.first + end + + should "be able to swap using the index" do + @instance.swap 0, "3" + assert_equal "3", @instance.stack.first.first + end + end + + context "deleting" do + setup do + @instance.use "1" + end + + should "delete the proper object" do + @instance.delete("1") + assert @instance.stack.empty? + end + + should "delete by index if given" do + @instance.delete(0) + assert @instance.stack.empty? + end + end + + context "getting an index of an object" do + should "return the proper index if it exists" do + @instance.use 1 + @instance.use 2 + @instance.use 3 + assert_equal 1, @instance.index(2) + end + end + + context "converting to an app" do + teardown do + Vagrant::Action.actions.clear + end + + should "preprend error halt to the chain" do + result = mock("result") + env = {:a => :b} + middleware = mock("middleware") + middleware.stubs(:is_a?).with(Class).returns(true) + middleware.expects(:new).with(anything, env).returns(result) + @instance.use middleware + result = @instance.to_app(env) + assert result.kind_of?(Vagrant::Action::ErrorHalt) + end + + should "make non-classes lambdas" do + env = Vagrant::Action::Environment.new(nil) + env.expects(:foo).once + + func = lambda { |x| x.foo } + @instance.use func + @instance.to_app(env).call(env) + end + + should "raise exception if given invalid middleware" do + @instance.use 7 + assert_raises(RuntimeError) { + @instance.to_app(nil) + } + end + end + + context "calling" do + def mock_middleware + middleware = Class.new do + def initialize(app, env) + @app = app + end + + def call(env) + @app.call(env) + end + end + end + + should "convert to an app then call with the env" do + mw = mock_middleware + mw.any_instance.expects(:call).with() do |env| + assert env.has_key?(:key) + true + end + + env = Vagrant::Action::Environment.new(nil) + env[:key] = :value + + @instance.use(mw) + @instance.call(env) + end + end + end +end diff --git a/test/vagrant/action/environment_test.rb b/test/vagrant/action/environment_test.rb new file mode 100644 index 000000000..46b06326f --- /dev/null +++ b/test/vagrant/action/environment_test.rb @@ -0,0 +1,31 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') + +class ActionEnvironmentTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::Environment + @instance = @klass.new(mock_environment) + end + + should "default values to those on the env" do + @instance.env.stubs(:key).returns("value") + assert_equal "value", @instance["key"] + end + + should "setup the logger" do + assert_equal @instance.env.logger, @instance.logger + end + + should "not be erroneous initially" do + assert !@instance.error? + end + + should "mark as erroneous" do + @instance.error!(:key) + assert_equal [:key, {}], @instance.error + end + + should "properly report erroneous" do + @instance.error!(:key) + assert @instance.error? + end +end diff --git a/test/vagrant/action/error_halt_test.rb b/test/vagrant/action/error_halt_test.rb new file mode 100644 index 000000000..4f3af8771 --- /dev/null +++ b/test/vagrant/action/error_halt_test.rb @@ -0,0 +1,21 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') + +class ErrorHaltTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::ErrorHalt + @app, @env = mock_action_data + @instance = @klass.new(@app, @env) + end + + should "continue the chain if no error" do + assert !@env.error? + @app.expects(:call).with(@env).once + @instance.call(@env) + end + + should "halt the chain if an error occured" do + @env.error!(:foo) + @app.expects(:call).never + @instance.call(@env) + end +end diff --git a/test/vagrant/action/exception_catcher_test.rb b/test/vagrant/action/exception_catcher_test.rb new file mode 100644 index 000000000..4245ce903 --- /dev/null +++ b/test/vagrant/action/exception_catcher_test.rb @@ -0,0 +1,30 @@ +require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') + +class ExceptionCatcherTest < Test::Unit::TestCase + setup do + @klass = Class.new + @klass.send(:include, Vagrant::Action::ExceptionCatcher) + @env = Vagrant::Action::Environment.new(mock_environment) + + @instance = @klass.new + end + + should "run block and return result if no exception" do + result = @instance.catch_action_exception(@env) do + true + end + + assert result + assert !@env.error? + end + + should "run block and return false with error environment on exception" do + result = @instance.catch_action_exception(@env) do + raise Vagrant::Action::ActionException.new(:foo, :foo => :bar) + end + + assert !result + assert @env.error? + assert_equal :foo, @env.error.first + end +end diff --git a/test/vagrant/action/vm/boot_test.rb b/test/vagrant/action/vm/boot_test.rb new file mode 100644 index 000000000..dddcd3c83 --- /dev/null +++ b/test/vagrant/action/vm/boot_test.rb @@ -0,0 +1,58 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class BootVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Boot + @app, @env = mock_action_data + + @vm = mock("vm") + @vm.stubs(:ssh).returns(mock("ssh")) + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + context "calling" do + should "run the proper methods on success" do + boot_seq = sequence("boot_seq") + @instance.expects(:boot).in_sequence(boot_seq) + @instance.expects(:wait_for_boot).returns(true).in_sequence(boot_seq) + @app.expects(:call).with(@env).once.in_sequence(boot_seq) + @instance.call(@env) + end + + should "error and halt chain if boot failed" do + boot_seq = sequence("boot_seq") + @instance.expects(:boot).in_sequence(boot_seq) + @instance.expects(:wait_for_boot).returns(false).in_sequence(boot_seq) + @app.expects(:call).never + @instance.call(@env) + end + end + + context "booting" do + should "start the VM in specified mode" do + mode = mock("boot_mode") + @env.env.config.vm.boot_mode = mode + @internal_vm.expects(:start).with(mode).once + @instance.boot + end + end + + context "waiting for boot" do + should "repeatedly ping the SSH port and return false with no response" do + seq = sequence('pings') + @vm.ssh.expects(:up?).times(@env.env.config.ssh.max_tries.to_i - 1).returns(false).in_sequence(seq) + @vm.ssh.expects(:up?).once.returns(true).in_sequence(seq) + assert @instance.wait_for_boot(0) + end + + should "ping the max number of times then just return" do + @vm.ssh.expects(:up?).times(@env.env.config.ssh.max_tries.to_i).returns(false) + assert !@instance.wait_for_boot(0) + end + end +end diff --git a/test/vagrant/action/vm/check_guest_additions_test.rb b/test/vagrant/action/vm/check_guest_additions_test.rb new file mode 100644 index 000000000..656d3faf2 --- /dev/null +++ b/test/vagrant/action/vm/check_guest_additions_test.rb @@ -0,0 +1,9 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class CheckGuestAdditionsVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::CheckGuestAdditions + end + + # TODO: This isn't tested. +end diff --git a/test/vagrant/action/vm/customize_test.rb b/test/vagrant/action/vm/customize_test.rb new file mode 100644 index 000000000..d2cefcdaa --- /dev/null +++ b/test/vagrant/action/vm/customize_test.rb @@ -0,0 +1,29 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class CustomizeVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Customize + @app, @env = mock_action_data + @instance = @klass.new(@app, @env) + + @vm = mock("vm") + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + end + + should "not run anything if no customize blocks exist" do + @internal_vm.expects(:save).never + @app.expects(:call).with(@env).once + @instance.call(@env) + end + + should "run the VM customization procs then save the VM" do + @env.env.config.vm.customize { |vm| } + @env.env.config.vm.expects(:run_procs!).with(@internal_vm) + @internal_vm.expects(:save).once + @app.expects(:call).with(@env).once + @instance.call(@env) + end +end diff --git a/test/vagrant/action/vm/destroy_test.rb b/test/vagrant/action/vm/destroy_test.rb new file mode 100644 index 000000000..7b71f4327 --- /dev/null +++ b/test/vagrant/action/vm/destroy_test.rb @@ -0,0 +1,26 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class DestroyVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Destroy + @app, @env = mock_action_data + + @vm = mock("vm") + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + context "destroying the VM" do + should "destroy VM and attached images" do + @internal_vm.expects(:destroy).with(:destroy_medium => :delete).once + @env["vm"].expects(:vm=).with(nil).once + @env.env.expects(:update_dotfile).once + @app.expects(:call).with(@env).once + @instance.call(@env) + end + end +end diff --git a/test/vagrant/action/vm/destroy_unused_network_interfaces_test.rb b/test/vagrant/action/vm/destroy_unused_network_interfaces_test.rb new file mode 100644 index 000000000..ba38c0b3f --- /dev/null +++ b/test/vagrant/action/vm/destroy_unused_network_interfaces_test.rb @@ -0,0 +1,46 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class DestroyUnusedNetworkInterfacesVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::DestroyUnusedNetworkInterfaces + @app, @env = mock_action_data + + @vm = mock("vm") + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + context "calling" do + setup do + @network_adapters = [] + @internal_vm.stubs(:network_adapters).returns(@network_adapters) + end + + def stub_interface(length=5) + interface = mock("interface") + adapter = mock("adapter") + adapter.stubs(:host_interface_object).returns(interface) + interface.stubs(:attached_vms).returns(Array.new(length)) + + @network_adapters << adapter + interface + end + + should "destroy only the unused network interfaces" do + stub_interface(5) + stub_interface(7) + results = [stub_interface(1), stub_interface(1)] + + results.each do |result| + result.expects(:destroy).once + end + + @app.expects(:call).with(@env).once + @instance.call(@env) + end + end +end diff --git a/test/vagrant/action/vm/export_test.rb b/test/vagrant/action/vm/export_test.rb new file mode 100644 index 000000000..a13f0bc18 --- /dev/null +++ b/test/vagrant/action/vm/export_test.rb @@ -0,0 +1,88 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class ExportVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Export + @app, @env = mock_action_data + + @vm = mock("vm") + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + context "calling" do + setup do + @internal_vm.stubs(:powered_off?).returns(true) + end + + should "call the proper methods then continue chain" do + seq = sequence("seq") + @instance.expects(:setup_temp_dir).in_sequence(seq) + @instance.expects(:export).in_sequence(seq) + @app.expects(:call).with(@env).in_sequence(seq) + + @instance.call(@env) + end + + should "halt the chain if not powered off" do + @internal_vm.stubs(:powered_off?).returns(false) + @instance.expects(:setup_temp_dir).never + @instance.expects(:export).never + @app.expects(:call).with(@env).never + + @instance.call(@env) + assert @env.error? + assert_equal :vm_power_off_to_package, @env.error.first + end + end + + context "setting up the temporary directory" do + setup do + @time_now = Time.now.to_i.to_s + Time.stubs(:now).returns(@time_now) + + @tmp_path = "foo" + @env.env.stubs(:tmp_path).returns(@tmp_path) + + @temp_dir = File.join(@env.env.tmp_path, @time_now) + FileUtils.stubs(:mkpath) + end + + should "create the temporary directory using the current time" do + FileUtils.expects(:mkpath).with(@temp_dir).once + @instance.setup_temp_dir + end + + should "set to the environment" do + @instance.setup_temp_dir + assert_equal @temp_dir, @env["export.temp_dir"] + end + end + + context "exporting" do + setup do + @ovf_path = mock("ovf_path") + @instance.stubs(:ovf_path).returns(@ovf_path) + end + + should "call export on the runner with the ovf path" do + @internal_vm.expects(:export).with(@ovf_path).once + @instance.export + end + end + + context "path to OVF file" do + setup do + @temp_dir = "foo" + @env["export.temp_dir"] = @temp_dir + end + + should "be the temporary directory joined with the OVF filename" do + assert_equal File.join(@temp_dir, @env.env.config.vm.box_ovf), @instance.ovf_path + end + end +end diff --git a/test/vagrant/action/vm/forward_ports_test.rb b/test/vagrant/action/vm/forward_ports_test.rb new file mode 100644 index 000000000..94490517d --- /dev/null +++ b/test/vagrant/action/vm/forward_ports_test.rb @@ -0,0 +1,270 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class ForwardPortsVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::ForwardPorts + @app, @env = mock_action_data + + @vm = mock("vm") + @vm.stubs(:name).returns("foo") + @env["vm"] = @vm + end + + context "initializing" do + should "call proper methods" do + @klass.any_instance.expects(:external_collision_check) + @klass.new(@app, @env) + end + end + + context "checking for colliding external ports" do + setup do + @env.env.config.vm.forwarded_ports.clear + @env.env.config.vm.forward_port("ssh", 22, 2222) + + @used_ports = [] + @klass.any_instance.stubs(:used_ports).returns(@used_ports) + @klass.any_instance.stubs(:handle_collision) + end + + should "not raise any errors if no forwarded ports collide" do + @used_ports << "80" + @klass.new(@app, @env) + assert !@env.error? + end + + should "handle collision if it happens" do + @used_ports << "2222" + @klass.any_instance.expects(:handle_collision).with("ssh", anything, anything).once + @klass.new(@app, @env) + assert !@env.error? + end + end + + context "with instance" do + setup do + @klass.any_instance.stubs(:external_collision_check) + @instance = @klass.new(@app, @env) + end + + context "handling collisions" do + setup do + @name = :foo + @options = { + :hostport => 0, + :auto => true + } + @used_ports = [1,2,3] + + @env.env.config.vm.auto_port_range = (1..5) + end + + should "error if auto forwarding is disabled" do + @options[:auto] = false + @instance.handle_collision(@name, @options, @used_ports) + assert @env.error? + assert_equal :vm_port_collision, @env.error.first + end + + should "set the host port to the first available port" do + assert_equal 0, @options[:hostport] + @instance.handle_collision(@name, @options, @used_ports) + assert_equal 4, @options[:hostport] + end + + should "add the newly used port to the list of used ports" do + assert !@used_ports.include?(4) + @instance.handle_collision(@name, @options, @used_ports) + assert @used_ports.include?(4) + end + + should "not use a host port which is being forwarded later" do + @env.env.config.vm.forward_port("http", 80, 4) + + assert_equal 0, @options[:hostport] + @instance.handle_collision(@name, @options, @used_ports) + assert_equal 5, @options[:hostport] + end + + should "raise an exception if there are no auto ports available" do + @env.env.config.vm.auto_port_range = (1..3) + @instance.handle_collision(@name, @options, @used_ports) + assert @env.error? + assert_equal :vm_port_auto_empty, @env.error.first + end + end + + context "calling" do + should "clear all previous ports and forward new ports" do + exec_seq = sequence("exec_seq") + @instance.expects(:clear).once.in_sequence(exec_seq) + @instance.expects(:forward_ports).once.in_sequence(exec_seq) + @app.expects(:call).once.with(@env).in_sequence(exec_seq) + @instance.call(@env) + end + end + + context "forwarding ports" do + setup do + @internal_vm = mock("internal_vm") + @vm.stubs(:vm).returns(@internal_vm) + end + + should "create a port forwarding for the VM" do + forwarded_ports = mock("forwarded_ports") + network_adapter = mock("network_adapter") + + @internal_vm.stubs(:network_adapters).returns([network_adapter]) + network_adapter.expects(:attachment_type).returns(:nat) + + @instance.expects(:forward_port).once + @internal_vm.expects(:save).once + @vm.expects(:reload!).once + @instance.forward_ports + end + + should "not port forward for non NAT interfaces" do + forwarded_ports = mock("forwarded_ports") + network_adapter = mock("network_adapter") + + @internal_vm.expects(:network_adapters).returns([network_adapter]) + network_adapter.expects(:attachment_type).returns(:host_only) + @internal_vm.expects(:save).once + @vm.expects(:reload!).once + @instance.forward_ports + end + end + + context "clearing forwarded ports" do + setup do + @instance.stubs(:used_ports).returns([:a]) + @instance.stubs(:clear_ports) + end + + should "call destroy on all forwarded ports" do + @instance.expects(:clear_ports).once + @vm.expects(:reload!) + @instance.clear + end + + should "do nothing if there are no forwarded ports" do + @instance.stubs(:used_ports).returns([]) + @vm.expects(:reload!).never + @instance.clear + end + end + + context "getting list of used ports" do + setup do + @vms = [] + VirtualBox::VM.stubs(:all).returns(@vms) + VirtualBox.stubs(:version).returns("3.1.0") + @vm.stubs(:uuid).returns(:bar) + end + + def mock_vm(options={}) + options = { + :running? => true, + :uuid => :foo + }.merge(options) + + vm = mock("vm") + options.each do |k,v| + vm.stubs(k).returns(v) + end + + vm + end + + def mock_fp(hostport) + fp = mock("fp") + fp.stubs(:hostport).returns(hostport.to_s) + fp + end + + should "ignore VMs which aren't running" do + @vms << mock_vm(:running? => false) + @vms[0].expects(:forwarded_ports).never + @instance.used_ports + end + + should "ignore VMs of the same uuid" do + @vms << mock_vm(:uuid => @vm.uuid) + @vms[0].expects(:forwarded_ports).never + @instance.used_ports + end + + should "return the forwarded ports for VB 3.2.x" do + VirtualBox.stubs(:version).returns("3.2.4") + fps = [mock_fp(2222), mock_fp(80)] + na = mock("na") + ne = mock("ne") + na.stubs(:nat_driver).returns(ne) + ne.stubs(:forwarded_ports).returns(fps) + @vms << mock_vm(:network_adapters => [na]) + assert_equal %W[2222 80], @instance.used_ports + end + end + + context "clearing ports" do + def mock_fp + fp = mock("fp") + fp.expects(:destroy).once + fp + end + + setup do + VirtualBox.stubs(:version).returns("3.2.8") + @adapters = [] + @internal_vm = mock("internal_vm") + @internal_vm.stubs(:network_adapters).returns(@adapters) + @vm.stubs(:vm).returns(@internal_vm) + end + + def mock_adapter + na = mock("adapter") + engine = mock("engine") + engine.stubs(:forwarded_ports).returns([mock_fp]) + na.stubs(:nat_driver).returns(engine) + na + end + + should "destroy each forwarded port" do + @adapters << mock_adapter + @adapters << mock_adapter + @instance.clear_ports + end + end + + context "forwarding ports implementation" do + setup do + VirtualBox.stubs(:version).returns("3.2.8") + + @internal_vm = mock("internal_vm") + @vm.stubs(:vm).returns(@internal_vm) + end + + should "forward ports" do + name, opts = @env.env.config.vm.forwarded_ports.first + + adapters = [] + adapter = mock("adapter") + engine = mock("engine") + fps = mock("forwarded ports") + adapter.stubs(:nat_driver).returns(engine) + engine.stubs(:forwarded_ports).returns(fps) + fps.expects(:<<).with do |port| + assert_equal name, port.name + assert_equal opts[:hostport], port.hostport + assert_equal opts[:guestport], port.guestport + true + end + + adapters[opts[:adapter]] = adapter + @internal_vm.stubs(:network_adapters).returns(adapters) + + @instance.forward_port(name, opts) + end + end + end +end diff --git a/test/vagrant/action/vm/halt_test.rb b/test/vagrant/action/vm/halt_test.rb new file mode 100644 index 000000000..75641fbae --- /dev/null +++ b/test/vagrant/action/vm/halt_test.rb @@ -0,0 +1,62 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class HaltVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Halt + @app, @env = mock_action_data + + @vm = mock("vm") + @vm.stubs(:name).returns("foo") + @vm.stubs(:ssh).returns(mock("ssh")) + @vm.stubs(:system).returns(mock("system")) + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + context "calling" do + setup do + @internal_vm.stubs(:running?).returns(true) + + @vm.system.stubs(:halt) + @internal_vm.stubs(:stop) + @internal_vm.stubs(:state).returns(:powered_off) + end + + should "do nothing if VM not running" do + @internal_vm.stubs(:running?).returns(false) + @vm.system.expects(:halt).never + @internal_vm.expects(:stop).never + @app.expects(:call).once + + @instance.call(@env) + end + + should "halt with the system and NOT force VM to stop if powered off" do + @internal_vm.expects(:state).with(true).returns(:powered_off) + @vm.system.expects(:halt).once + @internal_vm.expects(:stop).never + @app.expects(:call).once + + @instance.call(@env) + end + + should "halt with the system and force VM to stop if NOT powered off" do + @internal_vm.expects(:state).with(true).returns(:running) + @vm.system.expects(:halt).once + @internal_vm.expects(:stop).once + @app.expects(:call).once + + @instance.call(@env) + end + + should "not call halt on the system if forcing" do + @env["force"] = true + @vm.system.expects(:halt).never + @instance.call(@env) + end + end +end diff --git a/test/vagrant/action/vm/import_test.rb b/test/vagrant/action/vm/import_test.rb new file mode 100644 index 000000000..6467e885e --- /dev/null +++ b/test/vagrant/action/vm/import_test.rb @@ -0,0 +1,39 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class ImportVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Import + @app, @env = mock_action_data + @instance = @klass.new(@app, @env) + + ovf_file = "foo" + @box = mock("box") + @box.stubs(:ovf_file).returns(ovf_file) + @env.env.stubs(:box).returns(@box) + + @env.env.vm = Vagrant::VM.new + + VirtualBox::VM.stubs(:import) + end + + should "call import on VirtualBox with proper base" do + VirtualBox::VM.expects(:import).once.with(@env.env.box.ovf_file).returns("foo") + @instance.call(@env) + end + + should "call next in chain on success and set VM" do + vm = mock("vm") + VirtualBox::VM.stubs(:import).returns(vm) + @app.expects(:call).with(@env).once + @instance.call(@env) + + assert_equal vm, @env["vm"].vm + end + + should "mark environment erroneous and not continue chain on failure" do + @app.expects(:call).never + @instance.call(@env) + + assert @env.error? + end +end diff --git a/test/vagrant/action/vm/match_mac_address_test.rb b/test/vagrant/action/vm/match_mac_address_test.rb new file mode 100644 index 000000000..4bc5c5886 --- /dev/null +++ b/test/vagrant/action/vm/match_mac_address_test.rb @@ -0,0 +1,28 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class MatchMACAddressVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::MatchMACAddress + @app, @env = mock_action_data + + @vm = mock("vm") + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + should "match the mac addresses" do + nic = mock("nic") + nic.expects(:mac_address=).once + + update_seq = sequence("update_seq") + @internal_vm.expects(:network_adapters).returns([nic]).once.in_sequence(update_seq) + @internal_vm.expects(:save).once.in_sequence(update_seq) + @app.expects(:call).with(@env).once.in_sequence(update_seq) + + @instance.call(@env) + end +end diff --git a/test/vagrant/action/vm/network_test.rb b/test/vagrant/action/vm/network_test.rb new file mode 100644 index 000000000..9769e078c --- /dev/null +++ b/test/vagrant/action/vm/network_test.rb @@ -0,0 +1,246 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class NetworkVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Network + @app, @env = mock_action_data + + @vm = mock("vm") + @vm.stubs(:name).returns("foo") + @vm.stubs(:ssh).returns(mock("ssh")) + @vm.stubs(:system).returns(mock("system")) + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + end + + context "initializing" do + should "verify no bridge collisions for each network enabled" do + @env.env.config.vm.network("foo") + @klass.any_instance.expects(:verify_no_bridge_collision).once.with() do |options| + assert_equal "foo", options[:ip] + true + end + + @klass.new(@app, @env) + end + end + + context "with an instance" do + setup do + @klass.any_instance.stubs(:verify_no_bridge_collision) + @instance = @klass.new(@app, @env) + + @interfaces = [] + VirtualBox::Global.global.host.stubs(:network_interfaces).returns(@interfaces) + end + + def mock_interface(options=nil) + options = { + :interface_type => :host_only, + :name => "foo" + }.merge(options || {}) + + interface = mock("interface") + options.each do |k,v| + interface.stubs(k).returns(v) + end + + @interfaces << interface + interface + end + + context "calling" do + setup do + @env.env.config.vm.network("foo") + @instance.stubs(:enable_network?).returns(false) + end + + should "do nothing if network should not be enabled" do + @instance.expects(:assign_network).never + @app.expects(:call).with(@env).once + @vm.system.expects(:prepare_host_only_network).never + @vm.system.expects(:enable_host_only_network).never + + @instance.call(@env) + end + + should "assign and enable the network if networking enabled" do + @instance.stubs(:enable_network?).returns(true) + + run_seq = sequence("run") + @instance.expects(:assign_network).once.in_sequence(run_seq) + @app.expects(:call).with(@env).once.in_sequence(run_seq) + @vm.system.expects(:prepare_host_only_network).once.in_sequence(run_seq) + @vm.system.expects(:enable_host_only_network).once.in_sequence(run_seq) + + @instance.call(@env) + end + end + + context "checking if network is enabled" do + should "return true if the network options are set" do + @env.env.config.vm.network("foo") + assert @instance.enable_network? + end + + should "return false if the network was not set" do + assert !@instance.enable_network? + end + end + + context "assigning the network" do + setup do + @network_name = "foo" + @instance.stubs(:network_name).returns(@network_name) + + @network_adapters = [] + @internal_vm.stubs(:network_adapters).returns(@network_adapters) + + @options = { + :ip => "foo", + :adapter => 7 + } + + @env.env.config.vm.network(@options[:ip], @options) + end + + should "setup the specified network adapter" do + adapter = mock("adapter") + @network_adapters[@options[:adapter]] = adapter + + adapter.expects(:enabled=).with(true).once + adapter.expects(:attachment_type=).with(:host_only).once + adapter.expects(:host_interface=).with(@network_name).once + adapter.expects(:save).once + + @instance.assign_network + end + end + + context "network name" do + setup do + @instance.stubs(:matching_network?).returns(false) + + @options = { :ip => :foo, :netmask => :bar, :name => nil } + end + + should "return the network which matches" do + result = mock("result") + interface = mock_interface(:name => result) + + @instance.expects(:matching_network?).with(interface, @options).returns(true) + assert_equal result, @instance.network_name(@options) + end + + should "ignore non-host only interfaces" do + @options[:name] = "foo" + mock_interface(:name => @options[:name], + :interface_type => :bridged) + + @instance.network_name(@options) + assert @env.error? + end + + should "return the network which matches the name if given" do + @options[:name] = "foo" + + interface = mock_interface(:name => @options[:name]) + assert_equal @options[:name], @instance.network_name(@options) + end + + should "error and exit if the given network name is not found" do + @options[:name] = "foo" + + @interfaces.expects(:create).never + @instance.network_name(@options) + assert @env.error? + assert_equal :network_not_found, @env.error.first + end + + should "create a network for the IP and netmask" do + result = mock("result") + network_ip = :foo + + interface = mock_interface(:name => result) + interface.expects(:enable_static).with(network_ip, @options[:netmask]) + @interfaces.expects(:create).returns(interface) + @instance.expects(:network_ip).with(@options[:ip], @options[:netmask]).once.returns(network_ip) + + assert_equal result, @instance.network_name(@options) + end + end + + context "checking for a matching network" do + setup do + @interface = mock("interface") + @interface.stubs(:network_mask).returns("foo") + @interface.stubs(:ip_address).returns("192.168.0.1") + + @options = { + :netmask => "foo", + :ip => "baz" + } + end + + should "return false if the netmasks don't match" do + @options[:netmask] = "bar" + assert @interface.network_mask != @options[:netmask] # sanity + assert !@instance.matching_network?(@interface, @options) + end + + should "return true if the netmasks yield the same IP" do + tests = [["255.255.255.0", "192.168.0.1", "192.168.0.45"], + ["255.255.0.0", "192.168.45.1", "192.168.28.7"]] + + tests.each do |netmask, interface_ip, guest_ip| + @options[:netmask] = netmask + @options[:ip] = guest_ip + @interface.stubs(:network_mask).returns(netmask) + @interface.stubs(:ip_address).returns(interface_ip) + + assert @instance.matching_network?(@interface, @options) + end + end + end + + context "applying the netmask" do + should "return the proper result" do + tests = { + ["192.168.0.1","255.255.255.0"] => [192,168,0,0], + ["192.168.45.10","255.255.255.0"] => [192,168,45,0] + } + + tests.each do |k,v| + assert_equal v, @instance.apply_netmask(*k) + end + end + end + + context "splitting an IP" do + should "return the proper result" do + tests = { + "192.168.0.1" => [192,168,0,1] + } + + tests.each do |k,v| + assert_equal v, @instance.split_ip(k) + end + end + end + + context "network IP" do + should "return the proper result" do + tests = { + ["192.168.0.45", "255.255.255.0"] => "192.168.0.1" + } + + tests.each do |args, result| + assert_equal result, @instance.network_ip(*args) + end + end + end + + end +end diff --git a/test/vagrant/action/vm/package_test.rb b/test/vagrant/action/vm/package_test.rb new file mode 100644 index 000000000..3e7fc7666 --- /dev/null +++ b/test/vagrant/action/vm/package_test.rb @@ -0,0 +1,227 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class PackageVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Package + @app, @env = mock_action_data + + @vm = mock("vm") + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + end + + context "initializing" do + setup do + @tar_path = "foo" + File.stubs(:exist?).returns(false) + @klass.any_instance.stubs(:tar_path).returns(@tar_path) + end + + should "initialize fine" do + @klass.new(@app, @env) + assert !@env.error? + end + + should "error the environment if the output file exists" do + File.stubs(:exist?).with(@tar_path).returns(true) + @klass.new(@app, @env) + assert @env.error? + assert_equal :box_file_exists, @env.error.first + end + + should "set the output path to 'package' by default" do + @klass.new(@app, @env) + assert_equal "package", @env["package.output"] + end + + should "not set the output path if it is already set" do + @env["package.output"] = "foo" + @klass.new(@app, @env) + assert_equal "foo", @env["package.output"] + end + + should "set the included files to empty by default" do + @klass.new(@app, @env) + assert_equal [], @env["package.include"] + end + + should "not set the output path if it is already set" do + @env["package.include"] = "foo" + @klass.new(@app, @env) + assert_equal "foo", @env["package.include"] + end + end + + context "with an instance" do + setup do + File.stubs(:exist?).returns(false) + @instance = @klass.new(@app, @env) + + @env["export.temp_dir"] = "foo" + end + + context "calling" do + should "call the proper methods then continue chain" do + seq = sequence("seq") + @instance.expects(:verify_included_files).in_sequence(seq).returns(true) + @instance.expects(:compress).in_sequence(seq) + @app.expects(:call).with(@env).in_sequence(seq) + + @instance.call(@env) + end + + should "halt the chain if verify failed" do + @instance.expects(:verify_included_files).returns(false) + @instance.expects(:compress).never + @app.expects(:call).never + + @instance.call(@env) + end + + should "halt the chain if export didn't run" do + @env["export.temp_dir"] = nil + @app.expects(:call).never + @instance.call(@env) + + assert @env.error? + assert_equal :package_requires_export, @env.error.first + end + end + + context "verifying included files" do + setup do + @env["package.include"] = ["foo"] + File.stubs(:exist?).returns(true) + end + + should "error if included file is not found" do + File.expects(:exist?).with("foo").returns(false) + assert !@instance.verify_included_files + assert @env.error? + assert_equal :package_include_file_doesnt_exist, @env.error.first + end + + should "return true if all exist" do + assert @instance.verify_included_files + assert !@env.error? + end + end + + context "copying include files" do + setup do + @env["package.include"] = [] + end + + should "do nothing if no include files are specified" do + assert @env["package.include"].empty? + FileUtils.expects(:mkdir_p).never + FileUtils.expects(:cp).never + @instance.copy_include_files + end + + should "create the include directory and copy files to it" do + include_dir = File.join(@env["export.temp_dir"], "include") + copy_seq = sequence("copy_seq") + FileUtils.expects(:mkdir_p).with(include_dir).once.in_sequence(copy_seq) + + 5.times do |i| + file = mock("f#{i}") + @env["package.include"] << file + FileUtils.expects(:cp).with(file, include_dir).in_sequence(copy_seq) + end + + @instance.copy_include_files + end + end + + context "creating vagrantfile" do + setup do + @network_adapter = mock("nic") + @network_adapter.stubs(:mac_address).returns("mac_address") + @internal_vm.stubs(:network_adapters).returns([@network_adapter]) + end + + should "write the rendered vagrantfile to temp_path Vagrantfile" do + f = mock("file") + rendered = mock("rendered") + File.expects(:open).with(File.join(@env["export.temp_dir"], "Vagrantfile"), "w").yields(f) + Vagrant::Util::TemplateRenderer.expects(:render).returns(rendered).with("package_Vagrantfile", { + :base_mac => @internal_vm.network_adapters.first.mac_address + }) + f.expects(:write).with(rendered) + + @instance.create_vagrantfile + end + end + + context "compression" do + setup do + @env["package.include"] = [] + + @tar_path = "foo" + @instance.stubs(:tar_path).returns(@tar_path) + + @pwd = "bar" + FileUtils.stubs(:pwd).returns(@pwd) + FileUtils.stubs(:cd) + + @file = mock("file") + File.stubs(:open).yields(@file) + + @output = mock("output") + @tar = Archive::Tar::Minitar + Archive::Tar::Minitar::Output.stubs(:open).yields(@output) + @tar.stubs(:pack_file) + + @instance.stubs(:copy_include_files) + @instance.stubs(:create_vagrantfile) + end + + should "open the tar file with the tar path properly" do + File.expects(:open).with(@tar_path, Vagrant::Util::Platform.tar_file_options).once + @instance.compress + end + + should "open tar file" do + Archive::Tar::Minitar::Output.expects(:open).with(@file).once + @instance.compress + end + + #---------------------------------------------------------------- + # Methods below this comment test the block yielded by Minitar open + #---------------------------------------------------------------- + should "cd to the directory and append the directory" do + @files = [] + compress_seq = sequence("compress_seq") + + FileUtils.expects(:pwd).once.returns(@pwd).in_sequence(compress_seq) + @instance.expects(:copy_include_files).once.in_sequence(compress_seq) + @instance.expects(:create_vagrantfile).once.in_sequence(compress_seq) + FileUtils.expects(:cd).with(@env["export.temp_dir"]).in_sequence(compress_seq) + Dir.expects(:glob).returns(@files).in_sequence(compress_seq) + + 5.times do |i| + file = mock("file#{i}") + @tar.expects(:pack_file).with(file, @output).once.in_sequence(compress_seq) + @files << file + end + + FileUtils.expects(:cd).with(@pwd).in_sequence(compress_seq) + @instance.compress + end + + should "pop back to the current directory even if an exception is raised" do + cd_seq = sequence("cd_seq") + FileUtils.expects(:cd).with(@env["export.temp_dir"]).raises(Exception).in_sequence(cd_seq) + FileUtils.expects(:cd).with(@pwd).in_sequence(cd_seq) + + assert_raises(Exception) { + @instance.compress + } + end + end + + end +end diff --git a/test/vagrant/action/vm/persist_test.rb b/test/vagrant/action/vm/persist_test.rb new file mode 100644 index 000000000..555563a37 --- /dev/null +++ b/test/vagrant/action/vm/persist_test.rb @@ -0,0 +1,50 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class PersistVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Persist + @app, @env = mock_action_data + + @vm = mock("vm") + @vm.stubs(:uuid).returns("123") + @env["vm"] = @vm + end + + context "initializing" do + setup do + File.stubs(:file?).returns(true) + File.stubs(:exist?).returns(true) + @dotfile_path = "foo" + @env.env.stubs(:dotfile_path).returns(@dotfile_path) + end + + should "error environment if dotfile exists but is not a file" do + File.expects(:file?).with(@env.env.dotfile_path).returns(false) + @klass.new(@app, @env) + assert @env.error? + assert_equal :dotfile_error, @env.error.first + end + + should "initialize properly if dotfiles doesn't exist" do + File.expects(:exist?).with(@env.env.dotfile_path).returns(false) + @klass.new(@app, @env) + assert !@env.error? + end + end + + context "with an instance" do + setup do + File.stubs(:file?).returns(true) + File.stubs(:exist?).returns(true) + @instance = @klass.new(@app, @env) + end + + should "persist the dotfile then continue chain" do + update_seq = sequence("update_seq") + @env.env.expects(:update_dotfile).in_sequence(update_seq) + @app.expects(:call).with(@env).in_sequence(update_seq) + + @instance.call(@env) + end + end +end diff --git a/test/vagrant/action/vm/provision_test.rb b/test/vagrant/action/vm/provision_test.rb new file mode 100644 index 000000000..bdc34e2a9 --- /dev/null +++ b/test/vagrant/action/vm/provision_test.rb @@ -0,0 +1,134 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class ProvisionVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Provision + @app, @env = mock_action_data + + @vm = mock("vm") + @vm.stubs(:name).returns("foo") + @vm.stubs(:ssh).returns(mock("ssh")) + @vm.stubs(:system).returns(mock("system")) + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + end + + context "initializing" do + setup do + @klass.any_instance.stubs(:load_provisioner) + end + + should "load provisioner if provisioning enabled" do + @env["config"].vm.provisioner = :chef_solo + @klass.any_instance.expects(:load_provisioner).once + @klass.new(@app, @env) + end + + should "not load provisioner if disabled" do + @env["config"].vm.provisioner = nil + @klass.any_instance.expects(:load_provisioner).never + @klass.new(@app, @env) + end + end + + context "with an instance" do + setup do + # Set provisioner to nil so the provisioner isn't loaded on init + @env["config"].vm.provisioner = nil + @instance = @klass.new(@app, @env) + end + + context "loading a provisioner" do + context "with a Class provisioner" do + setup do + @prov = mock("instance") + @prov.stubs(:is_a?).with(Vagrant::Provisioners::Base).returns(true) + @prov.stubs(:prepare) + @klass = mock("klass") + @klass.stubs(:is_a?).with(Class).returns(true) + @klass.stubs(:new).with(@env).returns(@prov) + + @env["config"].vm.provisioner = @klass + end + + should "set the provisioner to an instantiation of the class" do + @klass.expects(:new).with(@env).once.returns(@prov) + assert_equal @prov, @instance.load_provisioner + end + + should "call prepare on the instance" do + @prov.expects(:prepare).once + @instance.load_provisioner + end + + should "error environment if the class is not a subclass of the provisioner base" do + @prov.expects(:is_a?).with(Vagrant::Provisioners::Base).returns(false) + @instance.load_provisioner + assert @env.error? + assert_equal :provisioner_invalid_class, @env.error.first + end + end + + context "with a Symbol provisioner" do + def provisioner_expectation(symbol, provisioner) + @env[:config].vm.provisioner = symbol + + instance = mock("instance") + instance.expects(:prepare).once + provisioner.expects(:new).with(@env).returns(instance) + assert_equal instance, @instance.load_provisioner + end + + should "raise an ActionException if its an unknown symbol" do + @env["config"].vm.provisioner = :this_will_never_exist + @instance.load_provisioner + assert @env.error? + assert_equal :provisioner_unknown_type, @env.error.first + end + + should "set :chef_solo to the ChefSolo provisioner" do + provisioner_expectation(:chef_solo, Vagrant::Provisioners::ChefSolo) + end + + should "set :chef_server to the ChefServer provisioner" do + provisioner_expectation(:chef_server, Vagrant::Provisioners::ChefServer) + end + end + end + + context "calling" do + setup do + Vagrant::Provisioners::ChefSolo.any_instance.stubs(:prepare) + @env["config"].vm.provisioner = :chef_solo + @prov = @instance.load_provisioner + end + + should "provision and continue chain" do + seq = sequence("seq") + @app.expects(:call).with(@env).in_sequence(seq) + @prov.expects(:provision!).in_sequence(seq) + + @instance.call(@env) + end + + should "continue chain and not provision if not enabled" do + @env["config"].vm.provisioner = nil + @prov.expects(:provision!).never + @app.expects(:call).with(@env).once + + @instance.call(@env) + end + + should "not provision if erroneous environment" do + @env.error!(:foo) + + @prov.expects(:provision!).never + @app.expects(:call).with(@env).once + + @instance.call(@env) + end + end + end +end diff --git a/test/vagrant/action/vm/resume_test.rb b/test/vagrant/action/vm/resume_test.rb new file mode 100644 index 000000000..d0fe21f17 --- /dev/null +++ b/test/vagrant/action/vm/resume_test.rb @@ -0,0 +1,35 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class ResumeVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Resume + @app, @env = mock_action_data + + @vm = mock("vm") + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + context "calling" do + should "run the proper methods when saved" do + @internal_vm.expects(:saved?).returns(true) + + seq = sequence("seq") + @env.env.actions.expects(:run).with(:start).once.in_sequence(seq) + @app.expects(:call).with(@env).once.in_sequence(seq) + @instance.call(@env) + end + + should "do nothing if VM is not saved" do + @internal_vm.expects(:saved?).returns(false) + + @vm.expects(:start).never + @app.expects(:call).with(@env).once + @instance.call(@env) + end + end +end diff --git a/test/vagrant/actions/vm/shared_folders_test.rb b/test/vagrant/action/vm/share_folders_test.rb similarity index 58% rename from test/vagrant/actions/vm/shared_folders_test.rb rename to test/vagrant/action/vm/share_folders_test.rb index bc86a98bc..c0ce8a7ec 100644 --- a/test/vagrant/actions/vm/shared_folders_test.rb +++ b/test/vagrant/action/vm/share_folders_test.rb @@ -1,9 +1,20 @@ require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') -class SharedFoldersActionTest < Test::Unit::TestCase +class ShareFoldersVMActionTest < Test::Unit::TestCase setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::SharedFolders) - @runner.stubs(:system).returns(linux_system(@vm)) + @klass = Vagrant::Action::VM::ShareFolders + @app, @env = mock_action_data + + @vm = mock("vm") + @vm.stubs(:name).returns("foo") + @vm.stubs(:ssh).returns(mock("ssh")) + @vm.stubs(:system).returns(mock("system")) + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) end def stub_shared_folders @@ -20,25 +31,33 @@ class SharedFoldersActionTest < Test::Unit::TestCase end end - @runner.stubs(:env).returns(env) + @env.stubs(:env).returns(env) env.config.vm.shared_folders end - context "before boot" do - should "clear folders and create metadata, in order" do + context "calling" do + should "run the methods in the proper order" do before_seq = sequence("before") - @action.expects(:clear_shared_folders).once.in_sequence(before_seq) - @action.expects(:create_metadata).once.in_sequence(before_seq) - @action.before_boot - end - end + @instance.expects(:clear_shared_folders).once.in_sequence(before_seq) + @instance.expects(:create_metadata).once.in_sequence(before_seq) + @app.expects(:call).with(@env).in_sequence(before_seq) + @instance.expects(:mount_shared_folders).once.in_sequence(before_seq) + @instance.expects(:setup_unison).once.in_sequence(before_seq) - context "after boot" do - should "mount folders then setup unison" do - seq = sequence("after") - @action.expects(:mount_shared_folders).once.in_sequence(seq) - @action.expects(:setup_unison).once.in_sequence(seq) - @action.after_boot + @instance.call(@env) + end + + should "run only the metadata actions if erroneous environment" do + @env.error!(:foo) + + before_seq = sequence("before") + @instance.expects(:clear_shared_folders).once.in_sequence(before_seq) + @instance.expects(:create_metadata).once.in_sequence(before_seq) + @app.expects(:call).with(@env).in_sequence(before_seq) + @instance.expects(:mount_shared_folders).never + @instance.expects(:setup_unison).never + + @instance.call(@env) end end @@ -59,7 +78,7 @@ class SharedFoldersActionTest < Test::Unit::TestCase end end - result = @action.shared_folders + result = @instance.shared_folders assert_equal data.length, result.length data.each do |name, value| guest, host = value @@ -77,8 +96,8 @@ class SharedFoldersActionTest < Test::Unit::TestCase config.vm.share_folder(name, guest, host, :sync => true) end - result = @action.shared_folders - assert_equal "#{guest}#{@runner.env.config.unison.folder_suffix}", result[name][:guestpath] + result = @instance.shared_folders + assert_equal "#{guest}#{@env.env.config.unison.folder_suffix}", result[name][:guestpath] assert_equal guest, result[name][:original][:guestpath] end @@ -89,8 +108,8 @@ class SharedFoldersActionTest < Test::Unit::TestCase folder = @folders["foo"].dup - @action.shared_folders - assert_equal folder, @runner.env.config.vm.shared_folders["foo"] + @instance.shared_folders + assert_equal folder, @env.env.config.vm.shared_folders["foo"] end end @@ -103,7 +122,7 @@ class SharedFoldersActionTest < Test::Unit::TestCase end should "only return the folders marked for syncing" do - result = @action.unison_folders + result = @instance.unison_folders assert_equal 1, result.length assert result.has_key?("foo") assert !result.has_key?("bar") @@ -114,7 +133,7 @@ class SharedFoldersActionTest < Test::Unit::TestCase setup do @shared_folder = mock("shared_folder") @shared_folders = [@shared_folder] - @vm.stubs(:shared_folders).returns(@shared_folders) + @internal_vm.stubs(:shared_folders).returns(@shared_folders) end should "call destroy on each shared folder then reload" do @@ -123,14 +142,14 @@ class SharedFoldersActionTest < Test::Unit::TestCase sf.expects(:destroy).once.in_sequence(destroy_seq) end - @runner.expects(:reload!).once.in_sequence(destroy_seq) - @action.clear_shared_folders + @vm.expects(:reload!).once.in_sequence(destroy_seq) + @instance.clear_shared_folders end should "do nothing if no shared folders existed" do @shared_folders.clear - @runner.expects(:reload!).never - @action.clear_shared_folders + @vm.expects(:reload!).never + @instance.clear_shared_folders end end @@ -143,16 +162,16 @@ class SharedFoldersActionTest < Test::Unit::TestCase shared_folders = [] data = %W[foo bar] shared_folders.expects(:<<).times(data.length).with() do |sf| - hostpath = File.expand_path("#{sf.name}host", @runner.env.root_path) + hostpath = File.expand_path("#{sf.name}host", @env.env.root_path) assert data.include?(sf.name) assert_equal hostpath, sf.host_path true end - @vm.stubs(:shared_folders).returns(shared_folders) - @vm.expects(:save).once + @internal_vm.stubs(:shared_folders).returns(shared_folders) + @internal_vm.expects(:save).once - @action.create_metadata + @instance.create_metadata end end @@ -160,24 +179,24 @@ class SharedFoldersActionTest < Test::Unit::TestCase setup do @folders = stub_shared_folders @ssh = mock("ssh") - @runner.ssh.stubs(:execute).yields(@ssh) - @runner.system.stubs(:mount_shared_folder) + @vm.ssh.stubs(:execute).yields(@ssh) + @vm.system.stubs(:mount_shared_folder) end should "mount all shared folders to the VM" do mount_seq = sequence("mount_seq") @folders.each do |name, data| - @runner.system.expects(:mount_shared_folder).with(@ssh, name, data[:guestpath]).in_sequence(mount_seq) + @vm.system.expects(:mount_shared_folder).with(@ssh, name, data[:guestpath]).in_sequence(mount_seq) end - @action.mount_shared_folders + @instance.mount_shared_folders end end context "setting up unison" do setup do @ssh = mock("ssh") - @runner.ssh.stubs(:execute).yields(@ssh) + @vm.ssh.stubs(:execute).yields(@ssh) @folders = stub_shared_folders do |config| config.vm.share_folder("foo", "bar", "baz", :sync => true) @@ -186,17 +205,17 @@ class SharedFoldersActionTest < Test::Unit::TestCase end should "do nothing if unison folders is empty" do - @action.stubs(:unison_folders).returns({}) - @runner.ssh.expects(:execute).never - @action.setup_unison + @instance.stubs(:unison_folders).returns({}) + @vm.ssh.expects(:execute).never + @instance.setup_unison end should "prepare unison then create for each folder" do seq = sequence("unison seq") - @runner.system.expects(:prepare_unison).with(@ssh).once.in_sequence(seq) - @action.unison_folders.each do |name, data| + @vm.system.expects(:prepare_unison).with(@ssh).once.in_sequence(seq) + @instance.unison_folders.each do |name, data| if data[:sync] - @runner.system.expects(:create_unison).with do |ssh, opts| + @vm.system.expects(:create_unison).with do |ssh, opts| assert_equal @ssh, ssh assert_equal data, opts @@ -205,7 +224,7 @@ class SharedFoldersActionTest < Test::Unit::TestCase end end - @action.setup_unison + @instance.setup_unison end end end diff --git a/test/vagrant/action/vm/suspend_test.rb b/test/vagrant/action/vm/suspend_test.rb new file mode 100644 index 000000000..0084b9db9 --- /dev/null +++ b/test/vagrant/action/vm/suspend_test.rb @@ -0,0 +1,35 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class SuspendVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Suspend + @app, @env = mock_action_data + + @vm = mock("vm") + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + context "calling" do + should "run the proper methods when running" do + @internal_vm.expects(:running?).returns(true) + + seq = sequence("seq") + @internal_vm.expects(:save_state).once.in_sequence(seq) + @app.expects(:call).with(@env).once.in_sequence(seq) + @instance.call(@env) + end + + should "do nothing if VM is not running" do + @internal_vm.expects(:running?).returns(false) + + @internal_vm.expects(:save_state).never + @app.expects(:call).with(@env).once + @instance.call(@env) + end + end +end diff --git a/test/vagrant/action_test.rb b/test/vagrant/action_test.rb new file mode 100644 index 000000000..ad0efe371 --- /dev/null +++ b/test/vagrant/action_test.rb @@ -0,0 +1,96 @@ +require File.join(File.dirname(__FILE__), '..', 'test_helper') + +class ActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action + end + + context "with a class" do + teardown do + @klass.actions.clear + end + + should "be able to register an action" do + @klass.register(:foo, :bar) + assert @klass.actions.has_key?(:foo) + assert_equal :bar, @klass.actions[:foo] + end + + should "be able to retrieve an action using []" do + @klass.register(:foo, :bar) + assert_equal :bar, @klass[:foo] + end + end + + context "with an instance" do + setup do + @instance = @klass.new(mock_environment) + end + + teardown do + @klass.actions.clear + end + + should "run the callable item with the proper context" do + callable = mock("callable") + callable.expects(:call).with() do |env| + assert env.kind_of?(Vagrant::Action::Environment) + assert_equal @instance.env, env.env + true + end + + @instance.run(callable) + end + + should "run the callable with the passed in options if given" do + options = { + :key => :value, + :another => %W[1 2 3] + } + + callable = mock("callable") + callable.expects(:call).with() do |env| + assert env.kind_of?(Vagrant::Action::Environment) + assert_equal @instance.env, env.env + + options.each do |k,v| + assert_equal v, env[k] + end + + true + end + + @instance.run(callable, options) + end + + should "run the registered callable if a symbol is given" do + callable = mock("callable") + callable.expects(:call).once + + @klass.register(:call, callable) + @instance.run(:call) + end + + should "run the given class if a class is given" do + callable = Class.new do + def initialize(app, env); end + end + + callable.any_instance.expects(:call).with() do |env| + assert_equal :foo, env[:bar] + true + end + + @instance.run(callable, :bar => :foo) + end + + should "error and exit if erroneous environment results" do + callable = lambda do |env| + env.error!(:key, :foo => :bar) + end + + @instance.expects(:error_and_exit).with(:key, :foo => :bar) + @instance.run(callable) + end + end +end diff --git a/test/vagrant/actions/base_test.rb b/test/vagrant/actions/base_test.rb deleted file mode 100644 index 5404f9d92..000000000 --- a/test/vagrant/actions/base_test.rb +++ /dev/null @@ -1,32 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') - -class BaseActionTest < Test::Unit::TestCase - should "include the util class so subclasses have access to it" do - assert Vagrant::Actions::Base.include?(Vagrant::Util) - end - - context "base instance" do - setup do - @mock_vm = mock("vm") - @base = Vagrant::Actions::Base.new(@mock_vm) - end - - should "allow read-only access to the runner" do - assert_equal @mock_vm, @base.runner - end - - should "implement prepare which does nothing" do - assert_nothing_raised do - assert @base.respond_to?(:prepare) - @base.prepare - end - end - - should "implement the execute! method which does nothing" do - assert_nothing_raised do - assert @base.respond_to?(:execute!) - @base.execute! - end - end - end -end diff --git a/test/vagrant/actions/box/add_test.rb b/test/vagrant/actions/box/add_test.rb deleted file mode 100644 index 75bc0a84f..000000000 --- a/test/vagrant/actions/box/add_test.rb +++ /dev/null @@ -1,36 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class AddBoxActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::Box::Add) - end - - context "prepare" do - setup do - @default_order = [Vagrant::Actions::Box::Download, Vagrant::Actions::Box::Unpackage, Vagrant::Actions::Box::Verify] - @runner.stubs(:directory).returns("foo") - File.stubs(:exists?).returns(false) - end - - def setup_action_expectations - default_seq = sequence("default_seq") - @default_order.each do |action| - @runner.expects(:add_action).with(action).once.in_sequence(default_seq) - end - end - - should "setup the proper sequence of actions" do - setup_action_expectations - @action.prepare - end - - should "result in an action exception if the box already exists" do - File.expects(:exists?).once.returns(true) - @runner.expects(:name).once.returns('foo') - @runner.expects(:add_action).never - assert_raise Vagrant::Actions::ActionException do - @action.prepare - end - end - end -end diff --git a/test/vagrant/actions/box/destroy_test.rb b/test/vagrant/actions/box/destroy_test.rb deleted file mode 100644 index 741aa7e0d..000000000 --- a/test/vagrant/actions/box/destroy_test.rb +++ /dev/null @@ -1,17 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class DestroyBoxActionTest < Test::Unit::TestCase - setup do - @name = "foo" - @dir = "foo" - @runner, @vm, @action = mock_action(Vagrant::Actions::Box::Destroy) - @runner.stubs(:directory).returns(@dir) - end - - context "executing" do - should "rm_rf the directory" do - FileUtils.expects(:rm_rf).with(@dir).once - @action.execute! - end - end -end diff --git a/test/vagrant/actions/box/download_test.rb b/test/vagrant/actions/box/download_test.rb deleted file mode 100644 index fd8aa76f7..000000000 --- a/test/vagrant/actions/box/download_test.rb +++ /dev/null @@ -1,137 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class DownloadBoxActionTest < Test::Unit::TestCase - setup do - @uri = "foo.com" - @runner, @vm, @action = mock_action(Vagrant::Actions::Box::Download) - @runner.stubs(:uri).returns(@uri) - @runner.stubs(:temp_path=) - - @runner.env.stubs(:tmp_path).returns("foo") - end - - context "preparing" do - setup do - @downloader = mock("downloader") - Vagrant::Downloaders::File.any_instance.stubs(:prepare) - Vagrant::Downloaders::HTTP.any_instance.stubs(:prepare) - end - - should "raise an exception if no URI type is matched" do\ - Vagrant::Downloaders::File.expects(:match?).returns(false) - Vagrant::Downloaders::HTTP.expects(:match?).returns(false) - assert_raises(Vagrant::Actions::ActionException) { - @action.prepare - } - end - - should "call #prepare on the downloader" do - @downloader.expects(:prepare).with(@runner.uri).once - Vagrant::Downloaders::File.expects(:new).returns(@downloader) - expect_file - @action.prepare - end - - should "set the downloader to file if the uri provided is a file" do - expect_file - @action.prepare - assert @action.downloader.is_a?(Vagrant::Downloaders::File) - end - - should "set the downloader to HTTP if the uri provided is a valid url" do - expect_http - @action.prepare - assert @action.downloader.is_a?(Vagrant::Downloaders::HTTP) - end - - def expect_file - Vagrant::Downloaders::File.expects(:match?).returns(true) - Vagrant::Downloaders::HTTP.expects(:match?).returns(false) - end - - def expect_http - Vagrant::Downloaders::File.expects(:match?).returns(false) - Vagrant::Downloaders::HTTP.expects(:match?).returns(true) - end - end - - context "executing" do - setup do - @path = "foo" - - @tempfile = mock("tempfile") - @tempfile.stubs(:path).returns(@path) - - @action.stubs(:with_tempfile).yields(@tempfile) - @action.stubs(:download_to) - end - - should "make a tempfile and copy the URI contents to it" do - @action.expects(:with_tempfile).yields(@tempfile) - @action.expects(:download_to).with(@tempfile) - @action.execute! - end - - should "save the tempfile path" do - @runner.expects(:temp_path=).with(@path).once - @action.execute! - end - end - - context "rescue" do - should "call cleanup method" do - @action.expects(:cleanup).once - @action.rescue(nil) - end - end - - context "tempfile" do - should "create a tempfile in the vagrant tmp directory" do - File.expects(:open).with { |name, bitmask| - name =~ /#{Vagrant::Actions::Box::Download::BASENAME}/ && name =~ /#{@runner.env.tmp_path}/ - }.once - @action.with_tempfile - end - - should "yield the tempfile object" do - @tempfile = mock("tempfile") - File.expects(:open).yields(@tempfile) - - @action.with_tempfile do |otherfile| - assert @tempfile.equal?(otherfile) - end - end - end - - context "cleaning up" do - setup do - @temp_path = "foo" - @runner.stubs(:temp_path).returns(@temp_path) - File.stubs(:exist?).returns(true) - end - - should "delete the temporary file if it exists" do - File.expects(:unlink).with(@temp_path).once - @action.cleanup - end - - should "not delete anything if it doesn't exist" do - File.stubs(:exist?).returns(false) - File.expects(:unlink).never - @action.cleanup - end - end - - context "downloading" do - setup do - @downloader = mock("downloader") - @action.stubs(:downloader).returns(@downloader) - end - - should "call download! on the download with the URI and tempfile" do - tempfile = "foo" - @downloader.expects(:download!).with(@runner.uri, tempfile) - @action.download_to(tempfile) - end - end -end diff --git a/test/vagrant/actions/box/unpackage_test.rb b/test/vagrant/actions/box/unpackage_test.rb deleted file mode 100644 index 276ec927b..000000000 --- a/test/vagrant/actions/box/unpackage_test.rb +++ /dev/null @@ -1,99 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class UnpackageBoxActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::Box::Unpackage) - @runner.stubs(:name).returns("foo") - @runner.stubs(:temp_path).returns("bar") - - @runner.env.stubs(:boxes_path).returns("bar") - end - - context "executing" do - setup do - @runner.stubs(:invoke_around_callback).yields - end - - should "execute the proper actions in the proper order" do - exec_seq = sequence("exec_seq") - @action.expects(:setup_box_dir).in_sequence(exec_seq) - @action.expects(:decompress).in_sequence(exec_seq) - @action.execute! - end - - should "execute it in a around block" do - @runner.expects(:invoke_around_callback).with(:unpackage).once - @action.execute! - end - end - - context "rescuing" do - setup do - File.stubs(:directory?).returns(false) - FileUtils.stubs(:rm_rf) - - @box_dir = mock("foo") - @action.stubs(:box_dir).returns(@box_dir) - end - - should "do nothing if a directory doesn't exist" do - FileUtils.expects(:rm_rf).never - @action.rescue(nil) - end - - should "remove the box directory if it exists" do - File.expects(:directory?).returns(true) - FileUtils.expects(:rm_rf).with(@box_dir).once - @action.rescue(nil) - end - end - - context "box directory" do - should "return the runner directory" do - result = mock("object") - @runner.expects(:directory).once.returns(result) - assert result.equal?(@action.box_dir) - end - end - - context "setting up the box directory" do - setup do - File.stubs(:directory?).returns(false) - FileUtils.stubs(:mkdir_p) - - @box_dir = "foo" - @action.stubs(:box_dir).returns(@box_dir) - end - - should "error and exit if the directory exists" do - File.expects(:directory?).returns(true) - @action.expects(:error_and_exit).with(:box_already_exists, :box_name => @runner.name).once - @action.setup_box_dir - end - - should "create the directory" do - FileUtils.expects(:mkdir_p).with(@box_dir).once - @action.setup_box_dir - end - end - - context "decompressing" do - setup do - @box_dir = "foo" - - @action.stubs(:box_dir).returns(@box_dir) - Dir.stubs(:chdir).yields - Archive::Tar::Minitar.stubs(:unpack) - end - - should "change to the box directory" do - Dir.expects(:chdir).with(@box_dir) - @action.decompress - end - - should "open the tar file within the new directory, and extract it all" do - Archive::Tar::Minitar.expects(:unpack).with(@runner.temp_path, @box_dir).once - @action.decompress - end - end -end diff --git a/test/vagrant/actions/box/verify_test.rb b/test/vagrant/actions/box/verify_test.rb deleted file mode 100644 index 7e5c6ee9d..000000000 --- a/test/vagrant/actions/box/verify_test.rb +++ /dev/null @@ -1,44 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class VerifyBoxActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::Box::Verify) - @runner.stubs(:name).returns("foo") - @runner.stubs(:temp_path).returns("bar") - end - - context "executing" do - should "execute the proper actions in the proper order" do - exec_seq = sequence("exec_seq") - @action.expects(:reload_configuration).in_sequence(exec_seq) - @action.expects(:verify_appliance).in_sequence(exec_seq) - @action.execute! - end - end - - context "reloading configuration" do - should "set the new box, load box, then load config" do - reload_seq = sequence("reload_seq") - @runner.env.config.vm.expects(:box=).with(@runner.name).in_sequence(reload_seq) - @runner.env.expects(:load_box!).in_sequence(reload_seq) - @runner.env.expects(:load_config!).in_sequence(reload_seq) - @action.reload_configuration - end - end - - context "verifying appliance" do - setup do - @runner.stubs(:ovf_file).returns("foo") - end - - should "create new appliance and return true if succeeds" do - VirtualBox::Appliance.expects(:new).with(@runner.ovf_file) - assert_nothing_raised { @action.verify_appliance } - end - - should "return false if an exception is raised" do - VirtualBox::Appliance.expects(:new).with(@runner.ovf_file).raises(VirtualBox::Exceptions::FileErrorException) - assert_raises(Vagrant::Actions::ActionException) { @action.verify_appliance } - end - end -end diff --git a/test/vagrant/actions/collection_test.rb b/test/vagrant/actions/collection_test.rb deleted file mode 100644 index 389765dd1..000000000 --- a/test/vagrant/actions/collection_test.rb +++ /dev/null @@ -1,113 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') - -class CollectionTest < Test::Unit::TestCase - class MockAction; end - class MockActionOther; end - - context "checking uniqueness" do - setup do - @actions = Vagrant::Actions::Collection.new([1]) - end - - should "return true if there are duplicate classes in the collection" do - @actions << 1 - assert @actions.duplicates? - end - - should "return false it all the classes are unique" do - @actions << 1.0 << "foo" - assert !@actions.duplicates? - end - - should "raise an exception when there are duplicates" do - @actions << 1 - assert_raise Vagrant::Actions::DuplicateActionException do - @actions.duplicates! - end - end - - should "not raise an exception when there are no duplicates" do - @actions << 1.0 << "foo" - assert_nothing_raised do - @actions.duplicates! - end - end - end - - context "verifying dependencies" do - setup do - @mock_action = mock('action') - @mock_action.stubs(:class).returns(MockAction) - - @mock_action2 = mock('action2') - @mock_action2.stubs(:class).returns(MockActionOther) - # see test_helper - stub_default_action_dependecies(@mock_action) - stub_default_action_dependecies(@mock_action2) - end - - context "that come before an action" do - setup do - @mock_action.stubs(:follows).returns([MockActionOther]) - end - should "raise an exception if they are not met" do - assert_raise Vagrant::Actions::DependencyNotSatisfiedException do - collection.new([@mock_action]).dependencies! - end - end - - should "not raise an exception if they are met" do - assert_nothing_raised do - collection.new([@mock_action2, @mock_action]).dependencies! - end - end - end - - context "that follow an an action" do - setup do - @mock_action.stubs(:precedes).returns([MockActionOther]) - end - - should "raise an exception if they are not met" do - assert_raise Vagrant::Actions::DependencyNotSatisfiedException do - collection.new([@mock_action]).dependencies! - end - end - - should "not raise an exception if they are met" do - assert_nothing_raised do - collection.new([@mock_action, @mock_action2]).dependencies! - end - end - end - - context "that are before and after an action" do - setup do - @mock_action.stubs(:precedes).returns([MockActionOther]) - @mock_action.stubs(:follows).returns([MockActionOther]) - end - - should "raise an exception if they are met" do - assert_raise Vagrant::Actions::DependencyNotSatisfiedException do - collection.new([@mock_action2, @mock_action]).dependencies! - end - end - - should "not raise and exception if they are met" do - assert_nothing_raised do - collection.new([@mock_action2, @mock_action, @mock_action2]).dependencies! - end - end - end - end - - context "klasses" do - should "return a list of the collection element's classes" do - @action = mock('action') - assert_equal collection.new([@action]).klasses, [@action.class] - assert_equal collection.new([@action, 1.0, "foo"]).klasses, [@action.class, Float, String] - end - end - - def collection; Vagrant::Actions::Collection end -end diff --git a/test/vagrant/actions/runner_test.rb b/test/vagrant/actions/runner_test.rb deleted file mode 100644 index 21f9adb21..000000000 --- a/test/vagrant/actions/runner_test.rb +++ /dev/null @@ -1,268 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') - -class ActionRunnerTest < Test::Unit::TestCase - class MockAction; end - class MockActionOther; end - - def mock_fake_action(action_klass = nil, runner = nil) - action = action_klass ? action_klass.new(runner) : mock("action") - action.stubs(:prepare) - action.stubs(:execute!) - action.stubs(:cleanup) - stub_default_action_dependecies(action) - action - end - - context "callbacks" do - setup do - @runner = Vagrant::Actions::Runner.new - end - - context "around callbacks" do - should "invoke before/after_name for around callbacks" do - block_obj = mock("block_obj") - around_seq = sequence("around_seq") - @runner.expects(:invoke_callback).with(:before_foo).once.in_sequence(around_seq) - block_obj.expects(:foo).once.in_sequence(around_seq) - @runner.expects(:invoke_callback).with(:after_foo).once.in_sequence(around_seq) - - @runner.invoke_around_callback(:foo) do - block_obj.foo - end - end - - should "forward arguments to invoke_callback" do - @runner.expects(:invoke_callback).with(:before_foo, "foo").once - @runner.expects(:invoke_callback).with(:after_foo, "foo").once - @runner.invoke_around_callback(:foo, "foo") do; end - end - end - - should "not invoke callback on actions which don't respond to it" do - action = mock("action") - action.stubs(:respond_to?).with(:foo).returns(false) - action.expects(:foo).never - - assert_nothing_raised do - @runner.actions << action - @runner.invoke_callback(:foo) - end - end - - should "invoke callback on actions which do respond to the method" do - action = mock("action") - action.expects(:foo).once - - @runner.actions << action - @runner.invoke_callback(:foo) - end - - should "collect all the results and return them as an array" do - result = [] - 3.times do |i| - action = mock("action#{i}") - action.expects(:foo).returns("foo#{i}").once - - @runner.actions << action - result << "foo#{i}" - end - - assert_equal result, @runner.invoke_callback(:foo) - end - end - - context "finding actions" do - setup do - @runner = Vagrant::Actions::Runner.new - end - - should "return nil if the action could not be found" do - assert_nil @runner.find_action(Vagrant::Actions::VM::Export) - end - - should "return the first instance of the action found" do - @runner.add_action(Vagrant::Actions::VM::Export) - @runner.add_action(Vagrant::Actions::VM::Export) - - assert @runner.actions[0].equal?(@runner.find_action(Vagrant::Actions::VM::Export)) - end - end - - context "adding actions" do - setup do - @runner = Vagrant::Actions::Runner.new - end - - should "initialize the action when added" do - action_klass = mock("action_class") - action_inst = mock("action_inst") - action_klass.expects(:new).once.returns(action_inst) - @runner.add_action(action_klass) - assert_equal 1, @runner.actions.length - end - - should "initialize the action with given arguments when added" do - action_klass = mock("action_class") - action_klass.expects(:new).with(@runner, "foo", "bar").once - @runner.add_action(action_klass, "foo", "bar") - end - end - - context "class method execute" do - should "run actions on class method execute!" do - vm = mock("vm") - execute_seq = sequence("execute_seq") - Vagrant::Actions::Runner.expects(:new).returns(vm).in_sequence(execute_seq) - vm.expects(:add_action).with("foo").in_sequence(execute_seq) - vm.expects(:execute!).once.in_sequence(execute_seq) - - Vagrant::Actions::Runner.execute!("foo") - end - - should "forward arguments to add_action on class method execute!" do - vm = mock("vm") - execute_seq = sequence("execute_seq") - Vagrant::Actions::Runner.expects(:new).returns(vm).in_sequence(execute_seq) - vm.expects(:add_action).with("foo", "bar", "baz").in_sequence(execute_seq) - vm.expects(:execute!).once.in_sequence(execute_seq) - - Vagrant::Actions::Runner.execute!("foo", "bar", "baz") - end - end - - context "instance method execute" do - setup do - @runner = Vagrant::Actions::Runner.new - @runner.stubs(:action_klasses).returns([Vagrant::Actions::Base]) - end - - should "clear the actions and run a single action if given to execute!" do - action = mock("action") - run_action = mock("action_run") - stub_default_action_dependecies(run_action) - run_class = mock("run_class") - run_class.expects(:new).once.returns(run_action) - @runner.actions << action - - [:prepare, :execute!, :cleanup].each do |method| - action.expects(method).never - run_action.expects(method).once - end - - @runner.execute!(run_class) - end - - should "clear actions after running execute!" do - @runner.actions << mock_fake_action - assert !@runner.actions.empty? # sanity - @runner.execute! - assert @runner.actions.empty? - end - - should "run #prepare on all actions, then #execute!" do - action_seq = sequence("action_seq") - actions = [] - [MockAction, MockActionOther].each_with_index do |klass, i| - action = mock("action#{i}") - action.expects(:class).returns(klass) - stub_default_action_dependecies(action) - @runner.actions << action - actions << action - end - - [:prepare, :execute!, :cleanup].each do |method| - actions.each do |action| - action.expects(method).once.in_sequence(action_seq) - end - end - - @runner.execute! - end - - context "exceptions" do - setup do - @actions = [MockAction, MockActionOther].map do |klass| - action = mock_fake_action - action.expects(:class).returns(klass) - action.stubs(:rescue) - @runner.actions << action - action - end - - @exception = Exception.new - end - - should "call #rescue on each action if an exception is raised during execute!" do - @actions.each do |a| - a.expects(:rescue).with(@exception).once - end - - @actions[0].stubs(:execute!).raises(@exception) - - @runner.expects(:error_and_exit).never - assert_raises(Exception) { @runner.execute! } - end - - should "call #rescue on each action if an exception is raised during prepare" do - @actions.each do |a| - a.expects(:rescue).with(@exception).once - end - - @actions[0].stubs(:prepare).raises(@exception) - - @runner.expects(:error_and_exit).never - assert_raises(Exception) { @runner.execute! } - end - - should "call error_and_exit if it is an ActionException" do - @exception = Vagrant::Actions::ActionException.new("foo") - @actions[0].stubs(:prepare).raises(@exception) - - @runner.expects(:error_and_exit).with(@exception.key, @exception.data).once - @runner.execute! - end - end - end - - context "actions" do - setup do - @runner = Vagrant::Actions::Runner.new - end - - should "setup actions to be an array" do - assert_nil @runner.instance_variable_get(:@actions) - actions = @runner.actions - assert actions.is_a?(Array) - assert actions.equal?(@runner.actions) - end - - should "be empty initially" do - assert @runner.actions.empty? - end - end - - context "duplicate action exceptions" do - setup do - @runner = Vagrant::Actions::Runner.new - end - - should "should be raised when a duplicate is added" do - action = mock_fake_action - 2.times {@runner.actions << action } - assert_raise Vagrant::Actions::DuplicateActionException do - @runner.execute! - end - end - - should "should not be raise when no duplicate actions are present" do - @runner.actions << mock_fake_action(Vagrant::Actions::Base, @runner) - @runner.actions << mock_fake_action(Vagrant::Actions::VM::Halt, @runner) - - assert_nothing_raised { @runner.execute! } - end - - should "should not raise when a single action is specified" do - assert_nothing_raised { @runner.execute!(Vagrant::Actions::Base) } - end - end -end diff --git a/test/vagrant/actions/vm/boot_test.rb b/test/vagrant/actions/vm/boot_test.rb deleted file mode 100644 index ab7069370..000000000 --- a/test/vagrant/actions/vm/boot_test.rb +++ /dev/null @@ -1,49 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class BootActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Boot) - @runner.stubs(:invoke_callback) - end - - context "execution" do - should "invoke the 'boot' around callback" do - boot_seq = sequence("boot_seq") - @runner.expects(:invoke_around_callback).with(:boot).once.in_sequence(boot_seq).yields - @action.expects(:boot).in_sequence(boot_seq) - @action.expects(:wait_for_boot).returns(true).in_sequence(boot_seq) - @action.execute! - end - - should "error and exit if the bootup failed" do - fail_boot_seq = sequence("fail_boot_seq") - @action.expects(:boot).once.in_sequence(fail_boot_seq) - @action.expects(:wait_for_boot).returns(false).in_sequence(fail_boot_seq) - @action.expects(:error_and_exit).with(:vm_failed_to_boot).once.in_sequence(fail_boot_seq) - @action.execute! - end - end - - context "booting" do - should "start the VM in specified mode" do - mode = mock("boot_mode") - @runner.env.config.vm.boot_mode = mode - @vm.expects(:start).with(mode).once - @action.boot - end - end - - context "waiting for boot" do - should "repeatedly ping the SSH port and return false with no response" do - seq = sequence('pings') - @runner.ssh.expects(:up?).times(@runner.env.config.ssh.max_tries.to_i - 1).returns(false).in_sequence(seq) - @runner.ssh.expects(:up?).once.returns(true).in_sequence(seq) - assert @action.wait_for_boot(0) - end - - should "ping the max number of times then just return" do - @runner.ssh.expects(:up?).times(@runner.env.config.ssh.max_tries.to_i).returns(false) - assert !@action.wait_for_boot(0) - end - end -end diff --git a/test/vagrant/actions/vm/customize_test.rb b/test/vagrant/actions/vm/customize_test.rb deleted file mode 100644 index dfddc29e8..000000000 --- a/test/vagrant/actions/vm/customize_test.rb +++ /dev/null @@ -1,21 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class CustomizeActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Customize) - end - - context "executing" do - should "run the VM customization procs then save the VM" do - @runner.env.config.vm.customize { |vm| } - @runner.env.config.vm.expects(:run_procs!).with(@vm) - @vm.expects(:save).once - @action.execute! - end - - should "not run anything if no customize blocks exist" do - @vm.expects(:save).never - @action.execute! - end - end -end diff --git a/test/vagrant/actions/vm/destroy_test.rb b/test/vagrant/actions/vm/destroy_test.rb deleted file mode 100644 index 896c9d583..000000000 --- a/test/vagrant/actions/vm/destroy_test.rb +++ /dev/null @@ -1,37 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class DestroyActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Destroy) - end - - context "executing" do - should "invoke an around callback around the destroy" do - @runner.expects(:invoke_around_callback).with(:destroy).once - @action.execute! - end - - should "destroy VM and clear persist" do - @runner.stubs(:invoke_around_callback).yields - clear_seq = sequence("clear") - @action.expects(:destroy_vm).in_sequence(clear_seq) - @action.expects(:update_dotfile).in_sequence(clear_seq) - @action.execute! - end - end - - context "destroying the VM" do - should "destroy VM and attached images" do - @vm.expects(:destroy).with(:destroy_medium => :delete).once - @runner.expects(:vm=).with(nil).once - @action.destroy_vm - end - end - - context "updating the dotfile" do - should "update the environment dotfile" do - @runner.env.expects(:update_dotfile).once - @action.update_dotfile - end - end -end diff --git a/test/vagrant/actions/vm/down_test.rb b/test/vagrant/actions/vm/down_test.rb deleted file mode 100644 index ff08e586e..000000000 --- a/test/vagrant/actions/vm/down_test.rb +++ /dev/null @@ -1,39 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class DownActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Down) - end - - context "preparing" do - setup do - @vm.stubs(:running?).returns(false) - end - - def setup_action_expectations(order) - default_seq = sequence("default_seq") - order.each do |action| - action = [action] unless action.is_a?(Array) - @runner.expects(:add_action).with(action.shift, *action).once.in_sequence(default_seq) - end - end - - should "add the destroy action alone if VM is not running" do - setup_action_expectations([Vagrant::Actions::VM::Network, Vagrant::Actions::VM::Destroy]) - @action.prepare - end - - should "add the halt action if the VM is running" do - @vm.expects(:running?).returns(true) - setup_action_expectations([[Vagrant::Actions::VM::Halt, {:force => true}], Vagrant::Actions::VM::Network, Vagrant::Actions::VM::Destroy]) - @action.prepare - end - end - - context "after halting" do - should "sleep" do - Kernel.expects(:sleep).once - @action.after_halt - end - end -end diff --git a/test/vagrant/actions/vm/export_test.rb b/test/vagrant/actions/vm/export_test.rb deleted file mode 100644 index d0ecababe..000000000 --- a/test/vagrant/actions/vm/export_test.rb +++ /dev/null @@ -1,88 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class ExportActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Export) - @action.stubs(:complete_progress) - end - - context "executing" do - should "setup the temp dir then export" do - exec_seq = sequence('execute') - @action.expects(:setup_temp_dir).once.in_sequence(exec_seq) - @action.expects(:export).once.in_sequence(exec_seq) - @action.execute! - end - end - - context "setting up the temporary directory" do - setup do - @time_now = Time.now.to_i.to_s - Time.stubs(:now).returns(@time_now) - - @tmp_path = "foo" - @runner.env.stubs(:tmp_path).returns(@tmp_path) - - @temp_dir = File.join(@runner.env.tmp_path, @time_now) - FileUtils.stubs(:mkpath) - end - - should "create the temporary directory using the current time" do - FileUtils.expects(:mkpath).with(@temp_dir).once - @action.setup_temp_dir - end - - should "set the temporary directory to the temp_dir variable" do - @action.setup_temp_dir - assert_equal @temp_dir, @action.temp_dir - end - end - - context "path to OVF file" do - setup do - @temp_dir = "foo" - @action.stubs(:temp_dir).returns(@temp_dir) - end - - should "be the temporary directory joined with the OVF filename" do - assert_equal File.join(@temp_dir, @runner.env.config.vm.box_ovf), @action.ovf_path - end - end - - context "exporting" do - setup do - @ovf_path = mock("ovf_path") - @action.stubs(:ovf_path).returns(@ovf_path) - end - - should "call export on the runner with the ovf path" do - @vm.expects(:export).with(@ovf_path).once - @action.export - end - end - - context "cleanup" do - setup do - @temp_dir = "foo" - @action.stubs(:temp_dir).returns(@temp_dir) - end - - should "remove the temporary directory" do - FileUtils.expects(:rm_r).with(@temp_dir).once - @action.cleanup - end - - should "not remove a directory if temp_dir is nil" do - FileUtils.expects(:rm_r).never - @action.stubs(:temp_dir).returns(nil) - @action.cleanup - end - end - - context "rescue" do - should "call cleanup method" do - @action.expects(:cleanup).once - @action.rescue(nil) - end - end -end diff --git a/test/vagrant/actions/vm/forward_ports_test.rb b/test/vagrant/actions/vm/forward_ports_test.rb deleted file mode 100644 index 2ffe6dfc5..000000000 --- a/test/vagrant/actions/vm/forward_ports_test.rb +++ /dev/null @@ -1,253 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class ForwardPortsActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::ForwardPorts) - end - - context "preparing" do - should "call proper sequence" do - prep_seq = sequence("prepare") - @action.expects(:external_collision_check).in_sequence(prep_seq) - @action.prepare - end - end - - context "checking for colliding external ports" do - setup do - @env = mock_environment do |config| - config.vm.forwarded_ports.clear - config.vm.forward_port("ssh", 22, 2222) - end - - @runner.stubs(:env).returns(@env) - - @used_ports = [] - @action.stubs(:used_ports).returns(@used_ports) - - # So no exceptions are raised - @action.stubs(:handle_collision) - end - - should "not raise any errors if no forwarded ports collide" do - @used_ports << "80" - assert_nothing_raised { @action.external_collision_check } - end - - should "handle the collision if it happens" do - @used_ports << "2222" - @action.expects(:handle_collision).with("ssh", anything, anything).once - @action.external_collision_check - end - end - - context "handling collisions" do - setup do - @name = :foo - @options = { - :hostport => 0, - :auto => true - } - @used_ports = [1,2,3] - - @runner.env.config.vm.auto_port_range = (1..5) - end - - should "raise an exception if auto forwarding is disabled" do - @options[:auto] = false - - assert_raises(Vagrant::Actions::ActionException) { - @action.handle_collision(@name, @options, @used_ports) - } - end - - should "set the host port to the first available port" do - assert_equal 0, @options[:hostport] - @action.handle_collision(@name, @options, @used_ports) - assert_equal 4, @options[:hostport] - end - - should "add the newly used port to the list of used ports" do - assert !@used_ports.include?(4) - @action.handle_collision(@name, @options, @used_ports) - assert @used_ports.include?(4) - end - - should "not use a host port which is being forwarded later" do - @runner.env.config.vm.forward_port("http", 80, 4) - - assert_equal 0, @options[:hostport] - @action.handle_collision(@name, @options, @used_ports) - assert_equal 5, @options[:hostport] - end - - should "raise an exception if there are no auto ports available" do - @runner.env.config.vm.auto_port_range = (1..3) - assert_raises(Vagrant::Actions::ActionException) { - @action.handle_collision(@name, @options, @used_ports) - } - end - end - - context "execution" do - should "clear all previous ports and forward new ports" do - exec_seq = sequence("exec_seq") - @action.expects(:clear).once.in_sequence(exec_seq) - @action.expects(:forward_ports).once.in_sequence(exec_seq) - @action.execute! - end - end - - context "forwarding ports" do - should "create a port forwarding for the VM" do - forwarded_ports = mock("forwarded_ports") - network_adapter = mock("network_adapter") - - @vm.stubs(:network_adapters).returns([network_adapter]) - network_adapter.expects(:attachment_type).returns(:nat) - - @action.expects(:forward_port).once - @vm.expects(:save).once - @runner.expects(:reload!).once - @action.forward_ports - end - - should "No port forwarding for non NAT interfaces" do - forwarded_ports = mock("forwarded_ports") - network_adapter = mock("network_adapter") - - @vm.expects(:network_adapters).returns([network_adapter]) - network_adapter.expects(:attachment_type).returns(:host_only) - @vm.expects(:save).once - @runner.expects(:reload!).once - @action.forward_ports - end - end - - context "clearing forwarded ports" do - setup do - @action.stubs(:used_ports).returns([:a]) - @action.stubs(:clear_ports) - end - - should "call destroy on all forwarded ports" do - @action.expects(:clear_ports).once - @runner.expects(:reload!) - @action.clear - end - - should "do nothing if there are no forwarded ports" do - @action.stubs(:used_ports).returns([]) - @runner.expects(:reload!).never - @action.clear - end - end - - context "getting list of used ports" do - setup do - @vms = [] - VirtualBox::VM.stubs(:all).returns(@vms) - VirtualBox.stubs(:version).returns("3.1.0") - @runner.stubs(:uuid).returns(:bar) - end - - def mock_vm(options={}) - options = { - :running? => true, - :uuid => :foo - }.merge(options) - - vm = mock("vm") - options.each do |k,v| - vm.stubs(k).returns(v) - end - - vm - end - - def mock_fp(hostport) - fp = mock("fp") - fp.stubs(:hostport).returns(hostport.to_s) - fp - end - - should "ignore VMs which aren't running" do - @vms << mock_vm(:running? => false) - @vms[0].expects(:forwarded_ports).never - @action.used_ports - end - - should "ignore VMs of the same uuid" do - @vms << mock_vm(:uuid => @runner.uuid) - @vms[0].expects(:forwarded_ports).never - @action.used_ports - end - - should "return the forwarded ports for VB 3.2.x" do - VirtualBox.stubs(:version).returns("3.2.4") - fps = [mock_fp(2222), mock_fp(80)] - na = mock("na") - ne = mock("ne") - na.stubs(:nat_driver).returns(ne) - ne.stubs(:forwarded_ports).returns(fps) - @vms << mock_vm(:network_adapters => [na]) - assert_equal %W[2222 80], @action.used_ports - end - end - - context "clearing ports" do - def mock_fp - fp = mock("fp") - fp.expects(:destroy).once - fp - end - - setup do - VirtualBox.stubs(:version).returns("3.2.8") - @adapters = [] - @vm.stubs(:network_adapters).returns(@adapters) - end - - def mock_adapter - na = mock("adapter") - engine = mock("engine") - engine.stubs(:forwarded_ports).returns([mock_fp]) - na.stubs(:nat_driver).returns(engine) - na - end - - should "destroy each forwarded port" do - @adapters << mock_adapter - @adapters << mock_adapter - @action.clear_ports - end - end - - context "forwarding ports implementation" do - setup do - VirtualBox.stubs(:version).returns("3.2.8") - end - - should "forward ports" do - name, opts = @runner.env.config.vm.forwarded_ports.first - - adapters = [] - adapter = mock("adapter") - engine = mock("engine") - fps = mock("forwarded ports") - adapter.stubs(:nat_driver).returns(engine) - engine.stubs(:forwarded_ports).returns(fps) - fps.expects(:<<).with do |port| - assert_equal name, port.name - assert_equal opts[:hostport], port.hostport - assert_equal opts[:guestport], port.guestport - true - end - - adapters[opts[:adapter]] = adapter - @vm.stubs(:network_adapters).returns(adapters) - - @action.forward_port(name, opts) - end - end -end diff --git a/test/vagrant/actions/vm/halt_test.rb b/test/vagrant/actions/vm/halt_test.rb deleted file mode 100644 index 85f240cd0..000000000 --- a/test/vagrant/actions/vm/halt_test.rb +++ /dev/null @@ -1,65 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class HaltActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Halt) - @runner.stubs(:system).returns(linux_system(@vm)) - end - - context "force?" do - should "not force by default" do - @action.options[:force] = nil - assert !@action.force? - end - - should "force if specified" do - @action.options[:force] = true - assert @action.force? - end - end - - context "executing" do - setup do - @vm.stubs(:running?).returns(true) - - @runner.system.stubs(:halt) - @vm.stubs(:stop) - @vm.stubs(:state).returns(:powered_off) - end - - should "invoke the 'halt' around callback" do - @runner.expects(:invoke_around_callback).with(:halt).once - @action.execute! - end - - should "halt with the system and NOT force VM to stop if powered off" do - @vm.expects(:state).with(true).returns(:powered_off) - - @runner.system.expects(:halt).once - @vm.expects(:stop).never - @action.execute! - end - - should "halt with the system and force VM to stop if NOT powered off" do - @vm.expects(:state).with(true).returns(:running) - - @runner.system.expects(:halt).once - @vm.expects(:stop).once - @action.execute! - end - - should "not call halt on the system if forcing" do - @action.stubs(:force).returns(true) - @runner.system.expects(:halt).never - @action.execute! - end - - should "raise an ActionException if VM is not running" do - @vm.stubs(:running?).returns(false) - @vm.expects(:stop).never - assert_raises(Vagrant::Actions::ActionException) { - @action.execute! - } - end - end -end diff --git a/test/vagrant/actions/vm/import_test.rb b/test/vagrant/actions/vm/import_test.rb deleted file mode 100644 index 2b1cebef9..000000000 --- a/test/vagrant/actions/vm/import_test.rb +++ /dev/null @@ -1,45 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class ImportActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @import = mock_action(Vagrant::Actions::VM::Import) - - @ovf_file = "foo" - @box = mock("box") - @box.stubs(:ovf_file).returns(@ovf_file) - @runner.env.stubs(:box).returns(@box) - - VirtualBox::VM.stubs(:import) - - @import.stubs(:complete_progress) - end - - should "run in a busy block" do - Vagrant::Busy.expects(:busy).once - @import.execute! - end - - should "invoke an around callback around the import" do - @runner.expects(:invoke_around_callback).with(:import).once - @import.execute! - end - - should "call import on VirtualBox::VM with the proper base" do - VirtualBox::VM.expects(:import).once.with(@ovf_file).returns("foo") - assert_nothing_raised { @import.execute! } - end - - should "raise an exception if import is nil" do - @runner.expects(:vm).returns(nil) - assert_raises(Vagrant::Actions::ActionException) { - @import.execute! - } - end - - should "set the resulting VM as the VM of the Vagrant VM object" do - new_vm = mock("new_vm") - @runner.expects(:vm=).with(new_vm).once - VirtualBox::VM.expects(:import).returns(new_vm).returns("foo") - @import.execute! - end -end diff --git a/test/vagrant/actions/vm/move_hard_drive_test.rb b/test/vagrant/actions/vm/move_hard_drive_test.rb deleted file mode 100644 index 3930918e5..000000000 --- a/test/vagrant/actions/vm/move_hard_drive_test.rb +++ /dev/null @@ -1,106 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class MoveHardDriveActionTest < Test::Unit::TestCase - setup do - @mock_vm, @vm, @action = mock_action(Vagrant::Actions::VM::MoveHardDrive) - @hd_location = "/foo" - - @mock_vm.env.config.vm.stubs(:hd_location).returns(@hd_location) - end - - - should "be able to identifiy a hard drive within the storage controllers" do - hd = mock('hd') - hd_image = mock('hd_image') - hd_image.expects(:is_a?).returns(true) - hd.expects(:image).returns(hd_image) - - dvd = mock('dvd') - controller = mock('controller') - controller.expects(:devices).returns([hd, dvd]) - - @vm.expects(:storage_controllers).once.returns([controller]) - assert_equal @action.find_hard_drive, hd - end - - context "execution" do - should "error and exit if the vm is not powered off" do - @mock_vm.expects(:powered_off?).returns(false) - @action.expects(:error_and_exit).with(:vm_power_off_to_move_hd).once - @action.execute! - end - - should "move the hard drive if vm is powered off" do - @mock_vm.expects(:powered_off?).returns(true) - @action.expects(:error_and_exit).never - @action.expects(:destroy_drive_after).once - @action.execute! - end - end - - context "new image path" do - setup do - @hd = mock("hd") - @image = mock("image") - @filename = "foo" - @hd.stubs(:image).returns(@image) - @image.stubs(:filename).returns(@filename) - @action.stubs(:hard_drive).returns(@hd) - end - - should "be the configured hd location and the existing hard drive filename" do - joined = File.join(@mock_vm.env.config.vm.hd_location, @filename) - assert_equal joined, @action.new_image_path - end - end - - context "cloning and attaching new image" do - setup do - @hd = mock("hd") - @image = mock("image") - @hd.stubs(:image).returns(@image) - @action.stubs(:hard_drive).returns(@hd) - @new_image_path = "foo" - @action.stubs(:new_image_path).returns(@new_image_path) - end - - should "clone to the new path" do - new_image = mock("new_image") - @image.expects(:clone).with(@new_image_path, Vagrant.config.vm.disk_image_format, true).returns(new_image).once - @hd.expects(:image=).with(new_image).once - @vm.expects(:save).once - @action.clone_and_attach - end - end - - context "destroying the old image" do - setup do - @hd = mock("hd") - @action.stubs(:hard_drive).returns(@hd) - end - - should "yield the block, and destroy the old image after" do - image = mock("image") - image.stubs(:filename).returns("foo") - destroy_seq = sequence("destroy_seq") - @hd.expects(:image).returns(image).in_sequence(destroy_seq) - @hd.expects(:foo).once.in_sequence(destroy_seq) - image.expects(:destroy).with(true).once.in_sequence(destroy_seq) - - @action.destroy_drive_after { @hd.foo } - end - - # Ensures that the image is not destroyed in an "ensure" block - should "not destroy the image if an exception is raised" do - image = mock("image") - image.expects(:destroy).never - @hd.expects(:image).returns(image) - - assert_raises(Exception) do - @action.destroy_drive_after do - raise Exception.new("FOO") - end - end - end - end -end diff --git a/test/vagrant/actions/vm/network_test.rb b/test/vagrant/actions/vm/network_test.rb deleted file mode 100644 index 6856247ee..000000000 --- a/test/vagrant/actions/vm/network_test.rb +++ /dev/null @@ -1,291 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class NetworkTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Network) - @runner.stubs(:system).returns(linux_system(@vm)) - - @interfaces = [] - VirtualBox::Global.global.host.stubs(:network_interfaces).returns(@interfaces) - end - - def mock_interface(options=nil) - options = { - :interface_type => :host_only, - :name => "foo" - }.merge(options || {}) - - interface = mock("interface") - options.each do |k,v| - interface.stubs(k).returns(v) - end - - @interfaces << interface - interface - end - - context "preparing" do - should "verify no bridge collisions for each network enabled" do - @runner.env.config.vm.network("foo") - @action.expects(:verify_no_bridge_collision).once.with() do |options| - assert_equal "foo", options[:ip] - true - end - - @action.prepare - end - end - - context "before destroy" do - setup do - @network_adapters = [] - @vm.stubs(:network_adapters).returns(@network_adapters) - end - - def stub_interface(length=5) - interface = mock("interface") - adapter = mock("adapter") - adapter.stubs(:host_interface_object).returns(interface) - interface.stubs(:attached_vms).returns(Array.new(length)) - - @network_adapters << adapter - interface - end - - should "destroy only the unused network interfaces" do - stub_interface(5) - stub_interface(7) - results = [stub_interface(1), stub_interface(1)] - - results.each do |result| - result.expects(:destroy).once - end - - @action.before_destroy - end - end - - context "before boot" do - setup do - @action.stubs(:enable_network?).returns(false) - end - - should "do nothing if network should not be enabled" do - @action.expects(:assign_network).never - @action.before_boot - end - - should "assign the network if host only networking is enabled" do - @action.stubs(:enable_network?).returns(true) - @action.expects(:assign_network).once - @action.before_boot - end - end - - context "after boot" do - setup do - @runner.env.config.vm.network("foo") - @action.stubs(:enable_network?).returns(true) - end - - should "prepare the host only network, then enable them" do - run_seq = sequence("run") - @runner.system.expects(:prepare_host_only_network).once.in_sequence(run_seq) - @runner.system.expects(:enable_host_only_network).once.in_sequence(run_seq) - @action.after_boot - end - - should "do nothing if network is not enabled" do - @action.stubs(:enable_network?).returns(false) - @runner.system.expects(:prepare_host_only_network).never - @action.after_boot - end - end - - context "checking if network is enabled" do - should "return true if the network options are set" do - @runner.env.config.vm.network("foo") - assert @action.enable_network? - end - - should "return false if the network was not set" do - assert !@action.enable_network? - end - end - - context "assigning the network" do - setup do - @network_name = "foo" - @action.stubs(:network_name).returns(@network_name) - - @network_adapters = [] - @vm.stubs(:network_adapters).returns(@network_adapters) - - @options = { - :ip => "foo", - :adapter => 7 - } - - @runner.env.config.vm.network(@options[:ip], @options) - end - - should "setup the specified network adapter" do - adapter = mock("adapter") - @network_adapters[@options[:adapter]] = adapter - - adapter.expects(:enabled=).with(true).once - adapter.expects(:attachment_type=).with(:host_only).once - adapter.expects(:host_interface=).with(@network_name).once - adapter.expects(:save).once - - @action.assign_network - end - end - - context "verify no bridge collision" do - setup do - @action.stubs(:matching_network?).returns(false) - @options = { :ip => :foo, :netmask => :bar, :name => nil } - end - - should "do nothing if everything is okay" do - mock_interface - - assert_nothing_raised { @action.verify_no_bridge_collision(@options) } - end - - should "raise an exception if a collision is found" do - mock_interface(:interface_type => :bridged) - @action.stubs(:matching_network?).returns(true) - - assert_raises(Vagrant::Actions::ActionException) { - @action.verify_no_bridge_collision(@options) - } - end - end - - context "network name" do - setup do - @action.stubs(:matching_network?).returns(false) - - @options = { :ip => :foo, :netmask => :bar, :name => nil } - end - - should "return the network which matches" do - result = mock("result") - interface = mock_interface(:name => result) - - @action.expects(:matching_network?).with(interface, @options).returns(true) - assert_equal result, @action.network_name(@options) - end - - should "ignore non-host only interfaces" do - @options[:name] = "foo" - mock_interface(:name => @options[:name], - :interface_type => :bridged) - - assert_raises(Vagrant::Actions::ActionException) { - @action.network_name(@options) - } - end - - should "return the network which matches the name if given" do - @options[:name] = "foo" - - interface = mock_interface(:name => @options[:name]) - assert_equal @options[:name], @action.network_name(@options) - end - - should "error and exit if the given network name is not found" do - @options[:name] = "foo" - - @interfaces.expects(:create).never - - assert_raises(Vagrant::Actions::ActionException) { - @action.network_name(@options) - } - end - - should "create a network for the IP and netmask" do - result = mock("result") - network_ip = :foo - - interface = mock_interface(:name => result) - interface.expects(:enable_static).with(network_ip, @options[:netmask]) - @interfaces.expects(:create).returns(interface) - @action.expects(:network_ip).with(@options[:ip], @options[:netmask]).once.returns(network_ip) - - assert_equal result, @action.network_name(@options) - end - end - - context "checking for a matching network" do - setup do - @interface = mock("interface") - @interface.stubs(:network_mask).returns("foo") - @interface.stubs(:ip_address).returns("192.168.0.1") - - @options = { - :netmask => "foo", - :ip => "baz" - } - end - - should "return false if the netmasks don't match" do - @options[:netmask] = "bar" - assert @interface.network_mask != @options[:netmask] # sanity - assert !@action.matching_network?(@interface, @options) - end - - should "return true if the netmasks yield the same IP" do - tests = [["255.255.255.0", "192.168.0.1", "192.168.0.45"], - ["255.255.0.0", "192.168.45.1", "192.168.28.7"]] - - tests.each do |netmask, interface_ip, guest_ip| - @options[:netmask] = netmask - @options[:ip] = guest_ip - @interface.stubs(:network_mask).returns(netmask) - @interface.stubs(:ip_address).returns(interface_ip) - - assert @action.matching_network?(@interface, @options) - end - end - end - - context "applying the netmask" do - should "return the proper result" do - tests = { - ["192.168.0.1","255.255.255.0"] => [192,168,0,0], - ["192.168.45.10","255.255.255.0"] => [192,168,45,0] - } - - tests.each do |k,v| - assert_equal v, @action.apply_netmask(*k) - end - end - end - - context "splitting an IP" do - should "return the proper result" do - tests = { - "192.168.0.1" => [192,168,0,1] - } - - tests.each do |k,v| - assert_equal v, @action.split_ip(k) - end - end - end - - context "network IP" do - should "return the proper result" do - tests = { - ["192.168.0.45", "255.255.255.0"] => "192.168.0.1" - } - - tests.each do |args, result| - assert_equal result, @action.network_ip(*args) - end - end - end -end diff --git a/test/vagrant/actions/vm/package_test.rb b/test/vagrant/actions/vm/package_test.rb deleted file mode 100644 index e3b4c5daa..000000000 --- a/test/vagrant/actions/vm/package_test.rb +++ /dev/null @@ -1,254 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class PackageActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Package) - end - - context "initialization" do - def get_action(output, include_files) - runner, vm, action = mock_action(Vagrant::Actions::VM::Package, { - :output => output, - :include => include_files - }) - return action - end - - should "make out_path 'package' by default if nil is given" do - action = get_action(nil, []) - assert_equal "package", action.out_path - end - - should "make include files an empty array by default" do - action = get_action("foo", nil) - assert action.include_files.is_a?(Array) - assert action.include_files.empty? - end - end - - context "executing" do - setup do - @action.stubs(:compress) - end - - should "compress" do - package_seq = sequence("package_seq") - @action.expects(:compress).in_sequence(package_seq) - @action.execute! - end - end - - context "out path" do - should "be the specified output file if given" do - result = mock("result") - @action.options[:output] = result - assert_equal result, @action.out_path - end - - should "default to 'package'" do - @action.options[:output] = nil - assert_equal "package", @action.out_path - end - end - - context "include files" do - should "specified array if given" do - @action.options[:include] = [1,2,3] - assert_equal @action.options[:include], @action.include_files - end - - should "be an empty array by default" do - @action.options[:include] = nil - assert_equal [], @action.include_files - end - end - - context "tar path" do - should "be the temporary directory with the name and extension attached" do - pwd = "foo" - FileUtils.stubs(:pwd).returns(pwd) - assert_equal File.join(pwd, "#{@action.out_path}#{@runner.env.config.package.extension}"), @action.tar_path - end - end - - context "temp path" do - setup do - @export = mock("export") - @action.expects(:export_action).returns(@export) - end - - should "use the export action's temp dir" do - path = mock("path") - @export.expects(:temp_dir).returns(path) - @action.temp_path - end - end - - context "copying include files" do - setup do - @include_files = [] - @action.stubs(:include_files).returns(@include_files) - - @temp_path = "foo" - @action.stubs(:temp_path).returns(@temp_path) - end - - should "do nothing if no include files are specified" do - assert @action.include_files.empty? - FileUtils.expects(:mkdir_p).never - FileUtils.expects(:cp).never - @action.copy_include_files - end - - should "create the include directory and copy files to it" do - include_dir = File.join(@action.temp_path, "include") - copy_seq = sequence("copy_seq") - FileUtils.expects(:mkdir_p).with(include_dir).once.in_sequence(copy_seq) - - 5.times do |i| - file = mock("f#{i}") - @include_files << file - FileUtils.expects(:cp).with(file, include_dir).in_sequence(copy_seq) - end - - @action.copy_include_files - end - end - - context "creating vagrantfile" do - setup do - @temp_path = "foo" - @action.stubs(:temp_path).returns(@temp_path) - - @network_adapter = mock("nic") - @network_adapter.stubs(:mac_address).returns("mac_address") - @vm.stubs(:network_adapters).returns([@network_adapter]) - end - - should "write the rendered vagrantfile to temp_path Vagrantfile" do - f = mock("file") - rendered = mock("rendered") - File.expects(:open).with(File.join(@action.temp_path, "Vagrantfile"), "w").yields(f) - Vagrant::Util::TemplateRenderer.expects(:render).returns(rendered).with("package_Vagrantfile", { - :base_mac => @runner.vm.network_adapters.first.mac_address - }) - f.expects(:write).with(rendered) - - @action.create_vagrantfile - end - end - - context "compression" do - setup do - @tar_path = "foo" - @action.stubs(:tar_path).returns(@tar_path) - - @temp_path = "foo" - @action.stubs(:temp_path).returns(@temp_path) - - @include_files = [] - @action.stubs(:include_files).returns(@include_files) - - @pwd = "bar" - FileUtils.stubs(:pwd).returns(@pwd) - FileUtils.stubs(:cd) - - @file = mock("file") - File.stubs(:open).yields(@file) - - @output = mock("output") - @tar = Archive::Tar::Minitar - Archive::Tar::Minitar::Output.stubs(:open).yields(@output) - @tar.stubs(:pack_file) - - @action.stubs(:copy_include_files) - @action.stubs(:create_vagrantfile) - end - - should "open the tar file with the tar path properly" do - File.expects(:open).with(@tar_path, Vagrant::Util::Platform.tar_file_options).once - @action.compress - end - - should "open tar file" do - Archive::Tar::Minitar::Output.expects(:open).with(@file).once - @action.compress - end - - #---------------------------------------------------------------- - # Methods below this comment test the block yielded by Minitar open - #---------------------------------------------------------------- - should "cd to the directory and append the directory" do - @files = [] - compress_seq = sequence("compress_seq") - - FileUtils.expects(:pwd).once.returns(@pwd).in_sequence(compress_seq) - @action.expects(:copy_include_files).once.in_sequence(compress_seq) - @action.expects(:create_vagrantfile).once.in_sequence(compress_seq) - FileUtils.expects(:cd).with(@temp_path).in_sequence(compress_seq) - Dir.expects(:glob).returns(@files).in_sequence(compress_seq) - - 5.times do |i| - file = mock("file#{i}") - @tar.expects(:pack_file).with(file, @output).once.in_sequence(compress_seq) - @files << file - end - - FileUtils.expects(:cd).with(@pwd).in_sequence(compress_seq) - @action.compress - end - - should "pop back to the current directory even if an exception is raised" do - cd_seq = sequence("cd_seq") - FileUtils.expects(:cd).with(@temp_path).raises(Exception).in_sequence(cd_seq) - FileUtils.expects(:cd).with(@pwd).in_sequence(cd_seq) - - assert_raises(Exception) { - @action.compress - } - end - end - - context "preparing the action" do - context "checking include files" do - setup do - @include_files = ['fooiest', 'booiest'] - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Package, { - :include => @include_files - }) - @runner.stubs(:find_action).returns("foo") - end - - should "check that all the include files exist" do - @include_files.each do |file| - File.expects(:exists?).with(file).returns(true) - end - @action.prepare - end - - should "raise an exception if the output file already exists" do - File.expects(:exist?).with(@action.tar_path).returns(false) - assert_raises(Vagrant::Actions::ActionException) { @action.prepare } - end - - should "raise an exception when an include file does not exist" do - File.expects(:exists?).once.returns(false) - assert_raises(Vagrant::Actions::ActionException) { @action.prepare } - end - end - - context "loading export reference" do - should "find and store a reference to the export action" do - @export = mock("export") - @runner.expects(:find_action).with(Vagrant::Actions::VM::Export).once.returns(@export) - @action.prepare - assert @export.equal?(@action.export_action) - end - - should "raise an exception if the export action couldn't be found" do - @runner.expects(:find_action).with(Vagrant::Actions::VM::Export).once.returns(nil) - assert_raises(Vagrant::Actions::ActionException) { @action.prepare } - end - end - end -end diff --git a/test/vagrant/actions/vm/provision_test.rb b/test/vagrant/actions/vm/provision_test.rb deleted file mode 100644 index 2688c29f5..000000000 --- a/test/vagrant/actions/vm/provision_test.rb +++ /dev/null @@ -1,99 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class ProvisionActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Provision) - end - - context "initialization" do - should "have a nil provisioner by default" do - assert_nil @action.provisioner - end - end - - context "executing" do - should "do nothing if the provisioner is nil" do - @action.expects(:provisioner).returns(nil) - assert_nothing_raised { @action.execute! } - end - - should "call `provision!` on the provisioner" do - provisioner = mock("provisioner") - provisioner.expects(:provision!).once - @action.expects(:provisioner).twice.returns(provisioner) - @action.execute! - end - end - - context "preparing" do - context "with a nil provisioner" do - setup do - @runner.env.config.vm.provisioner = nil - end - - should "not set a provisioner if set to nil" do - @action.prepare - assert_nil @action.provisioner - end - end - - context "with a Class provisioner" do - setup do - @instance = mock("instance") - @instance.stubs(:is_a?).with(Vagrant::Provisioners::Base).returns(true) - @instance.stubs(:prepare) - @klass = mock("klass") - @klass.stubs(:is_a?).with(Class).returns(true) - @klass.stubs(:new).with(@runner).returns(@instance) - - @runner.env.config.vm.provisioner = @klass - end - - should "set the provisioner to an instantiation of the class" do - @klass.expects(:new).with(@runner).once.returns(@instance) - assert_nothing_raised { @action.prepare } - assert_equal @instance, @action.provisioner - end - - should "call prepare on the instance" do - @instance.expects(:prepare).once - @action.prepare - end - - should "raise an exception if the class is not a subclass of the provisioner base" do - @instance.expects(:is_a?).with(Vagrant::Provisioners::Base).returns(false) - assert_raises(Vagrant::Actions::ActionException) { - @action.prepare - } - end - end - - context "with a Symbol provisioner" do - def provisioner_expectation(symbol, provisioner) - @runner.env.config.vm.provisioner = symbol - - instance = mock("instance") - instance.expects(:prepare).once - provisioner.expects(:new).with(@runner).returns(instance) - assert_nothing_raised { @action.prepare } - assert_equal instance, @action.provisioner - end - - should "raise an ActionException if its an unknown symbol" do - @runner.env.config.vm.provisioner = :this_will_never_exist - - assert_raises(Vagrant::Actions::ActionException) { - @action.prepare - } - end - - should "set :chef_solo to the ChefSolo provisioner" do - provisioner_expectation(:chef_solo, Vagrant::Provisioners::ChefSolo) - end - - should "set :chef_server to the ChefServer provisioner" do - provisioner_expectation(:chef_server, Vagrant::Provisioners::ChefServer) - end - end - end -end diff --git a/test/vagrant/actions/vm/reload_test.rb b/test/vagrant/actions/vm/reload_test.rb deleted file mode 100644 index d58551631..000000000 --- a/test/vagrant/actions/vm/reload_test.rb +++ /dev/null @@ -1,46 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class ReloadActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Reload) - end - - context "sub-actions" do - setup do - @default_order = [Vagrant::Actions::VM::Customize, Vagrant::Actions::VM::ForwardPorts, Vagrant::Actions::VM::SharedFolders, Vagrant::Actions::VM::Network, Vagrant::Actions::VM::Boot] - @vm.stubs(:running?).returns(false) - end - - def setup_action_expectations - default_seq = sequence("default_seq") - @default_order.each do |action| - @runner.expects(:add_action).with(action).once.in_sequence(default_seq) - end - end - - should "do the proper actions by default" do - setup_action_expectations - @action.prepare - end - - should "halt if the VM is running" do - @vm.expects(:running?).returns(true) - @default_order.unshift(Vagrant::Actions::VM::Halt) - setup_action_expectations - @action.prepare - end - - should "add in the provisioning step if enabled" do - env = mock_environment do |config| - # Dummy provisioner to test - config.vm.provisioner = "foo" - end - - @runner.stubs(:env).returns(env) - - @default_order.push(Vagrant::Actions::VM::Provision) - setup_action_expectations - @action.prepare - end - end -end diff --git a/test/vagrant/actions/vm/resume_test.rb b/test/vagrant/actions/vm/resume_test.rb deleted file mode 100644 index 37558b1cd..000000000 --- a/test/vagrant/actions/vm/resume_test.rb +++ /dev/null @@ -1,26 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class ResumeActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Resume) - end - - context "executing" do - setup do - @vm.stubs(:saved?).returns(true) - end - - should "save the state of the VM" do - @runner.expects(:start).once - @action.execute! - end - - should "raise an ActionException if the VM is not saved" do - @vm.expects(:saved?).returns(false) - @vm.expects(:start).never - assert_raises(Vagrant::Actions::ActionException) { - @action.execute! - } - end - end -end diff --git a/test/vagrant/actions/vm/start_test.rb b/test/vagrant/actions/vm/start_test.rb deleted file mode 100644 index 0846ae7d6..000000000 --- a/test/vagrant/actions/vm/start_test.rb +++ /dev/null @@ -1,73 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class StartActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Start) - - @action.options[:provision] = true - end - - context "sub-actions" do - setup do - @runner.stubs(:created?).returns(false) - @vm.stubs(:saved?).returns(true) - File.stubs(:file?).returns(true) - File.stubs(:exist?).returns(true) - @default_order = [Vagrant::Actions::VM::Boot] - end - - def setup_action_expectations - default_seq = sequence("default_seq") - @default_order.flatten.each do |action| - @runner.expects(:add_action).with(action, @action.options).once.in_sequence(default_seq) - end - end - - should "do the proper actions by default" do - setup_action_expectations - @action.prepare - end - - should "add customize to the beginning if its not saved" do - @vm.expects(:saved?).returns(false) - @default_order.unshift([Vagrant::Actions::VM::Customize, Vagrant::Actions::VM::ForwardPorts, Vagrant::Actions::VM::SharedFolders, Vagrant::Actions::VM::Network]) - setup_action_expectations - @action.prepare - end - - should "add do additional if VM is not created yet" do - @runner.stubs(:vm).returns(nil) - @default_order.unshift([Vagrant::Actions::VM::Customize, Vagrant::Actions::VM::ForwardPorts, Vagrant::Actions::VM::SharedFolders, Vagrant::Actions::VM::Network]) - setup_action_expectations - @action.prepare - end - - should "add provisioning if its enabled and not saved" do - @vm.env.config.vm.provisioner = :chef_solo - - @runner.stubs(:vm).returns(nil) - @default_order.unshift([Vagrant::Actions::VM::Customize, Vagrant::Actions::VM::ForwardPorts, Vagrant::Actions::VM::SharedFolders, Vagrant::Actions::VM::Network]) - @default_order << Vagrant::Actions::VM::Provision - setup_action_expectations - @action.prepare - end - end - - context "provision?" do - should "return false if no provisioner is set" do - @vm.env.config.vm.provisioner = nil - assert !@action.provision? - end - - should "return true if a provisioner is set" do - @vm.env.config.vm.provisioner = :chef_solo - assert @action.provision? - end - - should "return false if provisioning is specifically disabled" do - @vm.env.config.vm.provisioner = :chef_solo - @action.options[:provision] = false - assert !@action.provision? - end - end -end diff --git a/test/vagrant/actions/vm/suspend_test.rb b/test/vagrant/actions/vm/suspend_test.rb deleted file mode 100644 index e51a25094..000000000 --- a/test/vagrant/actions/vm/suspend_test.rb +++ /dev/null @@ -1,26 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class SuspendActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Suspend) - end - - context "executing" do - setup do - @vm.stubs(:running?).returns(true) - end - - should "save the state of the VM" do - @vm.expects(:save_state).once - @action.execute! - end - - should "raise an ActionException if the VM is not running" do - @vm.expects(:running?).returns(false) - @vm.expects(:save_state).never - assert_raises(Vagrant::Actions::ActionException) { - @action.execute! - } - end - end -end diff --git a/test/vagrant/actions/vm/up_test.rb b/test/vagrant/actions/vm/up_test.rb deleted file mode 100644 index a116e0188..000000000 --- a/test/vagrant/actions/vm/up_test.rb +++ /dev/null @@ -1,96 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') - -class UpActionTest < Test::Unit::TestCase - setup do - @runner, @vm, @action = mock_action(Vagrant::Actions::VM::Up) - end - - context "sub-actions" do - setup do - @runner.stubs(:created?).returns(false) - - File.stubs(:file?).returns(true) - File.stubs(:exist?).returns(true) - @default_order = [Vagrant::Actions::VM::Import, Vagrant::Actions::VM::Start] - - @dotfile_path = "foo" - @runner.env.stubs(:dotfile_path).returns(@dotfile_path) - end - - def setup_action_expectations - default_seq = sequence("default_seq") - @default_order.each do |action| - @runner.expects(:add_action).with(action, @action.options).once.in_sequence(default_seq) - end - end - - should "raise an ActionException if a dotfile exists but is not a file" do - File.expects(:file?).with(@runner.env.dotfile_path).returns(false) - assert_raises(Vagrant::Actions::ActionException) { - @action.prepare - } - end - - should "not raise an ActionException if dotfile doesn't exist" do - setup_action_expectations - File.stubs(:exist?).returns(false) - assert_nothing_raised { @action.prepare } - end - - should "not raise an ActionException if dotfile exists but is a file" do - File.stubs(:file?).returns(true) - File.stubs(:exist?).returns(true) - setup_action_expectations - assert_nothing_raised { @action.prepare } - end - - should "do the proper actions by default" do - setup_action_expectations - @action.prepare - end - - should "add in the action to move hard drive if config is set" do - env = mock_environment do |config| - File.expects(:directory?).with("foo").returns(true) - config.vm.hd_location = "foo" - end - - @runner.stubs(:env).returns(env) - env.stubs(:dotfile_path).returns(@dotfile_path) - - @default_order.insert(0, Vagrant::Actions::VM::MoveHardDrive) - setup_action_expectations - @action.prepare - end - end - - context "callbacks" do - should "call update dotfile and mac address setup after import" do - boot_seq = sequence("boot") - @action.expects(:update_dotfile).once.in_sequence(boot_seq) - @action.expects(:setup_mac_address).once.in_sequence(boot_seq) - @action.expects(:check_guest_additions).once.in_sequence(boot_seq) - @action.after_import - end - end - - context "updating the dotfile" do - should "call update dotfile on the VM's environment" do - @runner.stubs(:uuid) - @runner.env.expects(:update_dotfile).once - @action.update_dotfile - end - end - - context "setting up MAC address" do - should "match the mac address with the base" do - nic = mock("nic") - nic.expects(:mac_address=).once - - @vm.expects(:network_adapters).returns([nic]).once - @vm.expects(:save).once - - @action.setup_mac_address - end - end -end diff --git a/test/vagrant/box_test.rb b/test/vagrant/box_test.rb index 4c5efa894..2a1533528 100644 --- a/test/vagrant/box_test.rb +++ b/test/vagrant/box_test.rb @@ -116,7 +116,7 @@ class BoxTest < Test::Unit::TestCase end should "execute the Add action when add is called" do - @box.expects(:execute!).with(Vagrant::Actions::Box::Add).once + @box.env.actions.expects(:run).with(:box_add, { "box" => @box }) @box.add end @@ -134,7 +134,7 @@ class BoxTest < Test::Unit::TestCase context "destroying" do should "execute the destroy action" do - @box.expects(:execute!).with(Vagrant::Actions::Box::Destroy).once + @box.env.actions.expects(:run).with(:box_remove, { "box" => @box }) @box.destroy end end diff --git a/test/vagrant/commands/package_test.rb b/test/vagrant/commands/package_test.rb index f10f98c76..2b8cd2413 100644 --- a/test/vagrant/commands/package_test.rb +++ b/test/vagrant/commands/package_test.rb @@ -81,18 +81,11 @@ class CommandsPackageTest < Test::Unit::TestCase context "packaging a VM" do setup do @vm = mock("vm") - @vm.stubs(:powered_off?).returns(true) @options = {} @instance.stubs(:options).returns(@options) end - should "error and exit if VM is not powered off" do - @vm.stubs(:powered_off?).returns(false) - @instance.expects(:error_and_exit).with(:vm_power_off_to_package).once - @instance.package_vm(@vm) - end - should "package the VM with the proper arguments" do @options[:output] = "foo.box" @options[:include] = :bar diff --git a/test/vagrant/downloaders/file_test.rb b/test/vagrant/downloaders/file_test.rb index 1933aa501..2977aa44b 100644 --- a/test/vagrant/downloaders/file_test.rb +++ b/test/vagrant/downloaders/file_test.rb @@ -9,7 +9,7 @@ class FileDownloaderTest < Test::Unit::TestCase context "preparing" do should "raise an exception if the file does not exist" do File.expects(:file?).with(@uri).returns(false) - assert_raises(Vagrant::Actions::ActionException) { + assert_raises(Vagrant::Action::ActionException) { @downloader.prepare(@uri) } end diff --git a/test/vagrant/environment_test.rb b/test/vagrant/environment_test.rb index 5f6e37c47..68c6f458d 100644 --- a/test/vagrant/environment_test.rb +++ b/test/vagrant/environment_test.rb @@ -226,6 +226,7 @@ class EnvironmentTest < Test::Unit::TestCase @env.expects(:load_vm!).once.in_sequence(call_seq) @env.expects(:load_active_list!).once.in_sequence(call_seq) @env.expects(:load_commands!).once.in_sequence(call_seq) + @env.expects(:load_actions!).once.in_sequence(call_seq) assert_equal @env, @env.load! end end @@ -614,6 +615,19 @@ class EnvironmentTest < Test::Unit::TestCase assert_equal commands, @env.commands end end + + context "loading actions" do + setup do + @env = mock_environment + end + + should "initialize the Action object with the given environment" do + result = mock("result") + Vagrant::Action.expects(:new).with(@env).returns(result) + @env.load_actions! + assert_equal result, @env.actions + end + end end context "requiring properties" do diff --git a/test/vagrant/provisioners/base_test.rb b/test/vagrant/provisioners/base_test.rb index 1820bfa46..ec3947f63 100644 --- a/test/vagrant/provisioners/base_test.rb +++ b/test/vagrant/provisioners/base_test.rb @@ -7,13 +7,16 @@ class BaseProvisionerTest < Test::Unit::TestCase context "base instance" do setup do - @vm = mock("vm") - @base = Vagrant::Provisioners::Base.new(@vm) + @env = Vagrant::Action::Environment.new(mock_environment) + @base = Vagrant::Provisioners::Base.new(@env) end should "set the environment" do - base = Vagrant::Provisioners::Base.new(@vm) - assert_equal @vm, base.vm + assert_equal @env.env, @base.env + end + + should "return the VM which the provisioner is acting on" do + assert_equal @env.env.vm, @base.vm end should "implement provision! which does nothing" do diff --git a/test/vagrant/provisioners/chef_server_test.rb b/test/vagrant/provisioners/chef_server_test.rb index 026979c28..4bd4e1668 100644 --- a/test/vagrant/provisioners/chef_server_test.rb +++ b/test/vagrant/provisioners/chef_server_test.rb @@ -2,9 +2,12 @@ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') class ChefServerProvisionerTest < Test::Unit::TestCase setup do - @vm = mock_vm - @env = @vm.env - @action = Vagrant::Provisioners::ChefServer.new(@vm) + @action_env = Vagrant::Action::Environment.new(mock_environment) + @action_env.env.vm = mock_vm + + @action = Vagrant::Provisioners::ChefServer.new(@action_env) + @env = @action.env + @vm = @action.vm end context "provisioning" do @@ -33,19 +36,20 @@ class ChefServerProvisionerTest < Test::Unit::TestCase @action.stubs(:env).returns(@env) - assert_nothing_raised { @action.prepare } + @action.prepare + assert !@action_env.error? end - should "raise an exception if validation_key_path is nil" do + should "eraise an exception if validation_key_path is nil" do @env = mock_environment do |config| config.chef.validation_key_path = nil end @action.stubs(:env).returns(@env) - assert_raises(Vagrant::Actions::ActionException) { - @action.prepare - } + @action.prepare + assert @action_env.error? + assert_equal :chef_server_validation_key_required, @action_env.error.first end should "not raise an exception if validation_key_path does exist" do @@ -57,7 +61,8 @@ class ChefServerProvisionerTest < Test::Unit::TestCase @action.stubs(:validation_key_path).returns("9") File.expects(:file?).with(@action.validation_key_path).returns(true) - assert_nothing_raised { @action.prepare } + @action.prepare + assert !@action_env.error? end should "raise an exception if validation_key_path doesn't exist" do @@ -69,9 +74,9 @@ class ChefServerProvisionerTest < Test::Unit::TestCase @action.stubs(:validation_key_path).returns("9") File.expects(:file?).with(@action.validation_key_path).returns(false) - assert_raises(Vagrant::Actions::ActionException) { - @action.prepare - } + @action.prepare + assert @action_env.error? + assert_equal :chef_server_validation_key_doesnt_exist, @action_env.error.first end should "not raise an exception if chef_server_url is set" do @@ -81,7 +86,8 @@ class ChefServerProvisionerTest < Test::Unit::TestCase @action.stubs(:env).returns(@env) - assert_nothing_raised { @action.prepare } + @action.prepare + assert !@action_env.error? end should "raise an exception if chef_server_url is nil" do @@ -91,9 +97,9 @@ class ChefServerProvisionerTest < Test::Unit::TestCase @action.stubs(:env).returns(@env) - assert_raises(Vagrant::Actions::ActionException) { - @action.prepare - } + @action.prepare + assert @action_env.error? + assert_equal :chef_server_url_required, @action_env.error.first end end diff --git a/test/vagrant/provisioners/chef_solo_test.rb b/test/vagrant/provisioners/chef_solo_test.rb index 39496d200..a4d0452fb 100644 --- a/test/vagrant/provisioners/chef_solo_test.rb +++ b/test/vagrant/provisioners/chef_solo_test.rb @@ -2,9 +2,12 @@ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') class ChefSoloProvisionerTest < Test::Unit::TestCase setup do - @vm = mock_vm - @env = @vm.env - @action = Vagrant::Provisioners::ChefSolo.new(@vm) + @action_env = Vagrant::Action::Environment.new(mock_environment) + @action_env.env.vm = mock_vm + + @action = Vagrant::Provisioners::ChefSolo.new(@action_env) + @env = @action.env + @vm = @action.vm end context "preparing" do diff --git a/test/vagrant/provisioners/chef_test.rb b/test/vagrant/provisioners/chef_test.rb index bf9ca1768..32df5d159 100644 --- a/test/vagrant/provisioners/chef_test.rb +++ b/test/vagrant/provisioners/chef_test.rb @@ -2,16 +2,19 @@ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper') class ChefProvisionerTest < Test::Unit::TestCase setup do - @vm = mock_vm - @env = @vm.env - @action = Vagrant::Provisioners::Chef.new(@vm) + @action_env = Vagrant::Action::Environment.new(mock_environment) + @action_env.env.vm = mock_vm + + @action = Vagrant::Provisioners::Chef.new(@action_env) + @env = @action.env + @vm = @action.vm end context "preparing" do - should "raise an ActionException" do - assert_raises(Vagrant::Actions::ActionException) { - @action.prepare - } + should "error the environment" do + @action.prepare + assert @action_env.error? + assert_equal :chef_base_invalid_provisioner, @action_env.error.first end end diff --git a/test/vagrant/ssh_session_test.rb b/test/vagrant/ssh_session_test.rb index 1aa967a4d..3ca470653 100644 --- a/test/vagrant/ssh_session_test.rb +++ b/test/vagrant/ssh_session_test.rb @@ -19,7 +19,7 @@ class SshSessionTest < Test::Unit::TestCase context "checking exit status" do should "raise an ActionException if its non-zero" do - assert_raises(Vagrant::Actions::ActionException) { + assert_raises(Vagrant::Action::ActionException) { @instance.check_exit_status(1, "foo") } end @@ -30,7 +30,7 @@ class SshSessionTest < Test::Unit::TestCase :error_data => {} } result = Exception.new - Vagrant::Actions::ActionException.expects(:new).with(options[:error_key], options[:error_data]).once.returns(result) + Vagrant::Action::ActionException.expects(:new).with(options[:error_key], options[:error_data]).once.returns(result) assert_raises(Exception) { @instance.check_exit_status(1, "foo", options) diff --git a/test/vagrant/systems/linux_test.rb b/test/vagrant/systems/linux_test.rb index 1928ba539..9a86815ee 100644 --- a/test/vagrant/systems/linux_test.rb +++ b/test/vagrant/systems/linux_test.rb @@ -149,7 +149,7 @@ class LinuxSystemTest < Test::Unit::TestCase should "raise an ActionException if the command fails constantly" do @ssh.expects(:exec!).times(@limit).returns(!@success_return) - assert_raises(Vagrant::Actions::ActionException) { + assert_raises(Vagrant::Action::ActionException) { mount_folder } end diff --git a/test/vagrant/vm_test.rb b/test/vagrant/vm_test.rb index 5afcb9f8f..e9a4ce269 100644 --- a/test/vagrant/vm_test.rb +++ b/test/vagrant/vm_test.rb @@ -12,13 +12,6 @@ class VMTest < Test::Unit::TestCase Net::SSH.stubs(:start) end - context "being an action runner" do - should "be an action runner" do - vm = Vagrant::VM.new(:env => @env) - assert vm.is_a?(Vagrant::Actions::Runner) - end - end - context "finding a VM" do should "return return an uncreated VM object if the VM is not found" do VirtualBox::VM.expects(:find).returns(nil) @@ -152,65 +145,57 @@ class VMTest < Test::Unit::TestCase end context "packaging" do - should "queue up the actions and execute" do - action_seq = sequence("action_seq") - @vm.expects(:add_action).with(Vagrant::Actions::VM::Export, nil).once.in_sequence(action_seq) - @vm.expects(:add_action).with(Vagrant::Actions::VM::Package, nil).once.in_sequence(action_seq) - @vm.expects(:execute!).in_sequence(action_seq) - @vm.package + should "execute the package action" do + @vm.env.actions.expects(:run).with(:package, :foo => :bar).once + @vm.package(:foo => :bar) end end context "upping" do should "execute the up action" do - @vm.expects(:execute!).with(Vagrant::Actions::VM::Up, nil).once + @vm.env.actions.expects(:run).with(:up, nil).once @vm.up end end context "halting" do should "execute the halt action" do - @vm.expects(:execute!).with(Vagrant::Actions::VM::Halt, nil).once - @vm.halt - end - - should "force if specified" do - @vm.expects(:execute!).with(Vagrant::Actions::VM::Halt, {:foo => :bar}).once + @vm.env.actions.expects(:run).with(:halt, :foo => :bar).once @vm.halt({:foo => :bar}) end end context "reloading action" do should "execute the reload action" do - @vm.expects(:execute!).with(Vagrant::Actions::VM::Reload).once + @vm.env.actions.expects(:run).with(:reload).once @vm.reload end end context "provisioning" do should "execute the provision action" do - @vm.expects(:execute!).with(Vagrant::Actions::VM::Provision).once + @vm.env.actions.expects(:run).with(:provision).once @vm.provision end end context "destroying" do - should "execute the down action" do - @vm.expects(:execute!).with(Vagrant::Actions::VM::Down).once + should "execute the destroy action" do + @vm.env.actions.expects(:run).with(:destroy).once @vm.destroy end end context "suspending" do should "execute the suspend action" do - @vm.expects(:execute!).with(Vagrant::Actions::VM::Suspend).once + @vm.env.actions.expects(:run).with(:suspend).once @vm.suspend end end context "resuming" do should "execute the resume action" do - @vm.expects(:execute!).with(Vagrant::Actions::VM::Resume).once + @vm.env.actions.expects(:run).with(:resume).once @vm.resume end end @@ -227,7 +212,7 @@ class VMTest < Test::Unit::TestCase end should "execute the start action" do - @vm.expects(:execute!).once.with(Vagrant::Actions::VM::Start) + @vm.env.actions.expects(:run).with(:start).once @vm.start end end diff --git a/vagrant.gemspec b/vagrant.gemspec index 4e07c85c9..542d49128 100644 --- a/vagrant.gemspec +++ b/vagrant.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= s.authors = ["Mitchell Hashimoto", "John Bender"] - s.date = %q{2010-07-02} + s.date = %q{2010-07-07} s.default_executable = %q{vagrant} s.description = %q{Vagrant is a tool for building and distributing virtualized development environments.} s.email = ["mitchell.hashimoto@gmail.com", "john.m.bender@gmail.com"] @@ -33,6 +33,30 @@ Gem::Specification.new do |s| "keys/vagrant.ppk", "keys/vagrant.pub", "lib/vagrant.rb", + "lib/vagrant/action.rb", + "lib/vagrant/action/box/download.rb", + "lib/vagrant/action/box/unpackage.rb", + "lib/vagrant/action/builder.rb", + "lib/vagrant/action/builtin.rb", + "lib/vagrant/action/environment.rb", + "lib/vagrant/action/error_halt.rb", + "lib/vagrant/action/vm/boot.rb", + "lib/vagrant/action/vm/check_guest_additions.rb", + "lib/vagrant/action/vm/customize.rb", + "lib/vagrant/action/vm/destroy.rb", + "lib/vagrant/action/vm/destroy_unused_network_interfaces.rb", + "lib/vagrant/action/vm/export.rb", + "lib/vagrant/action/vm/forward_ports.rb", + "lib/vagrant/action/vm/halt.rb", + "lib/vagrant/action/vm/import.rb", + "lib/vagrant/action/vm/match_mac_address.rb", + "lib/vagrant/action/vm/network.rb", + "lib/vagrant/action/vm/package.rb", + "lib/vagrant/action/vm/persist.rb", + "lib/vagrant/action/vm/provision.rb", + "lib/vagrant/action/vm/resume.rb", + "lib/vagrant/action/vm/share_folders.rb", + "lib/vagrant/action/vm/suspend.rb", "lib/vagrant/actions/base.rb", "lib/vagrant/actions/box/add.rb", "lib/vagrant/actions/box/destroy.rb", @@ -113,6 +137,29 @@ Gem::Specification.new do |s| "templates/unison/crontab_entry.erb", "templates/unison/script.erb", "test/test_helper.rb", + "test/vagrant/action/box/download_test.rb", + "test/vagrant/action/box/unpackage_test.rb", + "test/vagrant/action/builder_test.rb", + "test/vagrant/action/environment_test.rb", + "test/vagrant/action/error_halt_test.rb", + "test/vagrant/action/vm/boot_test.rb", + "test/vagrant/action/vm/check_guest_additions_test.rb", + "test/vagrant/action/vm/customize_test.rb", + "test/vagrant/action/vm/destroy_test.rb", + "test/vagrant/action/vm/destroy_unused_network_interfaces_test.rb", + "test/vagrant/action/vm/export_test.rb", + "test/vagrant/action/vm/forward_ports_test.rb", + "test/vagrant/action/vm/halt_test.rb", + "test/vagrant/action/vm/import_test.rb", + "test/vagrant/action/vm/match_mac_address_test.rb", + "test/vagrant/action/vm/network_test.rb", + "test/vagrant/action/vm/package_test.rb", + "test/vagrant/action/vm/persist_test.rb", + "test/vagrant/action/vm/provision_test.rb", + "test/vagrant/action/vm/resume_test.rb", + "test/vagrant/action/vm/share_folders_test.rb", + "test/vagrant/action/vm/suspend_test.rb", + "test/vagrant/action_test.rb", "test/vagrant/actions/base_test.rb", "test/vagrant/actions/box/add_test.rb", "test/vagrant/actions/box/destroy_test.rb", @@ -190,6 +237,29 @@ Gem::Specification.new do |s| s.summary = %q{Vagrant is a tool for building and distributing virtualized development environments.} s.test_files = [ "test/test_helper.rb", + "test/vagrant/action/box/download_test.rb", + "test/vagrant/action/box/unpackage_test.rb", + "test/vagrant/action/builder_test.rb", + "test/vagrant/action/environment_test.rb", + "test/vagrant/action/error_halt_test.rb", + "test/vagrant/action/vm/boot_test.rb", + "test/vagrant/action/vm/check_guest_additions_test.rb", + "test/vagrant/action/vm/customize_test.rb", + "test/vagrant/action/vm/destroy_test.rb", + "test/vagrant/action/vm/destroy_unused_network_interfaces_test.rb", + "test/vagrant/action/vm/export_test.rb", + "test/vagrant/action/vm/forward_ports_test.rb", + "test/vagrant/action/vm/halt_test.rb", + "test/vagrant/action/vm/import_test.rb", + "test/vagrant/action/vm/match_mac_address_test.rb", + "test/vagrant/action/vm/network_test.rb", + "test/vagrant/action/vm/package_test.rb", + "test/vagrant/action/vm/persist_test.rb", + "test/vagrant/action/vm/provision_test.rb", + "test/vagrant/action/vm/resume_test.rb", + "test/vagrant/action/vm/share_folders_test.rb", + "test/vagrant/action/vm/suspend_test.rb", + "test/vagrant/action_test.rb", "test/vagrant/actions/base_test.rb", "test/vagrant/actions/box/add_test.rb", "test/vagrant/actions/box/destroy_test.rb",