arborist 0.0.1.pre20160128152542 → 0.0.1.pre20160606141735
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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -0
- data/ChangeLog +426 -1
- data/Manifest.txt +17 -2
- data/Nodes.md +70 -0
- data/Protocol.md +68 -9
- data/README.md +3 -5
- data/Rakefile +4 -1
- data/TODO.md +52 -20
- data/lib/arborist.rb +19 -6
- data/lib/arborist/cli.rb +39 -25
- data/lib/arborist/client.rb +97 -4
- data/lib/arborist/command/client.rb +2 -1
- data/lib/arborist/command/start.rb +51 -5
- data/lib/arborist/dependency.rb +286 -0
- data/lib/arborist/event.rb +7 -2
- data/lib/arborist/event/{node_matching.rb → node.rb} +11 -5
- data/lib/arborist/event/node_acked.rb +5 -7
- data/lib/arborist/event/node_delta.rb +30 -3
- data/lib/arborist/event/node_disabled.rb +16 -0
- data/lib/arborist/event/node_down.rb +10 -0
- data/lib/arborist/event/node_quieted.rb +11 -0
- data/lib/arborist/event/node_unknown.rb +10 -0
- data/lib/arborist/event/node_up.rb +10 -0
- data/lib/arborist/event/node_update.rb +2 -11
- data/lib/arborist/event/sys_node_added.rb +10 -0
- data/lib/arborist/event/sys_node_removed.rb +10 -0
- data/lib/arborist/exceptions.rb +4 -0
- data/lib/arborist/manager.rb +188 -18
- data/lib/arborist/manager/event_publisher.rb +1 -1
- data/lib/arborist/manager/tree_api.rb +92 -13
- data/lib/arborist/mixins.rb +17 -0
- data/lib/arborist/monitor.rb +10 -1
- data/lib/arborist/monitor/socket.rb +123 -2
- data/lib/arborist/monitor_runner.rb +6 -5
- data/lib/arborist/node.rb +420 -94
- data/lib/arborist/node/ack.rb +72 -0
- data/lib/arborist/node/host.rb +43 -8
- data/lib/arborist/node/resource.rb +73 -0
- data/lib/arborist/node/root.rb +6 -0
- data/lib/arborist/node/service.rb +89 -22
- data/lib/arborist/observer.rb +1 -1
- data/lib/arborist/subscription.rb +11 -6
- data/spec/arborist/client_spec.rb +93 -5
- data/spec/arborist/dependency_spec.rb +375 -0
- data/spec/arborist/event/node_delta_spec.rb +66 -0
- data/spec/arborist/event/node_down_spec.rb +84 -0
- data/spec/arborist/event/node_spec.rb +59 -0
- data/spec/arborist/event/node_update_spec.rb +14 -3
- data/spec/arborist/event_spec.rb +3 -3
- data/spec/arborist/manager/tree_api_spec.rb +295 -3
- data/spec/arborist/manager_spec.rb +240 -57
- data/spec/arborist/monitor_spec.rb +26 -3
- data/spec/arborist/node/ack_spec.rb +74 -0
- data/spec/arborist/node/host_spec.rb +79 -0
- data/spec/arborist/node/resource_spec.rb +56 -0
- data/spec/arborist/node/service_spec.rb +68 -2
- data/spec/arborist/node_spec.rb +288 -11
- data/spec/arborist/subscription_spec.rb +23 -14
- data/spec/arborist_spec.rb +0 -4
- data/spec/data/observers/webservices.rb +10 -2
- data/spec/spec_helper.rb +8 -0
- metadata +58 -15
- metadata.gz.sig +0 -0
- data/LICENSE +0 -29
@@ -0,0 +1,375 @@
|
|
1
|
+
#!/usr/bin/env rspec -cfd
|
2
|
+
|
3
|
+
require_relative '../spec_helper'
|
4
|
+
|
5
|
+
require 'timecop'
|
6
|
+
require 'arborist/dependency'
|
7
|
+
|
8
|
+
|
9
|
+
describe Arborist::Dependency do
|
10
|
+
|
11
|
+
|
12
|
+
it "can be constructed without identifiers" do
|
13
|
+
result = described_class.new( :any )
|
14
|
+
expect( result ).to be_empty
|
15
|
+
expect( result.all_identifiers ).to be_empty
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
it "can be constructed with an operator and a list of identifiers" do
|
20
|
+
result = described_class.new( :any, 'node1', 'node2' )
|
21
|
+
expect( result ).to include( 'node1', 'node2' )
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
it "can be constructed with an operator and a list of other dependencies" do
|
26
|
+
dep1 = described_class.new( :any, 'node1', 'node2' )
|
27
|
+
dep2 = described_class.new( :any, 'node2', 'node3' )
|
28
|
+
|
29
|
+
result = described_class.new( :all, dep1, dep2 )
|
30
|
+
expect( result.subdeps ).to include( dep1, dep2 )
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
it "can be constructed with an operator and a list of both dependencies and identifiers" do
|
35
|
+
dep1 = described_class.new( :any, 'node1', 'node2' )
|
36
|
+
dep2 = described_class.new( :any, 'node2', 'node3' )
|
37
|
+
|
38
|
+
result = described_class.new( :all, dep1, dep2, 'node4', 'node5' )
|
39
|
+
expect( result.subdeps ).to include( dep1, dep2 )
|
40
|
+
expect( result.identifiers ).to include( 'node4', 'node5' )
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
it "can return a Hash that describes itself" do
|
45
|
+
dep1 = described_class.new( :any, 'node1', 'node2' )
|
46
|
+
dep2 = described_class.new( :any, 'node2', 'node3' )
|
47
|
+
dep3 = described_class.new( :all, dep1, dep2, 'node4', 'node5' )
|
48
|
+
|
49
|
+
hash = dep3.to_h
|
50
|
+
|
51
|
+
expect( hash ).to include( :behavior, :identifiers, :subdeps )
|
52
|
+
|
53
|
+
expect( hash[:behavior] ).to eq( :all )
|
54
|
+
expect( hash[:identifiers] ).to be_an( Array ).and( include('node4', 'node5') )
|
55
|
+
expect( hash[:subdeps] ).to be_an( Array ).and( all be_a(Hash) )
|
56
|
+
expect( hash[:subdeps] ).to all( include(:behavior, :identifiers, :subdeps) )
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
it "can be constructed from a nested Hash" do
|
61
|
+
dep1 = described_class.new( :any, 'node1', 'node2' )
|
62
|
+
dep2 = described_class.new( :any, 'node2', 'node3' )
|
63
|
+
dep3 = described_class.new( :all, dep1, dep2, 'node4', 'node5' )
|
64
|
+
|
65
|
+
clone = described_class.from_hash( dep3.to_h )
|
66
|
+
|
67
|
+
expect( clone ).to eq( dep3 )
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
it "includes identifiers of all of its subdependencies" do
|
72
|
+
dep1 = described_class.new( :any, 'node1', 'node2' )
|
73
|
+
dep2 = described_class.new( :any, 'node2', 'node3' )
|
74
|
+
|
75
|
+
result = described_class.new( :all, dep1, dep2, 'node4', 'node5' )
|
76
|
+
expect( result.identifiers ).to include( 'node4', 'node5' )
|
77
|
+
expect( result.identifiers ).to_not include( 'node1', 'node2', 'node3' )
|
78
|
+
|
79
|
+
expect( result.all_identifiers.length ).to eq( 5 )
|
80
|
+
expect( result ).to include( 'node1', 'node2', 'node3', 'node4', 'node5' )
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
it "can return the list of identifiers that have been marked down" do
|
85
|
+
dep = described_class.new( :all, 'node1', 'node2', 'node3', 'node4', 'node5' )
|
86
|
+
dep.mark_down( 'node1' )
|
87
|
+
dep.mark_down( 'node4' )
|
88
|
+
|
89
|
+
expect( dep.down_identifiers ).to include( 'node1', 'node4' )
|
90
|
+
expect( dep.down_identifiers ).to_not include( 'node2', 'node3', 'node5' )
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
it "has a constructor for generating dependencies with a prefix" do
|
95
|
+
result = described_class.on( :all, 'node1', 'node2', prefixes: ['host1', 'host2'] )
|
96
|
+
expect( result ).to include( 'host1-node1', 'host1-node2', 'host2-node1', 'host2-node2' )
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
it "can mark one of its members as being down" do
|
101
|
+
dep = described_class.new( :all, 'node1', 'node2' )
|
102
|
+
dep.mark_down( 'node1' )
|
103
|
+
|
104
|
+
expect( dep ).to be_down
|
105
|
+
# down reason?
|
106
|
+
end
|
107
|
+
|
108
|
+
it "marks all downed dependencies with the same default timestamp" do
|
109
|
+
dep1 = described_class.new( :all, 'node1', 'node2' )
|
110
|
+
dep2 = described_class.new( :any, 'node1' )
|
111
|
+
dep3 = described_class.new( :all, dep1, dep2 )
|
112
|
+
dep3.mark_down( 'node1' )
|
113
|
+
|
114
|
+
expect( dep1.identifier_states['node1'] ).to eq( dep2.identifier_states['node1'] )
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
it "marks all downed dependencies with the provided timestamp" do
|
119
|
+
time = Time.parse( "2016-01-01 11:00:00" )
|
120
|
+
dep1 = described_class.new( :all, 'node1', 'node2' )
|
121
|
+
dep2 = described_class.new( :any, 'node1' )
|
122
|
+
dep3 = described_class.new( :all, dep1, dep2 )
|
123
|
+
dep3.mark_down( 'node1', time )
|
124
|
+
|
125
|
+
expect( dep1.identifier_states['node1'] ).to eq( time )
|
126
|
+
expect( dep2.identifier_states['node1'] ).to eq( time )
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
it "knows when the earliest dependency was marked down" do
|
131
|
+
dep = described_class.new( :all, 'node1', 'node2', 'node3' )
|
132
|
+
|
133
|
+
Timecop.freeze do
|
134
|
+
dep.mark_down( 'node1' )
|
135
|
+
earliest = Time.now
|
136
|
+
|
137
|
+
Timecop.travel( 60 ) do
|
138
|
+
dep.mark_down( 'node3' )
|
139
|
+
|
140
|
+
expect( dep.earliest_down_time ).to eq( earliest )
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
it "returns nil if asked for the earliest down mark, and no nodes are maked down" do
|
147
|
+
dep = described_class.new( :all, 'node1', 'node2', 'node3' )
|
148
|
+
expect( dep.earliest_down_time ).to be_nil
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
it "knows when the latest dependency was marked down" do
|
153
|
+
dep = described_class.new( :all, 'node1', 'node2', 'node3' )
|
154
|
+
|
155
|
+
Timecop.freeze do
|
156
|
+
dep.mark_down( 'node1' )
|
157
|
+
|
158
|
+
Timecop.freeze( 60 ) do
|
159
|
+
dep.mark_down( 'node3' )
|
160
|
+
latest = Time.now
|
161
|
+
|
162
|
+
expect( dep.latest_down_time ).to eq( latest )
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
it "returns nil if asked for the latest down mark, and no nodes are maked down" do
|
169
|
+
dep = described_class.new( :all, 'node1', 'node2', 'node3' )
|
170
|
+
expect( dep.latest_down_time ).to be_nil
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
it "propagates a node being marked down to its sub-dependencies" do
|
175
|
+
dep1 = described_class.new( :all, 'node1', 'node2' )
|
176
|
+
dep2 = described_class.new( :all, 'node2', 'node3' )
|
177
|
+
dep3 = described_class.new( :any, 'node2', 'node5' )
|
178
|
+
|
179
|
+
top_dep = described_class.new( :all, dep1, dep2, 'node4', dep3 )
|
180
|
+
|
181
|
+
top_dep.mark_down( 'node2' )
|
182
|
+
|
183
|
+
expect( dep1 ).to be_down
|
184
|
+
expect( dep2 ).to be_down
|
185
|
+
expect( dep3 ).to be_up
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
it "can return all of its sub-dependencies that are down" do
|
190
|
+
dep1 = described_class.new( :all, 'node1', 'node2' )
|
191
|
+
dep2 = described_class.new( :all, 'node2', 'node3' )
|
192
|
+
dep3 = described_class.new( :any, 'node2', 'node5' )
|
193
|
+
|
194
|
+
top_dep = described_class.new( :all, dep1, dep2, 'node4', dep3 )
|
195
|
+
top_dep.mark_down( 'node2' )
|
196
|
+
|
197
|
+
expect( top_dep.down_subdeps ).to include( dep1, dep2 )
|
198
|
+
expect( top_dep.down_subdeps ).to_not include( dep3 )
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
it "can return all of its sub-dependencies that are up" do
|
203
|
+
dep1 = described_class.new( :all, 'node1', 'node2' )
|
204
|
+
dep2 = described_class.new( :all, 'node2', 'node3' )
|
205
|
+
dep3 = described_class.new( :any, 'node2', 'node5' )
|
206
|
+
|
207
|
+
top_dep = described_class.new( :all, dep1, dep2, 'node4', dep3 )
|
208
|
+
top_dep.mark_down( 'node2' )
|
209
|
+
|
210
|
+
expect( top_dep.up_subdeps ).to include( dep3 )
|
211
|
+
expect( top_dep.up_subdeps ).to_not include( dep1, dep2 )
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
it "can iterate over its downed elements" do
|
216
|
+
dep1 = described_class.new( :all, 'node1', 'node2' )
|
217
|
+
dep2 = described_class.new( :all, 'node2', 'node3' )
|
218
|
+
dep3 = described_class.new( :any, 'node2', 'node5' )
|
219
|
+
|
220
|
+
top_dep = described_class.new( :all, 'node2', dep1, dep2, 'node4', dep3 )
|
221
|
+
top_dep.mark_down( 'node2' )
|
222
|
+
top_dep.mark_down( 'node5' )
|
223
|
+
|
224
|
+
results = top_dep.each_downed.to_a
|
225
|
+
expect( results.length ).to eq( 2 )
|
226
|
+
expect( results[0] ).to include( 'node2', an_instance_of(Time) )
|
227
|
+
expect( results[1] ).to include( 'node5', an_instance_of(Time) )
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
it "is equal to another node with the same identifiers" do
|
232
|
+
dep1 = described_class.new( :all, 'node1', 'node2', 'node3' )
|
233
|
+
dep2 = described_class.new( :all, 'node1', 'node2', 'node3' )
|
234
|
+
dep3 = described_class.new( :all, 'node1', 'node2', 'node4' )
|
235
|
+
|
236
|
+
expect( dep1 ).to eq( dep2 )
|
237
|
+
expect( dep1 ).to_not eq( dep3 )
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
it "is equal to another node with the same identifiers and subdeps" do
|
242
|
+
dep1 = described_class.new( :all, 'node1', 'node2', 'node3' )
|
243
|
+
dep2 = described_class.new( :all, 'node4', 'node5', 'node6' )
|
244
|
+
dep3 = described_class.new( :any, 'node4', 'node5', 'node6' )
|
245
|
+
|
246
|
+
dep4 = described_class.new( :all, 'node1', dep1, dep2 )
|
247
|
+
dep5 = described_class.new( :all, 'node1', dep1.dup, dep2.dup )
|
248
|
+
dep6 = described_class.new( :all, 'node1', dep1, dep3 )
|
249
|
+
dep7 = described_class.new( :all, 'node1', dep1, dep2, dep3 )
|
250
|
+
|
251
|
+
expect( dep4 ).to eq( dep5 )
|
252
|
+
expect( dep4 ).to_not eq( dep6 )
|
253
|
+
expect( dep4 ).to_not eq( dep7 )
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
it "is eql? to another node with the same identifiers, subdeps, and state" do
|
258
|
+
# The time values have to be the same too
|
259
|
+
Timecop.freeze do
|
260
|
+
dep1 = described_class.new( :all, 'node1', 'node2', 'node3' )
|
261
|
+
dep2 = described_class.new( :all, 'node3', 'node4', 'node5' )
|
262
|
+
|
263
|
+
dep4 = described_class.new( :all, 'node1', dep1, dep2 )
|
264
|
+
dep4.mark_down( 'node2' )
|
265
|
+
dep4.mark_down( 'node3' )
|
266
|
+
|
267
|
+
dep5 = described_class.new( :all, 'node1', dep1.dup, dep2.dup )
|
268
|
+
dep5.mark_down( 'node2' )
|
269
|
+
dep5.mark_down( 'node3' )
|
270
|
+
|
271
|
+
dep6 = described_class.new( :all, 'node1', dep1.dup, dep2.dup )
|
272
|
+
dep6.mark_down( 'node2' )
|
273
|
+
|
274
|
+
dep7 = described_class.new( :all, 'node1', dep1.dup, dep2.dup )
|
275
|
+
dep7.mark_down( 'node2' )
|
276
|
+
dep7.mark_down( 'node3' )
|
277
|
+
dep7.mark_down( 'node4' )
|
278
|
+
|
279
|
+
expect( dep4 ).to eql( dep5 )
|
280
|
+
expect( dep4 ).to_not eql( dep6 )
|
281
|
+
expect( dep4 ).to_not eql( dep7 )
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
|
286
|
+
it "knows how to Marshal itself" do
|
287
|
+
dep1 = described_class.new( :all, 'node1', 'node2' )
|
288
|
+
dep2 = described_class.new( :all, 'node2', 'node3' )
|
289
|
+
dep3 = described_class.new( :any, 'node2', 'node5' )
|
290
|
+
|
291
|
+
top_dep = described_class.new( :all, dep1, dep2, 'node4', dep3 )
|
292
|
+
|
293
|
+
expect( Marshal.load(Marshal.dump( top_dep )) ).to eq( top_dep )
|
294
|
+
end
|
295
|
+
|
296
|
+
|
297
|
+
# Mahlon's brain has a hard time with this. Note for future Mahlon!
|
298
|
+
# "This node depends on ALL of these other nodes to be up, for it to be up"
|
299
|
+
# "This node depends on ANY of these other nodes to be up, for it to be up"
|
300
|
+
|
301
|
+
# Another way to remember this:
|
302
|
+
# ANY -> any of these is sufficient
|
303
|
+
# ALL -> all of these are necessary
|
304
|
+
|
305
|
+
describe "with 'all' behavior" do
|
306
|
+
|
307
|
+
let( :dep ) { described_class.new(:all, 'node1', 'node2', 'node3') }
|
308
|
+
|
309
|
+
|
310
|
+
it "is up if none of its members have been marked down" do
|
311
|
+
expect( dep ).to be_up
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
it "is down if any of its members has been marked down" do
|
316
|
+
expect {
|
317
|
+
dep.mark_down( 'node2' )
|
318
|
+
}.to change { dep.down? }.from( false ).to( true )
|
319
|
+
end
|
320
|
+
|
321
|
+
|
322
|
+
it "can describe the reason it's down" do
|
323
|
+
dep.mark_down( 'node2' )
|
324
|
+
expect( dep.down_reason ).to match( /node2 is down as of/i )
|
325
|
+
end
|
326
|
+
|
327
|
+
|
328
|
+
it "can describe the reason if multiple nodes have been marked down" do
|
329
|
+
dep.mark_down( 'node1' )
|
330
|
+
dep.mark_down( 'node2' )
|
331
|
+
# :FIXME: Does order matter in the 'all' case? This assumes no.
|
332
|
+
expect( dep.down_reason ).to match( /node(1|2) \(and 1 other\) are down as of/i )
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
336
|
+
|
337
|
+
|
338
|
+
describe "with 'any' behavior" do
|
339
|
+
|
340
|
+
let( :dep ) { described_class.new(:any, 'node1', 'node2') }
|
341
|
+
|
342
|
+
|
343
|
+
it "is up if none of its members have been marked down" do
|
344
|
+
expect( dep ).to be_up
|
345
|
+
end
|
346
|
+
|
347
|
+
|
348
|
+
it "is up if only some of its members have been marked down" do
|
349
|
+
expect {
|
350
|
+
dep.mark_down( 'node2' )
|
351
|
+
}.to_not change { dep.down? }
|
352
|
+
end
|
353
|
+
|
354
|
+
|
355
|
+
it "is down if all of its members have been marked down" do
|
356
|
+
expect {
|
357
|
+
dep.mark_down( 'node2' )
|
358
|
+
dep.mark_down( 'node1' )
|
359
|
+
}.to change { dep.down? }.from( false ).to( true )
|
360
|
+
end
|
361
|
+
|
362
|
+
|
363
|
+
it "can describe the reason it's down" do
|
364
|
+
dep.mark_down( 'node2' )
|
365
|
+
dep.mark_down( 'node1' )
|
366
|
+
|
367
|
+
expect( dep.down_reason ).to match( /are all down as of/i ).and( include('node1', 'node2') )
|
368
|
+
end
|
369
|
+
|
370
|
+
end
|
371
|
+
|
372
|
+
|
373
|
+
|
374
|
+
end
|
375
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/env rspec -cfd
|
2
|
+
|
3
|
+
require_relative '../../spec_helper'
|
4
|
+
|
5
|
+
require 'arborist/event/node_delta'
|
6
|
+
|
7
|
+
|
8
|
+
describe Arborist::Event::NodeDelta do
|
9
|
+
|
10
|
+
class TestNode < Arborist::Node; end
|
11
|
+
|
12
|
+
|
13
|
+
let( :node ) do
|
14
|
+
TestNode.new( 'foo' ) do
|
15
|
+
parent 'bar'
|
16
|
+
description "A testing node"
|
17
|
+
tags :tree, :try, :triage, :trip
|
18
|
+
|
19
|
+
update(
|
20
|
+
"tcp_socket_connect" => {
|
21
|
+
"time" => "2016-02-25 16:04:35 -0800",
|
22
|
+
"duration" => 0.020619
|
23
|
+
}
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
describe "subscription support" do
|
30
|
+
|
31
|
+
it "matches a subscription with only an event type if the type is the same" do
|
32
|
+
sub = Arborist::Subscription.new( 'node.delta' ) {}
|
33
|
+
event = described_class.new( node, status: ['up', 'down'] )
|
34
|
+
|
35
|
+
expect( event ).to match( sub )
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
it "matches a subscription with a matching event type and matching criteria" do
|
40
|
+
sub = Arborist::Subscription.new( 'node.delta', 'tag' => 'triage' ) {}
|
41
|
+
event = described_class.new( node, status: ['up', 'down'] )
|
42
|
+
|
43
|
+
expect( event ).to match( sub )
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
it "matches a subscription with matching event type, node criteria, and delta criteria" do
|
48
|
+
criteria = {
|
49
|
+
'tag' => 'tree',
|
50
|
+
'delta' => {
|
51
|
+
'status' => [ 'up', 'down' ]
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
sub = Arborist::Subscription.new( 'node.delta', criteria ) {}
|
56
|
+
event = described_class.new( node, 'status' => ['up', 'down'] )
|
57
|
+
|
58
|
+
expect( event ).to match( sub )
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env rspec -cfd
|
2
|
+
|
3
|
+
require_relative '../../spec_helper'
|
4
|
+
|
5
|
+
require 'arborist/event/node_down'
|
6
|
+
|
7
|
+
|
8
|
+
describe Arborist::Event::NodeDown do
|
9
|
+
|
10
|
+
class TestNode < Arborist::Node; end
|
11
|
+
|
12
|
+
|
13
|
+
let( :node ) do
|
14
|
+
TestNode.new( 'foo' ) do
|
15
|
+
parent 'bar'
|
16
|
+
description "The prototypical node"
|
17
|
+
tags :chunker, :hunky, :flippin, :hippo
|
18
|
+
|
19
|
+
update(
|
20
|
+
'error' => 'Something bad happened!',
|
21
|
+
'song' => 'Around the World',
|
22
|
+
'artist' => 'Daft Punk',
|
23
|
+
'length' => '7:09',
|
24
|
+
'cider' => {
|
25
|
+
'description' => 'tasty',
|
26
|
+
'size' => '16oz',
|
27
|
+
},
|
28
|
+
'sausage' => {
|
29
|
+
'description' => 'pork',
|
30
|
+
'size' => 'monsterous',
|
31
|
+
'price' => {
|
32
|
+
'units' => 1200,
|
33
|
+
'currency' => 'usd'
|
34
|
+
}
|
35
|
+
},
|
36
|
+
'music' => '80s'
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
describe "subscription support" do
|
43
|
+
|
44
|
+
it "matches a subscription with only an event type if the type is the same" do
|
45
|
+
sub = Arborist::Subscription.new( 'node.down' ) {}
|
46
|
+
event = described_class.new( node )
|
47
|
+
|
48
|
+
expect( event ).to match( sub )
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
it "matches a subscription with a matching event type and matching criteria" do
|
53
|
+
sub = Arborist::Subscription.new( 'node.down', 'tag' => 'chunker' ) {}
|
54
|
+
event = described_class.new( node )
|
55
|
+
|
56
|
+
expect( event ).to match( sub )
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
it "doesn't match a subscription with a matching event type if the criteria don't match" do
|
61
|
+
sub = Arborist::Subscription.new( 'node.down', 'tag' => 'looper' ) {}
|
62
|
+
event = described_class.new( node )
|
63
|
+
|
64
|
+
expect( event ).to_not match( sub )
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
describe "payload" do
|
72
|
+
|
73
|
+
it "includes its attributes" do
|
74
|
+
event = described_class.new( node )
|
75
|
+
|
76
|
+
expect( event.payload ).to be_a( Hash )
|
77
|
+
expect( event.payload ).to include( :status, :error, :properties, :type )
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
end
|
84
|
+
|