arborist 0.1.0 → 0.2.0.pre20170519125456
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/ChangeLog +46 -2
- data/Manifest.txt +4 -4
- data/Rakefile +6 -4
- data/TODO.md +16 -0
- data/lib/arborist.rb +12 -51
- data/lib/arborist/client.rb +23 -46
- data/lib/arborist/command/client.rb +1 -0
- data/lib/arborist/command/watch.rb +4 -5
- data/lib/arborist/event_api.rb +35 -0
- data/lib/arborist/exceptions.rb +2 -2
- data/lib/arborist/manager.rb +432 -212
- data/lib/arborist/mixins.rb +9 -9
- data/lib/arborist/monitor_runner.rb +174 -137
- data/lib/arborist/node.rb +11 -4
- data/lib/arborist/observer/summarize.rb +1 -1
- data/lib/arborist/observer_runner.rb +163 -126
- data/lib/arborist/tree_api.rb +113 -0
- data/spec/arborist/client_spec.rb +63 -64
- data/spec/arborist/event_api_spec.rb +23 -0
- data/spec/arborist/manager_spec.rb +842 -66
- data/spec/arborist/monitor_runner_spec.rb +45 -85
- data/spec/arborist/observer_runner_spec.rb +86 -23
- data/spec/arborist/tree_api_spec.rb +30 -0
- data/spec/arborist_spec.rb +0 -5
- data/spec/spec_helper.rb +0 -13
- metadata +47 -45
- checksums.yaml.gz.sig +0 -3
- data.tar.gz.sig +0 -0
- data/lib/arborist/manager/event_publisher.rb +0 -126
- data/lib/arborist/manager/tree_api.rb +0 -302
- data/spec/arborist/manager/event_publisher_spec.rb +0 -65
- data/spec/arborist/manager/tree_api_spec.rb +0 -791
- metadata.gz.sig +0 -0
data/lib/arborist/mixins.rb
CHANGED
@@ -126,6 +126,15 @@ module Arborist
|
|
126
126
|
|
127
127
|
# Refinements to Numeric and Time to add convenience methods
|
128
128
|
module TimeRefinements
|
129
|
+
|
130
|
+
# Approximate Time Constants (in seconds)
|
131
|
+
MINUTES = 60
|
132
|
+
HOURS = 60 * MINUTES
|
133
|
+
DAYS = 24 * HOURS
|
134
|
+
WEEKS = 7 * DAYS
|
135
|
+
MONTHS = 30 * DAYS
|
136
|
+
YEARS = 365.25 * DAYS
|
137
|
+
|
129
138
|
refine Numeric do
|
130
139
|
|
131
140
|
### Number of seconds (returns receiver unmodified)
|
@@ -208,15 +217,6 @@ module Arborist
|
|
208
217
|
|
209
218
|
refine Time do
|
210
219
|
|
211
|
-
# Approximate Time Constants (in seconds)
|
212
|
-
MINUTES = 60
|
213
|
-
HOURS = 60 * MINUTES
|
214
|
-
DAYS = 24 * HOURS
|
215
|
-
WEEKS = 7 * DAYS
|
216
|
-
MONTHS = 30 * DAYS
|
217
|
-
YEARS = 365.25 * DAYS
|
218
|
-
|
219
|
-
|
220
220
|
### Returns +true+ if the receiver is a Time in the future.
|
221
221
|
def future?
|
222
222
|
return self > Time.now
|
@@ -1,135 +1,99 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
#encoding: utf-8
|
3
3
|
|
4
|
-
require '
|
4
|
+
require 'cztop'
|
5
|
+
require 'cztop/reactor'
|
6
|
+
require 'cztop/reactor/signal_handling'
|
5
7
|
require 'loggability'
|
6
8
|
|
7
9
|
require 'arborist' unless defined?( Arborist )
|
8
10
|
require 'arborist/client'
|
9
11
|
|
10
12
|
|
11
|
-
# Undo the useless scoping
|
12
|
-
class ZMQ::Loop
|
13
|
-
public_class_method :instance
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
13
|
# An event-driven runner for Arborist::Monitors.
|
18
14
|
class Arborist::MonitorRunner
|
19
15
|
extend Loggability
|
16
|
+
include CZTop::Reactor::SignalHandling
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
extend Loggability,
|
27
|
-
Arborist::MethodUtilities
|
28
|
-
|
29
|
-
log_to :arborist
|
30
|
-
|
31
|
-
### Create a ZMQ::Handler that acts as the agent that runs the specified
|
32
|
-
### +monitor+.
|
33
|
-
def initialize( reactor )
|
34
|
-
@reactor = reactor
|
35
|
-
@client = Arborist::Client.new
|
36
|
-
@pollitem = ZMQ::Pollitem.new( @client.tree_api, ZMQ::POLLOUT )
|
37
|
-
@pollitem.handler = self
|
38
|
-
|
39
|
-
@request_queue = {}
|
40
|
-
@registered = false
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
######
|
45
|
-
public
|
46
|
-
######
|
18
|
+
# Signals the runner handles
|
19
|
+
QUEUE_SIGS = [
|
20
|
+
:INT, :TERM, :HUP, :USR1,
|
21
|
+
# :TODO: :QUIT, :WINCH, :USR2, :TTIN, :TTOU
|
22
|
+
] & Signal.list.keys.map( &:to_sym )
|
47
23
|
|
48
|
-
# The ZMQ::Loop that this runner is registered with
|
49
|
-
attr_reader :reactor
|
50
24
|
|
51
|
-
|
52
|
-
# results.
|
53
|
-
attr_reader :request_queue
|
25
|
+
log_to :arborist
|
54
26
|
|
55
|
-
# The Arborist::Client that will provide the message packing and unpacking
|
56
|
-
attr_reader :client
|
57
27
|
|
58
|
-
|
59
|
-
|
60
|
-
|
28
|
+
### Create a new Arborist::MonitorRunner
|
29
|
+
def initialize
|
30
|
+
@monitors = []
|
31
|
+
@handler = nil
|
32
|
+
@reactor = CZTop::Reactor.new
|
33
|
+
@client = Arborist::Client.new
|
34
|
+
@request_queue = {}
|
35
|
+
end
|
61
36
|
|
62
37
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
negative = monitor.negative_criteria
|
67
|
-
include_down = monitor.include_down?
|
68
|
-
props = monitor.node_properties
|
38
|
+
######
|
39
|
+
public
|
40
|
+
######
|
69
41
|
|
70
|
-
|
71
|
-
|
72
|
-
|
42
|
+
##
|
43
|
+
# The Array of loaded Arborist::Monitors the runner should run.
|
44
|
+
attr_reader :monitors
|
73
45
|
|
74
|
-
|
75
|
-
|
76
|
-
|
46
|
+
##
|
47
|
+
# The ZMQ::Handler subclass that handles all async IO
|
48
|
+
attr_accessor :handler
|
77
49
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
50
|
+
##
|
51
|
+
# The reactor (a ZMQ::Loop) the runner uses to drive everything
|
52
|
+
attr_accessor :reactor
|
84
53
|
|
54
|
+
##
|
55
|
+
# The Queue of pending requests, keyed by the callback that should be called with the
|
56
|
+
# results.
|
57
|
+
attr_reader :request_queue
|
85
58
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
fetch = self.client.make_fetch_request( criteria,
|
90
|
-
include_down: include_down,
|
91
|
-
properties: properties,
|
92
|
-
exclude: negative
|
93
|
-
)
|
94
|
-
self.queue_request( fetch, &block )
|
95
|
-
end
|
59
|
+
##
|
60
|
+
# The Arborist::Client that will provide the message packing and unpacking
|
61
|
+
attr_reader :client
|
96
62
|
|
97
63
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
self.queue_request( update, &block )
|
103
|
-
end
|
64
|
+
### Load monitors from the specified +enumerator+.
|
65
|
+
def load_monitors( enumerator )
|
66
|
+
self.monitors.concat( enumerator.to_a )
|
67
|
+
end
|
104
68
|
|
105
69
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
self.
|
110
|
-
self.
|
70
|
+
### Run the specified +monitors+
|
71
|
+
def run
|
72
|
+
self.with_signal_handler( self.reactor, *QUEUE_SIGS ) do
|
73
|
+
self.reactor.register( self.client.tree_api, :write, &self.method(:handle_io_event) )
|
74
|
+
self.reactor.start_polling
|
111
75
|
end
|
76
|
+
end
|
112
77
|
|
113
78
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
79
|
+
### Restart the runner
|
80
|
+
def restart
|
81
|
+
# :TODO: Kill any running monitor children, cancel monitor timers, and reload
|
82
|
+
# monitors from the monitor enumerator
|
83
|
+
raise NotImplementedError
|
84
|
+
end
|
120
85
|
|
121
86
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
@registered = false
|
128
|
-
end
|
87
|
+
### Stop the runner.
|
88
|
+
def stop
|
89
|
+
self.log.info "Stopping the runner."
|
90
|
+
self.reactor.stop_polling
|
91
|
+
end
|
129
92
|
|
130
93
|
|
131
|
-
|
132
|
-
|
94
|
+
### Reactor callback -- handle the client's socket becoming writable.
|
95
|
+
def handle_io_event( event )
|
96
|
+
if event.writable?
|
133
97
|
if (( pair = self.request_queue.shift ))
|
134
98
|
callback, request = *pair
|
135
99
|
res = self.client.send_tree_api_request( request )
|
@@ -137,49 +101,82 @@ class Arborist::MonitorRunner
|
|
137
101
|
end
|
138
102
|
|
139
103
|
self.unregister if self.request_queue.empty?
|
140
|
-
|
104
|
+
else
|
105
|
+
raise "Unexpected %p on the tree API socket" % [ event ]
|
141
106
|
end
|
142
107
|
|
143
|
-
end
|
108
|
+
end
|
144
109
|
|
145
110
|
|
146
|
-
###
|
147
|
-
def
|
148
|
-
|
149
|
-
|
150
|
-
|
111
|
+
### Run the specified +monitor+ and update nodes with the results.
|
112
|
+
def run_monitor( monitor )
|
113
|
+
positive = monitor.positive_criteria
|
114
|
+
negative = monitor.negative_criteria
|
115
|
+
include_down = monitor.include_down?
|
116
|
+
props = monitor.node_properties
|
117
|
+
|
118
|
+
self.fetch( positive, include_down, props, negative ) do |nodes|
|
119
|
+
results = monitor.run( nodes )
|
120
|
+
monitor_key = monitor.key
|
121
|
+
|
122
|
+
results.each do |ident, properties|
|
123
|
+
properties['_monitor_key'] = monitor_key
|
124
|
+
end
|
125
|
+
|
126
|
+
self.update( results ) do
|
127
|
+
self.log.debug "Updated %d via the '%s' monitor" %
|
128
|
+
[ results.length, monitor.description ]
|
129
|
+
end
|
130
|
+
end
|
151
131
|
end
|
152
132
|
|
153
133
|
|
154
|
-
|
155
|
-
|
156
|
-
|
134
|
+
### Create a fetch request using the runner's client, then queue the request up
|
135
|
+
### with the specified +block+ as the callback.
|
136
|
+
def fetch( criteria, include_down, properties, negative={}, &block )
|
137
|
+
fetch = self.client.make_fetch_request( criteria,
|
138
|
+
include_down: include_down,
|
139
|
+
properties: properties,
|
140
|
+
exclude: negative
|
141
|
+
)
|
142
|
+
self.queue_request( fetch, &block )
|
143
|
+
end
|
157
144
|
|
158
|
-
# The Array of loaded Arborist::Monitors the runner should run.
|
159
|
-
attr_reader :monitors
|
160
145
|
|
161
|
-
|
162
|
-
|
146
|
+
### Create an update request using the runner's client, then queue the request up
|
147
|
+
### with the specified +block+ as the callback.
|
148
|
+
def update( nodemap, &block )
|
149
|
+
update = self.client.make_update_request( nodemap )
|
150
|
+
self.queue_request( update, &block )
|
151
|
+
end
|
163
152
|
|
164
|
-
|
165
|
-
|
153
|
+
|
154
|
+
### Add the specified +event+ to the queue to be published to the console event
|
155
|
+
### socket
|
156
|
+
def queue_request( request, &callback )
|
157
|
+
self.request_queue[ callback ] = request
|
158
|
+
self.register
|
159
|
+
end
|
166
160
|
|
167
161
|
|
168
|
-
###
|
169
|
-
def
|
170
|
-
|
162
|
+
### Returns +true+ if the runner's client socket is currently registered for writing.
|
163
|
+
def registered?
|
164
|
+
return self.reactor.event_enabled?( self.client.tree_api, :write )
|
171
165
|
end
|
172
166
|
|
173
167
|
|
174
|
-
###
|
175
|
-
def
|
176
|
-
self.
|
168
|
+
### Register the handler's pollitem as being ready to write if it isn't already.
|
169
|
+
def register
|
170
|
+
# self.log.debug "Registering for writing."
|
171
|
+
self.reactor.enable_events( self.client.tree_api, :write ) unless self.registered?
|
172
|
+
end
|
177
173
|
|
178
|
-
self.monitors.each do |mon|
|
179
|
-
self.add_timer_for( mon )
|
180
|
-
end
|
181
174
|
|
182
|
-
|
175
|
+
### Unregister the handler's pollitem from the reactor when there's nothing ready
|
176
|
+
### to write.
|
177
|
+
def unregister
|
178
|
+
# self.log.debug "Unregistering for writing."
|
179
|
+
self.reactor.disable_events( self.client.tree_api, :write ) if self.registered?
|
183
180
|
end
|
184
181
|
|
185
182
|
|
@@ -187,38 +184,78 @@ class Arborist::MonitorRunner
|
|
187
184
|
def add_timer_for( monitor )
|
188
185
|
interval = monitor.interval
|
189
186
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
self.reactor.register_timer( timer )
|
187
|
+
if monitor.splay.nonzero?
|
188
|
+
self.add_splay_timer_for( monitor )
|
189
|
+
else
|
190
|
+
self.add_interval_timer_for( monitor )
|
191
|
+
end
|
197
192
|
end
|
198
193
|
|
199
194
|
|
200
195
|
### Create a repeating ZMQ::Timer that will run the specified monitor on its interval.
|
201
|
-
def
|
196
|
+
def add_interval_timer_for( monitor )
|
202
197
|
interval = monitor.interval
|
203
198
|
self.log.info "Creating timer for %p" % [ monitor ]
|
204
199
|
|
205
|
-
return
|
206
|
-
self.
|
200
|
+
return self.reactor.add_periodic_timer( interval ) do
|
201
|
+
self.run_monitor( monitor )
|
207
202
|
end
|
208
203
|
end
|
209
204
|
|
210
205
|
|
211
206
|
### Create a one-shot ZMQ::Timer that will register the interval timer for the specified
|
212
207
|
### +monitor+ after a random number of seconds no greater than its splay.
|
213
|
-
def
|
208
|
+
def add_splay_timer_for( monitor )
|
214
209
|
delay = rand( monitor.splay )
|
215
210
|
self.log.debug "Splaying registration of %p for %ds" % [ monitor, delay ]
|
216
211
|
|
217
|
-
|
218
|
-
|
219
|
-
self.reactor.register_timer( interval_timer )
|
212
|
+
self.reactor.add_oneshot_timer( delay ) do
|
213
|
+
self.add_interval_timer_for( monitor )
|
220
214
|
end
|
221
215
|
end
|
222
216
|
|
217
|
+
|
218
|
+
#
|
219
|
+
# :section: Signal Handling
|
220
|
+
# These methods set up some behavior for starting, restarting, and stopping
|
221
|
+
# the manager when a signal is received.
|
222
|
+
#
|
223
|
+
|
224
|
+
### Handle signals.
|
225
|
+
def handle_signal( sig )
|
226
|
+
self.log.debug "Handling signal %s" % [ sig ]
|
227
|
+
case sig
|
228
|
+
when :INT, :TERM
|
229
|
+
self.on_termination_signal( sig )
|
230
|
+
|
231
|
+
when :HUP
|
232
|
+
self.on_hangup_signal( sig )
|
233
|
+
|
234
|
+
when :USR1
|
235
|
+
self.on_user1_signal( sig )
|
236
|
+
|
237
|
+
else
|
238
|
+
self.log.warn "Unhandled signal %s" % [ sig ]
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
### Handle a TERM signal. Shuts the handler down after handling any current request/s. Also
|
245
|
+
### aliased to #on_interrupt_signal.
|
246
|
+
def on_termination_signal( signo )
|
247
|
+
self.log.warn "Terminated (%p)" % [ signo ]
|
248
|
+
self.stop
|
249
|
+
end
|
250
|
+
alias_method :on_interrupt_signal, :on_termination_signal
|
251
|
+
|
252
|
+
|
253
|
+
### Handle a hangup by restarting the runner.
|
254
|
+
def on_hangup_signal( signo )
|
255
|
+
self.log.warn "Hangup (%p)" % [ signo ]
|
256
|
+
self.restart
|
257
|
+
end
|
258
|
+
|
259
|
+
|
223
260
|
end # class Arborist::MonitorRunner
|
224
261
|
|
data/lib/arborist/node.rb
CHANGED
@@ -180,8 +180,7 @@ class Arborist::Node
|
|
180
180
|
### them.
|
181
181
|
def self::add_loaded_instance( new_instance )
|
182
182
|
instances = Thread.current[ LOADED_INSTANCE_KEY ] or return
|
183
|
-
self.log.debug "Adding new instance %p to
|
184
|
-
[ new_instance, instances ]
|
183
|
+
self.log.debug "Adding new instance %p to node tree" % [ new_instance ]
|
185
184
|
instances << new_instance
|
186
185
|
end
|
187
186
|
|
@@ -921,8 +920,8 @@ class Arborist::Node
|
|
921
920
|
|
922
921
|
|
923
922
|
### Return a Hash of the node's state.
|
924
|
-
def to_h
|
925
|
-
|
923
|
+
def to_h( deep: false )
|
924
|
+
hash = {
|
926
925
|
identifier: self.identifier,
|
927
926
|
type: self.class.name.to_s.sub( /.+::/, '' ).downcase,
|
928
927
|
parent: self.parent,
|
@@ -938,6 +937,14 @@ class Arborist::Node
|
|
938
937
|
dependencies: self.dependencies.to_h,
|
939
938
|
quieted_reasons: self.quieted_reasons,
|
940
939
|
}
|
940
|
+
|
941
|
+
if deep
|
942
|
+
hash[ :children ] = self.children.each_with_object( {} ) do |(ident, node), h|
|
943
|
+
h[ ident ] = node.to_h( deep )
|
944
|
+
end
|
945
|
+
end
|
946
|
+
|
947
|
+
return hash
|
941
948
|
end
|
942
949
|
|
943
950
|
|