class Dopi::Step

Constants

DEFAULT_MAX_IN_FLIGHT
DEFAULT_MAX_PER_ROLE

Attributes

nodes[RW]
plan[RW]

Public Class Methods

new(step_parser, plan) click to toggle source
# File lib/dopi/step.rb, line 16
def initialize(step_parser, plan)
  @step_parser = step_parser
  @plan        = plan
  @nodes       = filter_nodes(plan.nodes, step_parser)

  @next_mutex   = Mutex.new
  @notify_mutex = Mutex.new
  @queue        = Queue.new

  command_sets.each{|command_set| state_add_child(command_set)}
end

Public Instance Methods

command_sets() click to toggle source
# File lib/dopi/step.rb, line 49
def command_sets
  @command_sets ||= @nodes.map do |node|
    delete_plugin_defaults
    set_plugin_defaults
    Dopi::CommandSet.new(@step_parser, self, node)
  end
end
load_state(state_hash) click to toggle source
# File lib/dopi/step.rb, line 78
def load_state(state_hash)
  command_sets.each do |command_set|
    command_set_state = state_hash[command_set.name] || []
    command_set.load_state(command_set_state)
  end
end
name() click to toggle source
# File lib/dopi/step.rb, line 35
def name
  @step_parser.name
end
run(run_options) click to toggle source
# File lib/dopi/step.rb, line 57
def run(run_options)
  if state_done?
    Dopi.log.info("Step '#{name}' is in state 'done'. Skipping")
    return
  end
  Dopi.log.info("Starting to run step '#{name}'")

  nodes_to_run = filter_nodes(@nodes, run_options[:run_for_nodes])
  command_sets_to_run = command_sets.select {|cs| nodes_to_run.include?(cs.node)}

  unless run_options[:noop]
    run_canary(run_options, command_sets_to_run) if canary_host
    run_command_sets(run_options, command_sets_to_run) unless state_failed?
  else
    command_sets_to_run.each{|command_set| command_set.run(run_options[:noop])}
  end

  Dopi.log.info("Step '#{name}' successfully finished.") if state_done?
  Dopi.log.error("Step '#{name}' failed! Stopping execution.") if state_failed?
end
state_hash() click to toggle source
# File lib/dopi/step.rb, line 85
def state_hash
  command_sets_hash = {}
  command_sets.each do |command_set|
    command_sets_hash[command_set.name] = command_set.state_hash
  end
  command_sets_hash
end
to_yaml_properties() click to toggle source

Loading queue object from yaml files results in not properly initialized queue and a type error when using it. Skip queue when converting to yaml. Will be nil after loading from yaml and must be re-created.

Calls superclass method Dopi::State#to_yaml_properties
# File lib/dopi/step.rb, line 31
def to_yaml_properties
  super - [:@queue]
end
valid?() click to toggle source
# File lib/dopi/step.rb, line 39
def valid?
  if @nodes.empty?
    Dopi.log.error("Step '#{name}': Nodes list is empty")
    return false
  end
  # since they are identical in respect to parsing
  # we only have to check one of them
  command_sets.first.valid?
end

Private Instance Methods

canary_host() click to toggle source
# File lib/dopi/step.rb, line 171
def canary_host
  @canary_host ||= @step_parser.canary_host || @plan.canary_host
end
delete_plugin_defaults() click to toggle source
# File lib/dopi/step.rb, line 175
def delete_plugin_defaults
  if @step_parser.delete_plugin_defaults == :all
    # Wipe all the defaults
    PluginManager.plugin_klass_list('^dopi/command/').each do |plugin_klass|
      @nodes.each{|node| plugin_klass.delete_plugin_defaults(node.name)}
    end
  else
    @step_parser.delete_plugin_defaults.each do |entry|
      plugin_list(entry[:plugins]).each do |plugin_klass|
        if entry[:delete_keys] == :all
          @nodes.each{|node| plugin_klass.delete_plugin_defaults(node.name)}
        else
          entry[:delete_keys].each do |key|
            @nodes.each{|node| plugin_klass.delete_plugin_default(node.name, key)}
          end
        end
      end
    end
  end
end
is_runnable?(node) click to toggle source

check if a node is runnable or if there are constrains which prevent it from running

