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