Merge branch 'warden'

This commit is contained in:
John Bender 2010-08-25 00:26:07 -07:00
commit 5c56e46739
16 changed files with 230 additions and 144 deletions

View File

@ -64,15 +64,6 @@ module Vagrant
end
Busy.busy(int_callback) { callable.call(action_environment) }
exit if action_environment.interrupted?
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

View File

@ -23,7 +23,7 @@ module Vagrant
@app.call(@env)
cleanup
recover(env) # called in both cases to cleanup workspace
end
def instantiate_downloader
@ -50,9 +50,9 @@ module Vagrant
end
end
def cleanup
def recover(env)
if temp_path && File.exist?(temp_path)
@env.logger.info "Cleaning up downloaded box..."
env.logger.info "Cleaning up downloaded box..."
File.unlink(temp_path)
end
end

View File

@ -28,11 +28,9 @@ module Vagrant
decompress
@app.call(@env)
cleanup if @env.error?
end
def cleanup
def recover(env)
if File.directory?(box_directory)
FileUtils.rm_rf(box_directory)
end

View File

@ -112,34 +112,11 @@ module Vagrant
def to_app(env)
# Prepend the error halt task so errneous environments are halted
# before the chain even begins.
items = stack.dup.unshift([Env::ErrorHalt, [], nil])
middleware = stack.dup.push([Env::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) }
Vagrant::Action::Warden.new(middleware, env)
end
# Runs the builder stack with the given environment.

View File

@ -33,11 +33,9 @@ module Vagrant
compress
@app.call(env)
cleanup if env.error?
end
def cleanup
def recover(env)
# Cleanup any packaged files if the packaging failed at some point.
File.delete(tar_path) if File.exist?(tar_path)
end

View File

@ -15,17 +15,16 @@ module Vagrant
@env = env
return env.error!(:vm_power_off_to_package) if !@env["vm"].vm.powered_off?
return if env.error?
setup_temp_dir
export
@app.call(env) if !env.error?
@app.call(env)
cleanup
recover(env) # called to cleanup temp directory
end
def cleanup
def recover(env)
if temp_dir && File.exist?(temp_dir)
FileUtils.rm_rf(temp_dir)
end

View File

@ -22,10 +22,12 @@ module Vagrant
end
# Import completed successfully. Continue the chain
@app.call(env) if !env.error?
@app.call(env)
end
def recover(env)
# Interrupted, destroy the VM
env["actions"].run(:destroy) if env.interrupted?
env["actions"].run(:destroy)
end
end
end

View File

@ -0,0 +1,53 @@
module Vagrant
class Action
class Warden
include Util
attr_accessor :actions, :stack
def initialize(actions, env)
@stack = []
@actions = actions.map { |m| finalize_action(m, env) }.reverse
end
def call(env)
return if @actions.empty?
# If the previous action passes and environment error on
@stack.push(@actions.pop).last.call(env) unless env.error?
# if the call action returned prematurely with an error
begin_rescue(env) if env.error?
end
def begin_rescue(env)
@stack.reverse.each do |act|
act.recover(env) if act.respond_to?(:recover)
end
exit if env.interrupted?
# Erroneous environment resulted. Properly display error message.
error_and_exit(*env.error)
end
def finalize_action(action, env)
klass, args, block = action
if klass.is_a?(Class)
# A action klass which is to be instantiated with the
# app, env, and any arguments given
klass.new(self, 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)
self.call(e)
end
else
raise "Invalid action: #{action.inspect}"
end
end
end
end
end

View File

@ -32,7 +32,7 @@ class DownloadBoxActionTest < Test::Unit::TestCase
@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.expects(:recover).with(@env).in_sequence(seq)
@instance.call(@env)
end
@ -42,7 +42,6 @@ class DownloadBoxActionTest < Test::Unit::TestCase
@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
@ -115,13 +114,13 @@ class DownloadBoxActionTest < Test::Unit::TestCase
should "delete the temporary file if it exists" do
File.expects(:unlink).with(@temp_path).once
@instance.cleanup
@instance.recover(@env)
end
should "not delete anything if it doesn't exist" do
File.stubs(:exist?).returns(false)
File.expects(:unlink).never
@instance.cleanup
@instance.recover(@env)
end
end