# File lib/dopi/step.rb, line 143
def is_runnable?(node)
  if max_per_role > 0
    running_groups[node.role] < max_per_role
  else
    true
  end
end
max_in_flight() click to toggle source
# File lib/dopi/step.rb, line 163
def max_in_flight
  @max_in_flight ||= @step_parser.max_in_flight || @plan.max_in_flight || DEFAULT_MAX_IN_FLIGHT
end
max_per_role() click to toggle source
# File lib/dopi/step.rb, line 167
def max_per_role
  @max_per_role ||= @step_parser.max_per_role || @plan.max_per_role || DEFAULT_MAX_PER_ROLE
end
next_command_set(command_sets_to_run) click to toggle source

This method returns the next command_set which is ready to run. If no node is ready because of constrains it will block the thread until notify_done was called from a finishing thread. If no command_set is in the state ready it will return nil.

# File lib/dopi/step.rb, line 122
def next_command_set(command_sets_to_run)
  @next_mutex.synchronize do
    ready_command_sets = command_sets_to_run.select{|n| n.state == :ready}
    return nil if ready_command_sets.empty?
    loop do
      return nil if state_failed? or signals[:stop]
      @notify_mutex.synchronize do
        queue.clear
        next_command_set = ready_command_sets.find{|cs| is_runnable?(cs.node)}
        unless next_command_set.nil?
          next_command_set.state_start
          return next_command_set
        end
      end
      queue.pop # wait until a thread notifies it has finished
    end
  end
end
notify_done() click to toggle source

notify the waiting thread that a command_set has finished it's run

# File lib/dopi/step.rb, line 111
def notify_done
  @notify_mutex.synchronize do
    queue.push(1)
  end
end
plugin_list(plugin_filter_list) click to toggle source
# File lib/dopi/step.rb, line 206
def plugin_list(plugin_filter_list)
  if plugin_filter_list == :all
    PluginManager.plugin_klass_list('^dopi/command/')
  else
    all_plugin_names = PluginManager.plugin_name_list('^dopi/command/').map{|p| p.sub('dopi/command/', '')}
    selected_plugin_names = plugin_filter_list.map do |filter|
      case filter
      when Regexp then all_plugin_names.select{|p| p =~ filter}
      else all_plugin_names.select{|p| p == filter}
      end
    end
    selected_plugin_names.flatten.uniq.map{|p| PluginManager.plugin_klass('dopi/command/' + p)}
  end
end
queue() click to toggle source

Will be skipped when dumping yaml, therefore nil after loading from yaml.

# File lib/dopi/step.rb, line 222
def queue
  @queue ||= Queue.new
end
run_canary(run_options, command_sets_to_run) click to toggle source
# File lib/dopi/step.rb, line 95
def run_canary(run_options, command_sets_to_run)
  pick = rand(command_sets_to_run.length - 1)
  command_sets_to_run[pick].run(run_options[:noop])
end
run_command_sets(run_options, command_sets_to_run) click to toggle source
# File lib/dopi/step.rb, line 100
def run_command_sets(run_options, command_sets_to_run)
  in_threads = max_in_flight == -1 ? command_sets_to_run.length : max_in_flight
  pick = lambda { next_command_set(command_sets_to_run) || Parallel::Stop }
  Parallel.each(pick, :in_threads => in_threads) do |command_set|
    plan.context_logger.log_context = command_set.node.name
    command_set.run(run_options[:noop])
    notify_done
  end
end
running_groups() click to toggle source

return a hash with the group names as keys and the amount of running nodes as value

# File lib/dopi/step.rb, line 153
def running_groups
  role_counter = Hash.new(0)
  command_sets.each do |command_set|
    if [:running, :starting].include? command_set.state
      role_counter[command_set.node.role] += 1
    end
  end
  role_counter
end
set_plugin_defaults() click to toggle source
# File lib/dopi/step.rb, line 196
def set_plugin_defaults
  @step_parser.set_plugin_defaults.each do |entry|
    defaults_hash = entry.dup
    defaults_hash.delete(:plugins)
    plugin_list(entry[:plugins]).each do |plugin_klass|
      @nodes.each{|node| plugin_klass.set_plugin_defaults(node.name, defaults_hash)}
    end
  end
end