Class: Vagrant::BatchAction
- Inherits:
-
Object
- Object
- Vagrant::BatchAction
- Defined in:
- lib/vagrant/batch_action.rb
Overview
This class executes multiple actions as a single batch, parallelizing the action calls if possible.
Instance Method Summary collapse
-
#action(machine, action, options = nil) ⇒ Object
Add an action to the batch of actions that will be run.
-
#custom(machine, &block) ⇒ Object
Custom runs a custom proc against a machine.
-
#initialize(allow_parallel = true) ⇒ BatchAction
constructor
A new instance of BatchAction.
-
#run ⇒ Object
Run all the queued up actions, parallelizing if possible.
Constructor Details
#initialize(allow_parallel = true) ⇒ BatchAction
Returns a new instance of BatchAction.
11 12 13 14 15 |
# File 'lib/vagrant/batch_action.rb', line 11 def initialize(allow_parallel=true) @actions = [] @allow_parallel = allow_parallel @logger = Log4r::Logger.new("vagrant::batch_action") end |
Instance Method Details
#action(machine, action, options = nil) ⇒ Object
Add an action to the batch of actions that will be run.
This will not run the action now. The action will be run when #run is called.
25 26 27 |
# File 'lib/vagrant/batch_action.rb', line 25 def action(machine, action, =nil) @actions << [machine, action, ] end |
#custom(machine, &block) ⇒ Object
Custom runs a custom proc against a machine.
32 33 34 |
# File 'lib/vagrant/batch_action.rb', line 32 def custom(machine, &block) @actions << [machine, block, nil] end |
#run ⇒ Object
Run all the queued up actions, parallelizing if possible.
This will parallelize if and only if the provider of every machine supports parallelization and parallelization is possible from initialization of the class.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/vagrant/batch_action.rb', line 41 def run par = false if @allow_parallel par = true @logger.info("Enabling parallelization by default.") end if par @actions.each do |machine, _, _| if !machine.[:parallel] @logger.info("Disabling parallelization because provider doesn't support it: #{machine.provider_name}") par = false break end end end if par && @actions.length <= 1 @logger.info("Disabling parallelization because only executing one action") par = false end @logger.info("Batch action will parallelize: #{par.inspect}") threads = [] @actions.each do |machine, action, | @logger.info("Starting action: #{machine} #{action} #{}") # 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 # 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 begin if action.is_a?(Proc) action.call(machine) else machine.send(:action, action, ) end rescue Exception => e # If we're not parallelizing, then raise the error. We also # don't raise the error if we've forked, because it'll hang # the process. raise if !par && Process.pid == start_pid # Store the exception that will be processed later Thread.current[:error] = e # We can only do the things below if we do not fork, otherwise # it'll hang the process. if Process.pid == start_pid # Let the user know that this process had an error early # so that they see it while other things are happening. machine.ui.error(I18n.t("vagrant.general.batch_notify_error")) end end # If we forked during the process run, we need to do a hard # exit here. Ruby's fork only copies the running process (which # would be us), so if we return from this thread, it results # in a zombie Ruby process. if Process.pid != start_pid # We forked. exit_status = true if Thread.current[:error] # We had an error, print the stack trace and exit immediately. exit_status = false error = Thread.current[:error] @logger.error(error.inspect) @logger.error(error.) @logger.error(error.backtrace.join("\n")) end Process.exit!(exit_status) end end # Set some attributes on the thread for later thread[:machine] = machine if !par thread.join(THREAD_MAX_JOIN_TIMEOUT) while thread.alive? end threads << thread end errors = [] threads.each do |thread| # Wait for the thread to complete thread.join(THREAD_MAX_JOIN_TIMEOUT) while thread.alive? # 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] = e. += "\n" += "\n#{e.backtrace.join("\n")}" errors << I18n.t("vagrant.general.batch_unexpected_error", machine: thread[:machine].name, message: ) else errors << I18n.t("vagrant.general.batch_vagrant_error", machine: thread[:machine].name, message: thread[:error].) end end end 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 |