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
@@ -1,791 +0,0 @@
|
|
1
|
-
#!/usr/bin/env rspec -cfd
|
2
|
-
|
3
|
-
require_relative '../../spec_helper'
|
4
|
-
|
5
|
-
|
6
|
-
describe Arborist::Manager::TreeAPI, :testing_manager do
|
7
|
-
|
8
|
-
before( :each ) do
|
9
|
-
@manager = nil
|
10
|
-
@manager_thread = Thread.new do
|
11
|
-
@manager = make_testing_manager()
|
12
|
-
Thread.current.abort_on_exception = true
|
13
|
-
@manager.run
|
14
|
-
Loggability[ Arborist ].info "Stopped the test manager"
|
15
|
-
end
|
16
|
-
|
17
|
-
count = 0
|
18
|
-
until (@manager && @manager.running?) || count > 30
|
19
|
-
sleep 0.1
|
20
|
-
count += 1
|
21
|
-
end
|
22
|
-
raise "Manager didn't start up" unless @manager.running?
|
23
|
-
end
|
24
|
-
|
25
|
-
after( :each ) do
|
26
|
-
@manager.simulate_signal( :TERM )
|
27
|
-
unless @manager_thread.join( 5 )
|
28
|
-
$stderr.puts "Manager thread didn't exit on its own; killing it."
|
29
|
-
@manager_thread.kill
|
30
|
-
end
|
31
|
-
|
32
|
-
count = 0
|
33
|
-
while @manager.zmq_loop.running? || count > 30
|
34
|
-
sleep 0.1
|
35
|
-
Loggability[ Arborist ].info "ZMQ loop still running"
|
36
|
-
count += 1
|
37
|
-
end
|
38
|
-
raise "ZMQ Loop didn't stop" if @manager.zmq_loop.running?
|
39
|
-
end
|
40
|
-
|
41
|
-
|
42
|
-
let( :manager ) { @manager }
|
43
|
-
|
44
|
-
let!( :sock ) do
|
45
|
-
sock = Arborist.zmq_context.socket( :REQ )
|
46
|
-
sock.linger = 0
|
47
|
-
sock.connect( TESTING_API_SOCK )
|
48
|
-
sock
|
49
|
-
end
|
50
|
-
|
51
|
-
let( :api_handler ) { described_class.new( rep_sock, manager ) }
|
52
|
-
|
53
|
-
|
54
|
-
describe "malformed requests" do
|
55
|
-
|
56
|
-
it "send an error response if the request can't be deserialized" do
|
57
|
-
sock.send( "whatevs, dude!" )
|
58
|
-
resmsg = sock.recv
|
59
|
-
|
60
|
-
hdr, body = unpack_message( resmsg )
|
61
|
-
expect( hdr ).to include(
|
62
|
-
'success' => false,
|
63
|
-
'reason' => /invalid request/i,
|
64
|
-
'category' => 'client'
|
65
|
-
)
|
66
|
-
expect( body ).to be_nil
|
67
|
-
end
|
68
|
-
|
69
|
-
|
70
|
-
it "send an error response if the request isn't a tuple" do
|
71
|
-
sock.send( MessagePack.pack({ version: 1, action: 'list' }) )
|
72
|
-
resmsg = sock.recv
|
73
|
-
|
74
|
-
hdr, body = unpack_message( resmsg )
|
75
|
-
expect( hdr ).to include(
|
76
|
-
'success' => false,
|
77
|
-
'reason' => /invalid request.*not a tuple/i,
|
78
|
-
'category' => 'client'
|
79
|
-
)
|
80
|
-
expect( body ).to be_nil
|
81
|
-
end
|
82
|
-
|
83
|
-
|
84
|
-
it "send an error response if the request is empty" do
|
85
|
-
sock.send( MessagePack.pack([]) )
|
86
|
-
resmsg = sock.recv
|
87
|
-
|
88
|
-
hdr, body = unpack_message( resmsg )
|
89
|
-
expect( hdr ).to include(
|
90
|
-
'success' => false,
|
91
|
-
'reason' => /invalid request.*incorrect length/i,
|
92
|
-
'category' => 'client'
|
93
|
-
)
|
94
|
-
expect( body ).to be_nil
|
95
|
-
end
|
96
|
-
|
97
|
-
|
98
|
-
it "send an error response if the request is an incorrect length" do
|
99
|
-
sock.send( MessagePack.pack([{}, {}, {}]) )
|
100
|
-
resmsg = sock.recv
|
101
|
-
|
102
|
-
hdr, body = unpack_message( resmsg )
|
103
|
-
expect( hdr ).to include(
|
104
|
-
'success' => false,
|
105
|
-
'reason' => /invalid request.*incorrect length/i,
|
106
|
-
'category' => 'client'
|
107
|
-
)
|
108
|
-
expect( body ).to be_nil
|
109
|
-
end
|
110
|
-
|
111
|
-
|
112
|
-
it "send an error response if the request's header is not a Map" do
|
113
|
-
sock.send( MessagePack.pack([nil, {}]) )
|
114
|
-
resmsg = sock.recv
|
115
|
-
|
116
|
-
hdr, body = unpack_message( resmsg )
|
117
|
-
expect( hdr ).to include(
|
118
|
-
'success' => false,
|
119
|
-
'reason' => /invalid request.*header is not a map/i,
|
120
|
-
'category' => 'client'
|
121
|
-
)
|
122
|
-
expect( body ).to be_nil
|
123
|
-
end
|
124
|
-
|
125
|
-
|
126
|
-
it "send an error response if the request's body is not Nil, a Map, or an Array of Maps" do
|
127
|
-
sock.send( MessagePack.pack([{version: 1, action: 'list'}, 18]) )
|
128
|
-
resmsg = sock.recv
|
129
|
-
|
130
|
-
hdr, body = unpack_message( resmsg )
|
131
|
-
expect( hdr ).to include(
|
132
|
-
'success' => false,
|
133
|
-
'reason' => /invalid request.*body must be nil, a map, or an array of maps/i,
|
134
|
-
'category' => 'client'
|
135
|
-
)
|
136
|
-
expect( body ).to be_nil
|
137
|
-
end
|
138
|
-
|
139
|
-
|
140
|
-
it "send an error response if missing a version" do
|
141
|
-
sock.send( MessagePack.pack([{action: 'list'}]) )
|
142
|
-
resmsg = sock.recv
|
143
|
-
|
144
|
-
hdr, body = unpack_message( resmsg )
|
145
|
-
expect( hdr ).to include(
|
146
|
-
'success' => false,
|
147
|
-
'reason' => /invalid request.*missing required header 'version'/i,
|
148
|
-
'category' => 'client'
|
149
|
-
)
|
150
|
-
expect( body ).to be_nil
|
151
|
-
end
|
152
|
-
|
153
|
-
|
154
|
-
it "send an error response if missing an action" do
|
155
|
-
sock.send( MessagePack.pack([{version: 1}]) )
|
156
|
-
resmsg = sock.recv
|
157
|
-
|
158
|
-
hdr, body = unpack_message( resmsg )
|
159
|
-
expect( hdr ).to include(
|
160
|
-
'success' => false,
|
161
|
-
'reason' => /invalid request.*missing required header 'action'/i,
|
162
|
-
'category' => 'client'
|
163
|
-
)
|
164
|
-
expect( body ).to be_nil
|
165
|
-
end
|
166
|
-
|
167
|
-
|
168
|
-
it "send an error response for unknown actions" do
|
169
|
-
badmsg = pack_message( :slap )
|
170
|
-
sock.send( badmsg )
|
171
|
-
resmsg = sock.recv
|
172
|
-
|
173
|
-
hdr, body = unpack_message( resmsg )
|
174
|
-
expect( hdr ).to include(
|
175
|
-
'success' => false,
|
176
|
-
'reason' => /invalid request.*no such action 'slap'/i,
|
177
|
-
'category' => 'client'
|
178
|
-
)
|
179
|
-
expect( body ).to be_nil
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
|
184
|
-
describe "status" do
|
185
|
-
|
186
|
-
|
187
|
-
it "returns a Map describing the manager and its state" do
|
188
|
-
msg = pack_message( :status )
|
189
|
-
|
190
|
-
sock.send( msg )
|
191
|
-
resmsg = sock.recv
|
192
|
-
|
193
|
-
hdr, body = unpack_message( resmsg )
|
194
|
-
expect( hdr ).to include( 'success' => true )
|
195
|
-
expect( body.length ).to eq( 4 )
|
196
|
-
expect( body ).to include( 'server_version', 'state', 'uptime', 'nodecount' )
|
197
|
-
end
|
198
|
-
|
199
|
-
end
|
200
|
-
|
201
|
-
|
202
|
-
describe "fetch" do
|
203
|
-
|
204
|
-
it "returns an array of full state maps for nodes matching specified criteria" do
|
205
|
-
msg = pack_message( :fetch, type: 'service', port: 22 )
|
206
|
-
|
207
|
-
sock.send( msg )
|
208
|
-
resmsg = sock.recv
|
209
|
-
|
210
|
-
hdr, body = unpack_message( resmsg )
|
211
|
-
expect( hdr ).to include( 'success' => true )
|
212
|
-
|
213
|
-
expect( body ).to be_a( Hash )
|
214
|
-
expect( body.length ).to eq( 3 )
|
215
|
-
|
216
|
-
expect( body.values ).to all( be_a(Hash) )
|
217
|
-
expect( body.values ).to all( include('status', 'type') )
|
218
|
-
end
|
219
|
-
|
220
|
-
|
221
|
-
it "returns an array of full state maps for nodes not matching specified negative criteria" do
|
222
|
-
msg = pack_message( :fetch, [ {}, {type: 'service', port: 22} ] )
|
223
|
-
|
224
|
-
sock.send( msg )
|
225
|
-
resmsg = sock.recv
|
226
|
-
|
227
|
-
hdr, body = unpack_message( resmsg )
|
228
|
-
expect( hdr ).to include( 'success' => true )
|
229
|
-
|
230
|
-
expect( body ).to be_a( Hash )
|
231
|
-
expect( body.length ).to eq( manager.nodes.length - 3 )
|
232
|
-
|
233
|
-
expect( body.values ).to all( be_a(Hash) )
|
234
|
-
expect( body.values ).to all( include('status', 'type') )
|
235
|
-
end
|
236
|
-
|
237
|
-
|
238
|
-
it "returns an array of full state maps for nodes combining positive and negative criteria" do
|
239
|
-
msg = pack_message( :fetch, [ {type: 'service'}, {port: 22} ] )
|
240
|
-
|
241
|
-
sock.send( msg )
|
242
|
-
resmsg = sock.recv
|
243
|
-
|
244
|
-
hdr, body = unpack_message( resmsg )
|
245
|
-
expect( hdr ).to include( 'success' => true )
|
246
|
-
|
247
|
-
expect( body ).to be_a( Hash )
|
248
|
-
expect( body.length ).to eq( 16 )
|
249
|
-
|
250
|
-
expect( body.values ).to all( be_a(Hash) )
|
251
|
-
expect( body.values ).to all( include('status', 'type') )
|
252
|
-
end
|
253
|
-
|
254
|
-
|
255
|
-
it "doesn't return nodes beneath downed nodes by default" do
|
256
|
-
manager.nodes['sidonie'].update( error: 'sunspots' )
|
257
|
-
msg = pack_message( :fetch, type: 'service', port: 22 )
|
258
|
-
|
259
|
-
sock.send( msg )
|
260
|
-
resmsg = sock.recv
|
261
|
-
|
262
|
-
hdr, body = unpack_message( resmsg )
|
263
|
-
expect( hdr ).to include( 'success' => true )
|
264
|
-
expect( body ).to be_a( Hash )
|
265
|
-
expect( body.length ).to eq( 2 )
|
266
|
-
expect( body ).to include( 'duir-ssh', 'yevaud-ssh' )
|
267
|
-
end
|
268
|
-
|
269
|
-
|
270
|
-
it "does return nodes beneath downed nodes if asked to" do
|
271
|
-
manager.nodes['sidonie'].update( error: 'plague of locusts' )
|
272
|
-
msg = pack_message( :fetch, {include_down: true}, type: 'service', port: 22 )
|
273
|
-
|
274
|
-
sock.send( msg )
|
275
|
-
resmsg = sock.recv
|
276
|
-
|
277
|
-
hdr, body = unpack_message( resmsg )
|
278
|
-
expect( hdr ).to include( 'success' => true )
|
279
|
-
expect( body ).to be_a( Hash )
|
280
|
-
expect( body.length ).to eq( 3 )
|
281
|
-
expect( body ).to include( 'duir-ssh', 'yevaud-ssh', 'sidonie-ssh' )
|
282
|
-
end
|
283
|
-
|
284
|
-
|
285
|
-
it "returns only identifiers if the `return` header is set to `nil`" do
|
286
|
-
msg = pack_message( :fetch, {return: nil}, type: 'service', port: 22 )
|
287
|
-
|
288
|
-
sock.send( msg )
|
289
|
-
resmsg = sock.recv
|
290
|
-
|
291
|
-
hdr, body = unpack_message( resmsg )
|
292
|
-
expect( hdr ).to include( 'success' => true )
|
293
|
-
expect( body ).to be_a( Hash )
|
294
|
-
expect( body.length ).to eq( 3 )
|
295
|
-
expect( body ).to include( 'duir-ssh', 'yevaud-ssh', 'sidonie-ssh' )
|
296
|
-
expect( body.values ).to all( be_empty )
|
297
|
-
end
|
298
|
-
|
299
|
-
|
300
|
-
it "returns only specified state if the `return` header is set to an Array of keys" do
|
301
|
-
msg = pack_message( :fetch, {return: %w[status tags addresses]},
|
302
|
-
type: 'service', port: 22 )
|
303
|
-
|
304
|
-
sock.send( msg )
|
305
|
-
resmsg = sock.recv
|
306
|
-
|
307
|
-
hdr, body = unpack_message( resmsg )
|
308
|
-
expect( hdr ).to include( 'success' => true )
|
309
|
-
expect( body.length ).to eq( 3 )
|
310
|
-
expect( body ).to include( 'duir-ssh', 'yevaud-ssh', 'sidonie-ssh' )
|
311
|
-
expect( body.values.map(&:keys) ).to all( contain_exactly('status', 'tags', 'addresses') )
|
312
|
-
end
|
313
|
-
|
314
|
-
|
315
|
-
end
|
316
|
-
|
317
|
-
|
318
|
-
describe "list" do
|
319
|
-
|
320
|
-
it "returns an array of node state" do
|
321
|
-
msg = pack_message( :list )
|
322
|
-
sock.send( msg )
|
323
|
-
resmsg = sock.recv
|
324
|
-
|
325
|
-
hdr, body = unpack_message( resmsg )
|
326
|
-
expect( hdr ).to include( 'success' => true )
|
327
|
-
expect( body.length ).to eq( manager.nodes.length )
|
328
|
-
expect( body ).to all( be_a(Hash) )
|
329
|
-
expect( body ).to include( hash_including('identifier' => '_') )
|
330
|
-
expect( body ).to include( hash_including('identifier' => 'duir') )
|
331
|
-
expect( body ).to include( hash_including('identifier' => 'sidonie') )
|
332
|
-
expect( body ).to include( hash_including('identifier' => 'sidonie-ssh') )
|
333
|
-
expect( body ).to include( hash_including('identifier' => 'sidonie-demon-http') )
|
334
|
-
expect( body ).to include( hash_including('identifier' => 'yevaud') )
|
335
|
-
end
|
336
|
-
|
337
|
-
it "can be limited by depth" do
|
338
|
-
msg = pack_message( :list, {depth: 1}, nil )
|
339
|
-
sock.send( msg )
|
340
|
-
resmsg = sock.recv
|
341
|
-
|
342
|
-
hdr, body = unpack_message( resmsg )
|
343
|
-
expect( hdr ).to include( 'success' => true )
|
344
|
-
expect( body.length ).to eq( 3 )
|
345
|
-
expect( body ).to all( be_a(Hash) )
|
346
|
-
expect( body ).to include( hash_including('identifier' => '_') )
|
347
|
-
expect( body ).to include( hash_including('identifier' => 'duir') )
|
348
|
-
expect( body ).to_not include( hash_including('identifier' => 'duir-ssh') )
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
|
-
|
353
|
-
describe "update" do
|
354
|
-
|
355
|
-
it "merges the properties sent with those of the targeted nodes" do
|
356
|
-
update_data = {
|
357
|
-
duir: {
|
358
|
-
ping: {
|
359
|
-
rtt: 254
|
360
|
-
}
|
361
|
-
},
|
362
|
-
sidonie: {
|
363
|
-
ping: {
|
364
|
-
rtt: 1208
|
365
|
-
}
|
366
|
-
},
|
367
|
-
yevaud: {
|
368
|
-
ping: {
|
369
|
-
rtt: 843
|
370
|
-
}
|
371
|
-
}
|
372
|
-
}
|
373
|
-
msg = pack_message( :update, update_data )
|
374
|
-
sock.send( msg )
|
375
|
-
resmsg = sock.recv
|
376
|
-
|
377
|
-
hdr, body = unpack_message( resmsg )
|
378
|
-
expect( hdr ).to include( 'success' => true )
|
379
|
-
expect( body ).to be_nil
|
380
|
-
|
381
|
-
expect( manager.nodes['duir'].properties['ping'] ).to include( 'rtt' => 254 )
|
382
|
-
expect( manager.nodes['sidonie'].properties['ping'] ).to include( 'rtt' => 1208 )
|
383
|
-
expect( manager.nodes['yevaud'].properties['ping'] ).to include( 'rtt' => 843 )
|
384
|
-
end
|
385
|
-
|
386
|
-
|
387
|
-
it "ignores unknown identifiers" do
|
388
|
-
msg = pack_message( :update, charlie_humperton: {ping: { rtt: 8 }} )
|
389
|
-
sock.send( msg )
|
390
|
-
resmsg = sock.recv
|
391
|
-
|
392
|
-
hdr, body = unpack_message( resmsg )
|
393
|
-
expect( hdr ).to include( 'success' => true )
|
394
|
-
end
|
395
|
-
|
396
|
-
it "fails with a client error if the body is invalid" do
|
397
|
-
msg = pack_message( :update, nil )
|
398
|
-
sock.send( msg )
|
399
|
-
resmsg = sock.recv
|
400
|
-
|
401
|
-
hdr, body = unpack_message( resmsg )
|
402
|
-
expect( hdr ).to include( 'success' => false )
|
403
|
-
expect( hdr['reason'] ).to match( /respond to #each/ )
|
404
|
-
end
|
405
|
-
end
|
406
|
-
|
407
|
-
|
408
|
-
describe "subscribe" do
|
409
|
-
|
410
|
-
it "adds a subscription for all event types to the root node by default" do
|
411
|
-
msg = pack_message( :subscribe, [{}, {}] )
|
412
|
-
|
413
|
-
resmsg = nil
|
414
|
-
expect {
|
415
|
-
sock.send( msg )
|
416
|
-
resmsg = sock.recv
|
417
|
-
}.to change { manager.subscriptions.length }.by( 1 ).and(
|
418
|
-
change { manager.root.subscriptions.length }.by( 1 )
|
419
|
-
)
|
420
|
-
hdr, body = unpack_message( resmsg )
|
421
|
-
|
422
|
-
sub_id = manager.subscriptions.keys.first
|
423
|
-
|
424
|
-
expect( hdr ).to include( 'success' => true )
|
425
|
-
expect( body ).to eq([ sub_id ])
|
426
|
-
end
|
427
|
-
|
428
|
-
|
429
|
-
it "adds a subscription to the specified node if an identifier is specified" do
|
430
|
-
msg = pack_message( :subscribe, {identifier: 'sidonie'}, [{}, {}] )
|
431
|
-
|
432
|
-
resmsg = nil
|
433
|
-
expect {
|
434
|
-
sock.send( msg )
|
435
|
-
resmsg = sock.recv
|
436
|
-
}.to change { manager.subscriptions.length }.by( 1 ).and(
|
437
|
-
change { manager.nodes['sidonie'].subscriptions.length }.by( 1 )
|
438
|
-
)
|
439
|
-
hdr, body = unpack_message( resmsg )
|
440
|
-
|
441
|
-
sub_id = manager.subscriptions.keys.first
|
442
|
-
|
443
|
-
expect( hdr ).to include( 'success' => true )
|
444
|
-
expect( body ).to eq([ sub_id ])
|
445
|
-
end
|
446
|
-
|
447
|
-
|
448
|
-
it "adds a subscription for particular event types if one is specified" do
|
449
|
-
msg = pack_message( :subscribe, {event_type: 'node.acked'}, [{}, {}] )
|
450
|
-
|
451
|
-
resmsg = nil
|
452
|
-
expect {
|
453
|
-
sock.send( msg )
|
454
|
-
resmsg = sock.recv
|
455
|
-
}.to change { manager.subscriptions.length }.by( 1 ).and(
|
456
|
-
change { manager.root.subscriptions.length }.by( 1 )
|
457
|
-
)
|
458
|
-
hdr, body = unpack_message( resmsg )
|
459
|
-
node = manager.subscriptions[ body.first ]
|
460
|
-
sub = node.subscriptions[ body.first ]
|
461
|
-
|
462
|
-
expect( sub.event_type ).to eq( 'node.acked' )
|
463
|
-
end
|
464
|
-
|
465
|
-
|
466
|
-
it "adds a subscription for events which match a pattern if one is specified" do
|
467
|
-
criteria = { type: 'host' }
|
468
|
-
|
469
|
-
msg = pack_message( :subscribe, [criteria, {}] )
|
470
|
-
|
471
|
-
resmsg = nil
|
472
|
-
expect {
|
473
|
-
sock.send( msg )
|
474
|
-
resmsg = sock.recv
|
475
|
-
}.to change { manager.subscriptions.length }.by( 1 ).and(
|
476
|
-
change { manager.root.subscriptions.length }.by( 1 )
|
477
|
-
)
|
478
|
-
hdr, body = unpack_message( resmsg )
|
479
|
-
node = manager.subscriptions[ body.first ]
|
480
|
-
sub = node.subscriptions[ body.first ]
|
481
|
-
|
482
|
-
expect( sub.event_type ).to be_nil
|
483
|
-
expect( sub.criteria ).to eq({ 'type' => 'host' })
|
484
|
-
end
|
485
|
-
|
486
|
-
|
487
|
-
it "adds a subscription for events which don't match a pattern if an exclusion pattern is given" do
|
488
|
-
criteria = { type: 'host' }
|
489
|
-
|
490
|
-
msg = pack_message( :subscribe, [{}, criteria] )
|
491
|
-
|
492
|
-
resmsg = nil
|
493
|
-
expect {
|
494
|
-
sock.send( msg )
|
495
|
-
resmsg = sock.recv
|
496
|
-
}.to change { manager.subscriptions.length }.by( 1 ).and(
|
497
|
-
change { manager.root.subscriptions.length }.by( 1 )
|
498
|
-
)
|
499
|
-
hdr, body = unpack_message( resmsg )
|
500
|
-
node = manager.subscriptions[ body.first ]
|
501
|
-
sub = node.subscriptions[ body.first ]
|
502
|
-
|
503
|
-
expect( sub.event_type ).to be_nil
|
504
|
-
expect( sub.negative_criteria ).to eq({ 'type' => 'host' })
|
505
|
-
end
|
506
|
-
|
507
|
-
end
|
508
|
-
|
509
|
-
|
510
|
-
describe "unsubscribe" do
|
511
|
-
|
512
|
-
let( :subscription ) do
|
513
|
-
manager.create_subscription( nil, 'node.delta', {type: 'host'} )
|
514
|
-
end
|
515
|
-
|
516
|
-
|
517
|
-
it "removes the subscription with the specified ID" do
|
518
|
-
msg = pack_message( :unsubscribe, {subscription_id: subscription.id}, nil )
|
519
|
-
|
520
|
-
resmsg = nil
|
521
|
-
expect {
|
522
|
-
sock.send( msg )
|
523
|
-
resmsg = sock.recv
|
524
|
-
}.to change { manager.subscriptions.length }.by( -1 ).and(
|
525
|
-
change { manager.root.subscriptions.length }.by( -1 )
|
526
|
-
)
|
527
|
-
hdr, body = unpack_message( resmsg )
|
528
|
-
|
529
|
-
expect( body ).to include( 'event_type' => 'node.delta', 'criteria' => {'type' => 'host'} )
|
530
|
-
end
|
531
|
-
|
532
|
-
|
533
|
-
it "ignores unsubscription of a non-existant ID" do
|
534
|
-
msg = pack_message( :unsubscribe, {subscription_id: 'the bears!'}, nil )
|
535
|
-
|
536
|
-
resmsg = nil
|
537
|
-
expect {
|
538
|
-
sock.send( msg )
|
539
|
-
resmsg = sock.recv
|
540
|
-
}.to_not change { manager.subscriptions.length }
|
541
|
-
hdr, body = unpack_message( resmsg )
|
542
|
-
|
543
|
-
expect( body ).to be_nil
|
544
|
-
end
|
545
|
-
|
546
|
-
end
|
547
|
-
|
548
|
-
|
549
|
-
describe "prune" do
|
550
|
-
|
551
|
-
it "removes a single node" do
|
552
|
-
msg = pack_message( :prune, {identifier: 'duir-ssh'}, nil )
|
553
|
-
sock.send( msg )
|
554
|
-
resmsg = sock.recv
|
555
|
-
|
556
|
-
hdr, body = unpack_message( resmsg )
|
557
|
-
expect( hdr ).to include( 'success' => true )
|
558
|
-
expect( body ).to eq( true )
|
559
|
-
expect( manager.nodes ).to_not include( 'duir-ssh' )
|
560
|
-
end
|
561
|
-
|
562
|
-
|
563
|
-
it "returns Nil without error if the node to prune didn't exist" do
|
564
|
-
msg = pack_message( :prune, {identifier: 'shemp-ssh'}, nil )
|
565
|
-
sock.send( msg )
|
566
|
-
resmsg = sock.recv
|
567
|
-
|
568
|
-
hdr, body = unpack_message( resmsg )
|
569
|
-
expect( hdr ).to include( 'success' => true )
|
570
|
-
expect( body ).to be_nil
|
571
|
-
end
|
572
|
-
|
573
|
-
|
574
|
-
it "removes children nodes along with the parent" do
|
575
|
-
msg = pack_message( :prune, {identifier: 'duir'}, nil )
|
576
|
-
sock.send( msg )
|
577
|
-
resmsg = sock.recv
|
578
|
-
|
579
|
-
hdr, body = unpack_message( resmsg )
|
580
|
-
expect( hdr ).to include( 'success' => true )
|
581
|
-
expect( body ).to eq( true )
|
582
|
-
expect( manager.nodes ).to_not include( 'duir' )
|
583
|
-
expect( manager.nodes ).to_not include( 'duir-ssh' )
|
584
|
-
end
|
585
|
-
|
586
|
-
|
587
|
-
it "returns an error to the client when missing required attributes" do
|
588
|
-
msg = pack_message( :prune )
|
589
|
-
sock.send( msg )
|
590
|
-
resmsg = sock.recv
|
591
|
-
|
592
|
-
hdr, body = unpack_message( resmsg )
|
593
|
-
expect( hdr ).to include( 'success' => false )
|
594
|
-
expect( hdr['reason'] ).to match( /no identifier/i )
|
595
|
-
end
|
596
|
-
end
|
597
|
-
|
598
|
-
|
599
|
-
describe "graft" do
|
600
|
-
|
601
|
-
it "can add a node with no explicit parent" do
|
602
|
-
header = {
|
603
|
-
identifier: 'guenter',
|
604
|
-
type: 'host',
|
605
|
-
}
|
606
|
-
attributes = {
|
607
|
-
description: 'The evil penguin node of doom.',
|
608
|
-
addresses: ['10.2.66.8'],
|
609
|
-
tags: ['internal', 'football']
|
610
|
-
}
|
611
|
-
msg = pack_message( :graft, header, attributes )
|
612
|
-
|
613
|
-
sock.send( msg )
|
614
|
-
resmsg = sock.recv
|
615
|
-
|
616
|
-
hdr, body = unpack_message( resmsg )
|
617
|
-
expect( hdr ).to include( 'success' => true )
|
618
|
-
expect( body ).to eq( 'guenter' )
|
619
|
-
|
620
|
-
new_node = manager.nodes[ 'guenter' ]
|
621
|
-
expect( new_node ).to be_a( Arborist::Node::Host )
|
622
|
-
expect( new_node.identifier ).to eq( header[:identifier] )
|
623
|
-
expect( new_node.description ).to eq( attributes[:description] )
|
624
|
-
expect( new_node.addresses ).to eq([ IPAddr.new(attributes[:addresses].first) ])
|
625
|
-
expect( new_node.tags ).to include( *attributes[:tags] )
|
626
|
-
end
|
627
|
-
|
628
|
-
|
629
|
-
it "can add a node with a parent specified" do
|
630
|
-
header = {
|
631
|
-
identifier: 'orgalorg',
|
632
|
-
type: 'host',
|
633
|
-
parent: 'duir'
|
634
|
-
}
|
635
|
-
attributes = {
|
636
|
-
description: 'The true form of the evil penguin node of doom.',
|
637
|
-
addresses: ['192.168.22.8'],
|
638
|
-
tags: ['evil', 'space', 'entity']
|
639
|
-
}
|
640
|
-
msg = pack_message( :graft, header, attributes )
|
641
|
-
|
642
|
-
sock.send( msg )
|
643
|
-
resmsg = sock.recv
|
644
|
-
|
645
|
-
hdr, body = unpack_message( resmsg )
|
646
|
-
expect( hdr ).to include( 'success' => true )
|
647
|
-
expect( body ).to eq( 'orgalorg' )
|
648
|
-
|
649
|
-
new_node = manager.nodes[ 'orgalorg' ]
|
650
|
-
expect( new_node ).to be_a( Arborist::Node::Host )
|
651
|
-
expect( new_node.identifier ).to eq( header[:identifier] )
|
652
|
-
expect( new_node.parent ).to eq( header[:parent] )
|
653
|
-
expect( new_node.description ).to eq( attributes[:description] )
|
654
|
-
expect( new_node.addresses ).to eq([ IPAddr.new(attributes[:addresses].first) ])
|
655
|
-
expect( new_node.tags ).to include( *attributes[:tags] )
|
656
|
-
end
|
657
|
-
|
658
|
-
|
659
|
-
it "can add a subordinate node" do
|
660
|
-
header = {
|
661
|
-
identifier: 'echo',
|
662
|
-
type: 'service',
|
663
|
-
parent: 'duir'
|
664
|
-
}
|
665
|
-
attributes = {
|
666
|
-
description: 'Mmmmm AppleTalk.'
|
667
|
-
}
|
668
|
-
msg = pack_message( :graft, header, attributes )
|
669
|
-
|
670
|
-
sock.send( msg )
|
671
|
-
resmsg = sock.recv
|
672
|
-
|
673
|
-
hdr, body = unpack_message( resmsg )
|
674
|
-
expect( hdr ).to include( 'success' => true )
|
675
|
-
expect( body ).to eq( 'duir-echo' )
|
676
|
-
|
677
|
-
new_node = manager.nodes[ 'duir-echo' ]
|
678
|
-
expect( new_node ).to be_a( Arborist::Node::Service )
|
679
|
-
expect( new_node.identifier ).to eq( 'duir-echo' )
|
680
|
-
expect( new_node.parent ).to eq( header[:parent] )
|
681
|
-
expect( new_node.description ).to eq( attributes[:description] )
|
682
|
-
expect( new_node.port ).to eq( 7 )
|
683
|
-
expect( new_node.protocol ).to eq( 'tcp' )
|
684
|
-
expect( new_node.app_protocol ).to eq( 'echo' )
|
685
|
-
end
|
686
|
-
|
687
|
-
|
688
|
-
it "errors if adding a subordinate node with no parent" do
|
689
|
-
header = {
|
690
|
-
identifier: 'echo',
|
691
|
-
type: 'service'
|
692
|
-
}
|
693
|
-
attributes = {
|
694
|
-
description: 'Mmmmm AppleTalk.'
|
695
|
-
}
|
696
|
-
msg = pack_message( :graft, header, attributes )
|
697
|
-
|
698
|
-
sock.send( msg )
|
699
|
-
resmsg = sock.recv
|
700
|
-
|
701
|
-
hdr, body = unpack_message( resmsg )
|
702
|
-
expect( hdr ).to include( 'success' => false )
|
703
|
-
expect( hdr['reason'] ).to match( /no host given/i )
|
704
|
-
end
|
705
|
-
|
706
|
-
end
|
707
|
-
|
708
|
-
|
709
|
-
describe "modify" do
|
710
|
-
|
711
|
-
it "can change operational attributes of a node" do
|
712
|
-
header = {
|
713
|
-
identifier: 'sidonie',
|
714
|
-
}
|
715
|
-
attributes = {
|
716
|
-
parent: '_',
|
717
|
-
addresses: ['192.168.32.32', '10.2.2.28']
|
718
|
-
}
|
719
|
-
msg = pack_message( :modify, header, attributes )
|
720
|
-
|
721
|
-
sock.send( msg )
|
722
|
-
resmsg = sock.recv
|
723
|
-
|
724
|
-
hdr, body = unpack_message( resmsg )
|
725
|
-
expect( hdr ).to include( 'success' => true )
|
726
|
-
|
727
|
-
node = manager.nodes[ 'sidonie' ]
|
728
|
-
expect(
|
729
|
-
node.addresses
|
730
|
-
).to eq( [IPAddr.new('192.168.32.32'), IPAddr.new('10.2.2.28')] )
|
731
|
-
expect( node.parent ).to eq( '_' )
|
732
|
-
end
|
733
|
-
|
734
|
-
|
735
|
-
it "ignores modifications to unsupported attributes" do
|
736
|
-
header = {
|
737
|
-
identifier: 'sidonie',
|
738
|
-
}
|
739
|
-
attributes = {
|
740
|
-
identifier: 'somethingelse'
|
741
|
-
}
|
742
|
-
msg = pack_message( :modify, header, attributes )
|
743
|
-
|
744
|
-
sock.send( msg )
|
745
|
-
resmsg = sock.recv
|
746
|
-
|
747
|
-
hdr, body = unpack_message( resmsg )
|
748
|
-
expect( hdr ).to include( 'success' => true )
|
749
|
-
|
750
|
-
expect( manager.nodes['sidonie'] ).to be_an( Arborist::Node )
|
751
|
-
expect( manager.nodes['sidonie'].identifier ).to eq( 'sidonie' )
|
752
|
-
end
|
753
|
-
|
754
|
-
|
755
|
-
it "errors on modifications to the root node" do
|
756
|
-
header = {
|
757
|
-
identifier: '_',
|
758
|
-
}
|
759
|
-
attributes = {
|
760
|
-
identifier: 'somethingelse'
|
761
|
-
}
|
762
|
-
msg = pack_message( :modify, header, attributes )
|
763
|
-
|
764
|
-
sock.send( msg )
|
765
|
-
resmsg = sock.recv
|
766
|
-
|
767
|
-
hdr, body = unpack_message( resmsg )
|
768
|
-
expect( hdr ).to include( 'success' => false )
|
769
|
-
expect( manager.nodes['_'].identifier ).to eq( '_' )
|
770
|
-
end
|
771
|
-
|
772
|
-
|
773
|
-
it "errors on modifications to nonexistent nodes" do
|
774
|
-
header = {
|
775
|
-
identifier: 'nopenopenope',
|
776
|
-
}
|
777
|
-
attributes = {
|
778
|
-
identifier: 'somethingelse'
|
779
|
-
}
|
780
|
-
msg = pack_message( :modify, header, attributes )
|
781
|
-
|
782
|
-
sock.send( msg )
|
783
|
-
resmsg = sock.recv
|
784
|
-
|
785
|
-
hdr, body = unpack_message( resmsg )
|
786
|
-
expect( hdr ).to include( 'success' => false )
|
787
|
-
end
|
788
|
-
end
|
789
|
-
|
790
|
-
end
|
791
|
-
|