diff --git a/lib/vagrant/action/multistep.rb b/lib/vagrant/action/multistep.rb index 7a74adaa4..7ec1a9d3e 100644 --- a/lib/vagrant/action/multistep.rb +++ b/lib/vagrant/action/multistep.rb @@ -121,8 +121,9 @@ module Vagrant # For each step, call it with proper inputs, using the output # of that call as inputs to the next. - step_outputs = {} - steps.inject(params) do |inputs, data| + entered_steps = [] + step_outputs = {} + result = steps.inject(params) do |inputs, data| name, step, mappings = data # If we have inputs to remap, remap them. @@ -139,11 +140,26 @@ module Vagrant # Call the actual step, using the results for the next # iteration. - step_outputs[name] = step.call(inputs) + entered_steps << step + begin + step_outputs[name] = step.call_enter(inputs) + rescue Exception => e + entered_steps.reverse.each do |s| + s.call_exit(e) + end + + raise + end # Return a shallow dup of the results step_outputs[name].dup end + + entered_steps.reverse.each do |s| + s.call_exit(nil) + end + + result end protected diff --git a/test/unit/vagrant/action/multistep_test.rb b/test/unit/vagrant/action/multistep_test.rb index 70e4d631e..87c6a268f 100644 --- a/test/unit/vagrant/action/multistep_test.rb +++ b/test/unit/vagrant/action/multistep_test.rb @@ -173,4 +173,61 @@ describe Vagrant::Action::MultiStep do g = described_class.new expect { g.step step_A, g.input(:foo) => :input_B }.to raise_error(ArgumentError) end + + it "should call the enter methods in order, and the exit in reverse order" do + step = Class.new(Vagrant::Action::Step) do + input :key + input :data + output :data + + def enter + @data << @key + return :data => @data + end + + def exit(error) + @data << @key + end + end + + g = described_class.new + g.step step + g.step :two, step, g.input(:key2) => :key + g.step :three, step, g.input(:key3) => :key + result = g.call(:data => [], :key => "1", :key2 => "2", :key3 => "3") + result[:data].should == %W[1 2 3 3 2 1] + end + + it "should halt the steps and call exit with the error if an error occurs" do + step = Class.new(Vagrant::Action::Step) do + input :key + input :data + output :data + + def enter + @data << @key + raise Exception, "E" if @data.last == "2" + return :data => @data + end + + def exit(error) + prefix = error ? error.message : "" + @data << "#{prefix}#{@key}" + end + end + + g = described_class.new + g.step step + g.step :two, step, g.input(:key2) => :key + g.step :three, step, g.input(:key3) => :key + + # Run the actual steps + data = [] + expect do + result = g.call(:data => data, :key => "1", :key2 => "2", :key3 => "3") + end.to raise_error(Exception) + + # Verify the result hit the methods in the proper order + data.should == %W[1 2 E2 E1] + end end