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,24 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe Nanite::DistinguishedName do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
test_dn = { 'C' => 'US',
|
7
|
+
'ST' => 'California',
|
8
|
+
'L' => 'Santa Barbara',
|
9
|
+
'O' => 'RightScale',
|
10
|
+
'OU' => 'Certification Services',
|
11
|
+
'CN' => 'rightscale.com/emailAddress=cert@rightscale.com' }
|
12
|
+
@dn = Nanite::DistinguishedName.new(test_dn)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should convert to string and X509 DN' do
|
16
|
+
@dn.to_s.should_not be_nil
|
17
|
+
@dn.to_x509.should_not be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should correctly encode' do
|
21
|
+
@dn.to_s.should == @dn.to_x509.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe Nanite::EncryptedDocument do
|
4
|
+
|
5
|
+
include SpecHelpers
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
@test_data = "Test Data to Sign"
|
9
|
+
@cert, @key = issue_cert
|
10
|
+
@doc = Nanite::EncryptedDocument.new(@test_data, @cert)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should create encrypted data' do
|
14
|
+
@doc.encrypted_data.should_not be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should decrypt correctly' do
|
18
|
+
@doc.decrypted_data(@key, @cert).should == @test_data
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/spec/job_spec.rb
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe Nanite::JobWarden do
|
4
|
+
|
5
|
+
describe "Creating a new Job" do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@serializer = mock("Serializer")
|
9
|
+
@warden = Nanite::JobWarden.new(@serializer)
|
10
|
+
|
11
|
+
@request = mock("Request")
|
12
|
+
@targets = mock("Targets")
|
13
|
+
@job = mock("Job", :token => "3faba24fcc")
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should instantiate a new Job" do
|
17
|
+
Nanite::Job.should_receive(:new).with(@request, @targets, nil, nil).and_return(@job)
|
18
|
+
@warden.new_job(@request, @targets)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should add the job to the job list" do
|
22
|
+
Nanite::Job.should_receive(:new).with(@request, @targets, nil, nil).and_return(@job)
|
23
|
+
@warden.jobs.size.should == 0
|
24
|
+
@warden.new_job(@request, @targets)
|
25
|
+
@warden.jobs.size.should == 1
|
26
|
+
@warden.jobs["3faba24fcc"].should == @job
|
27
|
+
end
|
28
|
+
|
29
|
+
it "return the newly crated job" do
|
30
|
+
Nanite::Job.should_receive(:new).with(@request, @targets, nil, nil).and_return(@job)
|
31
|
+
@warden.new_job(@request, @targets).should == @job
|
32
|
+
end
|
33
|
+
|
34
|
+
end # Creating a new Job
|
35
|
+
|
36
|
+
describe "Processing an intermediate message" do
|
37
|
+
before(:each) do
|
38
|
+
@intm_handler = lambda {|arg1, arg2, arg3| puts 'ehlo'}
|
39
|
+
@message = mock("Message", :token => "3faba24fcc", :from => 'nanite-agent')
|
40
|
+
@serializer = mock("Serializer", :load => @message)
|
41
|
+
@warden = Nanite::JobWarden.new(@serializer)
|
42
|
+
@job = Nanite::Job.new(stub("request", :token => "3faba24fcc"), [], @intm_handler)
|
43
|
+
@job.instance_variable_set(:@pending_keys, ["defaultkey"])
|
44
|
+
@job.instance_variable_set(:@intermediate_state, {"nanite-agent" => {"defaultkey" => [1]}})
|
45
|
+
@warden.jobs[@job.token] = @job
|
46
|
+
Nanite::Log.stub!(:debug)
|
47
|
+
Nanite::Log.stub!(:error)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should call the intermediate handler with three parameters" do
|
51
|
+
@intm_handler.should_receive(:call).with("defaultkey", "nanite-agent", 1)
|
52
|
+
@warden.process(@message)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should call the intermediate handler with four parameters" do
|
56
|
+
@intm_handler.stub!(:arity).and_return(4)
|
57
|
+
@intm_handler.should_receive(:call).with("defaultkey", "nanite-agent", 1, @job)
|
58
|
+
@warden.process(@message)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should not call the intermediate handler when it can't be found for the specified key" do
|
62
|
+
@intm_handler.should_not_receive(:call)
|
63
|
+
@job.instance_variable_set(:@intermediate_handler, nil)
|
64
|
+
@warden.process(@message)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should call the intermediate handler with one parameter which needs to be the result" do
|
68
|
+
@intm_handler.should_receive(:call).with(1, @job)
|
69
|
+
@intm_handler.stub!(:arity).and_return(2)
|
70
|
+
@warden.process(@message)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "Processing a Message" do
|
75
|
+
|
76
|
+
before(:each) do
|
77
|
+
@message = mock("Message", :token => "3faba24fcc")
|
78
|
+
@warden = Nanite::JobWarden.new(@serializer)
|
79
|
+
@job = mock("Job", :token => "3faba24fcc", :process => true, :completed? => false, :results => 42, :pending_keys => [], :intermediate_handler => true)
|
80
|
+
|
81
|
+
Nanite::Log.stub!(:debug)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should hand over processing to job" do
|
85
|
+
Nanite::Job.stub!(:new).and_return(@job)
|
86
|
+
@job.should_receive(:process).with(@message)
|
87
|
+
|
88
|
+
@warden.new_job("request", "targets")
|
89
|
+
@warden.process(@message)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should delete job from jobs after completion" do
|
93
|
+
Nanite::Job.stub!(:new).and_return(@job)
|
94
|
+
@job.should_receive(:process).with(@message)
|
95
|
+
@job.should_receive(:completed?).and_return(true)
|
96
|
+
@job.should_receive(:completed).and_return(nil)
|
97
|
+
|
98
|
+
@warden.jobs["3faba24fcc"].should be_nil
|
99
|
+
@warden.new_job("request", "targets")
|
100
|
+
@warden.jobs["3faba24fcc"].should == @job
|
101
|
+
@warden.process(@message)
|
102
|
+
@warden.jobs["3faba24fcc"].should be_nil
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should call completed block after completion" do
|
106
|
+
completed_block = mock("Completed", :arity => 1, :call => true)
|
107
|
+
|
108
|
+
Nanite::Job.stub!(:new).and_return(@job)
|
109
|
+
@job.should_receive(:process).with(@message)
|
110
|
+
@job.should_receive(:completed?).and_return(true)
|
111
|
+
@job.should_receive(:completed).exactly(3).times.and_return(completed_block)
|
112
|
+
|
113
|
+
@warden.new_job("request", "targets")
|
114
|
+
@warden.process(@message)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should pass in job result if arity of completed block is one" do
|
118
|
+
completed_block = mock("Completed")
|
119
|
+
|
120
|
+
Nanite::Job.stub!(:new).and_return(@job)
|
121
|
+
@job.should_receive(:process).with(@message)
|
122
|
+
@job.should_receive(:completed?).and_return(true)
|
123
|
+
@job.should_receive(:completed).exactly(3).times.and_return(completed_block)
|
124
|
+
@job.should_receive(:results).and_return("the job result")
|
125
|
+
completed_block.should_receive(:arity).and_return(1)
|
126
|
+
completed_block.should_receive(:call).with("the job result")
|
127
|
+
|
128
|
+
@warden.new_job("request", "targets")
|
129
|
+
@warden.process(@message)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should pass in job result and job if arity of completed block is two" do
|
133
|
+
completed_block = mock("Completed")
|
134
|
+
|
135
|
+
Nanite::Job.stub!(:new).and_return(@job)
|
136
|
+
@job.should_receive(:process).with(@message)
|
137
|
+
@job.should_receive(:completed?).and_return(true)
|
138
|
+
@job.should_receive(:completed).exactly(3).times.and_return(completed_block)
|
139
|
+
@job.should_receive(:results).and_return("the job result")
|
140
|
+
completed_block.should_receive(:arity).and_return(2)
|
141
|
+
completed_block.should_receive(:call).with("the job result", @job)
|
142
|
+
|
143
|
+
@warden.new_job("request", "targets")
|
144
|
+
@warden.process(@message)
|
145
|
+
end
|
146
|
+
|
147
|
+
end # Processing a Message
|
148
|
+
|
149
|
+
end # Nanite::JobWarden
|
150
|
+
|
151
|
+
|
152
|
+
describe Nanite::Job do
|
153
|
+
|
154
|
+
describe "Creating a Job" do
|
155
|
+
|
156
|
+
before(:each) do
|
157
|
+
@request = mock("Request", :token => "af534ceaaacdcd")
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should initialize the request" do
|
161
|
+
job = Nanite::Job.new(@request, nil, nil)
|
162
|
+
job.request.should == @request
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should initialize the targets" do
|
166
|
+
job = Nanite::Job.new(@request, "targets", nil)
|
167
|
+
job.targets.should == "targets"
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should initialize the job token to the request token" do
|
171
|
+
job = Nanite::Job.new(@request, nil, nil)
|
172
|
+
job.token.should == "af534ceaaacdcd"
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should initialize the results to an empty hash" do
|
176
|
+
job = Nanite::Job.new(@request, nil, nil)
|
177
|
+
job.results.should == {}
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should initialize the intermediate state to an empty hash" do
|
181
|
+
job = Nanite::Job.new(@request, nil, nil)
|
182
|
+
job.intermediate_state.should == {}
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should initialize the job block" do
|
186
|
+
job = Nanite::Job.new(@request, nil, nil, "my block")
|
187
|
+
job.completed.should == "my block"
|
188
|
+
end
|
189
|
+
|
190
|
+
end # Creating a new Job
|
191
|
+
|
192
|
+
|
193
|
+
describe "Processing a Message" do
|
194
|
+
|
195
|
+
before(:each) do
|
196
|
+
@request = mock("Request", :token => "feeefe132")
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should set the job result (for sender) to the message result for 'final' status messages" do
|
200
|
+
job = Nanite::Job.new(@request, [], nil)
|
201
|
+
message = Nanite::Result.new('token', 'to', 'results', 'from')
|
202
|
+
job.results.should == {}
|
203
|
+
job.process(message)
|
204
|
+
job.results.should == { 'from' => 'results' }
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should delete the message sender from the targets for 'final' status messages" do
|
208
|
+
job = Nanite::Job.new(@request, ['from'], nil)
|
209
|
+
message = Nanite::Result.new('token', 'to', 'results', 'from')
|
210
|
+
job.targets.should == ['from']
|
211
|
+
job.process(message)
|
212
|
+
job.targets.should == []
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should set the job result (for sender) to the message result for 'intermediate' status messages" do
|
216
|
+
job = Nanite::Job.new(@request, ['from'], nil)
|
217
|
+
message = Nanite::IntermediateMessage.new('token', 'to', 'from', 'messagekey', 'message')
|
218
|
+
job.process(message)
|
219
|
+
job.intermediate_state.should == { 'from' => { 'messagekey' => ['message'] } }
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should not delete the message sender from the targets for 'intermediate' status messages" do
|
223
|
+
job = Nanite::Job.new(@request, ['from'], nil)
|
224
|
+
message = Nanite::IntermediateMessage.new('token', 'to', 'from', 'messagekey', 'message')
|
225
|
+
job.targets.should == ['from']
|
226
|
+
job.process(message)
|
227
|
+
job.targets.should == ['from']
|
228
|
+
end
|
229
|
+
|
230
|
+
end # Processing a Message
|
231
|
+
|
232
|
+
|
233
|
+
describe "Completion" do
|
234
|
+
|
235
|
+
before(:each) do
|
236
|
+
@request = mock("Request", :token => "af534ceaaacdcd")
|
237
|
+
end
|
238
|
+
|
239
|
+
it "should be true is targets are empty" do
|
240
|
+
job = Nanite::Job.new(@request, {}, nil)
|
241
|
+
job.completed?.should == true
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should be false is targets are not empty" do
|
245
|
+
job = Nanite::Job.new(@request, { :a => 1 }, nil)
|
246
|
+
job.completed?.should == false
|
247
|
+
end
|
248
|
+
|
249
|
+
end # Completion
|
250
|
+
|
251
|
+
end # Nanite::Job
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
require 'nanite/local_state'
|
3
|
+
|
4
|
+
describe "Nanite::LocalState: " do
|
5
|
+
|
6
|
+
describe "Class" do
|
7
|
+
|
8
|
+
it "should a Hash" do
|
9
|
+
Nanite::LocalState.new({}).should be_kind_of(Hash)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should create empty hash if no hash passed in" do
|
13
|
+
Nanite::LocalState.new.should == {}
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should initialize hash with value passed in" do
|
17
|
+
state = Nanite::LocalState.new({:a => 1, :b => 2, :c => 3})
|
18
|
+
state.should == {:a => 1, :b => 2, :c => 3}
|
19
|
+
end
|
20
|
+
|
21
|
+
end # Class
|
22
|
+
|
23
|
+
|
24
|
+
describe "All services" do
|
25
|
+
|
26
|
+
it "should return empty array if no services are defined" do
|
27
|
+
state = Nanite::LocalState.new({:f => { :foo => 1 }, :b => { :bar => 2 }})
|
28
|
+
state.all_services.should == []
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return all :services values" do
|
32
|
+
state = Nanite::LocalState.new({:f => { :foo => 1 }, :b => { :services => "b's services" }, :c => { :services => "c's services" }})
|
33
|
+
state.all_services.should include("b's services")
|
34
|
+
state.all_services.should include("c's services")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should only return one entry for each service" do
|
38
|
+
state = Nanite::LocalState.new({:f => { :services => "services" }, :b => { :services => "services" }})
|
39
|
+
state.all_services.size == 1
|
40
|
+
state.all_services.should == ["services"]
|
41
|
+
end
|
42
|
+
|
43
|
+
end # All services
|
44
|
+
|
45
|
+
|
46
|
+
describe "All tags" do
|
47
|
+
|
48
|
+
it "should return empty array if no tags are defined" do
|
49
|
+
state = Nanite::LocalState.new({:f => { :foo => 1 }, :b => { :bar => 2 }})
|
50
|
+
state.all_tags.should == []
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should return all :tags values" do
|
54
|
+
state = Nanite::LocalState.new({:f => { :foo => 1 }, :b => { :tags => ["a", "b"] }, :c => { :tags => ["c", "d"] }})
|
55
|
+
state.all_tags.should include("a")
|
56
|
+
state.all_tags.should include("b")
|
57
|
+
state.all_tags.should include("c")
|
58
|
+
state.all_tags.should include("d")
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should only return one entry for each tag" do
|
62
|
+
state = Nanite::LocalState.new({:f => { :foo => 1 }, :b => { :tags => ["a", "b"] }, :c => { :tags => ["a", "c"] }})
|
63
|
+
state.all_tags.size == 3
|
64
|
+
state.all_tags.should include("a")
|
65
|
+
state.all_tags.should include("b")
|
66
|
+
state.all_tags.should include("c")
|
67
|
+
end
|
68
|
+
|
69
|
+
end # All tags
|
70
|
+
|
71
|
+
|
72
|
+
describe "Nanites lookup" do
|
73
|
+
|
74
|
+
it "should find services matching the service criteria if no tags criteria is specified" do
|
75
|
+
state = Nanite::LocalState.new({:a => { :services => "a's services" }, :b => { :services => "b's services" }})
|
76
|
+
state.nanites_for("b's services").should == [[:b, {:services => "b's services"}]]
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should find all services matching the service criteria if no tags criteria is specified" do
|
80
|
+
state = Nanite::LocalState.new({:a => { :services => "services" }, :b => { :services => "services" }, :c => { :services => "other services" }})
|
81
|
+
state.nanites_for("services").should include([:a, {:services => "services"}])
|
82
|
+
state.nanites_for("services").should include([:b, {:services => "services"}])
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should only services matching the service criteria that also match the tags criteria" do
|
86
|
+
state = Nanite::LocalState.new({:a => { :services => "a's services", :tags => ["a_1", "a_2"] }, :b => { :services => "b's services", :tags => ["b_1", "b_2"] }})
|
87
|
+
state.nanites_for("b's services").should == [[:b, {:tags=>["b_1", "b_2"], :services=>"b's services"}]]
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should find all services with matching tags even if the tag order is different" do
|
91
|
+
state = Nanite::LocalState.new({:a => { :services => "services", :tags => ["a_1", "a_2"] }, :b => { :services => "services", :tags => ["a_2", "a_1"] }})
|
92
|
+
state.nanites_for("services", ['a_1', 'a_2']).should == [[:a, {:tags=>["a_1", "a_2"], :services=>"services"}], [:b, {:tags=>["a_2", "a_1"], :services=>"services"}]]
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should also return all tags for services matching the service criteria that also match a single tags criterium" do
|
96
|
+
state = Nanite::LocalState.new({:a => { :services => "services", :tags => ["t_1", "t_2"] }})
|
97
|
+
state.nanites_for("services", ["t_1"]).should == [[:a, {:tags=>["t_1", "t_2"], :services=>"services"}]]
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should return services matching the service criteria and also match the tags criterium" do
|
101
|
+
state = Nanite::LocalState.new({:a => { :services => "a's services", :tags => ["a_1", "a_2"] }, :b => { :services => "b's services", :tags => ["b_1", "b_2"] }})
|
102
|
+
state.nanites_for("b's services", ["b_1"]).should == [[:b, {:tags=>["b_1", "b_2"], :services=>"b's services"}]]
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should ignore services matching the service criteria and but not the tags criteria" do
|
106
|
+
state = Nanite::LocalState.new({:a => { :services => "services", :tags => ["t_1", "t_2"] }, :b => { :services => "services", :tags => ["t_3", "t_4"] }})
|
107
|
+
state.nanites_for("services", ["t_1"]).should == [[:a, {:services => "services", :tags => ["t_1", "t_2"]}]]
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should lookup services matching the service criteria and and any of the tags criteria" do
|
111
|
+
state = Nanite::LocalState.new({'a' => { :services => "services", :tags => ["t_1", "t_2"] }, 'b' => { :services => "services", :tags => ["t_2", "t_3"] }})
|
112
|
+
state.nanites_for("services", ["t_1", "t_3"]).sort.should == [['a', {:services => "services", :tags => ["t_1", "t_2"]}], ['b', {:services => "services", :tags => ["t_2", "t_3"]}]]
|
113
|
+
end
|
114
|
+
|
115
|
+
end # Nanites lookup
|
116
|
+
|
117
|
+
describe "Updating a Nanite's status" do
|
118
|
+
it "should set the status for the nanite" do
|
119
|
+
state = Nanite::LocalState.new('a' => { :services => "service" })
|
120
|
+
state.update_status('a', 0.1)
|
121
|
+
state['a'][:status].should == 0.1
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should store the timestamp for the nanite" do
|
125
|
+
state = Nanite::LocalState.new('a' => { :services => "service" })
|
126
|
+
state.update_status('a', 0.1)
|
127
|
+
state['a'][:timestamp].should be_close(Time.now.utc.to_i, 1)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end # Nanite::LocalState
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
class Foo
|
4
|
+
include Nanite::Actor
|
5
|
+
expose :bar, :index, :i_kill_you
|
6
|
+
on_exception :handle_exception
|
7
|
+
|
8
|
+
def index(payload)
|
9
|
+
bar(payload)
|
10
|
+
end
|
11
|
+
|
12
|
+
def bar(payload)
|
13
|
+
['hello', payload]
|
14
|
+
end
|
15
|
+
|
16
|
+
def bar2(payload, deliverable)
|
17
|
+
deliverable
|
18
|
+
end
|
19
|
+
|
20
|
+
def i_kill_you(payload)
|
21
|
+
raise RuntimeError.new('I kill you!')
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle_exception(method, deliverable, error)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Bar
|
29
|
+
include Nanite::Actor
|
30
|
+
expose :i_kill_you
|
31
|
+
on_exception do |method, deliverable, error|
|
32
|
+
@scope = self
|
33
|
+
@called_with = [method, deliverable, error]
|
34
|
+
end
|
35
|
+
|
36
|
+
def i_kill_you(payload)
|
37
|
+
raise RuntimeError.new('I kill you!')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# No specs, simply ensures multiple methods for assigning on_exception callback,
|
42
|
+
# on_exception raises exception when called with an invalid argument.
|
43
|
+
class Doomed
|
44
|
+
include Nanite::Actor
|
45
|
+
on_exception do
|
46
|
+
end
|
47
|
+
on_exception lambda {}
|
48
|
+
on_exception :doh
|
49
|
+
end
|
50
|
+
|
51
|
+
# Mock the EventMachine deferrer.
|
52
|
+
class EMMock
|
53
|
+
def self.defer(op = nil, callback = nil)
|
54
|
+
callback.call(op.call)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "Nanite::Dispatcher" do
|
59
|
+
|
60
|
+
before(:each) do
|
61
|
+
Nanite::Log.stub!(:info)
|
62
|
+
Nanite::Log.stub!(:error)
|
63
|
+
amq = mock('amq', :queue => mock('queue', :publish => nil))
|
64
|
+
@actor = Foo.new
|
65
|
+
@registry = Nanite::ActorRegistry.new
|
66
|
+
@registry.register(@actor, nil)
|
67
|
+
@dispatcher = Nanite::Dispatcher.new(amq, @registry, Nanite::Serializer.new(:marshal), '0xfunkymonkey', {})
|
68
|
+
@dispatcher.evmclass = EMMock
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should dispatch a request" do
|
72
|
+
req = Nanite::Request.new('/foo/bar', 'you')
|
73
|
+
res = @dispatcher.dispatch(req)
|
74
|
+
res.should(be_kind_of(Nanite::Result))
|
75
|
+
res.token.should == req.token
|
76
|
+
res.results.should == ['hello', 'you']
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should dispatch the deliverable to actions that accept it" do
|
80
|
+
req = Nanite::Request.new('/foo/bar2', 'you')
|
81
|
+
res = @dispatcher.dispatch(req)
|
82
|
+
res.should(be_kind_of(Nanite::Result))
|
83
|
+
res.token.should == req.token
|
84
|
+
res.results.should == req
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should dispatch a request to the default action" do
|
88
|
+
req = Nanite::Request.new('/foo', 'you')
|
89
|
+
res = @dispatcher.dispatch(req)
|
90
|
+
res.should(be_kind_of(Nanite::Result))
|
91
|
+
res.token.should == req.token
|
92
|
+
res.results.should == ['hello', 'you']
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should handle custom prefixes" do
|
96
|
+
@registry.register(Foo.new, 'umbongo')
|
97
|
+
req = Nanite::Request.new('/umbongo/bar', 'you')
|
98
|
+
res = @dispatcher.dispatch(req)
|
99
|
+
res.should(be_kind_of(Nanite::Result))
|
100
|
+
res.token.should == req.token
|
101
|
+
res.results.should == ['hello', 'you']
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should call the on_exception callback if something goes wrong" do
|
105
|
+
req = Nanite::Request.new('/foo/i_kill_you', nil)
|
106
|
+
@actor.should_receive(:handle_exception).with(:i_kill_you, req, duck_type(:exception, :backtrace))
|
107
|
+
@dispatcher.dispatch(req)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should call on_exception Procs defined in a subclass with the correct arguments" do
|
111
|
+
actor = Bar.new
|
112
|
+
@registry.register(actor, nil)
|
113
|
+
req = Nanite::Request.new('/bar/i_kill_you', nil)
|
114
|
+
@dispatcher.dispatch(req)
|
115
|
+
called_with = actor.instance_variable_get("@called_with")
|
116
|
+
called_with[0].should == :i_kill_you
|
117
|
+
called_with[1].should == req
|
118
|
+
called_with[2].should be_kind_of(RuntimeError)
|
119
|
+
called_with[2].message.should == 'I kill you!'
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should call on_exception Procs defined in a subclass in the scope of the actor" do
|
123
|
+
actor = Bar.new
|
124
|
+
@registry.register(actor, nil)
|
125
|
+
req = Nanite::Request.new('/bar/i_kill_you', nil)
|
126
|
+
@dispatcher.dispatch(req)
|
127
|
+
actor.instance_variable_get("@scope").should == actor
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should log error if something goes wrong" do
|
131
|
+
Nanite::Log.should_receive(:error)
|
132
|
+
req = Nanite::Request.new('/foo/i_kill_you', nil)
|
133
|
+
@dispatcher.dispatch(req)
|
134
|
+
end
|
135
|
+
|
136
|
+
end # Nanite::Dispatcher
|