module Dopi

DOPi CLI gloable options

Initializes the logger for the CLI

DOPi Plugin: Custom Command

DOPi dummy command.

this simply prints the command hash if there was one given

DOPi Plugin: Custom Command

DOPi Plugin: File Replace

SSH custom command

DOPi Plugin: Reboot a node

DOPi Plugin: Wait For Login

DOPi Plugin: File Contains

SSH custom command

DOPi Plugin: Wait For Login

DOPi Plugin: Wait For Login

Simple command parser class to parse arguments

Dopi Credentials helper module

This module will provide a credentials method which contains all the credential objects for the plugin.

Make sure you call “validate_credentials” method from your validation method.

Implement a method “supported_credential_types” in your plugin if you want to limit the types which are supported and trow an error during validation if some other type is assigned

Simple command parser module for environment variable hashes

To set plugin specific defaults for the environment create a 'env_defaults' method which returns a hash with: { var => val, var2 => val2, … } This hash will be merged with the user specified hash.

Simple command parser module for exec

This is a mixin for command plugins that need to parse an exit Code of some sort

To set plugin specific defaults you can implement the 'expect_exit_codes_defaults' method which returns an array of expected exit codes

This is a mixin for command plugins that need to parse an output of some sort

Make sure to call the validation method from the class you use the module

To set plugin specific output parser patterns, implement the method 'parse_output_defaults' which should return a hash with the patterns.

This class represents the set commands for a specific node and step.

It will also manage the run group restrictions

This connector will execute commands over ssh

Various error classes for DOPi

Error hierarchy:

PluginLoaderError
StateTransitionError
NoRoleFoundError
CommandParsingError
CommandExecutionError
  CommandExecutionError
    ConnectionError
      NodeConnectionError
      CommandConnectionError

This class registers the plugins as they get loaded by ruby and can create instances based on the plugin name

This is the DOPi state store which persists the state of the steps between and during runs.

Constants

VERSION

Public Class Methods

add(raw_plan) click to toggle source
# File lib/dopi.rb, line 34
def self.add(raw_plan)
  raise StandardError, 'Plan not valid; did not add' unless valid?(raw_plan)
  plan_store.add(raw_plan)
end
list() click to toggle source
# File lib/dopi.rb, line 57
def self.list
  plan_store.list
end
log() click to toggle source
# File lib/dopi/log.rb, line 9
def self.log
  @log ||= DopCommon.log
end
logger=(logger) click to toggle source
# File lib/dopi/log.rb, line 13
def self.logger=(logger)
  @log = logger
  DopCommon.logger = logger
end
on_state_change(plan_name) { || ... } click to toggle source
# File lib/dopi.rb, line 112
def self.on_state_change(plan_name)
  ensure_plan_exists(plan_name)
  state_store = Dopi::StateStore.new(plan_name, plan_store)
  state_store.on_change do
    yield
  end
end
remove(plan_name, remove_dopi_state = true, remove_dopv_state = false) click to toggle source
# File lib/dopi.rb, line 53
def self.remove(plan_name, remove_dopi_state = true, remove_dopv_state = false)
  plan_store.remove(plan_name, remove_dopi_state, remove_dopv_state)
end
reset(plan_name, force = false) click to toggle source
# File lib/dopi.rb, line 101
def self.reset(plan_name, force = false)
  ensure_plan_exists(plan_name)
  plan_store.run_lock(plan_name) do
    state_store = Dopi::StateStore.new(plan_name, plan_store)
    plan = get_plan(plan_name)
    plan.load_state(state_store.state_hash)
    plan.state_reset_with_children(force)
    state_store.persist_state(plan)
  end
