diff --git a/lib/vagrant/batch_action.rb b/lib/vagrant/batch_action.rb index 8aabc1ba4..5df193666 100644 --- a/lib/vagrant/batch_action.rb +++ b/lib/vagrant/batch_action.rb @@ -53,15 +53,61 @@ module Vagrant @actions.each do |machine, action, options| @logger.info("Starting action: #{machine} #{action} #{options}") - thread = Thread.new { machine.send(:action, action, options) } + # Create the new thread to run our action. This is basically just + # calling the action but also contains some error handling in it + # as well. + thread = Thread.new do + Thread.current[:error] = nil + + begin + machine.send(:action, action, options) + rescue Exception => e + # If we're not parallelizing, then raise the error + raise if !par + + # Store the exception that will be processed later + Thread.current[:error] = e + end + end + + # Set some attributes on the thread for later + thread[:machine] = machine + thread.join if !par threads << thread end - # Join the threads, which will return immediately if parallelization - # if disabled, because we already joined on them. Otherwise, this - # will wait for completion of all threads. - threads.map(&:join) + errors = [] + + threads.each do |thread| + # Wait for the thread to complete + thread.join + + # If the thread had an error, then store the error to show later + if thread[:error] + e = thread[:error] + # If the error isn't a Vagrant error, then store the backtrace + # as well. + if !thread[:error].is_a?(Errors::VagrantError) + e = thread[:error] + message = e.message + message += "\n" + message += "\n#{e.backtrace.join("\n")}" + + errors << I18n.t("vagrant.general.batch_unexpected_error", + :machine => thread[:machine].name, + :message => message) + else + errors << I18n.t("vagrant.general.batch_vagrant_error", + :machine => thread[:machine].name, + :message => thread[:error].message) + end + end + end + + if !errors.empty? + raise Errors::BatchMultiError, :message => errors.join("\n\n") + end end end end diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index 82ce26b38..084d66c84 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -91,6 +91,10 @@ module Vagrant error_key(:base_vm_not_found) end + class BatchMultiError < VagrantError + error_key(:batch_multi_error) + end + class BoxAlreadyExists < VagrantError error_key(:already_exists, "vagrant.actions.box.unpackage") end diff --git a/plugins/providers/virtualbox/plugin.rb b/plugins/providers/virtualbox/plugin.rb index d313ab4ba..879deb023 100644 --- a/plugins/providers/virtualbox/plugin.rb +++ b/plugins/providers/virtualbox/plugin.rb @@ -9,7 +9,7 @@ module VagrantPlugins VirtualBox-based virtual machines. EOF - provider(:virtualbox) do + provider(:virtualbox, parallel: true) do require File.expand_path("../provider", __FILE__) Provider end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 093ee21c8..708fb542b 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -1,6 +1,16 @@ en: vagrant: general: + batch_unexpected_error: |- + An unexpected error ocurred when executing the action on the + '%{machine}' machine. Please report this as a bug: + + %{message} + batch_vagrant_error: |- + An error occurred while executing the action on the '%{machine}' + machine. Please handle this error then try again: + + %{message} config_upgrade_messages: |- There were warnings and/or errors while loading your Vagrantfile. Your Vagrantfile was written for an earlier version of Vagrant, @@ -57,6 +67,11 @@ en: Active provider: %{active_provider} Requested provider: %{requested_provider} base_vm_not_found: The base VM with the name '%{name}' was not found. + batch_multi_error: |- + An error occurred while executing multiple actions in parallel. + Any errors that occurred are shown below. + + %{message} box_metadata_file_not_found: |- The "metadata.json" file for the box '%{name}' was not found. Boxes require this file in order for Vagrant to determine the