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.
Files changed (63) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +430 -0
  3. data/Rakefile +76 -0
  4. data/TODO +24 -0
  5. data/bin/nanite-admin +65 -0
  6. data/bin/nanite-agent +79 -0
  7. data/bin/nanite-mapper +50 -0
  8. data/lib/nanite.rb +74 -0
  9. data/lib/nanite/actor.rb +71 -0
  10. data/lib/nanite/actor_registry.rb +26 -0
  11. data/lib/nanite/admin.rb +138 -0
  12. data/lib/nanite/agent.rb +264 -0
  13. data/lib/nanite/amqp.rb +58 -0
  14. data/lib/nanite/cluster.rb +250 -0
  15. data/lib/nanite/config.rb +112 -0
  16. data/lib/nanite/console.rb +39 -0
  17. data/lib/nanite/daemonize.rb +13 -0
  18. data/lib/nanite/identity.rb +16 -0
  19. data/lib/nanite/job.rb +104 -0
  20. data/lib/nanite/local_state.rb +38 -0
  21. data/lib/nanite/log.rb +66 -0
  22. data/lib/nanite/log/formatter.rb +39 -0
  23. data/lib/nanite/mapper.rb +309 -0
  24. data/lib/nanite/mapper_proxy.rb +67 -0
  25. data/lib/nanite/nanite_dispatcher.rb +92 -0
  26. data/lib/nanite/packets.rb +365 -0
  27. data/lib/nanite/pid_file.rb +52 -0
  28. data/lib/nanite/reaper.rb +39 -0
  29. data/lib/nanite/security/cached_certificate_store_proxy.rb +24 -0
  30. data/lib/nanite/security/certificate.rb +55 -0
  31. data/lib/nanite/security/certificate_cache.rb +66 -0
  32. data/lib/nanite/security/distinguished_name.rb +34 -0
  33. data/lib/nanite/security/encrypted_document.rb +46 -0
  34. data/lib/nanite/security/rsa_key_pair.rb +53 -0
  35. data/lib/nanite/security/secure_serializer.rb +68 -0
  36. data/lib/nanite/security/signature.rb +46 -0
  37. data/lib/nanite/security/static_certificate_store.rb +35 -0
  38. data/lib/nanite/security_provider.rb +47 -0
  39. data/lib/nanite/serializer.rb +52 -0
  40. data/lib/nanite/state.rb +168 -0
  41. data/lib/nanite/streaming.rb +125 -0
  42. data/lib/nanite/util.rb +58 -0
  43. data/spec/actor_registry_spec.rb +60 -0
  44. data/spec/actor_spec.rb +77 -0
  45. data/spec/agent_spec.rb +240 -0
  46. data/spec/cached_certificate_store_proxy_spec.rb +34 -0
  47. data/spec/certificate_cache_spec.rb +49 -0
  48. data/spec/certificate_spec.rb +27 -0
  49. data/spec/cluster_spec.rb +622 -0
  50. data/spec/distinguished_name_spec.rb +24 -0
  51. data/spec/encrypted_document_spec.rb +21 -0
  52. data/spec/job_spec.rb +251 -0
  53. data/spec/local_state_spec.rb +130 -0
  54. data/spec/nanite_dispatcher_spec.rb +136 -0
  55. data/spec/packet_spec.rb +220 -0
  56. data/spec/rsa_key_pair_spec.rb +33 -0
  57. data/spec/secure_serializer_spec.rb +41 -0
  58. data/spec/serializer_spec.rb +107 -0
  59. data/spec/signature_spec.rb +30 -0
  60. data/spec/spec_helper.rb +33 -0
  61. data/spec/static_certificate_store_spec.rb +30 -0
  62. data/spec/util_spec.rb +63 -0
  63. metadata +129 -0