end
run(plan_name, options = {}) { |plan| ... } click to toggle source
# File lib/dopi.rb, line 71
def self.run(plan_name, options = {})
  ensure_plan_exists(plan_name)
  update_state(plan_name)
  plan_store.run_lock(plan_name) do
    state_store = Dopi::StateStore.new(plan_name, plan_store)
    dopv_state_store = plan_store.state_store(plan_name, 'dopv')
    dopv_state_store.transaction(true) do
      dopv_node_info = dopv_state_store[:nodes] || {}
      api_node_info  = options[:node_info] || {}
      options[:node_info] = dopv_node_info.merge(api_node_info)
    end
    plan = get_plan(plan_name)
    plan.load_state(state_store.state_hash)
    manager = nil
    if block_given?
      manager = Thread.new { yield(plan) }
    else
      run_signal_handler(plan)
    end
    begin
      state_store_observer = Dopi::StateStoreObserver.new(plan, state_store)
      plan.add_observer(state_store_observer)
      plan.run(options)
      manager.join if manager
    ensure
      state_store_observer.update
    end
  end
end
show(plan_name) click to toggle source

TODO: this returns a plan with loaded state at the moment. THIS MAY BE CHANGED IN THE FUTURE!!

# File lib/dopi.rb, line 63
def self.show(plan_name)
  ensure_plan_exists(plan_name)
  state_store = Dopi::StateStore.new(plan_name, plan_store)
  plan = get_plan(plan_name)
  plan.load_state(state_store.state_hash)
  plan
end
update_plan(raw_plan, options = {}) click to toggle source
# File lib/dopi.rb, line 39
def self.update_plan(raw_plan, options = {})
  raise StandardError, 'Plan not valid; did not add' unless valid?(raw_plan)
  plan_name = plan_store.update(raw_plan)
  update_state(plan_name, options)
  plan_name
end
update_state(plan_name, options = {}) click to toggle source
# File lib/dopi.rb, line 46
def self.update_state(plan_name, options = {})
  plan_store.run_lock(plan_name) do
    state_store = Dopi::StateStore.new(plan_name, plan_store)
    state_store.update(options)
  end
end
valid?(raw_plan) click to toggle source
# File lib/dopi.rb, line 27
def self.valid?(raw_plan)
  hash, _ = plan_store.read_plan_file(raw_plan)
  plan_parser = DopCommon::Plan.new(hash)
  plan = Dopi::Plan.new(plan_parser)
  plan.valid?
end

Private Class Methods

ensure_plan_exists(plan_name) click to toggle source
# File lib/dopi.rb, line 160
def self.ensure_plan_exists(plan_name)
  unless plan_store.list.include?(plan_name)
    raise StandardError, "The plan #{plan_name} does not exist in the plan store"
  end
end
get_plan(plan_name) click to toggle source
# File lib/dopi.rb, line 126
def self.get_plan(plan_name)
  raise StandardError, 'Please update the plan state, there are pending updates' if pending_updates?(plan_name)
  plan_parser = plan_store.get_plan(plan_name)
  Dopi::Plan.new(plan_parser)
end
pending_updates?(plan_name) click to toggle source
# File lib/dopi.rb, line 132
def self.pending_updates?(plan_name)
  state_store = Dopi::StateStore.new(plan_name, plan_store)
  state_store.pending_updates?
end
plan_store() click to toggle source
# File lib/dopi.rb, line 122
def self.plan_store
  @plan_store ||= DopCommon::PlanStore.new(DopCommon.config.plan_store_dir)
end
run_signal_handler(plan) click to toggle source
# File lib/dopi.rb, line 137
def self.run_signal_handler(plan)
  plan.reset_signals
  signal_handler_thread = Thread.new do
    Dopi.log.info("Starting signal handling")
    signal_counter = 0
    DopCommon::SignalHandler.new.handle_signals(:INT, :TERM) do
      signal_counter += 1
      case signal_counter
      when 1
        Dopi.log.warn("Signal received! The run will halt after all currently running commands are finished")
        plan.send_signal(:stop)
      when 2
        Dopi.log.error("Signal received! Sending termination signal to all the processes!")
        plan.send_signal(:abort)
      when 3
        Dopi.log.error("Signal received! Sending KILL signal to all the processes!")
        plan.send_signal(:kill)
      end
    end
  end
  signal_handler_thread.abort_on_exception = true
end