Class: ScoutAgent::Mission

Inherits:
Object
  • Object
show all
Defined in:
lib/scout_agent/mission.rb

Overview

These objects are the data gathers periodically run by the agent. To gather a new type of data, you inherit from this Class and provide a build_report() method that calls some combination of report(), alert(), and error() to send data to the Scout server.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id, name, last_run, memory, options, log = WireTap.new(nil)) ⇒ Mission

Creates a new Scout Mission to run. It requires the id and name for the Mission as well as the last_run Time, memory, and options from the Mission database. You may optionally provide a log for the Mission to write messages to.



55
56
57
58
59
60
61
62
63
64
# File 'lib/scout_agent/mission.rb', line 55

def initialize(id, name, last_run, memory, options, log = WireTap.new(nil))
  @id             = id
  @name           = name
  @last_run       = last_run
  @memory         = memory
  @options        = options
  @log            = log
  @mission_log_db = !testing? && Database.load(:mission_log, @log)
  @queue_db       = !testing? && Database.load(:queue,       @log)
end

Instance Attribute Details

#idObject (readonly)

The numerical ID this Mission is known by to the Scout server. This is :testing for a Mission running in the agent’s test mode.



70
71
72
# File 'lib/scout_agent/mission.rb', line 70

def id
  @id
end

#last_runObject (readonly)

The last run Time for this Mission. Can be nil for new Missions.



74
75
76
# File 'lib/scout_agent/mission.rb', line 74

def last_run
  @last_run
end

#logObject (readonly) Also known as: logger

The log file this Mission is allowed to append messages to.



76
77
78
# File 'lib/scout_agent/mission.rb', line 76

def log
  @log
end

#nameObject (readonly)

The human readable name of this Mission.



72
73
74
# File 'lib/scout_agent/mission.rb', line 72

def name
  @name
end

Class Method Details

.inherited(new_mission) ⇒ Object

Hooks into Ruby’s Class loading process so we can keep track of the last loaded subclass.



25
26
27
# File 'lib/scout_agent/mission.rb', line 25

def self.inherited(new_mission)
  @prepared = new_mission
end

.needs(*libraries) ⇒ Object

You can use this method to indicate one or more libraries your Mission requires:

class MyNeedyPlugin < ScoutAgent::Mission
  needs "faster_csv", "elif"
  # ...
end

Your build_report() method will not be called if all libraries cannot be loaded. RubyGems will be loaded if needed to find the libraries.



41
42
43
44
45
46
47
# File 'lib/scout_agent/mission.rb', line 41

def self.needs(*libraries)
  if libraries.empty?
    @needs ||= Array.new
  else
    needs.push(*libraries.flatten)
  end
end

.preparedObject

This method returns the last Mission subclass that was loaded. The Mission runner uses this after compiling Mission code to get the newly loaded object.



17
18
19
# File 'lib/scout_agent/mission.rb', line 17

def self.prepared
  @prepared ||= nil
end

Instance Method Details

#alert(*new_alert) ⇒ Object Also known as: add_alert

:call-seq:

alert(subject, body)
alert(new_alert)

This method adds an alert that will be saved to the database at the end of the current Mission run. You can pass the subject and body of the generated alert separately, or a Hash containing :subject and :body values for the new_alert.



158
159
160
# File 'lib/scout_agent/mission.rb', line 158

def alert(*new_alert)
  alerts << build_alert_or_error(new_alert)
end

#alertsObject

This method returns an Array of the alerts that will be saved to the database at the end of this execution. It can be used to edit alerts you have already entered.



144
145
146
# File 'lib/scout_agent/mission.rb', line 144

def alerts
  data_for_server[:alerts]
end

#each_queued_message(&block) ⇒ Object

This method iterates over messages that have been queued from an external source for your Mission. Each message (the restored Ruby representation of the JSON data passed to the queue command) will be passed into your block one by one. If your block accepts more than one argument, you will also be passed queue Times with the messages. The act of reading these messages removes them from the database so the current Mission should be used to pass them to the server as meaningful data or save them to Mission memory for later use.

When a Mission is running in test mode, queued messages are read from options(:_\queue\_) and queue Times are simply Time.now.



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/scout_agent/mission.rb', line 255

def each_queued_message(&block)
  if testing?
    Array(option(:__queue__)).each do |message|
      if block.arity == 1
        block[message]
      else
        block[message, Time.now]
      end
    end
  else
    while message = @queue_db.peek(@id)
      if block.arity == 1
        block[message[:fields]]
      else
        block[message[:fields], message[:created_at]]
      end
      @queue_db.dequeue(message[:id]) or return # prevent infinite loop
    end
  end
end

#error(*new_error) ⇒ Object Also known as: add_error

:call-seq:

error(subject, body)
error(new_error)

This method adds an error that will be saved to the database at the end of the current Mission run. You can pass the subject and body of the generated error separately, or a Hash containing :subject and :body values for the new_error.



