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,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
@@ -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
@@ -0,0 +1,219 @@
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
+
37
+ describe "Processing a Message" do
38
+
39
+ before(:each) do
40
+ @message = mock("Message", :token => "3faba24fcc")
41
+ @warden = Nanite::JobWarden.new(@serializer)
42
+ @job = mock("Job", :token => "3faba24fcc", :process => true, :completed? => false, :results => 42, :pending_keys => [], :intermediate_handler => true)
43
+
44
+ Nanite::Log.stub!(:debug)
45
+ end
46
+
47
+ it "should log debug message about message to be processed" do
48
+ Nanite::Log.should_receive(:debug)
49
+ @warden.process(@message)
50
+ end
51
+
52
+ it "should hand over processing to job" do
53
+ Nanite::Job.stub!(:new).and_return(@job)
54
+ @job.should_receive(:process).with(@message)
55
+
56
+ @warden.new_job("request", "targets")
57
+ @warden.process(@message)
58
+ end
59
+
60
+ it "should delete job from jobs after completion" do
61
+ Nanite::Job.stub!(:new).and_return(@job)
62
+ @job.should_receive(:process).with(@message)
63
+ @job.should_receive(:completed?).and_return(true)
64
+ @job.should_receive(:completed).and_return(nil)
65
+
66
+ @warden.jobs["3faba24fcc"].should be_nil
67
+ @warden.new_job("request", "targets")
68
+ @warden.jobs["3faba24fcc"].should == @job
69
+ @warden.process(@message)
70
+ @warden.jobs["3faba24fcc"].should be_nil
71
+ end
72
+
73
+ it "should call completed block after completion" do
74
+ completed_block = mock("Completed", :arity => 1, :call => true)
75
+
76
+ Nanite::Job.stub!(:new).and_return(@job)
77
+ @job.should_receive(:process).with(@message)
78
+ @job.should_receive(:completed?).and_return(true)
79
+ @job.should_receive(:completed).exactly(3).times.and_return(completed_block)
80
+
81
+ @warden.new_job("request", "targets")
82
+ @warden.process(@message)
83
+ end
84
+
85
+ it "should pass in job result if arity of completed block is one" do
86
+ completed_block = mock("Completed")
87
+
88
+ Nanite::Job.stub!(:new).and_return(@job)
89
+ @job.should_receive(:process).with(@message)
90
+ @job.should_receive(:completed?).and_return(true)
91
+ @job.should_receive(:completed).exactly(3).times.and_return(completed_block)
92
+ @job.should_receive(:results).and_return("the job result")
93
+ completed_block.should_receive(:arity).and_return(1)
94
+ completed_block.should_receive(:call).with("the job result")
95
+
96
+ @warden.new_job("request", "targets")
97
+ @warden.process(@message)
98
+ end
99
+
100
+ it "should pass in job result and job if arity of completed block is two" do
101
+ completed_block = mock("Completed")
102
+
103
+ Nanite::Job.stub!(:new).and_return(@job)
104
+ @job.should_receive(:process).with(@message)
105
+ @job.should_receive(:completed?).and_return(true)
106
+ @job.should_receive(:completed).exactly(3).times.and_return(completed_block)
107
+ @job.should_receive(:results).and_return("the job result")
108
+ completed_block.should_receive(:arity).and_return(2)
109
+ completed_block.should_receive(:call).with("the job result", @job)
110
+
111
+ @warden.new_job("request", "targets")
112
+ @warden.process(@message)
113
+ end
114
+
115
+ end # Processing a Message
116
+
117
+ end # Nanite::JobWarden
118
+
119
+
120
+ describe Nanite::Job do
121
+
122
+ describe "Creating a Job" do
123
+
124
+ before(:each) do
125
+ @request = mock("Request", :token => "af534ceaaacdcd")
126
+ end
127
+
128
+ it "should initialize the request" do
129
+ job = Nanite::Job.new(@request, nil, nil)
130
+ job.request.should == @request
131
+ end
132
+
133
+ it "should initialize the targets" do
134
+ job = Nanite::Job.new(@request, "targets", nil)
135
+ job.targets.should == "targets"
136
+ end
137
+
138
+ it "should initialize the job token to the request token" do
139
+ job = Nanite::Job.new(@request, nil, nil)
140
+ job.token.should == "af534ceaaacdcd"
141
+ end
142
+
143
+ it "should initialize the results to an empty hash" do
144
+ job = Nanite::Job.new(@request, nil, nil)
145
+ job.results.should == {}
146
+ end
147
+
148
+ it "should initialize the intermediate state to an empty hash" do
149
+ job = Nanite::Job.new(@request, nil, nil)
150
+ job.intermediate_state.should == {}
151
+ end
152
+
153
+ it "should initialize the job block" do
154
+ job = Nanite::Job.new(@request, nil, nil, "my block")
155
+ job.completed.should == "my block"
156
+ end
157
+
158
+ end # Creating a new Job
159
+
160
+
161
+ describe "Processing a Message" do
162
+
163
+ before(:each) do
164
+ @request = mock("Request", :token => "feeefe132")
165
+ end
166
+
167
+ it "should set the job result (for sender) to the message result for 'final' status messages" do
168
+ job = Nanite::Job.new(@request, [], nil)
169
+ message = Nanite::Result.new('token', 'to', 'results', 'from')
170
+ job.results.should == {}
171
+ job.process(message)
172
+ job.results.should == { 'from' => 'results' }
173
+ end
174
+
175
+ it "should delete the message sender from the targets for 'final' status messages" do
176
+ job = Nanite::Job.new(@request, ['from'], nil)
177
+ message = Nanite::Result.new('token', 'to', 'results', 'from')
178
+ job.targets.should == ['from']
179
+ job.process(message)
180
+ job.targets.should == []
181
+ end
182
+
183
+ it "should set the job result (for sender) to the message result for 'intermediate' status messages" do
184
+ job = Nanite::Job.new(@request, ['from'], nil)
185
+ message = Nanite::IntermediateMessage.new('token', 'to', 'from', 'messagekey', 'message')
186
+ job.process(message)
187
+ job.intermediate_state.should == { 'from' => { 'messagekey' => ['message'] } }
188
+ end
189
+
190
+ it "should not delete the message sender from the targets for 'intermediate' status messages" do
191
+ job = Nanite::Job.new(@request, ['from'], nil)
192
+ message = Nanite::IntermediateMessage.new('token', 'to', 'from', 'messagekey', 'message')
193
+ job.targets.should == ['from']
194
+ job.process(message)
195
+ job.targets.should == ['from']
196
+ end
197
+
198
+ end # Processing a Message
199
+
200
+
201
+ describe "Completion" do
202
+
203
+ before(:each) do
204
+ @request = mock("Request", :token => "af534ceaaacdcd")
205
+ end
206
+
207
+ it "should be true is targets are empty" do
208
+ job = Nanite::Job.new(@request, {}, nil)
209
+ job.completed?.should == true
210
+ end
211
+
212
+ it "should be false is targets are not empty" do
213
+ job = Nanite::Job.new(@request, { :a => 1 }, nil)
214
+ job.completed?.should == false
215
+ end
216
+
217
+ end # Completion
218
+
219
+ end # Nanite::Job
@@ -0,0 +1,112 @@
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 also return all tags for services matching the service criteria that also match a single tags criterium" do
91
+ state = Nanite::LocalState.new({:a => { :services => "services", :tags => ["t_1", "t_2"] }})
92
+ state.nanites_for("services", ["t_1"]).should == [[:a, {:tags=>["t_1", "t_2"], :services=>"services"}]]
93
+ end
94
+
95
+ it "should return services matching the service criteria and also match the tags criterium" do
96
+ state = Nanite::LocalState.new({:a => { :services => "a's services", :tags => ["a_1", "a_2"] }, :b => { :services => "b's services", :tags => ["b_1", "b_2"] }})
97
+ state.nanites_for("b's services", ["b_1"]).should == [[:b, {:tags=>["b_1", "b_2"], :services=>"b's services"}]]
98
+ end
99
+
100
+ it "should ignore services matching the service criteria and but not the tags criteria" do
101
+ state = Nanite::LocalState.new({:a => { :services => "services", :tags => ["t_1", "t_2"] }, :b => { :services => "services", :tags => ["t_3", "t_4"] }})
102
+ state.nanites_for("services", ["t_1"]).should == [[:a, {:services => "services", :tags => ["t_1", "t_2"]}]]
103
+ end
104
+
105
+ it "should lookup services matching the service criteria and and any of the tags criteria" do
106
+ state = Nanite::LocalState.new({'a' => { :services => "services", :tags => ["t_1", "t_2"] }, 'b' => { :services => "services", :tags => ["t_2", "t_3"] }})
107
+ 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"]}]]
108
+ end
109
+
110
+ end # Nanites lookup
111
+
112
+ end # Nanite::LocalState