Initial attempt at refactoring commands out into evented actions. Heavy documentation on the "vagrant/actions/base.rb" class. New VM action runner functionality is well tested, as well.

This commit is contained in:
Mitchell Hashimoto 2010-02-12 19:53:34 -08:00
parent a4b9375abf
commit af1fcd0ae0
3 changed files with 146 additions and 4 deletions

View File

@ -0,0 +1,50 @@
module Vagrant
module Actions
# Base class for any command actions. A command action handles
# executing a step or steps on a given Vagrant::VM object. The
# action should define any callbacks that it will call, or
# attach itself to some callbacks on the VM object.
class Base
attr_reader :vm
include Vagrant::Util
# Initialization of the actions are done all at once. The guarantee
# is that when an action is initialized, no other action has had
# its `prepare` or `execute!` method called yet, so an action can
# setup anything it needs to with this safety. An example of this
# would be instance_evaling the vm instance to include a module so
# additionally functionality could be defined on the vm which other
# action `prepare` methods may rely on.
def initialize(vm)
@vm = vm
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, so no race conditions between
# action setups should be done here.
def prepare
# Examples:
#
# Perhaps we need an additional action to go, specifically
# maybe only if a configuration is set
#
#@vm.actions << FooAction if Vagrant.config[:foo] == :bar
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 NO EFFECT, and
# adding callbacks has unpredictable effects.
def execute!
# Example code:
#
# @vm.invoke_callback(:before_oven, "cookies")
# Do lots of stuff here
# @vm.invoke_callback(:after_oven, "more", "than", "one", "option")
end
end
end
end

View File

@ -1,7 +1,8 @@
module Vagrant
class VM
include Vagrant::Util
attr_reader :vm
attr_accessor :vm
attr_reader :actions
class << self
# Bring up the virtual machine. Imports the base image and
@ -21,6 +22,32 @@ module Vagrant
def initialize(vm=nil)
@vm = vm
@actions = []
end
def execute!
# Initialize each action. Prepare is not done together with
# this since initialization is a time which guarantees that
# prepare has not been called for any other action yet.
@actions.collect! do |action_class|
action_class.new(self)
end
# Call the prepare method on each once its
# initialized, then call the execute! method
[:prepare, :execute!].each do |method|
@actions.each do |action|
action.send(method)
end
end
end
def invoke_callback(name, *args)
# Attempt to call the method for the callback on each of the
# actions
@actions.each do |action|
action.send(name, *args) if action.respond_to?(name)
end
end
def create
@ -178,15 +205,15 @@ error
ovf_path = File.join(folder, "#{name}.ovf")
tar_path = "#{folder}.box"
logger.info "Exporting required VM files to working directory ..."
@vm.export(ovf_path)
# TODO use zlib ...
logger.info "Packaging VM into #{name}.box ..."
Tar.open(tar_path, File::CREAT | File::WRONLY, 0644, Tar::GNU) do |tar|
begin
# appending the expanded file path adds the whole folder tree
# appending the expanded file path adds the whole folder tree
# to the tar archive there must be a better way
working_dir = FileUtils.pwd
FileUtils.cd(to)

View File

@ -11,6 +11,71 @@ class VMTest < Test::Unit::TestCase
Net::SSH.stubs(:start)
end
context "callbacks" do
setup do
@vm = Vagrant::VM.new(@mock_vm)
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
@vm.actions << action
@vm.invoke_callback(:foo)
end
end
should "invoke callback on actions which do respond to the method" do
action = mock("action")
action.expects(:foo).once
@vm.actions << action
@vm.invoke_callback(:foo)
end
end
context "actions" do
setup do
@vm = Vagrant::VM.new(@mock_vm)
end
should "be empty initially" do
assert @vm.actions.empty?
end
should "be able to add actions" do
assert_nothing_raised do
@vm.actions << "Foo"
@vm.actions << "Bar"
assert_equal 2, @vm.actions.length
end
end
should "run #prepare on all actions, then #execute!" do
action_seq = sequence("action_seq")
actions = []
5.times do |i|
action = mock("action#{i}")
action_class = mock("action_class#{i}")
action_class.expects(:new).once.returns(action).in_sequence(action_seq)
@vm.actions << action_class
actions << action
end
[:prepare, :execute!].each do |method|
actions.each do |action|
action.expects(method).once.in_sequence(action_seq)
end
end
@vm.execute!
end
end
context "vagrant up" do
should "create a Vagrant::VM instance and call create" do
inst = mock("instance")