View File

@ -21,7 +21,6 @@ class UnpackageBoxActionTest < Test::Unit::TestCase
@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
@ -29,18 +28,6 @@ class UnpackageBoxActionTest < Test::Unit::TestCase
@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
@ -54,13 +41,13 @@ class UnpackageBoxActionTest < Test::Unit::TestCase
should "do nothing if not a directory" do
FileUtils.expects(:rm_rf).never
@instance.cleanup
@instance.recover(nil)
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
@instance.recover(nil)
end
end

View File

@ -166,7 +166,7 @@ class ActionBuilderTest < Test::Unit::TestCase
middleware.stubs(:is_a?).with(Class).returns(true)
middleware.expects(:new).with(anything, env).returns(result)
@instance.use middleware
result = @instance.to_app(env)
result = @instance.to_app(env).actions.first
assert result.kind_of?(Vagrant::Action::Env::ErrorHalt)
end

View File

@ -59,8 +59,6 @@ class PackageGeneralActionTest < Test::Unit::TestCase
@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.expects(:cleanup).never
@instance.call(@env)
end
@ -98,17 +96,6 @@ class PackageGeneralActionTest < Test::Unit::TestCase
assert @env.error?
assert_equal :package_requires_directory, @env.error.first
end
should "run cleanup if environment errors" 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.expects(:cleanup).in_sequence(seq)
@env.error!(:foo)
@instance.call(@env)
end
end
context "cleaning up" do
@ -123,14 +110,14 @@ class PackageGeneralActionTest < Test::Unit::TestCase
File.expects(:exist?).with(@instance.tar_path).returns(false)
File.expects(:delete).never
@instance.cleanup
@instance.recover(@env)
end
should "delete the packaged box if it exists" do
File.expects(:exist?).returns(true)
File.expects(:delete).with(@instance.tar_path).once
@instance.cleanup
@instance.recover(@env)
end
end

View File

@ -24,7 +24,7 @@ class ExportVMActionTest < Test::Unit::TestCase
@instance.expects(:setup_temp_dir).in_sequence(seq)
@instance.expects(:export).in_sequence(seq)
@app.expects(:call).with(@env).in_sequence(seq)
@instance.expects(:cleanup).in_sequence(seq)
@instance.expects(:recover).in_sequence(seq).with(@env)
@instance.call(@env)
end
@ -34,37 +34,12 @@ class ExportVMActionTest < Test::Unit::TestCase
@instance.expects(:setup_temp_dir).never
@instance.expects(:export).never
@app.expects(:call).with(@env).never
@instance.expects(:cleanup).never
@instance.expects(:recover).never
@instance.call(@env)
assert @env.error?
assert_equal :vm_power_off_to_package, @env.error.first
end
should "halt the chain if env error" do
@internal_vm.stubs(:powered_off?).returns(true)
@instance.expects(:setup_temp_dir).never
@instance.expects(:export).never
@app.expects(:call).with(@env).never
@instance.expects(:cleanup).never
@env.error!(:interrupt)
@instance.call(@env)
end
should "halt the chain if env error when call is reached" do
@internal_vm.stubs(:powered_off?).returns(true)
@instance.expects(:setup_temp_dir).once
@instance.expects(:export).once.with() do
@env.error!(:interrupt)
true
end
@app.expects(:call).with(@env).never
@instance.expects(:cleanup).once
@instance.call(@env)
end
end
context "cleaning up" do
@ -76,13 +51,13 @@ class ExportVMActionTest < Test::Unit::TestCase
should "delete the temporary file if it exists" do
File.expects(:unlink).with(@temp_dir).once
@instance.cleanup
@instance.recover(nil)
end
should "not delete anything if it doesn't exist" do
File.stubs(:exist?).returns(false)
File.expects(:unlink).never
@instance.cleanup
@instance.recover(nil)
end
end

View File

