rightscale-nanite 0.4.1 → 0.4.1.1

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.
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