ezmobius-nanite 0.4.0 → 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 (60) hide show
  1. data/README.rdoc +70 -20
  2. data/Rakefile +1 -1
  3. data/bin/nanite-agent +34 -8
  4. data/bin/nanite-mapper +18 -8
  5. data/lib/nanite.rb +71 -0
  6. data/lib/nanite/actor.rb +60 -0
  7. data/lib/nanite/actor_registry.rb +24 -0
  8. data/lib/nanite/admin.rb +138 -0
  9. data/lib/nanite/agent.rb +250 -0
  10. data/lib/nanite/amqp.rb +47 -0
  11. data/lib/nanite/cluster.rb +203 -0
  12. data/lib/nanite/config.rb +102 -0
  13. data/lib/nanite/console.rb +39 -0
  14. data/lib/nanite/daemonize.rb +13 -0
  15. data/lib/nanite/dispatcher.rb +90 -0
  16. data/lib/nanite/identity.rb +16 -0
  17. data/lib/nanite/job.rb +104 -0
  18. data/lib/nanite/local_state.rb +34 -0
  19. data/lib/nanite/log.rb +64 -0
  20. data/lib/nanite/log/formatter.rb +39 -0
  21. data/lib/nanite/mapper.rb +277 -0
  22. data/lib/nanite/mapper_proxy.rb +56 -0
  23. data/lib/nanite/packets.rb +231 -0
  24. data/lib/nanite/pid_file.rb +52 -0
  25. data/lib/nanite/reaper.rb +38 -0
  26. data/lib/nanite/security/cached_certificate_store_proxy.rb +24 -0
  27. data/lib/nanite/security/certificate.rb +55 -0
  28. data/lib/nanite/security/certificate_cache.rb +66 -0
  29. data/lib/nanite/security/distinguished_name.rb +34 -0
  30. data/lib/nanite/security/encrypted_document.rb +46 -0
  31. data/lib/nanite/security/rsa_key_pair.rb +53 -0
  32. data/lib/nanite/security/secure_serializer.rb +67 -0
  33. data/lib/nanite/security/signature.rb +40 -0
  34. data/lib/nanite/security/static_certificate_store.rb +35 -0
  35. data/lib/nanite/security_provider.rb +47 -0
  36. data/lib/nanite/serializer.rb +52 -0
  37. data/lib/nanite/state.rb +164 -0
  38. data/lib/nanite/streaming.rb +125 -0
  39. data/lib/nanite/util.rb +51 -0
  40. data/spec/actor_registry_spec.rb +62 -0
  41. data/spec/actor_spec.rb +59 -0
  42. data/spec/agent_spec.rb +235 -0
  43. data/spec/cached_certificate_store_proxy_spec.rb +34 -0
  44. data/spec/certificate_cache_spec.rb +49 -0
  45. data/spec/certificate_spec.rb +27 -0
  46. data/spec/cluster_spec.rb +300 -0
  47. data/spec/dispatcher_spec.rb +136 -0
  48. data/spec/distinguished_name_spec.rb +24 -0
  49. data/spec/encrypted_document_spec.rb +21 -0
  50. data/spec/job_spec.rb +219 -0
  51. data/spec/local_state_spec.rb +112 -0
  52. data/spec/packet_spec.rb +218 -0
  53. data/spec/rsa_key_pair_spec.rb +33 -0
  54. data/spec/secure_serializer_spec.rb +41 -0
  55. data/spec/serializer_spec.rb +107 -0
  56. data/spec/signature_spec.rb +30 -0
  57. data/spec/spec_helper.rb +23 -0
  58. data/spec/static_certificate_store_spec.rb +30 -0
  59. data/spec/util_spec.rb +63 -0
  60. metadata +63 -2