@ -37,14 +37,11 @@ class ImportVMActionTest < Test::Unit::TestCase
assert @env.error?
end
should "run the destroy action if interrupted" do
VirtualBox::VM.stubs(:import).returns(mock("vm"))
@app.expects(:call).once.with() do |env|
assert_equal @env, env
@env.error!(:interrupt)
end
@env.env.actions.expects(:run).with(:destroy).once
@instance.call(@env)
should "run the destroy action on recover" do
env = mock("env")
destroy = mock("destory")
env.expects(:[]).with("actions").returns(destroy)
destroy.expects(:run).with(:destroy)
@instance.recover(env)
end
end

View File

@ -0,0 +1,142 @@
require "test_helper"
class ActionWardenTest < Test::Unit::TestCase
setup do
@klass = Vagrant::Action::Warden
@instance = @klass.new([], {})
@klass.any_instance.stubs(:error_and_exit)
end
context "initializing" do
should "finalize the middleware" do
middleware = [1,2,3]
middleware.each do |m|
@klass.any_instance.expects(:finalize_action).with(m, {}).returns(m)
end
@warden = @klass.new(middleware, new_env)
assert_equal @warden.actions, [3,2,1]
end
end
context "setting up middleware" do
should "make non-classes lambdas" do
env = new_env
env.expects(:foo).once
func = lambda { |x| x.foo }
@instance.finalize_action(func, env).call(env)
end
should "raise exception if given invalid middleware" do
assert_raises(RuntimeError) {
@instance.finalize_action(7, nil)
}
end
end
context "calling" do
should "return if there are no actions to execute" do
@instance.actions.expects(:pop).never
assert !@instance.call(new_env)
end
should "move the last action to the front of the stack" do
@instance.actions << lambda { |env| }
assert @instance.stack.empty?
@instance.call(new_env)
assert !@instance.stack.empty?
assert @instance.actions.empty?
end
should "call the next action" do
action = mock('action')
action.expects(:call).with({})
@instance.actions << action
@instance.call(new_env)
end
should "begin recover on environment error" do
@instance.expects(:begin_rescue)
@instance.actions << lambda {}
@instance.actions.first.expects(:call).never
@instance.call(new_env_with_error)
end
should "not call the next action on env err" do
action = mock('action')
action.expects(:call).never
@instance.actions << action
@instance.expects(:begin_rescue)
@instance.call(new_env_with_error)
end
should "call begin recover when the called action returns with an env error" do
class Foo
def initialize(*args); end
def call(env)
return env.error!(:foo)
end
end
@instance.actions << Foo.new
@instance.expects(:begin_rescue)
@instance.call(new_env)
end
def new_env_with_error
env = new_env
env.error!(:foo)
env
end
end
context "recover" do
should "call recover on all items in the stack" do
mock_action = rescueable_mock("action")
mock_action.expects(:recover).times(2)
@instance.stack = [mock_action, mock_action]
@instance.begin_rescue(new_env)
end
should "call recover on stack in reversed order" do
seq = sequence("reverse")
first_mock_action = rescueable_mock("first")
second_mock_action = rescueable_mock("second")
@instance.stack = [first_mock_action, second_mock_action]
second_mock_action.expects(:recover).in_sequence(seq)
first_mock_action.expects(:recover).in_sequence(seq)
@instance.begin_rescue(new_env)
end
should "call error and exit" do
@instance.expects(:error_and_exit)
@instance.begin_rescue(new_env)
end
should "call exit if the environment is interupted" do
@instance.expects(:exit)
env = new_env
env.expects(:interrupted?).returns(true)
@instance.begin_rescue(env)
end
context "with many middleware" do
should "not call middleware after" do
end
end
end
def new_env
Vagrant::Action::Environment.new(nil)
end
def rescueable_mock(name)
mock_action = mock(name)
mock_action.stubs(:respond_to?).with(:recover).returns(true)
mock_action
end
end

View File

@ -88,24 +88,5 @@ class ActionTest < Test::Unit::TestCase
@instance.run(callable, :bar => :foo)
end
should "exit if environment was marked as interrupted" do
callable = lambda do |env|
env.error!(:interrupt)
end
@instance.stubs(:error_and_exit)
@instance.expects(:exit).once
@instance.run(callable)
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