@@ -0,0 +1,58 @@
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
+ unless defined? instance_exec # 1.9 and 1.8.7
34
+ module InstanceExecHelper; end
35
+ include InstanceExecHelper
36
+
37
+ # Evaluate the block with the given arguments within the context of
38
+ # this object, so self is set to the method receiver.
39
+ #
40
+ # From Mauricio's http://eigenclass.org/hiki/bounded+space+instance_exec
41
+ def instance_exec(*args, &block)
42
+ begin
43
+ old_critical, Thread.critical = Thread.critical, true
44
+ n = 0
45
+ n += 1 while respond_to?(method_name = "__instance_exec#{n}")
46
+ InstanceExecMethods.module_eval { define_method(method_name, &block) }
47
+ ensure
48
+ Thread.critical = old_critical
49
+ end
50
+
51
+ begin
52
+ send(method_name, *args)
53
+ ensure
54
+ InstanceExecMethods.module_eval { remove_method(method_name) } rescue nil
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,60 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Nanite::ActorRegistry do
4
+
5
+ class ::WebDocumentImporter
6
+ include Nanite::Actor
7
+ expose :import, :cancel
8
+
9
+ def import
10
+ 1
11
+ end
12
+ def cancel
13
+ 0
14
+ end
15
+ end
16
+
17
+ module ::Actors
18
+ class ComedyActor
19
+ include Nanite::Actor
20
+ expose :fun_tricks
21
+ def fun_tricks
22
+ :rabbit_in_the_hat
23
+ end
24
+ end
25
+ end
26
+
27
+ before(:each) do
28
+ Nanite::Log.stub!(:info)
29
+ @registry = Nanite::ActorRegistry.new
30
+ end
31
+
32
+ it "should know about all services" do
33
+ @registry.register(WebDocumentImporter.new, nil)
34
+ @registry.register(Actors::ComedyActor.new, nil)
35
+ @registry.services.sort.should == ["/actors/comedy_actor/fun_tricks", "/web_document_importer/cancel", "/web_document_importer/import"]
36
+ end
37
+
38
+ it "should not register anything except Nanite::Actor" do
39
+ lambda { @registry.register(String.new, nil) }.should raise_error(ArgumentError)
40
+ end
41
+
42
+ it "should register an actor" do
43
+ importer = WebDocumentImporter.new
44
+ @registry.register(importer, nil)
45
+ @registry.actors['web_document_importer'].should == importer
46
+ end
47
+
48
+ it "should log info message that actor was registered" do
49
+ importer = WebDocumentImporter.new
50
+ Nanite::Log.should_receive(:info).with("[actor] #{importer.class.to_s}")
51
+ @registry.register(importer, nil)
52
+ end
53
+
54
+ it "should handle actors registered with a custom prefix" do
55
+ importer = WebDocumentImporter.new
56
+ @registry.register(importer, 'monkey')
57
+ @registry.actor_for('monkey').should == importer
58
+ end
59
+
60
+ end # Nanite::ActorRegistry
@@ -0,0 +1,77 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Nanite::Actor do
4
+ class ::WebDocumentImporter
5
+ include Nanite::Actor
6
+ expose :import, :cancel
7
+
8
+ def import
9
+ 1
10
+ end
11
+ def cancel
12
+ 0
13
+ end
14
+ def continue
15
+ 1
16
+ end
17
+ end
18
+
19
+ module ::Actors
20
+ class ComedyActor
21
+ include Nanite::Actor
22
+ expose :fun_tricks
23
+ def fun_tricks
24
+ :rabbit_in_the_hat
25
+ end
26
+ end
27
+ end
28
+
29
+ class ::Actors::InvalidActor
30
+ include Nanite::Actor
31
+ expose :non_existing
32
+ end
33
+
34
+ describe ".expose" do
35
+ before :each do
36
+ @exposed = WebDocumentImporter.instance_variable_get(:@exposed).dup
37
+ end
38
+
39
+ after :each do
40
+ WebDocumentImporter.instance_variable_set(:@exposed, @exposed)
41
+ end
42
+
43
+
44
+ it "should single expose method only once" do
45
+ 3.times { WebDocumentImporter.expose(:continue) }
46
+ WebDocumentImporter.provides_for("webfiles").should == ["/webfiles/import", "/webfiles/cancel", "/webfiles/continue"]
47
+ end
48
+ end
49
+
50
+ describe ".default_prefix" do
51
+ it "is calculated as default prefix as const path of class name" do
52
+ Actors::ComedyActor.default_prefix.should == "actors/comedy_actor"
53
+ WebDocumentImporter.default_prefix.should == "web_document_importer"
54
+ end
55
+ end
56
+
57
+ describe ".provides_for(prefix)" do
58
+ before :each do
59
+ @provides = Actors::ComedyActor.provides_for("money")
60
+ end
61
+
62
+ it "returns an array" do
63
+ @provides.should be_kind_of(Array)
64
+ end
65
+
66
+ it "maps exposed service methods to prefix" do
67
+ @provides.should == ["/money/fun_tricks"]
68
+ wdi_provides = WebDocumentImporter.provides_for("webfiles")
69
+ wdi_provides.should include("/webfiles/import")
70
+ wdi_provides.should include("/webfiles/cancel")
71
+ end
72
+
73
+ it "should not include methods not existing in the actor class" do
74
+ Actors::InvalidActor.provides_for("money").should_not include("/money/non_existing")
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,240 @@
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
+ it "for threadpool_size" do
191
+ agent = Nanite::Agent.start(:threadpool_size => 5)
192
+ agent.dispatcher.evmclass.threadpool_size.should == 5
193
+ end
194
+
195
+ end
196
+
197
+ describe "Security" do
198
+
199
+ before(:each) do
200
+ EM.stub!(:add_periodic_timer)
201
+ AMQP.stub!(:connect)
202
+ @amq = mock("AMQueue", :queue => mock("queue", :subscribe => {}, :publish => {}), :fanout => mock("fanout", :publish => nil))
203
+ MQ.stub!(:new).and_return(@amq)
204
+ serializer = Nanite::Serializer.new
205
+ @request = Nanite::Request.new('/foo/bar', '')
206
+ @push = Nanite::Push.new('/foo/bar', '')
207
+ @agent = Nanite::Agent.start
208
+ end
209
+
210
+ it 'should correctly deny requests' do
211
+ security = mock("Security")
212
+ @agent.register_security(security)
213
+
214
+ security.should_receive(:authorize).twice.and_return(false)
215
+ @agent.dispatcher.should_not_receive(:dispatch)
216
+ @agent.__send__(:receive, @request)
217
+ @agent.__send__(:receive, @push)
218
+ end
219
+
220
+ it 'should correctly authorize requests' do
221
+ security = mock("Security")
222
+ @agent.register_security(security)
223
+
224
+ security.should_receive(:authorize).twice.and_return(true)
225
+ @agent.dispatcher.stub!(:dispatch)
226
+ @agent.dispatcher.should_receive(:dispatch).twice
227
+ @agent.__send__(:receive, @request)
228
+ @agent.__send__(:receive, @push)
229
+ end
230
+
231
+ it 'should be ignored when not specified' do
232
+ @agent.dispatcher.stub!(:dispatch)
233
+ @agent.dispatcher.should_receive(:dispatch).twice
234
+ @agent.__send__(:receive, @request)
235
+ @agent.__send__(:receive, @push)
236
+ end
237
+
238
+ end
239
+
240
+ 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