Merge branch 'warden'
This commit is contained in:
commit
5c56e46739
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue