rightscale-nanite 0.4.1 → 0.4.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/lib/nanite.rb +71 -0
  2. data/lib/nanite/actor.rb +60 -0
  3. data/lib/nanite/actor_registry.rb +24 -0
  4. data/lib/nanite/admin.rb +153 -0
  5. data/lib/nanite/agent.rb +250 -0
  6. data/lib/nanite/amqp.rb +47 -0
  7. data/lib/nanite/cluster.rb +203 -0
  8. data/lib/nanite/config.rb +102 -0
  9. data/lib/nanite/console.rb +39 -0
  10. data/lib/nanite/daemonize.rb +13 -0
  11. data/lib/nanite/dispatcher.rb +90 -0
  12. data/lib/nanite/identity.rb +16 -0
  13. data/lib/nanite/job.rb +104 -0
  14. data/lib/nanite/local_state.rb +34 -0
  15. data/lib/nanite/log.rb +64 -0
  16. data/lib/nanite/log/formatter.rb +39 -0
  17. data/lib/nanite/mapper.rb +277 -0
  18. data/lib/nanite/mapper_proxy.rb +56 -0
  19. data/lib/nanite/packets.rb +231 -0
  20. data/lib/nanite/pid_file.rb +52 -0
  21. data/lib/nanite/reaper.rb +38 -0
  22. data/lib/nanite/security/cached_certificate_store_proxy.rb +24 -0
  23. data/lib/nanite/security/certificate.rb +55 -0
  24. data/lib/nanite/security/certificate_cache.rb +66 -0
  25. data/lib/nanite/security/distinguished_name.rb +34 -0
  26. data/lib/nanite/security/encrypted_document.rb +46 -0
  27. data/lib/nanite/security/rsa_key_pair.rb +53 -0
  28. data/lib/nanite/security/secure_serializer.rb +67 -0
  29. data/lib/nanite/security/signature.rb +40 -0
  30. data/lib/nanite/security/static_certificate_store.rb +35 -0
  31. data/lib/nanite/security_provider.rb +47 -0
  32. data/lib/nanite/serializer.rb +52 -0
  33. data/lib/nanite/state.rb +164 -0
  34. data/lib/nanite/streaming.rb +125 -0
  35. data/lib/nanite/util.rb +51 -0
  36. data/spec/actor_registry_spec.rb +62 -0
  37. data/spec/actor_spec.rb +59 -0
  38. data/spec/agent_spec.rb +235 -0
  39. data/spec/cached_certificate_store_proxy_spec.rb +34 -0
  40. data/spec/certificate_cache_spec.rb +49 -0
  41. data/spec/certificate_spec.rb +27 -0
  42. data/spec/cluster_spec.rb +300 -0
  43. data/spec/dispatcher_spec.rb +136 -0
  44. data/spec/distinguished_name_spec.rb +24 -0
  45. data/spec/encrypted_document_spec.rb +21 -0
  46. data/spec/job_spec.rb +219 -0
  47. data/spec/local_state_spec.rb +112 -0
  48. data/spec/packet_spec.rb +218 -0
  49. data/spec/rsa_key_pair_spec.rb +33 -0
  50. data/spec/secure_serializer_spec.rb +41 -0
  51. data/spec/serializer_spec.rb +107 -0
  52. data/spec/signature_spec.rb +30 -0
  53. data/spec/spec_helper.rb +23 -0
  54. data/spec/static_certificate_store_spec.rb +30 -0
  55. data/spec/util_spec.rb +63 -0
  56. metadata +62 -1
@@ -0,0 +1,235 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe "Agent:" do
4
+
5
+ describe "Default Option" do
6
+
7
+ before(:all) do
8
+ EM.stub!(:add_periodic_timer)
9
+ AMQP.stub!(:connect)
10
+ @amq = mock("AMQueue", :queue => mock("queue", :subscribe => {}), :fanout => mock("fanout", :publish => nil))
11
+ MQ.stub!(:new).and_return(@amq)
12
+ @agent = Nanite::Agent.start
13
+ end
14
+
15
+ it "for daemonize is false" do
16
+ @agent.options.should include(:daemonize)
17
+ @agent.options[:daemonize].should == false
18
+ end
19
+
20
+ it "for format is marshal" do
21
+ @agent.options.should include(:format)
22
+ @agent.options[:format].should == :marshal
23
+ end
24
+
25
+ it "for console is false" do
26
+ @agent.options.should include(:console)
27
+ @agent.options[:console].should == false
28
+ end
29
+
30
+ it "for user is nanite" do
31
+ @agent.options.should include(:user)
32
+ @agent.options[:user].should == "nanite"
33
+ end
34
+
35
+ it "for pass(word) is testing" do
36
+ @agent.options.should include(:pass)
37
+ @agent.options[:pass].should == "testing"
38
+ end
39
+
40
+ it "for secure is false" do
41
+ @agent.options.should include(:secure)
42
+ @agent.options[:secure].should == false
43
+ end
44
+
45
+ it "for host is 0.0.0.0" do
46
+ @agent.options.should include(:host)
47
+ @agent.options[:host].should == "0.0.0.0"
48
+ end
49
+
50
+ it "for log_level is info" do
51
+ @agent.options.should include(:log_level)
52
+ @agent.options[:log_level].should == :info
53
+ end
54
+
55
+ it "for vhost is /nanite" do
56
+ @agent.options.should include(:vhost)
57
+ @agent.options[:vhost].should == "/nanite"
58
+ end
59
+
60
+ it "for ping_time is 15" do
61
+ @agent.options.should include(:ping_time)
62
+ @agent.options[:ping_time].should == 15
63
+ end
64
+
65
+ it "for default_services is []" do
66
+ @agent.options.should include(:default_services)
67
+ @agent.options[:default_services].should == []
68
+ end
69
+
70
+ it "for root is #{File.expand_path(File.join(File.dirname(__FILE__), '..'))}" do
71
+ @agent.options.should include(:root)
72
+ @agent.options[:root].should == File.expand_path(File.join(File.dirname(__FILE__), '..'))
73
+ end
74
+
75
+ it "for file_root is #{File.expand_path(File.join(File.dirname(__FILE__), '..', 'files'))}" do
76
+ @agent.options.should include(:file_root)
77
+ @agent.options[:file_root].should == File.expand_path(File.join(File.dirname(__FILE__), '..', 'files'))
78
+ end
79
+
80
+ end
81
+
82
+ describe "Options from config.yml" do
83
+
84
+ before(:all) do
85
+ @agent = Nanite::Agent.start
86
+ end
87
+
88
+ end
89
+
90
+ describe "Passed in Options" do
91
+
92
+ before(:each) do
93
+ EM.stub!(:add_periodic_timer)
94
+ AMQP.stub!(:connect)
95
+ @amq = mock("AMQueue", :queue => mock("queue", :subscribe => {}), :fanout => mock("fanout", :publish => nil))
96
+ MQ.stub!(:new).and_return(@amq)
97
+ end
98
+
99
+ # TODO figure out how to stub call to daemonize
100
+ # it "for daemonize should override default (false)" do
101
+ # agent = Nanite::Agent.start(:daemonize => true)
102
+ # agent.options.should include(:daemonize)
103
+ # agent.options[:daemonize].should == true
104
+ # end
105
+
106
+ it "for format should override default (marshal)" do
107
+ agent = Nanite::Agent.start(:format => :json)
108
+ agent.options.should include(:format)
109
+ agent.options[:format].should == :json
110
+ end
111
+
112
+ # TODO figure out how to avoid console output
113
+ # it "for console should override default (false)" do
114
+ # agent = Nanite::Agent.start(:console => true)
115
+ # agent.options.should include(:console)
116
+ # agent.options[:console].should == true
117
+ # end
118
+
119
+ it "for user should override default (nanite)" do
120
+ agent = Nanite::Agent.start(:user => "me")
121
+ agent.options.should include(:user)
122
+ agent.options[:user].should == "me"
123
+ end
124
+
125
+ it "for pass(word) should override default (testing)" do
126
+ agent = Nanite::Agent.start(:pass => "secret")
127
+ agent.options.should include(:pass)
128
+ agent.options[:pass].should == "secret"
129
+ end
130
+
131
+ it "for secure should override default (false)" do
132
+ agent = Nanite::Agent.start(:secure => true)
133
+ agent.options.should include(:secure)
134
+ agent.options[:secure].should == true
135
+ end
136
+
137
+ it "for host should override default (0.0.0.0)" do
138
+ agent = Nanite::Agent.start(:host => "127.0.0.1")
139
+ agent.options.should include(:host)
140
+ agent.options[:host].should == "127.0.0.1"
141
+ end
142
+
143
+ it "for log_level should override default (info)" do
144
+ agent = Nanite::Agent.start(:log_level => :debug)
145
+ agent.options.should include(:log_level)
146
+ agent.options[:log_level].should == :debug
147
+ end
148
+
149
+ it "for vhost should override default (/nanite)" do
150
+ agent = Nanite::Agent.start(:vhost => "/virtual_host")
151
+ agent.options.should include(:vhost)
152
+ agent.options[:vhost].should == "/virtual_host"
153
+ end
154
+
155
+ it "for ping_time should override default (15)" do
156
+ agent = Nanite::Agent.start(:ping_time => 5)
157
+ agent.options.should include(:ping_time)
158
+ agent.options[:ping_time].should == 5
159
+ end
160
+
161
+ it "for default_services should override default ([])" do
162
+ agent = Nanite::Agent.start(:default_services => [:test])
163
+ agent.options.should include(:default_services)
164
+ agent.options[:default_services].should == [:test]
165
+ end
166
+
167
+ it "for root should override default (#{File.expand_path(File.join(File.dirname(__FILE__), '..'))})" do
168
+ agent = Nanite::Agent.start(:root => File.expand_path(File.dirname(__FILE__)))
169
+ agent.options.should include(:root)
170
+ agent.options[:root].should == File.expand_path(File.dirname(__FILE__))
171
+ end
172
+
173
+ it "for file_root should override default (#{File.expand_path(File.join(File.dirname(__FILE__), '..', 'files'))})" do
174
+ agent = Nanite::Agent.start(:file_root => File.expand_path(File.dirname(__FILE__)))
175
+ agent.options.should include(:file_root)
176
+ agent.options[:file_root].should == File.expand_path(File.dirname(__FILE__))
177
+ end
178
+
179
+ it "for a single tag should result in the agent's tags being set" do
180
+ agent = Nanite::Agent.start(:tag => "sample_tag")
181
+ agent.tags.should include("sample_tag")
182
+ end
183
+
184
+ it "for multiple tags should result in the agent's tags being set" do
185
+ agent = Nanite::Agent.start(:tag => ["sample_tag_1", "sample_tag_2"])
186
+ agent.tags.should include("sample_tag_1")
187
+ agent.tags.should include("sample_tag_2")
188
+ end
189
+
190
+ end
191
+
192
+ describe "Security" do
193
+
194
+ before(:each) do
195
+ EM.stub!(:add_periodic_timer)
196
+ AMQP.stub!(:connect)
197
+ @amq = mock("AMQueue", :queue => mock("queue", :subscribe => {}, :publish => {}), :fanout => mock("fanout", :publish => nil))
198
+ MQ.stub!(:new).and_return(@amq)
199
+ serializer = Nanite::Serializer.new
200
+ @request = Nanite::Request.new('/foo/bar', '')
201
+ @push = Nanite::Push.new('/foo/bar', '')
202
+ @agent = Nanite::Agent.start
203
+ end
204
+
205
+ it 'should correctly deny requests' do
206
+ security = mock("Security")
207
+ @agent.register_security(security)
208
+
209
+ security.should_receive(:authorize).twice.and_return(false)
210
+ @agent.dispatcher.should_not_receive(:dispatch)
211
+ @agent.__send__(:receive, @request)
212
+ @agent.__send__(:receive, @push)
213
+ end
214
+
215
+ it 'should correctly authorize requests' do
216
+ security = mock("Security")
217
+ @agent.register_security(security)
218
+
219
+ security.should_receive(:authorize).twice.and_return(true)
220
+ @agent.dispatcher.stub!(:dispatch)
221
+ @agent.dispatcher.should_receive(:dispatch).twice
222
+ @agent.__send__(:receive, @request)
223
+ @agent.__send__(:receive, @push)
224
+ end
225
+
226
+ it 'should be ignored when not specified' do
227
+ @agent.dispatcher.stub!(:dispatch)
228
+ @agent.dispatcher.should_receive(:dispatch).twice
229
+ @agent.__send__(:receive, @request)
230
+ @agent.__send__(:receive, @push)
231
+ end
232
+
233
+ end
234
+
235
+ end
@@ -0,0 +1,34 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Nanite::CachedCertificateStoreProxy do
4
+
5
+ include SpecHelpers
6
+
7
+ before(:all) do
8
+ @signer, key = issue_cert
9
+ @recipient, key = issue_cert
10
+ @store = mock("Store")
11
+ @proxy = Nanite::CachedCertificateStoreProxy.new(@store)
12
+ end
13
+
14
+ it 'should not raise and return nil for non existent certificates' do
15
+ res = nil
16
+ @store.should_receive(:get_recipients).with(nil).and_return(nil)
17
+ lambda { res = @proxy.get_recipients(nil) }.should_not raise_error
18
+ res.should == nil
19
+ @store.should_receive(:get_signer).with(nil).and_return(nil)
20
+ lambda { res = @proxy.get_signer(nil) }.should_not raise_error
21
+ res.should == nil
22
+ end
23
+
24
+ it 'should return recipient certificates' do
25
+ @store.should_receive(:get_recipients).with('anything').and_return(@recipient)
26
+ @proxy.get_recipients('anything').should == @recipient
27
+ end
28
+
29
+ it 'should return signer certificates' do
30
+ @store.should_receive(:get_signer).with('anything').and_return(@signer)
31
+ @proxy.get_signer('anything').should == @signer
32
+ end
33
+
34
+ end
@@ -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,300 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Nanite::Cluster do
4
+
5
+ describe "Intialization" do
6
+
7
+ before(:each) do
8
+ @fanout = mock("fanout")
9
+ @binding = mock("binding", :subscribe => true)
10
+ @queue = mock("queue", :bind => @binding)
11
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
12
+ @serializer = mock("Serializer")
13
+ @reaper = mock("Reaper")
14
+ @mapper = mock("Mapper")
15
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
16
+ end
17
+
18
+ describe "of Heartbeat (Queue)" do
19
+
20
+ it "should setup the heartbeat (queue) for id" do
21
+ @amq.should_receive(:queue).with("heartbeat-the_identity", anything()).and_return(@queue)
22
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
23
+ end
24
+
25
+ it "should make the heartbeat (queue) exclusive" do
26
+ @amq.should_receive(:queue).with("heartbeat-the_identity", { :exclusive => true }).and_return(@queue)
27
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
28
+ end
29
+
30
+ it "should bind the heartbeat (queue) to 'heartbeat' fanout" do
31
+ @amq.should_receive(:fanout).with("heartbeat", { :durable => true }).and_return(@fanout)
32
+ @queue.should_receive(:bind).with(@fanout).and_return(@binding)
33
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
34
+ end
35
+
36
+ end # of Heartbeat (Queue)
37
+
38
+
39
+ describe "of Registration (Queue)" do
40
+
41
+ it "should setup the registration (queue) for id" do
42
+ @amq.should_receive(:queue).with("registration-the_identity", anything()).and_return(@queue)
43
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
44
+ end
45
+
46
+ it "should make the registration (queue) exclusive" do
47
+ @amq.should_receive(:queue).with("registration-the_identity", { :exclusive => true }).and_return(@queue)
48
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
49
+ end
50
+
51
+ it "should bind the registration (queue) to 'registration' fanout" do
52
+ @amq.should_receive(:fanout).with("registration", { :durable => true }).and_return(@fanout)
53
+ @queue.should_receive(:bind).with(@fanout).and_return(@binding)
54
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
55
+ end
56
+
57
+ end # of Registration (Queue)
58
+
59
+ describe "of Request (Queue)" do
60
+
61
+ it "should setup the request (queue) for id" do
62
+ @amq.should_receive(:queue).with("request-the_identity", anything()).and_return(@queue)
63
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
64
+ end
65
+
66
+ it "should make the request (queue) exclusive" do
67
+ @amq.should_receive(:queue).with("request-the_identity", { :exclusive => true }).and_return(@queue)
68
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
69
+ end
70
+
71
+ it "should bind the request (queue) to 'request' fanout" do
72
+ @amq.should_receive(:fanout).with("request", { :durable => true }).and_return(@fanout)
73
+ @queue.should_receive(:bind).with(@fanout).and_return(@binding)
74
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
75
+ end
76
+
77
+ end # of Request (Queue)
78
+
79
+
80
+ describe "Reaper" do
81
+
82
+ it "should be created" do
83
+ Nanite::Reaper.should_receive(:new).with(anything()).and_return(@reaper)
84
+ cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
85
+ end
86
+
87
+ it "should use the agent timeout" do
88
+ Nanite::Reaper.should_receive(:new).with(443).and_return(@reaper)
89
+ cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper)
90
+ end
91
+
92
+ end # Reaper
93
+
94
+ end # Intialization
95
+
96
+
97
+ describe "Target Selection" do
98
+
99
+ before(:each) do
100
+ @fanout = mock("fanout")
101
+ @binding = mock("binding", :subscribe => true)
102
+ @queue = mock("queue", :bind => @binding)
103
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
104
+ @serializer = mock("Serializer")
105
+ @reaper = mock("Reaper")
106
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
107
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
108
+ end
109
+
110
+ it "should return array containing targets for request" do
111
+ target = mock("Supplied Target")
112
+ request = mock("Request", :target => target)
113
+ @cluster.targets_for(request).should be_instance_of(Array)
114
+ end
115
+
116
+ it "should use target from request" do
117
+ target = mock("Supplied Target")
118
+ request = mock("Request", :target => target)
119
+ @cluster.targets_for(request).should == [target]
120
+ end
121
+
122
+ it "should use targets choosen by least loaded selector (:least_loaded)" do
123
+ targets = { "target 3" => 3 }
124
+ request = mock("Request", :target => nil, :selector => :least_loaded, :type => "service", :tags => [])
125
+ @cluster.should_receive(:least_loaded).with("service", []).and_return(targets)
126
+ @cluster.targets_for(request).should == ["target 3"]
127
+ end
128
+
129
+ it "should use targets choosen by all selector (:all)" do
130
+ targets = { "target 1" => 1, "target 2" => 2, "target 3" => 3 }
131
+ request = mock("Request", :target => nil, :selector => :all, :type => "service", :tags => [])
132
+ @cluster.should_receive(:all).with("service", []).and_return(targets)
133
+ @cluster.targets_for(request).should == ["target 1", "target 2", "target 3"]
134
+ end
135
+
136
+ it "should use targets choosen by random selector (:random)" do
137
+ targets = { "target 3" => 3 }
138
+ request = mock("Request", :target => nil, :selector => :random, :type => "service", :tags => [])
139
+ @cluster.should_receive(:random).with("service", []).and_return(targets)
140
+ @cluster.targets_for(request).should == ["target 3"]
141
+ end
142
+
143
+ it "should use targets choosen by round-robin selector (:rr)" do
144
+ targets = { "target 2" => 2 }
145
+ request = mock("Request", :target => nil, :selector => :rr, :type => "service", :tags => [])
146
+ @cluster.should_receive(:rr).with("service", []).and_return(targets)
147
+ @cluster.targets_for(request).should == ["target 2"]
148
+ end
149
+
150
+ end # Target Selection
151
+
152
+
153
+ describe "Nanite Registration" do
154
+
155
+ before(:each) do
156
+ @fanout = mock("fanout")
157
+ @binding = mock("binding", :subscribe => true)
158
+ @queue = mock("queue", :bind => @binding)
159
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
160
+ @serializer = mock("Serializer")
161
+ @reaper = mock("Reaper", :timeout => true)
162
+ Nanite::Log.stub!(:info)
163
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
164
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
165
+ @register_packet = Nanite::Register.new("nanite_id", ["the_nanite_services"], "nanite_status",[])
166
+ end
167
+
168
+ it "should add the Nanite to the nanites map" do
169
+ @cluster.register(@register_packet)
170
+ @cluster.nanites['nanite_id'].should_not be_nil
171
+ end
172
+
173
+ it "should use hash of the Nanite's services and status as value" do
174
+ @cluster.register(@register_packet)
175
+ @cluster.nanites['nanite_id'].keys.size == 2
176
+ @cluster.nanites['nanite_id'].keys.should include(:services)
177
+ @cluster.nanites['nanite_id'].keys.should include(:status)
178
+ @cluster.nanites['nanite_id'][:services].should == ["the_nanite_services"]
179
+ @cluster.nanites['nanite_id'][:status].should == "nanite_status"
180
+ end
181
+
182
+ it "should add nanite to reaper" do
183
+ @reaper.should_receive(:timeout).with('nanite_id', 33)
184
+ @cluster.register(@register_packet)
185
+ end
186
+
187
+ it "should log info message that nanite was registered" do
188
+ Nanite::Log.should_receive(:info)
189
+ @cluster.register(@register_packet)
190
+ end
191
+
192
+ end # Nanite Registration
193
+
194
+
195
+ describe "Route" do
196
+
197
+ before(:each) do
198
+ @fanout = mock("fanout")
199
+ @binding = mock("binding", :subscribe => true)
200
+ @queue = mock("queue", :bind => @binding)
201
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
202
+ @serializer = mock("Serializer")
203
+ @reaper = mock("Reaper")
204
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
205
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
206
+ @request = mock("Request")
207
+ end
208
+
209
+ it "should publish request to all targets" do
210
+ target1 = mock("Target 1")
211
+ target2 = mock("Target 2")
212
+ @cluster.should_receive(:publish).with(@request, target1)
213
+ @cluster.should_receive(:publish).with(@request, target2)
214
+ EM.run {
215
+ @cluster.route(@request, [target1, target2])
216
+ EM.stop
217
+ }
218
+ end
219
+
220
+ end # Route
221
+
222
+
223
+ describe "Publish" do
224
+
225
+ before(:each) do
226
+ @fanout = mock("fanout")
227
+ @binding = mock("binding", :subscribe => true)
228
+ @queue = mock("queue", :bind => @binding, :publish => true)
229
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
230
+ @serializer = mock("Serializer", :dump => "dumped_value")
231
+ @reaper = mock("Reaper")
232
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
233
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
234
+ @request = mock("Request", :persistent => true)
235
+ @target = mock("Target of Request")
236
+ end
237
+
238
+ it "should serialize request before publishing it" do
239
+ @request.should_receive(:target=).with(@target)
240
+ @request.should_receive(:target=)
241
+ @request.should_receive(:target)
242
+ @serializer.should_receive(:dump).with(@request).and_return("serialized_request")
243
+ @cluster.publish(@request, @target)
244
+ end
245
+
246
+ it "should publish request to target queue" do
247
+ @request.should_receive(:target=).with(@target)
248
+ @request.should_receive(:target=)
249
+ @request.should_receive(:target)
250
+ @queue.should_receive(:publish).with("dumped_value", anything())
251
+ @cluster.publish(@request, @target)
252
+ end
253
+
254
+ it "should persist request based on request setting" do
255
+ @request.should_receive(:target=).with(@target)
256
+ @request.should_receive(:target=)
257
+ @request.should_receive(:target)
258
+ @request.should_receive(:persistent).and_return(false)
259
+ @queue.should_receive(:publish).with(anything(), { :persistent => false })
260
+ @cluster.publish(@request, @target)
261
+ end
262
+
263
+ end # Publish
264
+
265
+ describe "Agent Request Handling" do
266
+
267
+ before(:each) do
268
+ @fanout = mock("fanout")
269
+ @binding = mock("binding", :subscribe => true)
270
+ @queue = mock("queue", :bind => @binding, :publish => true)
271
+ @amq = mock("AMPQueue", :queue => @queue, :fanout => @fanout)
272
+ @serializer = mock("Serializer", :dump => "dumped_value")
273
+ @target = mock("Target of Request")
274
+ @reaper = mock("Reaper")
275
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
276
+ @request_without_target = mock("Request", :target => nil, :token => "Token",
277
+ :reply_to => "Reply To", :from => "From", :persistent => true, :identity => "Identity")
278
+ @request_with_target = mock("Request", :target => "Target", :token => "Token",
279
+ :reply_to => "Reply To", :from => "From", :persistent => true)
280
+ @mapper_with_target = mock("Mapper", :identity => "id")
281
+ @mapper_without_target = mock("Mapper", :request => false, :identity => @request_without_target.identity)
282
+ @cluster_with_target = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper_with_target)
283
+ @cluster_without_target = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper_without_target)
284
+ Nanite::Cluster.stub!(:mapper).and_return(@mapper)
285
+ end
286
+
287
+ it "should forward requests with targets" do
288
+ @mapper_with_target.should_receive(:send_request).with(@request_with_target, anything())
289
+ @cluster_with_target.__send__(:handle_request, @request_with_target)
290
+ end
291
+
292
+ it "should reply back with nil results for requests with no target when offline queue is disabled" do
293
+ @mapper_without_target.should_receive(:send_request).with(@request_without_target, anything())
294
+ Nanite::Result.should_receive(:new).with(@request_without_target.token, @request_without_target.from, nil, @request_without_target.identity)
295
+ @cluster_without_target.__send__(:handle_request, @request_without_target)
296
+ end
297
+
298
+ end # Agent Request Handling
299
+
300
+ end # Nanite::Cluster