From 88c675694a384324103f9a784a7176a9b97d6f85 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Tue, 4 Jun 2019 14:37:33 -0700 Subject: [PATCH] Update trigger abort behavior when running parallel actions When the provider supports parallel actions and actions are being run in parallel, do not immediately kill the process on failure. Instead terminate the action thread and log the exit code. Once all running actions have completed, the process will then exit with the stored exit code. --- lib/vagrant/batch_action.rb | 14 ++++++++++ lib/vagrant/plugin/v2/trigger.rb | 13 +++++++-- templates/locales/en.yml | 3 ++ test/unit/vagrant/batch_action_test.rb | 19 +++++++++++++ test/unit/vagrant/plugin/v2/trigger_test.rb | 31 +++++++++++++++++++++ 5 files changed, 78 insertions(+), 2 deletions(-) diff --git a/lib/vagrant/batch_action.rb b/lib/vagrant/batch_action.rb index d27e87d7e..e7bc39318 100644 --- a/lib/vagrant/batch_action.rb +++ b/lib/vagrant/batch_action.rb @@ -71,6 +71,10 @@ module Vagrant thread = Thread.new do Thread.current[:error] = nil + # Note that this thread is being used for running + # a batch action + Thread.current[:batch_parallel_action] = par + # Record our pid when we started in order to figure out if # we've forked... start_pid = Process.pid @@ -160,6 +164,16 @@ module Vagrant if !errors.empty? raise Errors::BatchMultiError, message: errors.join("\n\n") end + + # Check if any threads set an exit code and exit if found. If + # multiple threads have exit code values set, the first encountered + # will be the value used. + threads.each do |thread| + if thread[:exit_code] + @logger.debug("Found exit code set within batch action thread. Exiting") + Process.exit!(thread[:exit_code]) + end + end end end end diff --git a/lib/vagrant/plugin/v2/trigger.rb b/lib/vagrant/plugin/v2/trigger.rb index ca6ea5a34..7ab4d73d8 100644 --- a/lib/vagrant/plugin/v2/trigger.rb +++ b/lib/vagrant/plugin/v2/trigger.rb @@ -300,8 +300,17 @@ module Vagrant # # @param [Integer] code Code to exit Vagrant on def trigger_abort(exit_code) - @ui.warn(I18n.t("vagrant.trigger.abort")) - Process.exit!(exit_code) + if Thread.current[:batch_parallel_action] + @ui.warn(I18n.t("vagrant.trigger.abort_threaded")) + @logger.debug("Trigger abort within parallel batch action. " \ + "Setting exit code and terminating.") + Thread.current[:exit_code] = exit_code + Thread.current.terminate + else + @ui.warn(I18n.t("vagrant.trigger.abort")) + @logger.debug("Trigger abort within non-parallel action, exiting directly") + Process.exit!(exit_code) + end end # Calls the given ruby block for execution diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 5927d6b21..e5543ad57 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -297,6 +297,9 @@ en: Trigger configured to continue on error... abort: |- Vagrant has been configured to abort. Terminating now... + abort_threaded: |- + Vagrant has been configured to abort. Vagrant will terminate + after remaining running actions have completed... start: |- Running %{type} triggers %{stage} %{action} ... fire_with_name: |- diff --git a/test/unit/vagrant/batch_action_test.rb b/test/unit/vagrant/batch_action_test.rb index f62913aab..e1e600303 100644 --- a/test/unit/vagrant/batch_action_test.rb +++ b/test/unit/vagrant/batch_action_test.rb @@ -63,5 +63,24 @@ describe Vagrant::BatchAction do subject.action(machine, "up") subject.run end + + context "with provider supporting parallel actions" do + let(:provider_options) { {parallel: true} } + + it "should flag threads as being parallel actions" do + parallel = nil + subject.custom(machine) { |m| parallel = Thread.current[:batch_parallel_action] } + subject.custom(machine) { |*_| } + subject.run + expect(parallel).to eq(true) + end + + it "should exit the process if exit_code has been set" do + subject.custom(machine) { |m| Thread.current[:exit_code] = 1} + subject.custom(machine) { |*_| } + expect(Process).to receive(:exit!).with(1) + subject.run + end + end end end diff --git a/test/unit/vagrant/plugin/v2/trigger_test.rb b/test/unit/vagrant/plugin/v2/trigger_test.rb index b39f7e30e..370b4b5db 100644 --- a/test/unit/vagrant/plugin/v2/trigger_test.rb +++ b/test/unit/vagrant/plugin/v2/trigger_test.rb @@ -423,6 +423,37 @@ describe Vagrant::Plugin::V2::Trigger do expect(Process).to receive(:exit!).with(3) subject.send(:trigger_abort, 3) end + + context "when running in parallel" do + let(:thread) { + @t ||= Thread.new do + Thread.current[:batch_parallel_action] = true + Thread.stop + subject.send(:trigger_abort, exit_code) + end + } + let(:exit_code) { 22 } + + before do + expect(Process).not_to receive(:exit!) + sleep(0.1) until thread.stop? + end + + after { @t = nil } + + it "should terminate the thread" do + expect(thread).to receive(:terminate).and_call_original + thread.wakeup + thread.join(1) while thread.alive? + end + + it "should set the exit code into the thread data" do + expect(thread).to receive(:terminate).and_call_original + thread.wakeup + thread.join(1) while thread.alive? + expect(thread[:exit_code]).to eq(exit_code) + end + end end context "#ruby" do