Class: Vagrant::BatchAction

Inherits:
Object
  • Object
show all
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

Constructor Details

#initialize(allow_parallel = true) ⇒ BatchAction

Returns a new instance of BatchAction.



9
10
11
12
13
# File 'lib/vagrant/batch_action.rb', line 9

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.

Parameters:

  • machine (Machine)

    The machine to run the action on

  • action (Symbol)

    The action to run

  • options (Hash) (defaults to: nil)

    Any additional options to send in.



23
24
25
# File 'lib/vagrant/batch_action.rb', line 23

def action(machine, action, options=nil)
  @actions << [machine, action, options]
end

#runObject

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.



32
33
34
35
36
37
38
39
40
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
# File 'lib/vagrant/batch_action.rb', line 32

def run
  par = false

  if @allow_parallel
    par = true
    @logger.info("Enabling parallelization by default.")
  end

  if par
    @actions.each do |machine, _, _|
      if !machine.provider_options[:parallel]
        @logger.info("Disabling parallelization because provider doesn't support it: #{machine.provider_name}")
        par = false
        break
      end
    end
  end

  @logger.info("Batch action will parallelize: #{par.inspect}")

  threads = []
  @actions.each do |machine, action, options|
    @logger.info("Starting action: #{machine} #{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

  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