arborist 0.2.0.pre20170519125456 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +670 -1
- data/History.md +67 -0
- data/Manifest.txt +9 -6
- data/README.md +1 -3
- data/Rakefile +39 -4
- data/TODO.md +22 -31
- data/lib/arborist.rb +9 -2
- data/lib/arborist/cli.rb +67 -85
- data/lib/arborist/client.rb +125 -59
- data/lib/arborist/command/ack.rb +86 -0
- data/lib/arborist/command/reset.rb +48 -0
- data/lib/arborist/command/start.rb +11 -1
- data/lib/arborist/command/summary.rb +173 -0
- data/lib/arborist/command/tree.rb +215 -0
- data/lib/arborist/command/watch.rb +22 -22
- data/lib/arborist/dependency.rb +24 -4
- data/lib/arborist/event.rb +18 -2
- data/lib/arborist/event/node.rb +6 -2
- data/lib/arborist/event/node_warn.rb +16 -0
- data/lib/arborist/manager.rb +179 -48
- data/lib/arborist/mixins.rb +11 -0
- data/lib/arborist/monitor.rb +29 -17
- data/lib/arborist/monitor/connection_batching.rb +293 -0
- data/lib/arborist/monitor/socket.rb +101 -167
- data/lib/arborist/monitor_runner.rb +101 -24
- data/lib/arborist/node.rb +297 -68
- data/lib/arborist/node/ack.rb +1 -1
- data/lib/arborist/node/host.rb +26 -5
- data/lib/arborist/node/resource.rb +14 -5
- data/lib/arborist/node/root.rb +12 -3
- data/lib/arborist/node/service.rb +29 -26
- data/lib/arborist/node_subscription.rb +65 -0
- data/lib/arborist/observer.rb +8 -0
- data/lib/arborist/observer/action.rb +6 -0
- data/lib/arborist/subscription.rb +22 -16
- data/lib/arborist/tree_api.rb +7 -2
- data/spec/arborist/client_spec.rb +157 -51
- data/spec/arborist/dependency_spec.rb +21 -0
- data/spec/arborist/event/node_spec.rb +5 -0
- data/spec/arborist/event_spec.rb +3 -3
- data/spec/arborist/manager_spec.rb +626 -347
- data/spec/arborist/mixins_spec.rb +19 -0
- data/spec/arborist/monitor/socket_spec.rb +1 -2
- data/spec/arborist/monitor_runner_spec.rb +81 -29
- data/spec/arborist/monitor_spec.rb +89 -14
- data/spec/arborist/node/host_spec.rb +68 -0
- data/spec/arborist/node/resource_spec.rb +2 -0
- data/spec/arborist/node/root_spec.rb +13 -0
- data/spec/arborist/node/service_spec.rb +9 -0
- data/spec/arborist/node_spec.rb +673 -111
- data/spec/arborist/node_subscription_spec.rb +54 -0
- data/spec/arborist/observer/action_spec.rb +6 -0
- data/spec/arborist/observer_runner_spec.rb +8 -1
- data/spec/arborist/tree_api_spec.rb +111 -8
- data/spec/data/monitors/pings.rb +0 -11
- data/spec/data/monitors/port_checks.rb +0 -9
- data/spec/data/nodes/sidonie.rb +1 -0
- data/spec/data/nodes/vhosts.rb +23 -0
- data/spec/data/nodes/yevaud.rb +4 -2
- data/spec/spec_helper.rb +71 -1
- metadata +91 -28
- metadata.gz.sig +0 -0
- data/Events.md +0 -35
- data/Monitors.md +0 -155
- data/Nodes.md +0 -70
- data/Observers.md +0 -72
- data/Protocol.md +0 -276
- data/Tutorial.md +0 -8
data/lib/arborist/node/ack.rb
CHANGED
data/lib/arborist/node/host.rb
CHANGED
@@ -48,6 +48,10 @@ class Arborist::Node::Host < Arborist::Node
|
|
48
48
|
# The network address(es) of this Host as an Array of IPAddr objects
|
49
49
|
attr_reader :addresses
|
50
50
|
|
51
|
+
##
|
52
|
+
# An optional hostname.
|
53
|
+
dsl_accessor :hostname
|
54
|
+
|
51
55
|
|
52
56
|
### Set one or more node +attributes+. Supported attributes (in addition to
|
53
57
|
### those supported by Node) are: +addresses+.
|
@@ -56,7 +60,8 @@ class Arborist::Node::Host < Arborist::Node
|
|
56
60
|
|
57
61
|
super
|
58
62
|
|
59
|
-
if attributes['
|
63
|
+
self.hostname( attributes['hostname'] ) if attributes[ 'hostname' ]
|
64
|
+
if attributes[ 'addresses' ]
|
60
65
|
self.addresses.clear
|
61
66
|
Array( attributes['addresses'] ).each do |addr|
|
62
67
|
self.address( addr )
|
@@ -68,20 +73,30 @@ class Arborist::Node::Host < Arborist::Node
|
|
68
73
|
### Return the host's operational attributes.
|
69
74
|
def operational_values
|
70
75
|
properties = super
|
71
|
-
return properties.merge(
|
76
|
+
return properties.merge(
|
77
|
+
hostname: @hostname,
|
78
|
+
addresses: self.addresses.map(&:to_s)
|
79
|
+
)
|
72
80
|
end
|
73
81
|
|
74
82
|
|
75
83
|
### Set an IP address of the host.
|
76
84
|
def address( new_address )
|
77
85
|
self.log.debug "Adding address %p to %p" % [ new_address, self ]
|
86
|
+
|
87
|
+
if new_address =~ /^[[:alnum:]][a-z0-9\-]+/i && ! @hostname
|
88
|
+
@hostname = new_address
|
89
|
+
end
|
90
|
+
|
78
91
|
@addresses += normalize_address( new_address )
|
92
|
+
@addresses.uniq!
|
79
93
|
end
|
80
94
|
|
81
95
|
|
82
96
|
### Returns +true+ if the node matches the specified +key+ and +val+ criteria.
|
83
97
|
def match_criteria?( key, val )
|
84
98
|
return case key
|
99
|
+
when 'hostname' then @hostname == val
|
85
100
|
when 'address'
|
86
101
|
search_addr = IPAddr.new( val )
|
87
102
|
@addresses.any? {|a| search_addr.include?(a) }
|
@@ -103,8 +118,11 @@ class Arborist::Node::Host < Arborist::Node
|
|
103
118
|
#
|
104
119
|
|
105
120
|
### Return a Hash of the host node's state.
|
106
|
-
def to_h
|
107
|
-
return super.merge(
|
121
|
+
def to_h( * )
|
122
|
+
return super.merge(
|
123
|
+
hostname: @hostname,
|
124
|
+
addresses: self.addresses.map(&:to_s)
|
125
|
+
)
|
108
126
|
end
|
109
127
|
|
110
128
|
|
@@ -113,13 +131,16 @@ class Arborist::Node::Host < Arborist::Node
|
|
113
131
|
def marshal_load( hash )
|
114
132
|
super
|
115
133
|
@addresses = hash[:addresses].map {|addr| IPAddr.new(addr) }
|
134
|
+
@hostname = hash[:hostname]
|
116
135
|
end
|
117
136
|
|
118
137
|
|
119
138
|
### Equality operator -- returns +true+ if +other_node+ is equal to the
|
120
139
|
### receiver. Overridden to also compare addresses.
|
121
140
|
def ==( other_host )
|
122
|
-
return super &&
|
141
|
+
return super &&
|
142
|
+
other_host.addresses == self.addresses &&
|
143
|
+
other_host.hostname == @hostname
|
123
144
|
end
|
124
145
|
|
125
146
|
end # class Arborist::Node::Host
|
@@ -27,7 +27,7 @@ class Arborist::Node::Resource < Arborist::Node
|
|
27
27
|
### Set service +attributes+.
|
28
28
|
def modify( attributes )
|
29
29
|
attributes = stringify_keys( attributes )
|
30
|
-
|
30
|
+
super
|
31
31
|
self.category( attributes['category'] )
|
32
32
|
end
|
33
33
|
|
@@ -36,7 +36,9 @@ class Arborist::Node::Resource < Arborist::Node
|
|
36
36
|
### monitor state.
|
37
37
|
def operational_values
|
38
38
|
return super.merge(
|
39
|
-
addresses: self.addresses.map( &:to_s )
|
39
|
+
addresses: self.addresses.map( &:to_s ),
|
40
|
+
hostname: self.hostname,
|
41
|
+
category: self.category
|
40
42
|
)
|
41
43
|
end
|
42
44
|
|
@@ -54,6 +56,12 @@ class Arborist::Node::Resource < Arborist::Node
|
|
54
56
|
end
|
55
57
|
|
56
58
|
|
59
|
+
### Delegate the resource's hostname to it's parent host.
|
60
|
+
def hostname
|
61
|
+
return @host.hostname
|
62
|
+
end
|
63
|
+
|
64
|
+
|
57
65
|
### Overridden to disallow modification of a Resource parent, as it needs a
|
58
66
|
### reference to the Host node for delegation.
|
59
67
|
def parent( new_parent=nil )
|
@@ -63,9 +71,10 @@ class Arborist::Node::Resource < Arborist::Node
|
|
63
71
|
|
64
72
|
|
65
73
|
### Serialize the resource node. Return a Hash of the host node's state.
|
66
|
-
def to_h
|
74
|
+
def to_h( * )
|
67
75
|
return super.merge(
|
68
|
-
addresses: self.addresses.map( &:to_s )
|
76
|
+
addresses: self.addresses.map( &:to_s ),
|
77
|
+
category: self.category
|
69
78
|
)
|
70
79
|
end
|
71
80
|
|
@@ -78,7 +87,7 @@ class Arborist::Node::Resource < Arborist::Node
|
|
78
87
|
search_addr = IPAddr.new( val )
|
79
88
|
self.addresses.any? {|a| search_addr.include?(a) }
|
80
89
|
when 'category'
|
81
|
-
self.category
|
90
|
+
Array( val ).include?( self.category )
|
82
91
|
else
|
83
92
|
super
|
84
93
|
end
|
data/lib/arborist/node/root.rb
CHANGED
@@ -51,9 +51,18 @@ class Arborist::Node::Root < Arborist::Node
|
|
51
51
|
end
|
52
52
|
|
53
53
|
|
54
|
-
###
|
55
|
-
def update( properties )
|
56
|
-
|
54
|
+
### Don't allow properties to be set on the root node.
|
55
|
+
def update( properties, monitor_key='_' )
|
56
|
+
return super( {} )
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
### Callback for when a node goes from disabled to unknown.
|
61
|
+
### Override, so we immediately transition from unknown to up.
|
62
|
+
def on_node_enabled( transition )
|
63
|
+
super
|
64
|
+
events = self.update( {} ) # up!
|
65
|
+
self.publish_events( events )
|
57
66
|
end
|
58
67
|
|
59
68
|
|
@@ -54,6 +54,19 @@ class Arborist::Node::Service < Arborist::Node
|
|
54
54
|
public
|
55
55
|
######
|
56
56
|
|
57
|
+
##
|
58
|
+
# Get/set the port the service binds to
|
59
|
+
dsl_accessor :port
|
60
|
+
|
61
|
+
##
|
62
|
+
# Get/set the application protocol the service uses
|
63
|
+
dsl_accessor :app_protocol
|
64
|
+
|
65
|
+
##
|
66
|
+
# Get/set the network protocol the service uses
|
67
|
+
dsl_accessor :protocol
|
68
|
+
|
69
|
+
|
57
70
|
### Set service +attributes+.
|
58
71
|
def modify( attributes )
|
59
72
|
attributes = stringify_keys( attributes )
|
@@ -66,27 +79,6 @@ class Arborist::Node::Service < Arborist::Node
|
|
66
79
|
end
|
67
80
|
|
68
81
|
|
69
|
-
### Get/set the port the service is bound to.
|
70
|
-
def port( new_port=nil )
|
71
|
-
return @port unless new_port
|
72
|
-
@port = new_port
|
73
|
-
end
|
74
|
-
|
75
|
-
|
76
|
-
### Get/set the (layer 7) protocol used by the service
|
77
|
-
def app_protocol( new_proto=nil )
|
78
|
-
return @app_protocol unless new_proto
|
79
|
-
@app_protocol = new_proto
|
80
|
-
end
|
81
|
-
|
82
|
-
|
83
|
-
### Get/set the transport layer protocol the service uses
|
84
|
-
def protocol( new_proto=nil )
|
85
|
-
return @protocol unless new_proto
|
86
|
-
@protocol = new_proto
|
87
|
-
end
|
88
|
-
|
89
|
-
|
90
82
|
### Set an IP address of the service. This must be one of the addresses of its
|
91
83
|
### containing host.
|
92
84
|
def address( new_address )
|
@@ -109,18 +101,28 @@ class Arborist::Node::Service < Arborist::Node
|
|
109
101
|
end
|
110
102
|
|
111
103
|
|
104
|
+
### Delegate the service's hostname to it's parent host.
|
105
|
+
def hostname
|
106
|
+
return @host.hostname
|
107
|
+
end
|
108
|
+
|
109
|
+
|
112
110
|
### Returns +true+ if the node matches the specified +key+ and +val+ criteria.
|
113
111
|
def match_criteria?( key, val )
|
114
112
|
self.log.debug "Matching %p: %p against %p" % [ key, val, self ]
|
113
|
+
array_val = Array( val )
|
115
114
|
return case key
|
116
115
|
when 'port'
|
117
|
-
|
118
|
-
|
116
|
+
vals = array_val.collect do |port|
|
117
|
+
port = default_port_for( port, @protocol ) unless port.is_a?( Integer )
|
118
|
+
port.to_i
|
119
|
+
end
|
120
|
+
vals.include?( self.port )
|
119
121
|
when 'address'
|
120
122
|
search_addr = IPAddr.new( val )
|
121
123
|
self.addresses.any? {|a| search_addr.include?(a) }
|
122
|
-
when 'protocol' then self.protocol
|
123
|
-
when 'app', 'app_protocol' then self.app_protocol
|
124
|
+
when 'protocol' then array_val.include?( self.protocol )
|
125
|
+
when 'app', 'app_protocol' then array_val.include?( self.app_protocol )
|
124
126
|
else
|
125
127
|
super
|
126
128
|
end
|
@@ -132,6 +134,7 @@ class Arborist::Node::Service < Arborist::Node
|
|
132
134
|
def operational_values
|
133
135
|
return super.merge(
|
134
136
|
addresses: self.addresses.map( &:to_s ),
|
137
|
+
hostname: self.hostname,
|
135
138
|
port: self.port,
|
136
139
|
protocol: self.protocol,
|
137
140
|
app_protocol: self.app_protocol,
|
@@ -161,7 +164,7 @@ class Arborist::Node::Service < Arborist::Node
|
|
161
164
|
#
|
162
165
|
|
163
166
|
### Return a Hash of the host node's state.
|
164
|
-
def to_h
|
167
|
+
def to_h( * )
|
165
168
|
return super.merge(
|
166
169
|
addresses: self.addresses.map(&:to_s),
|
167
170
|
protocol: self.protocol,
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'arborist' unless defined?( Arborist )
|
5
|
+
require 'arborist/subscription'
|
6
|
+
|
7
|
+
|
8
|
+
# An inter-node event subscription
|
9
|
+
class Arborist::NodeSubscription < Arborist::Subscription
|
10
|
+
|
11
|
+
### Create a new subscription object that will send events to the given
|
12
|
+
### +node+.
|
13
|
+
def initialize( node )
|
14
|
+
@node = node
|
15
|
+
super()
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
######
|
20
|
+
public
|
21
|
+
######
|
22
|
+
|
23
|
+
##
|
24
|
+
# The target node
|
25
|
+
attr_reader :node
|
26
|
+
|
27
|
+
|
28
|
+
### Return the identifier of the subscribed node.
|
29
|
+
def node_identifier
|
30
|
+
return self.node.identifier
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
### Return an ID derived from the node's identifier.
|
35
|
+
def generate_id
|
36
|
+
return "%s-subscription" % [ self.node_identifier ]
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
### Check the node to make sure it can handle published events.
|
41
|
+
def check_callback
|
42
|
+
raise NameError, "node doesn't implement handle_event" unless
|
43
|
+
self.node.respond_to?( :handle_event )
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
### Publish any of the specified +events+ which match the subscription.
|
48
|
+
def on_events( *events )
|
49
|
+
events.flatten.each do |event|
|
50
|
+
self.node.handle_event( event )
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
### Return a String representation of the object suitable for debugging.
|
56
|
+
def inspect
|
57
|
+
return "#<%p:%#x for the %s node>" % [
|
58
|
+
self.class,
|
59
|
+
self.object_id * 2,
|
60
|
+
self.node.identifier,
|
61
|
+
]
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
end # class Arborist::NodeSubscription
|
data/lib/arborist/observer.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
#encoding: utf-8
|
3
3
|
|
4
4
|
require 'arborist' unless defined?( Arborist )
|
5
|
+
require 'arborist/client'
|
5
6
|
|
6
7
|
|
7
8
|
# The Arborist entity responsible for observing changes to the tree and
|
@@ -124,6 +125,13 @@ class Arborist::Observer
|
|
124
125
|
end
|
125
126
|
|
126
127
|
|
128
|
+
### Return a client singleton for optional observer callbacks to the
|
129
|
+
### manager.
|
130
|
+
def client
|
131
|
+
return Arborist::Client.instance
|
132
|
+
end
|
133
|
+
|
134
|
+
|
127
135
|
#
|
128
136
|
# Observe Methods
|
129
137
|
#
|
@@ -78,6 +78,12 @@ class Arborist::Observer::Action
|
|
78
78
|
else
|
79
79
|
self.block.call( event.dup )
|
80
80
|
end
|
81
|
+
rescue => err
|
82
|
+
self.log.error "Exception while running observer: %s: %s\n%s" % [
|
83
|
+
err.class.name,
|
84
|
+
err.message,
|
85
|
+
err.backtrace.join("\n ")
|
86
|
+
]
|
81
87
|
ensure
|
82
88
|
self.event_history.clear
|
83
89
|
end
|
@@ -21,13 +21,13 @@ class Arborist::Subscription
|
|
21
21
|
### Instantiate a new Subscription object given an +event+ pattern
|
22
22
|
### and event +criteria+.
|
23
23
|
def initialize( event_type=nil, criteria={}, negative_criteria={}, &callback )
|
24
|
-
raise LocalJumpError, "requires a callback block" unless callback
|
25
|
-
|
26
24
|
@callback = callback
|
27
25
|
@event_type = event_type
|
28
26
|
@criteria = stringify_keys( criteria )
|
29
27
|
@negative_criteria = stringify_keys( negative_criteria )
|
30
28
|
|
29
|
+
self.check_callback
|
30
|
+
|
31
31
|
@id = self.generate_id
|
32
32
|
end
|
33
33
|
|
@@ -59,9 +59,9 @@ class Arborist::Subscription
|
|
59
59
|
end
|
60
60
|
|
61
61
|
|
62
|
-
###
|
63
|
-
def
|
64
|
-
|
62
|
+
### Check to make sure the subscription will function as it's set up.
|
63
|
+
def check_callback
|
64
|
+
raise LocalJumpError, "requires a callback block" unless self.callback
|
65
65
|
end
|
66
66
|
|
67
67
|
|
@@ -76,17 +76,6 @@ class Arborist::Subscription
|
|
76
76
|
end
|
77
77
|
|
78
78
|
|
79
|
-
### Returns +true+ if the receiver is interested in publishing the specified +event+.
|
80
|
-
def interested_in?( event )
|
81
|
-
self.log.debug "Testing %p against type = %p and criteria = %p but not %p" %
|
82
|
-
[ event, self.event_type, self.criteria, self.negative_criteria ]
|
83
|
-
rval = event.match( self )
|
84
|
-
self.log.debug " event %s match." % [ rval ? "did" : "did NOT" ]
|
85
|
-
return rval
|
86
|
-
end
|
87
|
-
alias_method :is_interested_in?, :interested_in?
|
88
|
-
|
89
|
-
|
90
79
|
### Return a String representation of the object suitable for debugging.
|
91
80
|
def inspect
|
92
81
|
return "#<%p:%#x [%s] for %s events matching: %p %s-> %p>" % [
|
@@ -100,4 +89,21 @@ class Arborist::Subscription
|
|
100
89
|
]
|
101
90
|
end
|
102
91
|
|
92
|
+
|
93
|
+
### Create an identifier for this subscription object.
|
94
|
+
def generate_id
|
95
|
+
return SecureRandom.uuid
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
### Returns +true+ if the receiver is interested in publishing the specified +event+.
|
100
|
+
def interested_in?( event )
|
101
|
+
self.log.debug "Testing %p against type = %p and criteria = %p but not %p" %
|
102
|
+
[ event, self.event_type, self.criteria, self.negative_criteria ]
|
103
|
+
rval = event.match( self )
|
104
|
+
self.log.debug " event %s match." % [ rval ? "did" : "did NOT" ]
|
105
|
+
return rval
|
106
|
+
end
|
107
|
+
alias_method :is_interested_in?, :interested_in?
|
108
|
+
|
103
109
|
end # class Arborist::Subscription
|
data/lib/arborist/tree_api.rb
CHANGED
@@ -23,6 +23,10 @@ module Arborist::TreeAPI
|
|
23
23
|
|
24
24
|
### Return a CZTop::Message with a payload containing the specified +header+ and +body+.
|
25
25
|
def self::encode( header, body=nil )
|
26
|
+
raise Arborist::MessageError, "header is not a Map" unless
|
27
|
+
header.is_a?( Hash )
|
28
|
+
|
29
|
+
self.log.debug "Encoding header: %p with body: %p" % [ header, body ]
|
26
30
|
header = stringify_keys( header )
|
27
31
|
header['version'] = PROTOCOL_VERSION
|
28
32
|
|
@@ -31,6 +35,7 @@ module Arborist::TreeAPI
|
|
31
35
|
|
32
36
|
payload = MessagePack.pack([ header, body ])
|
33
37
|
|
38
|
+
self.log.debug "Making zmq message with payload: %p" % [ payload ]
|
34
39
|
return CZTop::Message.new( payload )
|
35
40
|
end
|
36
41
|
|
@@ -65,8 +70,8 @@ module Arborist::TreeAPI
|
|
65
70
|
### Return a CZTop::Message containing a TreeAPI request with the specified
|
66
71
|
### +verb+ and +data+.
|
67
72
|
def self::request( verb, *data )
|
68
|
-
|
69
|
-
|
73
|
+
body = data.pop
|
74
|
+
header = data.pop || {}
|
70
75
|
|
71
76
|
header.merge!( action: verb )
|
72
77
|
|