Fix bug where Call didn't propagate recovery. Warden has no recovery.
The issue here is that when a middleware failed and a recovery sequence started, it would halt at the "call" step because the "Call" didn't properly recover the child sequence. An additional issue was that a Warden had no "recover" method, meaning embedded Wardens wouldn't recover their stacks properly.
This commit is contained in:
parent
a6e0d3908f
commit
50d7b0aba4
|
@ -104,16 +104,6 @@ module Vagrant
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
# Returns the current stack of middlewares. You probably won't
|
|
||||||
# need to use this directly, and it's recommended that you don't.
|
|
||||||
#
|
|
||||||
# @return [Array]
|
|
||||||
def stack
|
|
||||||
@stack ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
# Converts the builder stack to a runnable action sequence.
|
# Converts the builder stack to a runnable action sequence.
|
||||||
#
|
#
|
||||||
# @param [Vagrant::Action::Environment] env The action environment
|
# @param [Vagrant::Action::Environment] env The action environment
|
||||||
|
@ -123,6 +113,16 @@ module Vagrant
|
||||||
# and predictable behavior upon exceptions.
|
# and predictable behavior upon exceptions.
|
||||||
Warden.new(stack.dup, env)
|
Warden.new(stack.dup, env)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
# Returns the current stack of middlewares. You probably won't
|
||||||
|
# need to use this directly, and it's recommended that you don't.
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
def stack
|
||||||
|
@stack ||= []
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,6 +29,7 @@ module Vagrant
|
||||||
@app = app
|
@app = app
|
||||||
@callable = callable
|
@callable = callable
|
||||||
@block = block
|
@block = block
|
||||||
|
@child_app = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
|
@ -42,11 +43,17 @@ module Vagrant
|
||||||
@block.call(new_env, builder)
|
@block.call(new_env, builder)
|
||||||
|
|
||||||
# Run the result with our new environment
|
# Run the result with our new environment
|
||||||
final_env = runner.run(builder, new_env)
|
@child_app = builder.to_app(new_env)
|
||||||
|
final_env = runner.run(@child_app, new_env)
|
||||||
|
|
||||||
# Call the next step using our final environment
|
# Call the next step using our final environment
|
||||||
@app.call(final_env)
|
@app.call(final_env)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def recover(env)
|
||||||
|
# Call back into our compiled application and recover it.
|
||||||
|
@child_app.recover(env) if @child_app
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,15 +49,17 @@ module Vagrant
|
||||||
|
|
||||||
# Something went horribly wrong. Start the rescue chain then
|
# Something went horribly wrong. Start the rescue chain then
|
||||||
# reraise the exception to properly kick us out of limbo here.
|
# reraise the exception to properly kick us out of limbo here.
|
||||||
begin_rescue(env)
|
recover(env)
|
||||||
raise
|
raise
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Begins the recovery sequence for all middlewares which have run.
|
# We implement the recover method ourselves in case a Warden is
|
||||||
# It does this by calling `recover` (if it exists) on each middleware
|
# embedded within another Warden. To recover, we just do our own
|
||||||
# which has already run, in reverse order.
|
# recovery process on our stack.
|
||||||
def begin_rescue(env)
|
def recover(env)
|
||||||
|
@logger.info("Beginning recovery process...")
|
||||||
|
|
||||||
@stack.each do |act|
|
@stack.each do |act|
|
||||||
if act.respond_to?(:recover)
|
if act.respond_to?(:recover)
|
||||||
@logger.info("Calling recover: #{act}")
|
@logger.info("Calling recover: #{act}")
|
||||||
|
@ -65,6 +67,8 @@ module Vagrant
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@logger.info("Recovery complete.")
|
||||||
|
|
||||||
# Clear stack so that warden down the middleware chain doesn't
|
# Clear stack so that warden down the middleware chain doesn't
|
||||||
# rescue again.
|
# rescue again.
|
||||||
@stack.clear
|
@stack.clear
|
||||||
|
|
|
@ -52,4 +52,63 @@ describe Vagrant::Action::Builtin::Call do
|
||||||
|
|
||||||
received.should == :bar
|
received.should == :bar
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should call the recover method for the sequence in an error" do
|
||||||
|
# Basic variables
|
||||||
|
callable = lambda { |env| }
|
||||||
|
|
||||||
|
# Build the steps for the test
|
||||||
|
basic_step = Class.new do
|
||||||
|
def initialize(app, env)
|
||||||
|
@app = app
|
||||||
|
@env = env
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
step_a = Class.new(basic_step) do
|
||||||
|
def call(env)
|
||||||
|
env[:steps] << :call_A
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def recover(env)
|
||||||
|
env[:steps] << :recover_A
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
step_b = Class.new(basic_step) do
|
||||||
|
def call(env)
|
||||||
|
env[:steps] << :call_B
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def recover(env)
|
||||||
|
env[:steps] << :recover_B
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
instance = described_class.new(app, env, callable) do |_env, builder|
|
||||||
|
builder.use step_a
|
||||||
|
builder.use step_b
|
||||||
|
end
|
||||||
|
|
||||||
|
env[:steps] = []
|
||||||
|
instance.call(env)
|
||||||
|
instance.recover(env)
|
||||||
|
|
||||||
|
env[:steps].should == [:call_A, :call_B, :recover_B, :recover_A]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should recover even if it failed in the callable" do
|
||||||
|
callable = lambda { |env| raise "error" }
|
||||||
|
|
||||||
|
instance = described_class.new(app, env, callable) { |_env, _builder| }
|
||||||
|
instance.call(env) rescue nil
|
||||||
|
expect { instance.recover(env) }.
|
||||||
|
to_not raise_error
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue