class MotherBrain::CommandRunner

Attributes

args[R]

@return [Array]

environment[R]

@return [String]

execute[R]

@return [Proc]

job[R]
node_filter[R]

@return [Array]

scope[R]

@return [MB::Plugin, MB::Component]

Public Class Methods

new(job, environment, scope, execute, node_filter, *args) click to toggle source

@param [String] environment

environment to run this command on

@param [MB::Plugin, MB::Component] scope

scope to execute this command in.

* executing the command in the scope of an entire plugin will give you easy access to
  component commands and other plugin level commands
* executing the command in the scope of a component will give you easy access to the
  other commands available in that component

@param [Proc] execute

the code to execute when the command runner is run

@example
  proc {
    on("some_nodes") { service("nginx").run("stop") }
  }

@param [Array] node_filter

a list of nodes to filter commands on

@param [Array] args

any additional arguments to pass to the execution block
# File lib/mb/command_runner.rb, line 36
def initialize(job, environment, scope, execute, node_filter, *args)
  @job         = job
  @environment = environment
  @scope       = scope
  @execute     = execute
  @node_filter = node_filter
  @args        = args

  @on_procs    = []
  @async       = false

  run
end

Public Instance Methods

apply() click to toggle source

Run the stored procs created by on() that have not been ran yet.

# File lib/mb/command_runner.rb, line 60
def apply
  # TODO: This needs to happen in parallel but can't due to the way running multiple
  # actions on a single node works. Actions work on a node and don't know about other
  # actions which are being run on that node, so in a single node environment the
  # state of a node can get weird when actions stomp all over each other.
  #
  # We should refactor this to APPLY actions to nodes and then allow them to converge
  # together before we run them. This will allow us to execute multiple actions on a node at once
  # without getting weird race conditions.
  @on_procs.each do |on_proc|
    on_proc.call
  end

  @on_procs.clear
end
async(&block) click to toggle source

Run the block asynchronously.

# File lib/mb/command_runner.rb, line 84
def async(&block)
  @nodes = []

  @async = true
  instance_eval(&block)
  @async = false

  apply

  node_querier.bulk_chef_run job, @nodes
end
async?() click to toggle source

Are we inside an async block?

@return [Boolean]

# File lib/mb/command_runner.rb, line 79
def async?
  !!@async
end
command(command_name) click to toggle source
# File lib/mb/command_runner.rb, line 166
def command(command_name)
  scope.command!(command_name).invoke(job, environment, node_filter)
end
component(component_name) click to toggle source

Select a component for the purposes of invoking a command. NB: returns a proxy object

@param [String] component_name the name of the component you want to

invoke a command on

@return [InvokableComponent] proxy for the actual component,

only useful if you call #invoke on it
# File lib/mb/command_runner.rb, line 162
def component(component_name)
  InvokableComponent.new(job, environment, scope.component(component_name))
end
on(*group_names, &block) click to toggle source

Run the block specified on the nodes in the groups specified.

@param [Array<String>] group_names groups to run on

@option options [Integer] :any

the number of nodes to run on, which nodes are chosen doesn't matter

@option options [Integer] :max_concurrent

the number of nodes to run on at a time

@example running on masters and slaves, only 2 of them, 1 at a time

on("masters", "slaves", any: 2, max_concurrent: 1) do
  # actions
end
# File lib/mb/command_runner.rb, line 110
def on(*group_names, &block)
  options = group_names.last.kind_of?(Hash) ? group_names.pop : {}

  unless block_given?
    raise PluginSyntaxError, "Block required"
  end

  clean_room = CleanRoom.new(scope)
  clean_room.instance_eval(&block)
  actions = clean_room.send(:actions)

  nodes = group_names.map do |name|
    scope.group!(name.to_s)
  end.flat_map do |group|
    group.nodes(environment)
  end.uniq

  nodes = MB::NodeFilter.filter(node_filter, nodes) if node_filter

  return unless nodes.any?

  if options[:any]
    nodes = nodes.sample(options[:any])
  end

  options[:max_concurrent] ||= nodes.count
  node_groups = nodes.each_slice(options[:max_concurrent]).to_a

  run_chef = !async?

  @on_procs << -> {
    node_groups.each do |nodes|
      actions.each do |action|
        action.run(job, environment, nodes, run_chef)
      end
    end
  }

  if async?
    @nodes |= nodes
  else
    apply
  end
end
run() click to toggle source
# File lib/mb/command_runner.rb, line 50
def run
  if execute.arity.nonzero?
    curried_execute = proc { execute.call(*args) }
    instance_eval(&curried_execute)
  else
    instance_eval(&execute)
  end
end