@@ -0,0 +1,51 @@
1
+ class String
2
+ ##
3
+ # Convert to snake case.
4
+ #
5
+ # "FooBar".snake_case #=> "foo_bar"
6
+ # "HeadlineCNNNews".snake_case #=> "headline_cnn_news"
7
+ # "CNN".snake_case #=> "cnn"
8
+ #
9
+ # @return [String] Receiver converted to snake case.
10
+ #
11
+ # @api public
12
+ def snake_case
13
+ return self.downcase if self =~ /^[A-Z]+$/
14
+ self.gsub(/([A-Z]+)(?=[A-Z][a-z]?)|\B[A-Z]/, '_\&') =~ /_*(.*)/
15
+ return $+.downcase
16
+ end
17
+
18
+ ##
19
+ # Convert a constant name to a path, assuming a conventional structure.
20
+ #
21
+ # "FooBar::Baz".to_const_path # => "foo_bar/baz"
22
+ #
23
+ # @return [String] Path to the file containing the constant named by receiver
24
+ # (constantized string), assuming a conventional structure.
25
+ #
26
+ # @api public
27
+ def to_const_path
28
+ snake_case.gsub(/::/, "/")
29
+ end
30
+ end
31
+
32
+ class Object
33
+ module InstanceExecHelper; end
34
+ include InstanceExecHelper
35
+ def instance_exec(*args, &block)
36
+ begin
37
+ old_critical, Thread.critical = Thread.critical, true
38
+ n = 0
39
+ n += 1 while respond_to?(mname="__instance_exec#{n}")
40
+ InstanceExecHelper.module_eval{ define_method(mname, &block) }
41
+ ensure
42
+ Thread.critical = old_critical
43
+ end
44
+ begin
45
+ ret = send(mname, *args)
46
+ ensure
47
+ InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
48
+ end
49
+ ret
50
+ end
51
+ end
@@ -0,0 +1,62 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Nanite::ActorRegistry do
4
+
5
+ before(:all) do
6
+ class WebDocumentImporter
7
+ include Nanite::Actor
8
+ expose :import, :cancel
9
+
10
+ def import
11
+ 1
12
+ end
13
+ def cancel
14
+ 0
15
+ end
16
+ end
17
+
18
+ module Actors
19
+ class ComedyActor
20
+ include Nanite::Actor
21
+ expose :fun_tricks
22
+ def fun_tricks
23
+ :rabbit_in_the_hat
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ before(:each) do
30
+ Nanite::Log.stub!(:info)
31
+ @registry = Nanite::ActorRegistry.new
32
+ end
33
+
34
+ it "should know about all services" do
35
+ @registry.register(WebDocumentImporter.new, nil)
36
+ @registry.register(Actors::ComedyActor.new, nil)
37
+ @registry.services.sort.should == ["/actors/comedy_actor/fun_tricks", "/web_document_importer/cancel", "/web_document_importer/import"]
38
+ end
39
+
40
+ it "should not register anything except Nanite::Actor" do
41
+ lambda { @registry.register(String.new, nil) }.should raise_error(ArgumentError)
42
+ end
43
+
44
+ it "should register an actor" do
45
+ importer = WebDocumentImporter.new
46
+ @registry.register(importer, nil)
47
+ @registry.actors['web_document_importer'].should == importer
48
+ end
49
+
50
+ it "should log info message that actor was registered" do
51
+ importer = WebDocumentImporter.new
52
+ Nanite::Log.should_receive(:info).with("Registering #{importer.inspect} with prefix nil")
53
+ @registry.register(importer, nil)
54
+ end
55
+
56
+ it "should handle actors registered with a custom prefix" do
57
+ importer = WebDocumentImporter.new
58
+ @registry.register(importer, 'monkey')
59
+ @registry.actors['monkey'].should == importer
60
+ end
61
+
62
+ end # Nanite::ActorRegistry
@@ -0,0 +1,59 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ class WebDocumentImporter
4
+ include Nanite::Actor
5
+ expose :import, :cancel
6
+
7
+ def import
8
+ 1
9
+ end
10
+ def cancel
11
+ 0
12
+ end
13
+ def continue
14
+ 1
15
+ end
16
+ end
17
+
18
+ module Actors
19
+ class ComedyActor
20
+ include Nanite::Actor
21
+ expose :fun_tricks
22
+ def fun_tricks
23
+ :rabbit_in_the_hat
24
+ end
25
+ end
26
+ end
27
+
28
+ describe Nanite::Actor do
29
+
30
+ describe ".expose" do
31
+ it "should single expose method only once" do
32
+ 3.times { WebDocumentImporter.expose(:continue) }
33
+ WebDocumentImporter.provides_for("webfiles").should == ["/webfiles/import", "/webfiles/cancel", "/webfiles/continue"]
34
+ end
35
+ end
36
+
37
+ describe ".default_prefix" do
38
+ it "is calculated as default prefix as const path of class name" do
39
+ Actors::ComedyActor.default_prefix.should == "actors/comedy_actor"
40
+ WebDocumentImporter.default_prefix.should == "web_document_importer"
41
+ end
42
+ end
43
+
44
+ describe ".provides_for(prefix)" do
45
+ before :each do
46
+ @provides = Actors::ComedyActor.provides_for("money")
47
+ end
48
+ it "returns an array" do
49
+ @provides.should be_kind_of(Array)
50
+ end
51
+
52
+ it "maps exposed service methods to prefix" do
53
+ @provides.should == ["/money/fun_tricks"]
54
+ wdi_provides = WebDocumentImporter.provides_for("webfiles")
55
+ wdi_provides.should include("/webfiles/import")
56
+ wdi_provides.should include("/webfiles/cancel")
57
+ end
58
+ end
59
+ end
@@ -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