Class: Maestro::MaestroWorker
- Inherits:
-
Object
- Object
- Maestro::MaestroWorker
- Defined in:
- lib/maestro_plugin/maestro_worker.rb
Overview
Helper Class for Maestro Plugins written in Ruby. The lifecycle of the plugin starts with a call to the main entry point perform called by the Maestro agent, all the other methods are helpers that can be used to deal with parsing, errors, etc. The lifecycle ends with a call to run_callbacks which can be customized by specifying an on_complete_handler_method, or on_complete_handler_block.
Constant Summary collapse
- CONTEXT_OUTPUTS_META =
Workitem constants
'__context_outputs__'
- OUTPUT_META =
'__output__'
- PREVIOUS_CONTEXT_OUTPUTS_META =
'__previous_context_outputs__'
- STREAMING_META =
'__streaming__'
- ERROR_META =
'__error__'
- WAITING_META =
'__waiting__'
- CANCEL_META =
'__cancel__'
- NOT_NEEDED =
'__not_needed__'
- LINKS_META =
'__links__'
- PERSIST_META =
'__persist__'
- MODEL_META =
'__model__'
- RECORD_ID_META =
'__record_id__'
- RECORD_FIELD_META =
'__record_field__'
- RECORD_VALUE_META =
'__record_value__'
- RECORD_FIELDS_META =
'__record_fields__'
- RECORD_VALUES_META =
'__record_values__'
- FILTER_META =
'__filter__'
- NAME_META =
'__name__'
- CREATE_META =
'__create__'
- UPDATE_META =
'__update__'
- DELETE_META =
'__delete__'
Class Attribute Summary collapse
-
.exception_handler_block ⇒ Object
readonly
Returns the value of attribute exception_handler_block.
-
.exception_handler_method ⇒ Object
readonly
Returns the value of attribute exception_handler_method.
-
.on_complete_handler_block ⇒ Object
readonly
Returns the value of attribute on_complete_handler_block.
-
.on_complete_handler_method ⇒ Object
readonly
Returns the value of attribute on_complete_handler_method.
Instance Attribute Summary collapse
-
#action ⇒ Object
Returns the value of attribute action.
-
#workitem ⇒ Object
Returns the value of attribute workitem.
Class Method Summary collapse
-
.mock! ⇒ Object
Call this to mock calls to outbound systems.
- .mock? ⇒ Boolean
-
.on_complete(handler = nil, &block) ⇒ Object
Register a callback method or block that gets called when the action was successfully completed.
-
.on_exception(handler = nil, &block) ⇒ Object
Register a callback method or block that gets called when an exception occurs during the processing of an action.
-
.unmock! ⇒ Object
Call this to unmock calls to outbound systems.
Instance Method Summary collapse
-
#add_link(name, url) ⇒ Object
Adds a link to be displayed in the Maestro UI.
-
#as_boolean(value) ⇒ Object
Return boolean version of a value.
-
#as_int(value, default = 0) ⇒ Object
Return numeric version of value.
-
#cancel ⇒ Object
Send the “cancel” message to the server.
- #create_record_with_fields(model, record_fields, record_values = nil) ⇒ Object
- #delete_record(model, filter) ⇒ Object
- #error ⇒ Object
- #error? ⇒ Boolean
- #fields ⇒ Object (also: #get_fields)
-
#get_boolean_field(field) ⇒ Object
Helper that renders a field as a boolean.
-
#get_field(field, default = nil) ⇒ Object
Get a field from workitem, supporting default value.
-
#get_int_field(field, default = 0) ⇒ Object
Helper that renders a field as an int.
-
#handle_exception(e) ⇒ Object
Fire supplied exception handlers if supplied, otherwise do nothing.
- #is_json?(string) ⇒ Boolean (also: #is_json)
-
#not_needed ⇒ Object
Send the “not needed” message to the server.
- #output ⇒ Object
-
#perform(action, workitem) ⇒ Object
Perform the specified action with the provided workitem.
-
#read_output_value(name) ⇒ Object
Read a value from the context output.
- #reset_buffered_output ⇒ Object
- #run_callbacks ⇒ Object
-
#save_output_value(name, value) ⇒ Object
Set a value in the context output.
- #set_error(error) ⇒ Object
- #set_field(field, value) ⇒ Object
-
#set_waiting(should_wait) ⇒ Object
Sets the current task as waiting.
-
#update_fields_in_record(model, name_or_id, record_field, record_value) ⇒ Object
persistence.
-
#write_output(output, options = {}) ⇒ Object
Sends the specified ouput string to the server for persistence If called with :buffer as an option, the output will be queued up until a number of writes has occurred, or a reasonable period since the last sent occurred.
Class Attribute Details
.exception_handler_block ⇒ Object (readonly)
Returns the value of attribute exception_handler_block.
51 52 53 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 51 def exception_handler_block @exception_handler_block end |
.exception_handler_method ⇒ Object (readonly)
Returns the value of attribute exception_handler_method.
51 52 53 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 51 def exception_handler_method @exception_handler_method end |
.on_complete_handler_block ⇒ Object (readonly)
Returns the value of attribute on_complete_handler_block.
51 52 53 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 51 def on_complete_handler_block @on_complete_handler_block end |
.on_complete_handler_method ⇒ Object (readonly)
Returns the value of attribute on_complete_handler_method.
51 52 53 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 51 def on_complete_handler_method @on_complete_handler_method end |
Instance Attribute Details
#action ⇒ Object
Returns the value of attribute action.
87 88 89 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 87 def action @action end |
#workitem ⇒ Object
Returns the value of attribute workitem.
87 88 89 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 87 def workitem @workitem end |
Class Method Details
.mock! ⇒ Object
Call this to mock calls to outbound systems.
72 73 74 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 72 def mock! @mock = true end |
.mock? ⇒ Boolean
81 82 83 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 81 def mock? @mock end |
.on_complete(handler = nil, &block) ⇒ Object
Register a callback method or block that gets called when the action was successfully completed. Block callbacks get the workitem as parameter.
66 67 68 69 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 66 def on_complete(handler = nil, &block) @on_complete_handler_method = handler @on_complete_handler_block = block end |
.on_exception(handler = nil, &block) ⇒ Object
Register a callback method or block that gets called when an exception occurs during the processing of an action. handler
can be a symbol or string with a method name, or a block. Both will get the exception as the first parameter, and the block handler will receive the participant instance as the second parameter
58 59 60 61 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 58 def on_exception(handler = nil, &block) @exception_handler_method = handler @exception_handler_block = block end |
.unmock! ⇒ Object
Call this to unmock calls to outbound systems.
77 78 79 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 77 def unmock! @mock = false end |
Instance Method Details
#add_link(name, url) ⇒ Object
Adds a link to be displayed in the Maestro UI.
377 378 379 380 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 377 def add_link(name, url) set_field(LINKS_META, []) if fields[LINKS_META].nil? fields[LINKS_META] << {'name' => name, 'url' => url} end |
#as_boolean(value) ⇒ Object
Return boolean version of a value
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 400 def as_boolean(value) res = false if value if value.is_a?(TrueClass) || value.is_a?(FalseClass) res = value elsif value.is_a?(Fixnum) res = value != 0 elsif value.respond_to?(:to_s) value = value.to_s.downcase res = (value == 't' || value == 'true') end end res end |
#as_int(value, default = 0) ⇒ Object
Return numeric version of value
385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 385 def as_int(value, default = 0) res = default if value if value.is_a?(Fixnum) res = value elsif value.respond_to?(:to_i) res = value.to_i end end res end |
#cancel ⇒ Object
Send the “cancel” message to the server
271 272 273 274 275 276 277 278 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 271 def cancel workitem[CANCEL_META] = true rescue Exception => e Maestro.log.warn "Failed To Send Cancel Message To Server #{e.class} #{e}: #{e.backtrace.join("\n")}" ensure workitem.delete(CANCEL_META) end |
#create_record_with_fields(model, record_fields, record_values = nil) ⇒ Object
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 309 def create_record_with_fields(model, record_fields, record_values = nil) workitem[PERSIST_META] = true workitem[CREATE_META] = true workitem[MODEL_META] = model unless record_fields.is_a? Hash Maestro.log.warn 'deprecation: create_record_with_fields should be called with a Hash' record_fields = record_fields.join(',') if record_fields.respond_to? 'join' record_values = record_values.join(',') if record_values.respond_to? 'join' end workitem[RECORD_FIELDS_META] = record_fields workitem[RECORD_VALUES_META] = record_values workitem.delete(PERSIST_META) workitem.delete(CREATE_META) end |
#delete_record(model, filter) ⇒ Object
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 328 def delete_record(model, filter) workitem[PERSIST_META] = true workitem[DELETE_META] = true workitem[MODEL_META] = model if filter.is_a? Hash workitem[FILTER_META] = filter else Maestro.log.warn 'deprecation: delete_record should be called with a Hash' workitem[NAME_META] = filter.to_s end workitem.delete(PERSIST_META) workitem.delete(DELETE_META) end |
#error ⇒ Object
246 247 248 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 246 def error fields[ERROR_META] end |
#error? ⇒ Boolean
250 251 252 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 250 def error? !(error.nil? or error.empty?) end |
#fields ⇒ Object Also known as: get_fields
365 366 367 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 365 def fields workitem['fields'] end |
#get_boolean_field(field) ⇒ Object
Helper that renders a field as a boolean
361 362 363 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 361 def get_boolean_field(field) as_boolean(get_field(field)) end |
#get_field(field, default = nil) ⇒ Object
Get a field from workitem, supporting default value
349 350 351 352 353 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 349 def get_field(field, default = nil) value = fields[field] value = default if !default.nil? && (value.nil? || (value.respond_to?(:empty?) && value.empty?)) value end |
#get_int_field(field, default = 0) ⇒ Object
Helper that renders a field as an int
356 357 358 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 356 def get_int_field(field, default = 0) as_int(get_field(field), default) end |
#handle_exception(e) ⇒ Object
Fire supplied exception handlers if supplied, otherwise do nothing
126 127 128 129 130 131 132 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 126 def handle_exception(e) if self.class.exception_handler_method send(self.class.exception_handler_method, e) elsif self.class.exception_handler_block self.class.exception_handler_block.call(e, self) end end |
#is_json?(string) ⇒ Boolean Also known as: is_json
89 90 91 92 93 94 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 89 def is_json?(string) JSON.parse string true rescue Exception false end |
#not_needed ⇒ Object
Send the “not needed” message to the server.
282 283 284 285 286 287 288 289 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 282 def not_needed workitem[NOT_NEEDED] = true rescue Exception => e Maestro.log.warn "Failed To Send Not Needed Message To Server #{e.class} #{e}: #{e.backtrace.join("\n")}" ensure workitem.delete(NOT_NEEDED) end |
#output ⇒ Object
237 238 239 240 241 242 243 244 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 237 def output if MaestroWorker.mock? workitem[OUTPUT_META] else Maestro.log.warn "Output is only accessible when mock is enabled in tests. Otherwise is directly sent to Maestro" nil end end |
#perform(action, workitem) ⇒ Object
Perform the specified action with the provided workitem. Invokes the method specified by the action parameter.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 100 def perform(action, workitem) @action, @workitem = action, workitem send(action) write_output('') # Triggers any remaining buffered output to be sent run_callbacks rescue MaestroDev::Plugin::PluginError => e write_output('') # Triggers any remaining buffered output to be sent set_error(e.) rescue Exception => e write_output('') # Triggers any remaining buffered output to be sent lowerstack = e.backtrace.find_index(caller[0]) stack = lowerstack ? e.backtrace[0..lowerstack - 1] : e.backtrace msg = "Unexpected error executing task: #{e.class} #{e} at\n" + stack.join("\n") Maestro.log.warn("#{msg}\nFull stack:\n" + e.backtrace.join("\n")) # Let user-supplied exception handler do its thing handle_exception(e) set_error(msg) ensure # Older agents expected this method to *maybe* return something # .. something that no longer exists, but if we return anything # it will be *wrong* :P return nil end |
#read_output_value(name) ⇒ Object
Read a value from the context output
151 152 153 154 155 156 157 158 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 151 def read_output_value(name) if get_field(PREVIOUS_CONTEXT_OUTPUTS_META).nil? set_field(CONTEXT_OUTPUTS_META, {}) if get_field(CONTEXT_OUTPUTS_META).nil? get_field(CONTEXT_OUTPUTS_META)[name] else get_field(PREVIOUS_CONTEXT_OUTPUTS_META)[name] end end |
#reset_buffered_output ⇒ Object
232 233 234 235 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 232 def reset_buffered_output @buffered_output = '' @last_write_output = Time.now end |
#run_callbacks ⇒ Object
134 135 136 137 138 139 140 141 142 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 134 def run_callbacks return if self.class.on_complete_handler_block.nil? && self.class.on_complete_handler_method.nil? if self.class.on_complete_handler_method send(self.class.on_complete_handler_method) else self.class.on_complete_handler_block.call(workitem) end end |
#save_output_value(name, value) ⇒ Object
Set a value in the context output
145 146 147 148 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 145 def save_output_value(name, value) set_field(CONTEXT_OUTPUTS_META, {}) if get_field(CONTEXT_OUTPUTS_META).nil? get_field(CONTEXT_OUTPUTS_META)[name] = value end |
#set_error(error) ⇒ Object
254 255 256 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 254 def set_error(error) set_field(ERROR_META, error) end |
#set_field(field, value) ⇒ Object
372 373 374 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 372 def set_field(field, value) fields[field] = value end |
#set_waiting(should_wait) ⇒ Object
Sets the current task as waiting
261 262 263 264 265 266 267 268 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 261 def set_waiting(should_wait) workitem[WAITING_META] = should_wait rescue Exception => e Maestro.log.warn "Failed To Send Waiting Message To Server #{e.class} #{e}: #{e.backtrace.join("\n")}" ensure workitem.delete(WAITING_META) unless should_wait end |
#update_fields_in_record(model, name_or_id, record_field, record_value) ⇒ Object
persistence
294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 294 def update_fields_in_record(model, name_or_id, record_field, record_value) workitem[PERSIST_META] = true workitem[UPDATE_META] = true workitem[MODEL_META] = model workitem[RECORD_ID_META] = name_or_id.to_s workitem[RECORD_FIELD_META] = record_field workitem[RECORD_VALUE_META] = record_value workitem.delete(PERSIST_META) workitem.delete(UPDATE_META) end |
#write_output(output, options = {}) ⇒ Object
Sends the specified ouput string to the server for persistence If called with :buffer as an option, the output will be queued up until a number of writes has occurred, or a reasonable period since the last sent occurred. Any call without the :buffer option will cause any buffered output to be sent immediately.
Example:
write_output("I am Sam\n") <-- send immediately
write_output("Sam I am\n", :buffer => true) <-- buffer for later
write_output("I like Ham\n") <-- sends 'Sam I am\nI like Ham\n'
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/maestro_plugin/maestro_worker.rb', line 169 def write_output(output, = {}) # First time thru? We need to do some setup! reset_buffered_output if @buffered_output.nil? @buffered_output += output # If a) we have data to write, and # b) its been > 2 seconds since we last sent # # The 2 second factor is there to allow slowly accumulating data to be sent out more regularly. if !@buffered_output.empty? && (![:buffer] || Time.now - @last_write_output > 2) # Ensure the output is json-able. # It seems some code doesn't wholly respect encoding rules. We've found some http responses that # don't have the correct encoding, despite the response headers stating 'utf-8', etc. Same goes # for shell output streams, that don't seem to respect the apps encoding. # What this code does is to try to json encode the @buffered_output. First a direct conversion, # if that fails, try to force-encoding to utf-8, if that fails, try to remove all chars with # code > 127. If that fails - we gave it a good shot, and maybe just insert a 'redacted' string # so at least the task doesn't fail :) begin @buffered_output.to_json rescue Exception => e1 Maestro.log.warn("Unable to 'to_json' output [#{e1}]: #{@buffered_output}") begin test = @buffered_output test.force_encoding('UTF-8') test.to_json # If forcing encoding worked, updated buffered_output Maestro.log.warn("Had to force encoding to utf-8 for workitem stream") @buffered_output = test rescue Exception begin test = @buffered_output.gsub(/[^\x00-\x7f]/, '?') test.to_json # If worked, updated buffered_output Maestro.log.warn("Had to strip top-bit-set chars for workitem stream") @buffered_output = test rescue Exception Maestro.log.warn("Had to redact block of output, unable to 'to_json' it for workitem stream") @buffered_output = '?_?' end end end if !MaestroWorker.mock? workitem[OUTPUT_META] = @buffered_output else # Test mode, we want to retain output - normal operation clears out # data after it is sent workitem[OUTPUT_META] = '' if !workitem[OUTPUT_META] workitem[OUTPUT_META] = workitem[OUTPUT_META] + @buffered_output end workitem[STREAMING_META] = true reset_buffered_output end rescue Exception => e Maestro.log.warn "Unable To Write Output To Server #{e.class} #{e}: #{e.backtrace.join("\n")}" ensure workitem.delete(STREAMING_META) end |