shift-nanite 0.4.1.2
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.
- data/LICENSE +201 -0
- data/README.rdoc +430 -0
- data/Rakefile +76 -0
- data/TODO +24 -0
- data/bin/nanite-admin +65 -0
- data/bin/nanite-agent +79 -0
- data/bin/nanite-mapper +50 -0
- data/lib/nanite.rb +74 -0
- data/lib/nanite/actor.rb +71 -0
- data/lib/nanite/actor_registry.rb +26 -0
- data/lib/nanite/admin.rb +138 -0
- data/lib/nanite/agent.rb +264 -0
- data/lib/nanite/amqp.rb +58 -0
- data/lib/nanite/cluster.rb +250 -0
- data/lib/nanite/config.rb +112 -0
- data/lib/nanite/console.rb +39 -0
- data/lib/nanite/daemonize.rb +13 -0
- data/lib/nanite/identity.rb +16 -0
- data/lib/nanite/job.rb +104 -0
- data/lib/nanite/local_state.rb +38 -0
- data/lib/nanite/log.rb +66 -0
- data/lib/nanite/log/formatter.rb +39 -0
- data/lib/nanite/mapper.rb +309 -0
- data/lib/nanite/mapper_proxy.rb +67 -0
- data/lib/nanite/nanite_dispatcher.rb +92 -0
- data/lib/nanite/packets.rb +365 -0
- data/lib/nanite/pid_file.rb +52 -0
- data/lib/nanite/reaper.rb +39 -0
- data/lib/nanite/security/cached_certificate_store_proxy.rb +24 -0
- data/lib/nanite/security/certificate.rb +55 -0
- data/lib/nanite/security/certificate_cache.rb +66 -0
- data/lib/nanite/security/distinguished_name.rb +34 -0
- data/lib/nanite/security/encrypted_document.rb +46 -0
- data/lib/nanite/security/rsa_key_pair.rb +53 -0
- data/lib/nanite/security/secure_serializer.rb +68 -0
- data/lib/nanite/security/signature.rb +46 -0
- data/lib/nanite/security/static_certificate_store.rb +35 -0
- data/lib/nanite/security_provider.rb +47 -0
- data/lib/nanite/serializer.rb +52 -0
- data/lib/nanite/state.rb +168 -0
- data/lib/nanite/streaming.rb +125 -0
- data/lib/nanite/util.rb +58 -0
- data/spec/actor_registry_spec.rb +60 -0
- data/spec/actor_spec.rb +77 -0
- data/spec/agent_spec.rb +240 -0
- data/spec/cached_certificate_store_proxy_spec.rb +34 -0
- data/spec/certificate_cache_spec.rb +49 -0
- data/spec/certificate_spec.rb +27 -0
- data/spec/cluster_spec.rb +622 -0
- data/spec/distinguished_name_spec.rb +24 -0
- data/spec/encrypted_document_spec.rb +21 -0
- data/spec/job_spec.rb +251 -0
- data/spec/local_state_spec.rb +130 -0
- data/spec/nanite_dispatcher_spec.rb +136 -0
- data/spec/packet_spec.rb +220 -0
- data/spec/rsa_key_pair_spec.rb +33 -0
- data/spec/secure_serializer_spec.rb +41 -0
- data/spec/serializer_spec.rb +107 -0
- data/spec/signature_spec.rb +30 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/static_certificate_store_spec.rb +30 -0
- data/spec/util_spec.rb +63 -0
- metadata +129 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe Nanite::CertificateCache do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@cache = Nanite::CertificateCache.new(2)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should allow storing and retrieving objects' do
|
10
|
+
@cache['some_id'].should be_nil
|
11
|
+
@cache['some_id'] = 'some_value'
|
12
|
+
@cache['some_id'].should == 'some_value'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should not store more than required' do
|
16
|
+
@cache[1] = 'oldest'
|
17
|
+
@cache[2] = 'older'
|
18
|
+
@cache[1].should == 'oldest'
|
19
|
+
@cache[2].should == 'older'
|
20
|
+
|
21
|
+
@cache[3] = 'new'
|
22
|
+
@cache[3].should == 'new'
|
23
|
+
|
24
|
+
@cache[1].should be_nil
|
25
|
+
@cache[2].should == 'older'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should use LRU to remove entries' do
|
29
|
+
@cache[1] = 'oldest'
|
30
|
+
@cache[2] = 'older'
|
31
|
+
@cache[1].should == 'oldest'
|
32
|
+
@cache[2].should == 'older'
|
33
|
+
|
34
|
+
@cache[1] = 'new'
|
35
|
+
@cache[3] = 'newer'
|
36
|
+
@cache[1].should == 'new'
|
37
|
+
@cache[3].should == 'newer'
|
38
|
+
|
39
|
+
@cache[2].should be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should store items returned by block' do
|
43
|
+
@cache[1].should be_nil
|
44
|
+
item = @cache.get(1) { 'item' }
|
45
|
+
item.should == 'item'
|
46
|
+
@cache[1].should == 'item'
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe Nanite::Certificate do
|
4
|
+
|
5
|
+
include SpecHelpers
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
@certificate, key = issue_cert
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should save' do
|
12
|
+
filename = File.join(File.dirname(__FILE__), "cert.pem")
|
13
|
+
@certificate.save(filename)
|
14
|
+
File.size(filename).should be > 0
|
15
|
+
File.delete(filename)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should load' do
|
19
|
+
filename = File.join(File.dirname(__FILE__), "cert.pem")
|
20
|
+
@certificate.save(filename)
|
21
|
+
cert = Nanite::Certificate.load(filename)
|
22
|
+
File.delete(filename)
|
23
|
+
cert.should_not be_nil
|
24
|
+
cert.data.should == @certificate.data
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,622 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe Nanite::Cluster do
|
4
|
+
|
5
|
+
include SpecHelpers
|
6
|
+
|
7
|
+
describe "Intialization" do
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
@fanout = mock("fanout")
|
11
|
+
@binding = mock("binding", :subscribe => true)
|
12
|
+
@queue = mock("queue", :bind => @binding)
|
13
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
14
|
+
@serializer = mock("Serializer")
|
15
|
+
@reaper = mock("Reaper")
|
16
|
+
@mapper = mock("Mapper")
|
17
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "of Heartbeat (Queue)" do
|
21
|
+
|
22
|
+
it "should setup the heartbeat (queue) for id" do
|
23
|
+
@amq.should_receive(:queue).with("heartbeat-the_identity", anything()).and_return(@queue)
|
24
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should make the heartbeat (queue) exclusive" do
|
28
|
+
@amq.should_receive(:queue).with("heartbeat-the_identity", { :exclusive => true }).and_return(@queue)
|
29
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should bind the heartbeat (queue) to 'heartbeat' fanout" do
|
33
|
+
@amq.should_receive(:fanout).with("heartbeat", { :durable => true }).and_return(@fanout)
|
34
|
+
@queue.should_receive(:bind).with(@fanout).and_return(@binding)
|
35
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
36
|
+
end
|
37
|
+
|
38
|
+
end # of Heartbeat (Queue)
|
39
|
+
|
40
|
+
|
41
|
+
describe "of Registration (Queue)" do
|
42
|
+
|
43
|
+
it "should setup the registration (queue) for id" do
|
44
|
+
@amq.should_receive(:queue).with("registration-the_identity", anything()).and_return(@queue)
|
45
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should make the registration (queue) exclusive" do
|
49
|
+
@amq.should_receive(:queue).with("registration-the_identity", { :exclusive => true }).and_return(@queue)
|
50
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should bind the registration (queue) to 'registration' fanout" do
|
54
|
+
@amq.should_receive(:fanout).with("registration", { :durable => true }).and_return(@fanout)
|
55
|
+
@queue.should_receive(:bind).with(@fanout).and_return(@binding)
|
56
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
57
|
+
end
|
58
|
+
|
59
|
+
end # of Registration (Queue)
|
60
|
+
|
61
|
+
describe "of Request (Queue)" do
|
62
|
+
|
63
|
+
it "should setup the request (queue) for id" do
|
64
|
+
@amq.should_receive(:queue).with("request-the_identity", anything()).and_return(@queue)
|
65
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should make the request (queue) exclusive" do
|
69
|
+
@amq.should_receive(:queue).with("request-the_identity", { :exclusive => true }).and_return(@queue)
|
70
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should bind the request (queue) to 'request' fanout" do
|
74
|
+
@amq.should_receive(:fanout).with("request", { :durable => true }).and_return(@fanout)
|
75
|
+
@queue.should_receive(:bind).with(@fanout).and_return(@binding)
|
76
|
+
cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
|
77
|
+
end
|
78
|
+
|
79
|
+
end # of Request (Queue)
|
80
|
+
|
81
|
+
|
82
|
+
describe "Reaper" do
|
83
|
+
|
84
|
+
it "should be created" do
|
85
|
+
Nanite::Reaper.should_receive(:new).with(anything()).and_return(@reaper)
|
86
|
+
cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should use the agent timeout" do
|
90
|
+
Nanite::Reaper.should_receive(:new).with(443).and_return(@reaper)
|
91
|
+
cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper)
|
92
|
+
end
|
93
|
+
|
94
|
+
end # Reaper
|
95
|
+
|
96
|
+
describe "State" do
|
97
|
+
begin
|
98
|
+
require 'nanite/state'
|
99
|
+
rescue LoadError
|
100
|
+
end
|
101
|
+
|
102
|
+
if defined?(Redis)
|
103
|
+
it "should use a local state by default" do
|
104
|
+
cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper)
|
105
|
+
cluster.nanites.instance_of?(Nanite::LocalState).should == true
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should set up a redis state when requested" do
|
109
|
+
state = Nanite::State.new("")
|
110
|
+
Nanite::State.should_receive(:new).with("localhost:1234").and_return(state)
|
111
|
+
cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper, "localhost:1234")
|
112
|
+
cluster.nanites.instance_of?(Nanite::State).should == true
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end # Intialization
|
117
|
+
|
118
|
+
|
119
|
+
describe "Target Selection" do
|
120
|
+
|
121
|
+
before(:each) do
|
122
|
+
@fanout = mock("fanout")
|
123
|
+
@binding = mock("binding", :subscribe => true)
|
124
|
+
@queue = mock("queue", :bind => @binding)
|
125
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
126
|
+
@serializer = mock("Serializer")
|
127
|
+
@reaper = mock("Reaper")
|
128
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
129
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
130
|
+
|
131
|
+
@all_known_nanites = [
|
132
|
+
['nanite-1', {
|
133
|
+
:status => 0.21,
|
134
|
+
:services => ['/foo/bar', '/you/too'],
|
135
|
+
:tags => ['a', 'b', 'c'],
|
136
|
+
:timestamp => Time.now
|
137
|
+
}],
|
138
|
+
['nanite-2', {
|
139
|
+
:status => 1.99,
|
140
|
+
:services => ['/foo/bar', '/you/too', '/maybe/he'],
|
141
|
+
:tags => ['b', 'c', 'e'],
|
142
|
+
:timestamp => Time.now
|
143
|
+
}],
|
144
|
+
['nanite-3', {
|
145
|
+
:status => 0.5,
|
146
|
+
:services => ['/foo/bar', '/maybe/he'],
|
147
|
+
:tags => [],
|
148
|
+
:timestamp => Time.now - 60 * 10
|
149
|
+
}],
|
150
|
+
['nanite-4', {
|
151
|
+
:status => 2.01,
|
152
|
+
:services => ['/foo/bar', '/you/too'],
|
153
|
+
:tags => ['a', 'b', 'c'],
|
154
|
+
:timestamp => Time.now - 10
|
155
|
+
}],
|
156
|
+
]
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should return array containing targets for request" do
|
160
|
+
target = mock("Supplied Target")
|
161
|
+
request = mock("Request", :target => target)
|
162
|
+
@cluster.targets_for(request).should be_instance_of(Array)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should use target from request" do
|
166
|
+
target = mock("Supplied Target")
|
167
|
+
request = mock("Request", :target => target)
|
168
|
+
@cluster.targets_for(request).should == [target]
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should use targets choosen by least loaded selector (:least_loaded)" do
|
172
|
+
request = mock("Request", :target => nil, :selector => :least_loaded, :type => "/foo/bar", :tags => [])
|
173
|
+
@cluster.should_receive(:nanites_providing).with('/foo/bar', []).and_return(@all_known_nanites)
|
174
|
+
|
175
|
+
@cluster.targets_for(request).should == ["nanite-1"]
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should use targets choosen by all selector (:all)" do
|
179
|
+
request = mock("Request", :target => nil, :selector => :all, :type => "/foo/bar", :tags => [])
|
180
|
+
@cluster.should_receive(:nanites_providing).with('/foo/bar', []).and_return(@all_known_nanites)
|
181
|
+
|
182
|
+
@cluster.targets_for(request).should == ["nanite-1", "nanite-2", "nanite-3", "nanite-4"]
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should use targets choosen by random selector (:random)" do
|
186
|
+
request = mock("Request", :target => nil, :selector => :random, :type => "/foo/bar", :tags => [])
|
187
|
+
@cluster.should_receive(:nanites_providing).with('/foo/bar', []).and_return(@all_known_nanites)
|
188
|
+
|
189
|
+
@cluster.should_receive(:rand).with(4).and_return(2)
|
190
|
+
@cluster.targets_for(request).should == ["nanite-3"]
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should use targets choosen by round-robin selector (:rr)" do
|
194
|
+
request = mock("Request", :target => nil, :selector => :rr, :type => "/foo/bar", :tags => [])
|
195
|
+
@cluster.stub!(:nanites_providing).with('/foo/bar', []).and_return(@all_known_nanites)
|
196
|
+
|
197
|
+
@cluster.instance_variable_set("@last", {})
|
198
|
+
|
199
|
+
@cluster.targets_for(request).should == ["nanite-1"]
|
200
|
+
@cluster.targets_for(request).should == ["nanite-2"]
|
201
|
+
@cluster.targets_for(request).should == ["nanite-3"]
|
202
|
+
@cluster.targets_for(request).should == ["nanite-4"]
|
203
|
+
@cluster.targets_for(request).should == ["nanite-1"]
|
204
|
+
@cluster.targets_for(request).should == ["nanite-2"]
|
205
|
+
@cluster.targets_for(request).should == ["nanite-3"]
|
206
|
+
@cluster.targets_for(request).should == ["nanite-4"]
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should pass the tag filter down" do
|
210
|
+
request = mock("Request", :target => nil, :selector => :least_loaded, :type => "/foo/bar", :tags => ['a'])
|
211
|
+
@cluster.should_receive(:nanites_providing).with('/foo/bar', ['a']).and_return(@all_known_nanites)
|
212
|
+
|
213
|
+
@cluster.targets_for(request).should == ["nanite-1"]
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should ignore timedout nanites" do
|
217
|
+
@all_known_nanites[0][1][:timestamp] = Time.local(2000)
|
218
|
+
|
219
|
+
nanites = mock("Nanites", :nanites_for => @all_known_nanites)
|
220
|
+
@cluster.should_receive(:nanites).and_return(nanites)
|
221
|
+
|
222
|
+
request = mock("Request", :target => nil, :selector => :least_loaded, :type => "/foo/bar", :tags => [])
|
223
|
+
|
224
|
+
@cluster.targets_for(request).should == ["nanite-2"]
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should ignore timedout nanites - even when loading all nanites" do
|
228
|
+
@all_known_nanites[0][1][:timestamp] = Time.local(2000)
|
229
|
+
|
230
|
+
nanites = mock("Nanites", :nanites_for => @all_known_nanites)
|
231
|
+
@cluster.should_receive(:nanites).and_return(nanites)
|
232
|
+
|
233
|
+
request = mock("Request", :target => nil, :selector => :all, :type => "/foo/bar", :tags => [])
|
234
|
+
|
235
|
+
@cluster.targets_for(request).should == ["nanite-2", "nanite-4"]
|
236
|
+
end
|
237
|
+
|
238
|
+
end # Target Selection
|
239
|
+
|
240
|
+
|
241
|
+
describe "Nanite Registration" do
|
242
|
+
|
243
|
+
before(:each) do
|
244
|
+
@fanout = mock("fanout")
|
245
|
+
@binding = mock("binding", :subscribe => true)
|
246
|
+
@queue = mock("queue", :bind => @binding)
|
247
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
248
|
+
@serializer = mock("Serializer")
|
249
|
+
@reaper = mock("Reaper", :register => true)
|
250
|
+
Nanite::Log.stub!(:info)
|
251
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
252
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
253
|
+
@register_packet = Nanite::Register.new("nanite_id", ["the_nanite_services"], "nanite_status",[])
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should add the Nanite to the nanites map" do
|
257
|
+
@cluster.register(@register_packet)
|
258
|
+
@cluster.nanites['nanite_id'].should_not be_nil
|
259
|
+
end
|
260
|
+
|
261
|
+
it "should use hash of the Nanite's services and status as value" do
|
262
|
+
@cluster.register(@register_packet)
|
263
|
+
@cluster.nanites['nanite_id'].keys.size == 2
|
264
|
+
@cluster.nanites['nanite_id'].keys.should include(:services)
|
265
|
+
@cluster.nanites['nanite_id'].keys.should include(:status)
|
266
|
+
@cluster.nanites['nanite_id'][:services].should == ["the_nanite_services"]
|
267
|
+
@cluster.nanites['nanite_id'][:status].should == "nanite_status"
|
268
|
+
end
|
269
|
+
|
270
|
+
it "should add nanite to reaper" do
|
271
|
+
@reaper.should_receive(:register).with('nanite_id', 33)
|
272
|
+
@cluster.register(@register_packet)
|
273
|
+
end
|
274
|
+
|
275
|
+
it "should log info message that nanite was registered" do
|
276
|
+
Nanite::Log.should_receive(:info)
|
277
|
+
@cluster.register(@register_packet)
|
278
|
+
end
|
279
|
+
|
280
|
+
it "should save the timestamp that the nanite was updated" do
|
281
|
+
@cluster.register(@register_packet)
|
282
|
+
@cluster.nanites['nanite_id'][:timestamp].should be_close(Time.now.utc.to_i, 1)
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "with registered callbacks" do
|
286
|
+
before(:each) do
|
287
|
+
@register_callback = lambda {|request, mapper|}
|
288
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper, nil, :register => @register_callback)
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should call the registration callback" do
|
292
|
+
@register_callback.should_receive(:call).with("nanite_id", @mapper)
|
293
|
+
@cluster.register(@register_packet)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe "when sending an invalid packet to the registration queue" do
|
298
|
+
it "should log a message statement" do
|
299
|
+
Nanite::Log.logger.should_receive(:warn).with("RECV [register] Invalid packet type: Nanite::Ping")
|
300
|
+
@cluster.register(Nanite::Ping.new(nil, nil))
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end # Nanite Registration
|
304
|
+
|
305
|
+
describe "Unregister" do
|
306
|
+
before(:each) do
|
307
|
+
@fanout = mock("fanout")
|
308
|
+
@binding = mock("binding", :subscribe => true)
|
309
|
+
@queue = mock("queue", :bind => @binding)
|
310
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
311
|
+
@serializer = mock("Serializer")
|
312
|
+
@reaper = mock("Reaper", :timeout => true, :unregister => nil)
|
313
|
+
Nanite::Log.stub!(:info)
|
314
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
315
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
316
|
+
@cluster.nanites["nanite_id"] = "nanite_id"
|
317
|
+
@unregister_packet = Nanite::UnRegister.new("nanite_id")
|
318
|
+
end
|
319
|
+
|
320
|
+
it "should delete the nanite" do
|
321
|
+
@cluster.register(@unregister_packet)
|
322
|
+
@cluster.nanites["nanite_id"].should == nil
|
323
|
+
end
|
324
|
+
|
325
|
+
it "should unregister the nanite from the reaper" do
|
326
|
+
@reaper.should_receive(:unregister).with('nanite_id')
|
327
|
+
@cluster.register(@unregister_packet)
|
328
|
+
end
|
329
|
+
|
330
|
+
describe "with registered callbacks" do
|
331
|
+
before(:each) do
|
332
|
+
@unregister_callback = lambda {|request, mapper| }
|
333
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper, nil, :unregister => @unregister_callback)
|
334
|
+
@cluster.nanites["nanite_id"] = "nanite_id"
|
335
|
+
end
|
336
|
+
|
337
|
+
it "should call the unregister callback" do
|
338
|
+
@unregister_callback.should_receive(:call).with("nanite_id", @mapper)
|
339
|
+
@cluster.register(@unregister_packet)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
describe "Nanite timed out" do
|
345
|
+
before(:each) do
|
346
|
+
@fanout = mock("fanout")
|
347
|
+
@binding = mock("binding", :subscribe => true)
|
348
|
+
@queue = mock("queue", :bind => @binding)
|
349
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
350
|
+
@serializer = mock("Serializer")
|
351
|
+
Nanite::Log.stub!(:info)
|
352
|
+
@register_packet = Nanite::Register.new("nanite_id", ["the_nanite_services"], "nanite_status",[])
|
353
|
+
end
|
354
|
+
|
355
|
+
it "should remove the nanite when timed out" do
|
356
|
+
EM.run do
|
357
|
+
@cluster = Nanite::Cluster.new(@amq, 0.01, "the_identity", @serializer, @mapper)
|
358
|
+
@cluster.register(@register_packet)
|
359
|
+
EM.add_timer(1.1) {
|
360
|
+
@cluster.nanites["nanite_id"].should == nil
|
361
|
+
EM.stop_event_loop
|
362
|
+
}
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
it "should call the timed out callback handler when registered" do
|
367
|
+
EM.run do
|
368
|
+
@cluster = Nanite::Cluster.new(@amq, 0.01, "the_identity", @serializer, @mapper)
|
369
|
+
@cluster.register(@register_packet)
|
370
|
+
@cluster.should_receive(:nanite_timed_out).twice
|
371
|
+
EM.add_timer(1.1) {
|
372
|
+
EM.stop_event_loop
|
373
|
+
}
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
it "should not remove the agent when the callback returned false" do
|
378
|
+
EM.run do
|
379
|
+
@cluster = Nanite::Cluster.new(@amq, 0.01, "the_identity", @serializer, @mapper)
|
380
|
+
@cluster.register(@register_packet)
|
381
|
+
@cluster.stub!(:nanite_timed_out).and_return(false)
|
382
|
+
EM.add_timer(1.1) {
|
383
|
+
@cluster.nanites["nanite_id"].should_not == nil
|
384
|
+
EM.stop_event_loop
|
385
|
+
}
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
describe "nanite_timed_out" do
|
391
|
+
before(:each) do
|
392
|
+
@fanout = mock("fanout")
|
393
|
+
@binding = mock("binding", :subscribe => true)
|
394
|
+
@queue = mock("queue", :bind => @binding)
|
395
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
396
|
+
@serializer = mock("Serializer")
|
397
|
+
end
|
398
|
+
|
399
|
+
it "should remove the nanite from the list" do
|
400
|
+
run_in_em do
|
401
|
+
cluster = Nanite::Cluster.new(@amq, 0, "the_identity", @serializer, @mapper)
|
402
|
+
cluster.nanites['123456'] = { :services => ['/ls/al'], :status => 0.03, :tags => nil, :timestamp => Time.now.utc.to_i - 1 }
|
403
|
+
|
404
|
+
cluster.nanite_timed_out("123456")
|
405
|
+
cluster.nanites.should == {}
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
it "should not remove the nanite from the list when it was updated with the timeout timeframe" do
|
410
|
+
run_in_em do
|
411
|
+
cluster = Nanite::Cluster.new(@amq, 5, "the_identity", @serializer, @mapper)
|
412
|
+
cluster.nanites['123456'] = { :services => ['/ls/al'], :status => 0.03, :tags => nil, :timestamp => Time.now.utc.to_i - 2 }
|
413
|
+
cluster.nanite_timed_out("123456")
|
414
|
+
cluster.nanites.should_not == {}
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
it "should not fail when the Nanite wasn't found in the state storage" do
|
419
|
+
run_in_em do
|
420
|
+
cluster = Nanite::Cluster.new(@amq, 5, "the_identity", @serializer, @mapper)
|
421
|
+
cluster.nanites['654321'] = {}
|
422
|
+
lambda do
|
423
|
+
cluster.nanite_timed_out("123456")
|
424
|
+
end.should_not raise_error
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
describe "Route" do
|
430
|
+
|
431
|
+
before(:each) do
|
432
|
+
@fanout = mock("fanout")
|
433
|
+
@binding = mock("binding", :subscribe => true)
|
434
|
+
@queue = mock("queue", :bind => @binding)
|
435
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
436
|
+
@serializer = mock("Serializer")
|
437
|
+
@reaper = mock("Reaper")
|
438
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
439
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
440
|
+
@request = mock("Request")
|
441
|
+
end
|
442
|
+
|
443
|
+
it "should publish request to all targets" do
|
444
|
+
target1 = mock("Target 1")
|
445
|
+
target2 = mock("Target 2")
|
446
|
+
@cluster.should_receive(:publish).with(@request, target1)
|
447
|
+
@cluster.should_receive(:publish).with(@request, target2)
|
448
|
+
EM.run {
|
449
|
+
@cluster.route(@request, [target1, target2])
|
450
|
+
EM.stop
|
451
|
+
}
|
452
|
+
end
|
453
|
+
|
454
|
+
end # Route
|
455
|
+
|
456
|
+
|
457
|
+
describe "Publish" do
|
458
|
+
|
459
|
+
before(:each) do
|
460
|
+
@fanout = mock("fanout")
|
461
|
+
@binding = mock("binding", :subscribe => true)
|
462
|
+
@queue = mock("queue", :bind => @binding, :publish => true)
|
463
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
464
|
+
@serializer = mock("Serializer", :dump => "dumped_value")
|
465
|
+
@reaper = mock("Reaper")
|
466
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
467
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
468
|
+
@request = mock("Request", :persistent => true, :target => nil, :target= => nil, :to_s => nil)
|
469
|
+
@target = mock("Target of Request")
|
470
|
+
end
|
471
|
+
|
472
|
+
it "should serialize request before publishing it" do
|
473
|
+
@request.should_receive(:target=).with(@target)
|
474
|
+
@request.should_receive(:target=)
|
475
|
+
@request.should_receive(:target)
|
476
|
+
@serializer.should_receive(:dump).with(@request).and_return("serialized_request")
|
477
|
+
@cluster.publish(@request, @target)
|
478
|
+
end
|
479
|
+
|
480
|
+
it "should publish request to target queue" do
|
481
|
+
@request.should_receive(:target=).with(@target)
|
482
|
+
@request.should_receive(:target=)
|
483
|
+
@request.should_receive(:target)
|
484
|
+
@queue.should_receive(:publish).with("dumped_value", anything())
|
485
|
+
@cluster.publish(@request, @target)
|
486
|
+
end
|
487
|
+
|
488
|
+
it "should persist request based on request setting" do
|
489
|
+
@request.should_receive(:target=).with(@target)
|
490
|
+
@request.should_receive(:target=)
|
491
|
+
@request.should_receive(:target)
|
492
|
+
@request.should_receive(:persistent).and_return(false)
|
493
|
+
@queue.should_receive(:publish).with(anything(), { :persistent => false })
|
494
|
+
@cluster.publish(@request, @target)
|
495
|
+
end
|
496
|
+
|
497
|
+
end # Publish
|
498
|
+
|
499
|
+
describe "Agent Request Handling" do
|
500
|
+
|
501
|
+
before(:each) do
|
502
|
+
@fanout = mock("fanout")
|
503
|
+
@binding = mock("binding", :subscribe => true)
|
504
|
+
@queue = mock("queue", :bind => @binding, :publish => true)
|
505
|
+
@amq = mock("AMPQueue", :queue => @queue, :fanout => @fanout)
|
506
|
+
@serializer = mock("Serializer", :dump => "dumped_value")
|
507
|
+
@target = mock("Target of Request")
|
508
|
+
@reaper = mock("Reaper")
|
509
|
+
Nanite::Reaper.stub!(:new).and_return(@reaper)
|
510
|
+
@request_without_target = mock("Request", :target => nil, :token => "Token",
|
511
|
+
:reply_to => "Reply To", :from => "From", :persistent => true, :identity => "Identity",
|
512
|
+
:payload => "Payload", :to_s => nil)
|
513
|
+
@request_with_target = mock("Request", :target => "Target", :token => "Token",
|
514
|
+
:reply_to => "Reply To", :from => "From", :persistent => true, :payload => "Payload", :to_s => nil)
|
515
|
+
@mapper_with_target = mock("Mapper", :identity => "id")
|
516
|
+
@mapper_without_target = mock("Mapper", :request => false, :identity => @request_without_target.identity)
|
517
|
+
@cluster_with_target = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper_with_target)
|
518
|
+
@cluster_without_target = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper_without_target)
|
519
|
+
Nanite::Cluster.stub!(:mapper).and_return(@mapper)
|
520
|
+
end
|
521
|
+
|
522
|
+
it "should forward requests with targets" do
|
523
|
+
@mapper_with_target.should_receive(:send_request).with(@request_with_target, anything())
|
524
|
+
@cluster_with_target.__send__(:handle_request, @request_with_target)
|
525
|
+
end
|
526
|
+
|
527
|
+
it "should reply back with nil results for requests with no target when offline queue is disabled" do
|
528
|
+
@mapper_without_target.should_receive(:send_request).with(@request_without_target, anything())
|
529
|
+
Nanite::Result.should_receive(:new).with(@request_without_target.token, @request_without_target.from, nil, @request_without_target.identity)
|
530
|
+
@cluster_without_target.__send__(:handle_request, @request_without_target)
|
531
|
+
end
|
532
|
+
|
533
|
+
it "should hand in an intermediate handler" do
|
534
|
+
@mapper_with_target.should_receive(:send_request) do |request, opts|
|
535
|
+
opts[:intermediate_handler].should be_instance_of(Proc)
|
536
|
+
end
|
537
|
+
|
538
|
+
@cluster_with_target.__send__(:handle_request, @request_with_target)
|
539
|
+
end
|
540
|
+
|
541
|
+
it "should forward the message when send_request failed" do
|
542
|
+
@mapper_with_target.stub!(:send_request).and_return(false)
|
543
|
+
@cluster_with_target.should_receive(:forward_response)
|
544
|
+
@cluster_with_target.__send__(:handle_request, @request_with_target)
|
545
|
+
end
|
546
|
+
|
547
|
+
describe "when getting push requests from an agent" do
|
548
|
+
it "should send the push message through the mapper" do
|
549
|
+
push = Nanite::Push.new(nil, nil)
|
550
|
+
@mapper_with_target.should_receive(:send_push).with(push)
|
551
|
+
@cluster_with_target.__send__(:handle_request, push)
|
552
|
+
end
|
553
|
+
end
|
554
|
+
end # Agent Request Handling
|
555
|
+
|
556
|
+
describe "Heartbeat" do
|
557
|
+
before(:each) do
|
558
|
+
@fanout = mock("fanout")
|
559
|
+
@binding = mock("binding", :subscribe => true)
|
560
|
+
@queue = mock("queue", :bind => @binding, :publish => true)
|
561
|
+
@amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
|
562
|
+
@serializer = mock("Serializer", :dump => "dumped_value")
|
563
|
+
Nanite::Log.stub!(:info)
|
564
|
+
@ping = stub("ping", :status => 0.3, :identity => "nanite_id")
|
565
|
+
end
|
566
|
+
|
567
|
+
it "should update the nanite status" do
|
568
|
+
run_in_em do
|
569
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
570
|
+
@cluster.nanites["nanite_id"] = {:status => "nanite_status"}
|
571
|
+
@cluster.send :handle_ping, @ping
|
572
|
+
@cluster.nanites["nanite_id"][:status].should == 0.3
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
it "should reset the agent time out" do
|
577
|
+
run_in_em do
|
578
|
+
@cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
579
|
+
@cluster.reaper.should_receive(:update).with("nanite_id", 33)
|
580
|
+
@cluster.nanites["nanite_id"] = {:status => "nanite_status"}
|
581
|
+
@cluster.send :handle_ping, @ping
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
it "should store the timestamp of the last update" do
|
586
|
+
run_in_em do
|
587
|
+
cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
|
588
|
+
cluster.reaper.should_receive(:update).with("nanite_id", 33)
|
589
|
+
cluster.nanites["nanite_id"] = {:status => "nanite_status"}
|
590
|
+
cluster.send :handle_ping, @ping
|
591
|
+
cluster.nanites["nanite_id"][:timestamp].should be_close(Time.now.utc.to_i, 1)
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
describe "when timing out after a heartbeat" do
|
596
|
+
it "should remove the nanite" do
|
597
|
+
run_in_em(false) do
|
598
|
+
@cluster = Nanite::Cluster.new(@amq, 0.1, "the_identity", @serializer, @mapper)
|
599
|
+
@cluster.nanites["nanite_id"] = {:status => "nanite_status"}
|
600
|
+
@cluster.send :handle_ping, @ping
|
601
|
+
EM.add_timer(1.5) do
|
602
|
+
@cluster.nanites["nanite_id"].should == nil
|
603
|
+
EM.stop_event_loop
|
604
|
+
end
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
it "should call the timeout callback when defined" do
|
609
|
+
run_in_em(false) do
|
610
|
+
@timeout_callback = lambda {|nanite, mapper| }
|
611
|
+
@timeout_callback.should_receive(:call).with("nanite_id", @mapper)
|
612
|
+
@cluster = Nanite::Cluster.new(@amq, 0.1, "the_identity", @serializer, @mapper, nil, :timeout => @timeout_callback)
|
613
|
+
@cluster.nanites["nanite_id"] = {:status => "nanite_status"}
|
614
|
+
@cluster.send :handle_ping, @ping
|
615
|
+
EM.add_timer(1.5) do
|
616
|
+
EM.stop_event_loop
|
617
|
+
end
|
618
|
+
end
|
619
|
+
end
|
620
|
+
end
|
621
|
+
end
|
622
|
+
end # Nanite::Cluster
|