arborist 0.0.1.pre20160106113421 → 0.0.1.pre20160128152542
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 +4 -4
- data/Manifest.txt +9 -3
- data/README.md +15 -6
- data/Rakefile +16 -2
- data/TODO.md +10 -1
- data/Tutorial.md +8 -0
- data/bin/arborist +8 -0
- data/lib/arborist.rb +12 -11
- data/lib/arborist/cli.rb +380 -0
- data/lib/arborist/client.rb +16 -0
- data/lib/arborist/command/client.rb +30 -0
- data/lib/arborist/command/config.rb +20 -0
- data/lib/arborist/command/start.rb +60 -0
- data/lib/arborist/command/watch.rb +88 -0
- data/lib/arborist/event/node_matching.rb +5 -0
- data/lib/arborist/loader.rb +43 -0
- data/lib/arborist/loader/file.rb +72 -0
- data/lib/arborist/manager.rb +4 -3
- data/lib/arborist/monitor.rb +8 -22
- data/lib/arborist/node.rb +57 -37
- data/lib/arborist/observer.rb +4 -18
- data/spec/arborist/client_spec.rb +1 -1
- data/spec/arborist/manager/tree_api_spec.rb +1 -0
- data/spec/arborist/manager_spec.rb +15 -4
- data/spec/arborist/monitor_spec.rb +13 -9
- data/spec/arborist/node_spec.rb +36 -6
- data/spec/arborist_spec.rb +36 -5
- data/spec/data/monitors/pings.rb +1 -0
- data/spec/data/monitors/port_checks.rb +0 -3
- data/spec/spec_helper.rb +4 -1
- metadata +18 -11
- data/bin/amanagerd +0 -10
- data/bin/amonitord +0 -12
- data/bin/aobserverd +0 -12
data/lib/arborist/node.rb
CHANGED
@@ -29,10 +29,6 @@ class Arborist::Node
|
|
29
29
|
# loaded.
|
30
30
|
LOADED_INSTANCE_KEY = :loaded_node_instances
|
31
31
|
|
32
|
-
##
|
33
|
-
# The glob pattern to use for searching for node
|
34
|
-
NODE_FILE_PATTERN = '**/*.rb'
|
35
|
-
|
36
32
|
|
37
33
|
##
|
38
34
|
# The struct for the 'ack' operational property
|
@@ -57,18 +53,25 @@ class Arborist::Node
|
|
57
53
|
state :unknown,
|
58
54
|
:up,
|
59
55
|
:down,
|
60
|
-
:acked
|
56
|
+
:acked,
|
57
|
+
:disabled
|
61
58
|
|
62
59
|
event :update do
|
63
|
-
transition
|
64
|
-
transition
|
65
|
-
transition
|
60
|
+
transition [:down, :unknown, :acked] => :up, if: :last_contact_successful?
|
61
|
+
transition [:up, :unknown] => :down, unless: :last_contact_successful?
|
62
|
+
transition :down => :acked, if: :ack_set?
|
63
|
+
transition [:unknown, :up] => :disabled, if: :ack_set?
|
64
|
+
transition :disabled => :unknown, unless: :ack_set?
|
66
65
|
end
|
67
66
|
|
68
67
|
after_transition any => :acked, do: :on_ack
|
69
68
|
after_transition :acked => :up, do: :on_ack_cleared
|
70
69
|
after_transition :down => :up, do: :on_node_up
|
71
70
|
after_transition [:unknown, :up] => :down, do: :on_node_down
|
71
|
+
after_transition [:unknown, :up] => :disabled, do: :on_node_disabled
|
72
|
+
after_transition :disabled => :unknown, do: :on_node_enabled
|
73
|
+
|
74
|
+
after_transition any => any, do: :log_transition
|
72
75
|
|
73
76
|
after_transition do: :add_status_to_update_delta
|
74
77
|
end
|
@@ -100,6 +103,8 @@ class Arborist::Node
|
|
100
103
|
### them.
|
101
104
|
def self::add_loaded_instance( new_instance )
|
102
105
|
instances = Thread.current[ LOADED_INSTANCE_KEY ] or return
|
106
|
+
self.log.debug "Adding new instance %p to loaded instances %p" %
|
107
|
+
[ new_instance, instances ]
|
103
108
|
instances << new_instance
|
104
109
|
end
|
105
110
|
|
@@ -137,24 +142,9 @@ class Arborist::Node
|
|
137
142
|
end
|
138
143
|
|
139
144
|
|
140
|
-
### Return an iterator for all the
|
141
|
-
def self::each_in(
|
142
|
-
|
143
|
-
paths = if path.directory?
|
144
|
-
Pathname.glob( directory + NODE_FILE_PATTERN )
|
145
|
-
else
|
146
|
-
[ path ]
|
147
|
-
end
|
148
|
-
|
149
|
-
return paths.flat_map do |file|
|
150
|
-
file_url = "file://%s" % [ file.expand_path ]
|
151
|
-
nodes = self.load( file )
|
152
|
-
self.log.debug "Loaded nodes %p..." % [ nodes ]
|
153
|
-
nodes.each do |node|
|
154
|
-
node.source = file_url
|
155
|
-
end
|
156
|
-
nodes
|
157
|
-
end
|
145
|
+
### Return an iterator for all the nodes supplied by the specified +loader+.
|
146
|
+
def self::each_in( loader )
|
147
|
+
return loader.nodes
|
158
148
|
end
|
159
149
|
|
160
150
|
|
@@ -323,8 +313,11 @@ class Arborist::Node
|
|
323
313
|
self.log.debug "Updated: %p" % [ new_properties ]
|
324
314
|
|
325
315
|
self.last_contacted = Time.now
|
326
|
-
|
327
|
-
|
316
|
+
if new_properties.key?( 'ack' )
|
317
|
+
self.ack = new_properties.delete( 'ack' )
|
318
|
+
else
|
319
|
+
self.error = new_properties.delete( 'error' )
|
320
|
+
end
|
328
321
|
|
329
322
|
self.properties.merge!( new_properties, &self.method(:merge_and_record_delta) )
|
330
323
|
compact_hash( self.properties )
|
@@ -504,6 +497,8 @@ class Arborist::Node
|
|
504
497
|
return "%s as of %s" % [ self.status.upcase, self.last_contacted ]
|
505
498
|
when 'acked'
|
506
499
|
return "ACKed by %s %s" % [ self.ack.sender, self.ack.time.as_delta ]
|
500
|
+
when 'disabled'
|
501
|
+
return "disabled by %s %s" % [ self.ack.sender, self.ack.time.as_delta ]
|
507
502
|
else
|
508
503
|
return "in an unknown state"
|
509
504
|
end
|
@@ -607,17 +602,21 @@ class Arborist::Node
|
|
607
602
|
|
608
603
|
### Ack the node with the specified +ack_data+, which should contain
|
609
604
|
def ack=( ack_data )
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
605
|
+
if ack_data
|
606
|
+
self.log.info "Node %s ACKed with data: %p" % [ self.identifier, ack_data ]
|
607
|
+
ack_data['time'] ||= Time.now
|
608
|
+
ack_values = ack_data.values_at( *Arborist::Node::ACK.members.map(&:to_s) )
|
609
|
+
new_ack = Arborist::Node::ACK.new( *ack_values )
|
610
|
+
|
611
|
+
if missing = ACK_REQUIRED_PROPERTIES.find {|prop| new_ack[prop].nil? }
|
612
|
+
raise "Missing required ACK attribute %s" % [ missing ]
|
613
|
+
end
|
615
614
|
|
616
|
-
|
617
|
-
|
615
|
+
@ack = new_ack
|
616
|
+
else
|
617
|
+
self.log.info "Node %s ACK cleared explicitly" % [ self.identifier ]
|
618
|
+
@ack = nil
|
618
619
|
end
|
619
|
-
|
620
|
-
@ack = new_ack
|
621
620
|
end
|
622
621
|
|
623
622
|
|
@@ -642,6 +641,13 @@ class Arborist::Node
|
|
642
641
|
# :section: State Callbacks
|
643
642
|
#
|
644
643
|
|
644
|
+
### Log every status transition
|
645
|
+
def log_transition( transition )
|
646
|
+
self.log.debug "Transitioned %s from %s to %s" %
|
647
|
+
[ self.identifier, transition.from, transition.to ]
|
648
|
+
end
|
649
|
+
|
650
|
+
|
645
651
|
### Callback for when an acknowledgement is set.
|
646
652
|
def on_ack( transition )
|
647
653
|
self.log.warn "ACKed: %s" % [ self.status_description ]
|
@@ -652,12 +658,14 @@ class Arborist::Node
|
|
652
658
|
|
653
659
|
### Callback for when an acknowledgement is cleared.
|
654
660
|
def on_ack_cleared( transition )
|
661
|
+
self.error = nil
|
655
662
|
self.log.warn "ACK cleared for %s" % [ self.identifier ]
|
656
663
|
end
|
657
664
|
|
658
665
|
|
659
666
|
### Callback for when a node goes from down to up
|
660
667
|
def on_node_up( transition )
|
668
|
+
self.error = nil
|
661
669
|
self.log.warn "%s is %s" % [ self.identifier, self.status_description ]
|
662
670
|
end
|
663
671
|
|
@@ -669,6 +677,18 @@ class Arborist::Node
|
|
669
677
|
end
|
670
678
|
|
671
679
|
|
680
|
+
### Callback for when a node goes from up to disabled
|
681
|
+
def on_node_disabled( transition )
|
682
|
+
self.log.warn "%s is %s" % [ self.identifier, self.status_description ]
|
683
|
+
end
|
684
|
+
|
685
|
+
|
686
|
+
### Callback for when a node goes from disabled to unknown
|
687
|
+
def on_node_enabled( transition )
|
688
|
+
self.log.warn "%s is %s" % [ self.identifier, self.status_description ]
|
689
|
+
end
|
690
|
+
|
691
|
+
|
672
692
|
### Add the transition from one state to another to the data used to build
|
673
693
|
### deltas for the #update event.
|
674
694
|
def add_status_to_update_delta( transition )
|
data/lib/arborist/observer.rb
CHANGED
@@ -61,27 +61,13 @@ class Arborist::Observer
|
|
61
61
|
end
|
62
62
|
|
63
63
|
|
64
|
-
### Return an iterator for all the
|
65
|
-
def self::each_in(
|
66
|
-
|
67
|
-
paths = if path.directory?
|
68
|
-
Pathname.glob( directory + OBSERVER_FILE_PATTERN )
|
69
|
-
else
|
70
|
-
[ path ]
|
71
|
-
end
|
72
|
-
|
73
|
-
return paths.flat_map do |file|
|
74
|
-
file_url = "file://%s" % [ file.expand_path ]
|
75
|
-
observers = self.load( file )
|
76
|
-
self.log.debug "Loaded observers %p..." % [ observers ]
|
77
|
-
observers.each do |observer|
|
78
|
-
observer.source = file_url
|
79
|
-
end
|
80
|
-
observers
|
81
|
-
end
|
64
|
+
### Return an iterator for all the observers supplied by the specified +loader+.
|
65
|
+
def self::each_in( loader )
|
66
|
+
return loader.observers
|
82
67
|
end
|
83
68
|
|
84
69
|
|
70
|
+
|
85
71
|
### Create a new Observer with the specified +description+.
|
86
72
|
def initialize( description, &block )
|
87
73
|
@description = description
|
@@ -109,7 +109,7 @@ describe Arborist::Client do
|
|
109
109
|
end
|
110
110
|
|
111
111
|
|
112
|
-
it "can update the properties of managed nodes" do
|
112
|
+
it "can update the properties of managed nodes", :no_ci do
|
113
113
|
client.update( duir: { ping: {rtt: 24} } )
|
114
114
|
|
115
115
|
expect( manager.nodes['duir'].properties ).to include( 'ping' )
|
@@ -424,6 +424,7 @@ describe Arborist::Manager::TreeAPI, :testing_manager do
|
|
424
424
|
manager.create_subscription( nil, 'node.delta', {type: 'host'} )
|
425
425
|
end
|
426
426
|
|
427
|
+
|
427
428
|
it "removes the subscription with the specified ID" do
|
428
429
|
msg = pack_message( :unsubscribe, {subscription_id: subscription.id}, nil )
|
429
430
|
|
@@ -239,21 +239,32 @@ describe Arborist::Manager do
|
|
239
239
|
expect( manager.nodes[ 'host_a' ] ).to be_down
|
240
240
|
manager.nodes[ 'host_c' ].update( error: "gamma rays" )
|
241
241
|
expect( manager.nodes[ 'host_c' ] ).to be_down
|
242
|
+
manager.nodes[ 'host_b_nfs' ].
|
243
|
+
update( ack: {sender: 'nancy_kerrigan', message: 'bad case of disk rot'} )
|
244
|
+
expect( manager.nodes[ 'host_b_nfs' ] ).to be_disabled
|
245
|
+
expect( manager.nodes[ 'host_b_nfs' ] ).to_not be_down
|
242
246
|
|
243
247
|
iter = manager.reachable_nodes
|
244
248
|
|
245
249
|
expect( iter ).to be_a( Enumerator )
|
246
250
|
|
247
|
-
nodes = iter.
|
248
|
-
expect( nodes
|
249
|
-
expect( nodes.map(&:identifier) ).to include(
|
251
|
+
nodes = iter.map( &:identifier )
|
252
|
+
expect( nodes ).to include(
|
250
253
|
"_",
|
251
254
|
"router",
|
252
255
|
"host_b",
|
253
256
|
"host_b_www",
|
254
|
-
"host_b_nfs",
|
255
257
|
"host_b_ssh"
|
256
258
|
)
|
259
|
+
expect( nodes ).to_not include(
|
260
|
+
"host_b_nfs",
|
261
|
+
"host_c",
|
262
|
+
"host_c_www",
|
263
|
+
"host_a",
|
264
|
+
'host_a_www',
|
265
|
+
'host_a_smtp',
|
266
|
+
'host_a_imap'
|
267
|
+
)
|
257
268
|
end
|
258
269
|
|
259
270
|
|
@@ -25,7 +25,11 @@ describe Arborist::Monitor do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
|
28
|
-
let( :testing_nodes ) {
|
28
|
+
let( :testing_nodes ) {{
|
29
|
+
'trunk' => trunk_node.to_hash,
|
30
|
+
'branch' => branch_node.to_hash,
|
31
|
+
'leaf' => leaf_node.to_hash
|
32
|
+
}}
|
29
33
|
|
30
34
|
|
31
35
|
it "can be created with just a description" do
|
@@ -112,7 +116,7 @@ describe Arborist::Monitor do
|
|
112
116
|
|
113
117
|
output = mon.run( testing_nodes )
|
114
118
|
expect( output ).to be_a( Hash )
|
115
|
-
expect( output ).to include( *(testing_nodes.
|
119
|
+
expect( output ).to include( *(testing_nodes.keys) )
|
116
120
|
end
|
117
121
|
|
118
122
|
|
@@ -159,7 +163,7 @@ describe Arborist::Monitor do
|
|
159
163
|
exec_input {|*| }
|
160
164
|
exec_arguments do |nodes|
|
161
165
|
Loggability[ Arborist ].debug "In the argument-builder."
|
162
|
-
nodes.
|
166
|
+
nodes.keys
|
163
167
|
end
|
164
168
|
end
|
165
169
|
|
@@ -183,7 +187,7 @@ describe Arborist::Monitor do
|
|
183
187
|
exec 'cat'
|
184
188
|
|
185
189
|
exec_input do |nodes, writer|
|
186
|
-
writer.puts( nodes.
|
190
|
+
writer.puts( nodes.keys )
|
187
191
|
end
|
188
192
|
handle_results do |pid, out, err|
|
189
193
|
return out.readlines.map( &:chomp )
|
@@ -192,7 +196,7 @@ describe Arborist::Monitor do
|
|
192
196
|
|
193
197
|
results = mon.run( testing_nodes )
|
194
198
|
|
195
|
-
expect( results ).to eq( testing_nodes.
|
199
|
+
expect( results ).to eq( testing_nodes.keys )
|
196
200
|
end
|
197
201
|
|
198
202
|
|
@@ -203,7 +207,7 @@ describe Arborist::Monitor do
|
|
203
207
|
|
204
208
|
exec_arguments {|*| }
|
205
209
|
exec_input do |nodes, writer|
|
206
|
-
writer.puts( nodes.
|
210
|
+
writer.puts( nodes.keys )
|
207
211
|
end
|
208
212
|
handle_results do |pid, out, err|
|
209
213
|
out.readlines.map( &:chomp ).map( &:upcase )
|
@@ -212,7 +216,7 @@ describe Arborist::Monitor do
|
|
212
216
|
|
213
217
|
results = mon.run( testing_nodes )
|
214
218
|
|
215
|
-
expect( results ).to eq( testing_nodes.
|
219
|
+
expect( results ).to eq( testing_nodes.keys.map(&:upcase) )
|
216
220
|
end
|
217
221
|
|
218
222
|
|
@@ -220,7 +224,7 @@ describe Arborist::Monitor do
|
|
220
224
|
the_module = Module.new do
|
221
225
|
|
222
226
|
def exec_input( nodes, writer )
|
223
|
-
writer.puts( nodes.
|
227
|
+
writer.puts( nodes.keys )
|
224
228
|
end
|
225
229
|
|
226
230
|
def handle_results( pid, out, err )
|
@@ -241,7 +245,7 @@ describe Arborist::Monitor do
|
|
241
245
|
|
242
246
|
expect( results ).to be_a( Hash )
|
243
247
|
expect( results.size ).to eq( 3 )
|
244
|
-
expect( results ).to include( *testing_nodes.
|
248
|
+
expect( results ).to include( *testing_nodes.keys )
|
245
249
|
expect( results['trunk'] ).to eq({ echoed: 'yep' })
|
246
250
|
expect( results['branch'] ).to eq({ echoed: 'yep' })
|
247
251
|
expect( results['leaf'] ).to eq({ echoed: 'yep' })
|
data/spec/arborist/node_spec.rb
CHANGED
@@ -145,11 +145,39 @@ describe Arborist::Node do
|
|
145
145
|
expect( node ).to be_acked
|
146
146
|
end
|
147
147
|
|
148
|
-
it "transitions to `
|
149
|
-
node.
|
150
|
-
node.update(
|
148
|
+
it "transitions to `disabled` from `up` status if it's updated with an `ack` property" do
|
149
|
+
node.status = 'up'
|
150
|
+
node.update( ack: {message: "Maintenance", sender: 'mahlon'} )
|
151
151
|
|
152
|
-
expect( node ).to
|
152
|
+
expect( node ).to be_disabled
|
153
|
+
end
|
154
|
+
|
155
|
+
it "stays `disabled` if it gets an error" do
|
156
|
+
node.status = 'up'
|
157
|
+
node.update( ack: {message: "Maintenance", sender: 'mahlon'} )
|
158
|
+
node.update( error: "take me to the virus hospital" )
|
159
|
+
|
160
|
+
expect( node ).to be_disabled
|
161
|
+
expect( node.ack ).to_not be_nil
|
162
|
+
end
|
163
|
+
|
164
|
+
it "stays `disabled` if it gets a successful update" do
|
165
|
+
node.status = 'up'
|
166
|
+
node.update( ack: {message: "Maintenance", sender: 'mahlon'} )
|
167
|
+
node.update( ping: {time: 0.02} )
|
168
|
+
|
169
|
+
expect( node ).to be_disabled
|
170
|
+
expect( node.ack ).to_not be_nil
|
171
|
+
end
|
172
|
+
|
173
|
+
it "transitions to `unknown` from `disabled` status if its ack is cleared" do
|
174
|
+
node.status = 'up'
|
175
|
+
node.update( ack: {message: "Maintenance", sender: 'mahlon'} )
|
176
|
+
node.update( ack: nil )
|
177
|
+
|
178
|
+
expect( node ).to_not be_disabled
|
179
|
+
expect( node ).to be_unknown
|
180
|
+
expect( node.ack ).to be_nil
|
153
181
|
end
|
154
182
|
|
155
183
|
end
|
@@ -307,8 +335,9 @@ describe Arborist::Node do
|
|
307
335
|
|
308
336
|
|
309
337
|
it "an ACKed node stays ACKed when reconstituted" do
|
310
|
-
node.update(
|
311
|
-
|
338
|
+
node.update( error: "there's a fire" )
|
339
|
+
node.update( ack: {
|
340
|
+
message: 'We know about the fire. It rages on.',
|
312
341
|
sender: '1986 Labyrinth David Bowie'
|
313
342
|
})
|
314
343
|
cloned_node = concrete_class.from_hash( node.to_hash )
|
@@ -409,6 +438,7 @@ describe Arborist::Node do
|
|
409
438
|
|
410
439
|
|
411
440
|
it "generates a node.acked event when a node is acked" do
|
441
|
+
node.update( error: 'ping failed ')
|
412
442
|
events = node.update(ack: {
|
413
443
|
message: "I have a poisonous friend. She's living in the house.",
|
414
444
|
sender: 'Seabound'
|
data/spec/arborist_spec.rb
CHANGED
@@ -10,14 +10,16 @@ describe Arborist do
|
|
10
10
|
|
11
11
|
before( :all ) do
|
12
12
|
@original_config_env = ENV[Arborist::CONFIG_ENV]
|
13
|
-
@data_dir = Pathname( __FILE__ ).dirname + 'data'
|
14
|
-
@nodes_dir = @data_dir + 'nodes'
|
15
13
|
end
|
16
14
|
|
17
15
|
before( :each ) do
|
18
16
|
ENV.delete(Arborist::CONFIG_ENV)
|
19
17
|
end
|
20
18
|
|
19
|
+
after( :each ) do
|
20
|
+
Arborist::Node::Root.reset
|
21
|
+
end
|
22
|
+
|
21
23
|
after( :all ) do
|
22
24
|
ENV[Arborist::CONFIG_ENV] = @original_config_env
|
23
25
|
end
|
@@ -28,7 +30,7 @@ describe Arborist do
|
|
28
30
|
end
|
29
31
|
|
30
32
|
|
31
|
-
describe "configurability" do
|
33
|
+
describe "configurability", log: :fatal do
|
32
34
|
|
33
35
|
before( :each ) do
|
34
36
|
Configurability.configure_objects( Configurability.default_config )
|
@@ -133,8 +135,37 @@ describe Arborist do
|
|
133
135
|
end
|
134
136
|
|
135
137
|
|
136
|
-
it "can
|
137
|
-
|
138
|
+
it "can construct a Manager for all nodes provided by a Loader" do
|
139
|
+
loader = instance_double( Arborist::Loader )
|
140
|
+
expect( loader ).to receive( :nodes ).and_return([
|
141
|
+
testing_node('trunk'),
|
142
|
+
testing_node('branch', 'trunk'),
|
143
|
+
testing_node('leaf', 'branch')
|
144
|
+
])
|
145
|
+
|
146
|
+
expect( described_class.manager_for(loader) ).to be_a( Arborist::Manager )
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
it "can construct a MonitorRunner for all monitors provided by a Loader" do
|
151
|
+
loader = instance_double( Arborist::Loader )
|
152
|
+
expect( loader ).to receive( :monitors ).and_return([
|
153
|
+
:a_monitor,
|
154
|
+
:another_monitor
|
155
|
+
])
|
156
|
+
|
157
|
+
expect( described_class.monitor_runner_for(loader) ).to be_a( Arborist::MonitorRunner )
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
it "can construct an ObserverRunner for all observers provided by a Loader" do
|
162
|
+
loader = instance_double( Arborist::Loader )
|
163
|
+
expect( loader ).to receive( :observers ).and_return([
|
164
|
+
:an_observer,
|
165
|
+
:another_observer
|
166
|
+
])
|
167
|
+
|
168
|
+
expect( described_class.observer_runner_for(loader) ).to be_a( Arborist::ObserverRunner )
|
138
169
|
end
|
139
170
|
|
140
171
|
|