183
184
185
# File 'lib/scout_agent/mission.rb', line 183

def error(*new_error)
  errors << build_alert_or_error(new_error)
end

#errorsObject

This method returns an Array of the errors that will be saved to the database at the end of this execution. It can be used to edit errors you have already entered.



169
170
171
# File 'lib/scout_agent/mission.rb', line 169

def errors
  data_for_server[:errors]
end

#hint(new_hint) ⇒ Object Also known as: add_hint

This method adds a new_hint that will be saved to the database at the end of the current Mission run. The passed new_hint should be a Hash of the data values you wish to track.



133
134
135
# File 'lib/scout_agent/mission.rb', line 133

def hint(new_hint)
  hints << new_hint
end

#hintsObject

This method returns an Array of the hints that will be saved to the database at the end of this execution. It can be used to edit hints you have already entered.



124
125
126
# File 'lib/scout_agent/mission.rb', line 124

def hints
  data_for_server[:hints]
end

#memory(name = nil) ⇒ Object

:call-seq:

memory(name)
memory.delete(name)
memory.clear

The primary usage of this method is to fetch values saved into the Mission memory during a previous execution by name. The name lookup rules are The same as those decribed in the option() method, so you can use String and Symbol keys interchangeably.

When called without a name, this method returns a Hash of the current Mission memory (not the remembered values from the previous execution). This is mainly helpful if you want to edit the values you have added to be saved during this run.



205
206
207
208
209
210
211
212
# File 'lib/scout_agent/mission.rb', line 205

def memory(name = nil)
  if name.nil?
    data_for_server[:memory]
  else
    @memory[name] ||
    @memory[name.is_a?(String) ? name.to_sym : String(name)]
  end
end

#option(name) ⇒ Object

Fetches an option passed to this Mission from the Web interface by name. If a direct lookup fails, it will be attempted again after converting a String name to a Symbol or any other name to a String. This makes the lookups feel familiar to HashWithIndifferentAccess fans.



94
95
96
97
# File 'lib/scout_agent/mission.rb', line 94

def option(name)
  @options[name] ||
  @options[name.is_a?(String) ? name.to_sym : String(name)]
end

#remember(*args) ⇒ Object

:call-seq:

remember(name, value)
remember(name1, value1, name2, value2, ...)
remember(name => value)
remember(name1 => value1, name2 => value2, ...)
remember(name1, value1, ..., name2 => value2, ...)

Adds all name and value pairs into the Mission memory that will be saved from the current execution.

It’s important to note that Mission memory is not automatically copied from the previous executions, so you must call this method during each execution if you need to keep saving a value.



229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/scout_agent/mission.rb', line 229

def remember(*args)
  # divide Hash and non-Hash arguments
  hashes, other = args.partition { |value| value.is_a? Hash }
  # merge all Hash arguments into the Mission memory
  hashes.each do |hash|
    memory.merge!(hash)
  end
  # merge all non-Hash arguments into the Mission memory
  (0...other.size).step(2) do |i|
    memory.merge!(other[i] => other[i + 1])
  end
end

#report(new_report) ⇒ Object Also known as: add_report

This method adds a new_report that will be saved to the database at the end of the current Mission run. The passed new_report should be a Hash of the data values you wish to track.



113
114
115
# File 'lib/scout_agent/mission.rb', line 113

def report(new_report)
  reports << new_report
end

#reportsObject

This method returns an Array of the reports that will be saved to the database at the end of this execution. It can be used to edit reports you have already entered.



104
105
106
# File 'lib/scout_agent/mission.rb', line 104

def reports
  data_for_server[:reports]
end

#runObject

This method is the wrapper over Scout’s older interface for Mission (then called a Plugin) execution. It hands off to the newer build_report(), then records the results of that run.

Old plugins will work because they override this method. New plugins can now leave this method in place, add a build_report() method instead, and use the new helper methods to build up content inside which will automatically be returned as the end result of the run.



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/scout_agent/mission.rb', line 286

def run
  # 
  # run the plugin in a new Thread so signals will be handled in the main
  # Thread and not accidentally caught by rescue clauses in the body of the
  # mission
  # 
  Thread.new do
    Thread.current.abort_on_exception = true
    if self.class.needs.all? { |l| library_available?(l) }
      begin
        build_report
      rescue Exception => error          # an error while running the code
        raise if error.is_a? SystemExit  # don't catch exit() calls
        error "#{@name} run failed with an error", <<-END_ERROR.trim
        Error:
          #{error.class}
        Message:
          #{error.message}
        Backtrace:
        #{error.backtrace.map { |line| "  #{line}\n" }.join}
        END_ERROR
      end
    end
  end.join  # wait for completion so we have reports to write
  write_reports_and_update_memory
end

#testing?Boolean

Returns true if this Mission is currently being run in the agent’s test mode.

Returns:

  • (Boolean)


84
85
86
# File 'lib/scout_agent/mission.rb', line 84

def testing?
  @id == :testing
end