Class: ScoutAgent::Agent::MasterAgent

Inherits:
ScoutAgent::Agent show all
Defined in:
lib/scout_agent/agent/master_agent.rb

Overview

This agent is the main event loop for the platform. It’s primary function is to run each Mission downloaded from the server at the correct time. As part of that, it regularly updates the Mission list from the server and also prepares and sends check-ins to the server after a set of mission runs. Snapshots are also periodically run here and added to check-ins.

This loop also manages regular maintenance like VACUUMing SQLite databases and cleaning out old log files.

Constant Summary collapse

MISSION_ERRORS =

These are the error codes and descriptions for failure conditions the mission process may not be able to report.

{ 76 => "was already running",
                              16 => "couldn't be compiled",
6 => "couldn't be loaded" }
MISSION_ERROR_NAMES =

These are the MISSION_ERRORS by name (the last word of the description as a Symbol).

Hash[ *MISSION_ERRORS.map { |code, name|
[name[/\S+\z/].to_sym, code]

Instance Attribute Summary

Attributes inherited from ScoutAgent::Agent

#log

Instance Method Summary collapse

Methods inherited from ScoutAgent::Agent

#authorize

Methods included from Tracked

#clear_status, #force_status_database_reload, #status, #status_database, #status_log

Constructor Details

#initializeMasterAgent

Prepares the primary event loop for execution. The main function at this point is to ensure that we can load all of the needed databases.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/scout_agent/agent/master_agent.rb', line 39

def initialize
  super  # setup our log and status
  
  @running     = true
  @main_loop   = nil
  @mission_pid = nil
  @server      = Server.new(log)
  @db          = Database.load(:mission_log, log)
  @queue       = Database.load(:queue,       log)
  @snapshots   = Database.load(:snapshots,   log)
  
  if [@db, @queue, @snapshots].any? { |db| db.nil? }
    log.fatal("Could not load all required databases.")
    exit
  end
end

Instance Method Details

#finishObject

This method is called automatically when this Agent process receives a stop request, like a TERM signal. It prepares the Agent to shutdown, but doesn’t trigger an immediate stop. Instead, the Agent will check to see if this has been called after each phase of the main event loop. This allows it to avoid repeating work when it relaunches.

This request is also forwarded to a currently running Mission, if there is one.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/scout_agent/agent/master_agent.rb', line 102

def finish
  if @running
    log.info("Shutting down.")
    if @mission_pid
      log.info("Forwarding shutdown request to the running mission.")
      begin
        Process.kill("TERM", @mission_pid)
      rescue Exception  # unable to signal mission
        log.warn("Mission could not be signaled.")
        # do nothing:  mission already ended
      end
    end
  else
    log.warn("Received multiple shutdown signals.")
  end
  @running = false
  notice_changes
end

#notice_changesObject

This method is called automatically when this Agent process receives an ALRM signal and all it does is to wake up the event loop Thread, if it is not already running. This allows the Agent to notice external changes quicker.



86
87
88
89
90
# File 'lib/scout_agent/agent/master_agent.rb', line 86

def notice_changes
  @main_loop.run if @main_loop
rescue ThreadError  # Thread was already killed
  # do nothing:  we're shutting down and can't notice new things
end

#runObject

This method outlines the steps of the event loop: update our plan from the server, run Missions and snapshots, check-in, handle maintenance, and rest.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/scout_agent/agent/master_agent.rb', line 61

def run
  log.info("Running.")
  @main_loop = Thread.new do
    Thread.current.abort_on_exception = true
    loop do
      %w[ fetch_plan
          execute_missions
          prepare_snapshot
          checkin
          perform_maintenance
          wait_for_orders ].each do |stage|
        send(stage)
        check_running_status
      end
    end
  end
  @main_loop.join
end