lib/arborist/node.rb in arborist-0.0.1.pre20161005182540 vs lib/arborist/node.rb in arborist-0.1.0
- old
+ new
@@ -42,15 +42,23 @@
description
dependencies
status_changed
last_contacted
ack
- error
+ errors
quieted_reasons
config
]
+ # Node states that are unreachable by default.
+ UNREACHABLE_STATES = %w[
+ down
+ disabled
+ quieted
+ ]
+
+
autoload :Root, 'arborist/node/root'
autoload :Ack, 'arborist/node/ack'
# Log via the Arborist logger
@@ -112,12 +120,12 @@
:disabled,
:quieted
event :update do
transition [:up, :unknown] => :disabled, if: :ack_set?
- transition [:down, :unknown, :acked] => :up, if: :last_contact_successful?
- transition [:up, :unknown] => :down, unless: :last_contact_successful?
+ transition [:down, :unknown, :acked] => :up, unless: :has_errors?
+ transition [:up, :unknown, :acked] => :down, if: :has_errors?
transition :down => :acked, if: :ack_set?
transition :disabled => :unknown, unless: :ack_set?
end
event :handle_event do
@@ -272,11 +280,11 @@
# Primary state
@status = 'unknown'
@status_changed = Time.at( 0 )
# Attributes that govern state
- @error = nil
+ @errors = {}
@ack = nil
@last_contacted = Time.at( 0 )
@quieted_reasons = {}
# Event-handling
@@ -319,12 +327,13 @@
##
# The Time the node's status last changed.
attr_accessor :status_changed
##
- # The last error encountered by a monitor attempting to update this node.
- attr_accessor :error
+ # The Hash of last errors encountered by a monitor attempting to update this
+ # node, keyed by the monitor's `key`.
+ attr_accessor :errors
##
# The acknowledgement currently in effect. Should be an instance of Arborist::Node::ACK
attr_accessor :ack
@@ -476,17 +485,21 @@
### Update specified +properties+ for the node.
def update( new_properties )
new_properties = stringify_keys( new_properties )
- self.log.debug "Updated: %p" % [ new_properties ]
+ monitor_key = new_properties[ '_monitor_key' ] || '_'
+ self.log.debug "Updated via a %s monitor: %p" % [ monitor_key, new_properties ]
self.last_contacted = Time.now
+
if new_properties.key?( 'ack' )
self.ack = new_properties.delete( 'ack' )
+ elsif new_properties['error']
+ self.errors[ monitor_key ] = new_properties.delete( 'error' )
else
- self.error = new_properties.delete( 'error' )
+ self.errors.delete( monitor_key )
end
self.properties.merge!( new_properties, &self.method(:merge_and_record_delta) )
compact_hash( self.properties )
@@ -585,10 +598,12 @@
when 'status'
self.status == val
when 'type'
self.log.debug "Checking node type %p against %p" % [ self.type, val ]
self.type == val
+ when 'parent'
+ self.parent == val
when 'tag' then @tags.include?( val.to_s )
when 'tags' then Array(val).all? {|tag| @tags.include?(tag) }
when 'identifier' then @identifier == val
when 'config'
val.all? {|ikey, ival| hash_matches(@config, ikey, ival) }
@@ -678,11 +693,12 @@
self.log.debug "No handler for a %s event!" % [ event.type ]
end
super # to state-machine
- return self.collect_events
+ self.publish_events( event, *self.pending_update_events )
+ self.collect_events
end
### Returns +true+ if this node's dependencies are not met.
def dependencies_down?
@@ -790,10 +806,24 @@
def operational?
return self.identifier.start_with?( '_' )
end
+ ### Returns +true+ if the node's status indicates it shouldn't be
+ ### included by default when traversing nodes.
+ def unreachable?
+ return UNREACHABLE_STATES.include?( self.status )
+ end
+
+
+ ### Returns +true+ if the node's status indicates it is included by
+ ### default when traversing nodes.
+ def reachable?
+ return !self.unreachable?
+ end
+
+
### Register the specified +node+ as a child of this node, replacing any existing
### node with the same identifier.
def add_child( node )
self.log.debug "Adding node %p as a child. Parent = %p" % [ node, node.parent ]
raise "%p is not a child of %p" % [ node, self ] if
@@ -878,11 +908,11 @@
@status = old_node.status
@properties = old_node.properties.dup
@ack = old_node.ack.dup if old_node.ack
@last_contacted = old_node.last_contacted
@status_changed = old_node.status_changed
- @error = old_node.error
+ @errors = old_node.errors
@quieted_reasons = old_node.quieted_reasons
# Only merge in downed dependencies.
old_node.dependencies.each_downed do |identifier, time|
@dependencies.mark_down( identifier, time )
@@ -902,11 +932,11 @@
status: self.status,
properties: self.properties.dup,
ack: self.ack ? self.ack.to_h : nil,
last_contacted: self.last_contacted ? self.last_contacted.iso8601 : nil,
status_changed: self.status_changed ? self.status_changed.iso8601 : nil,
- error: self.error,
+ errors: self.errors,
dependencies: self.dependencies.to_h,
quieted_reasons: self.quieted_reasons,
}
end
@@ -932,11 +962,11 @@
@status = hash[:status]
@status_changed = Time.parse( hash[:status_changed] )
@ack = Arborist::Node::Ack.from_hash( hash[:ack] ) if hash[:ack]
- @error = hash[:error]
+ @errors = hash[:errors]
@properties = hash[:properties] || {}
@last_contacted = Time.parse( hash[:last_contacted] )
@quieted_reasons = hash[:quieted_reasons] || {}
self.log.debug "Deps are: %p" % [ hash[:dependencies] ]
@dependencies = hash[:dependencies]
@@ -986,17 +1016,27 @@
end
### State machine guard predicate -- Returns +true+ if the last time the node
### was monitored resulted in an update.
- def last_contact_successful?
- self.log.debug "Checking to see if last contact was successful (it %s)" %
- [ self.error ? "wasn't" : "was" ]
- return !self.error
+ def has_errors?
+ has_errors = ! self.errors.empty?
+ self.log.debug "Checking to see if last contact cleared remaining errors (it %s)" %
+ [ has_errors ? "did not" : "did" ]
+ self.log.debug "Errors are: %p" % [ self.errors ]
+ return has_errors
end
+ ### Return a string describing the errors that are set on the node.
+ def errors_description
+ return "No errors" if self.errors.empty?
+ return self.errors.map do |key, msg|
+ "%s: %s" % [ key, msg ]
+ end.join( '; ' )
+ end
+
#
# :section: State Callbacks
#
### Log every status transition
@@ -1033,18 +1073,18 @@
end
### Callback for when a node goes from down to up
def on_node_up( transition )
- self.error = nil
+ self.errors.clear
self.log.warn "%s is %s" % [ self.identifier, self.status_description ]
end
### Callback for when a node goes from up to down
def on_node_down( transition )
self.log.error "%s is %s" % [ self.identifier, self.status_description ]
- self.update_delta[ 'error' ] = [ nil, self.error ]
+ self.update_delta[ 'errors' ] = [ nil, self.errors_description ]
end
### Callback for when a node goes from up to disabled
def on_node_disabled( transition )