rightscale-nanite 0.4.1.2 → 0.4.1.3

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/Rakefile CHANGED
@@ -8,9 +8,9 @@ rescue LoadError
8
8
  require 'rake/rdoctask'
9
9
  end
10
10
  require 'rake/clean'
11
+ require 'lib/nanite'
11
12
 
12
13
  GEM = "nanite"
13
- VER = "0.4.1"
14
14
  AUTHOR = "Ezra Zygmuntowicz"
15
15
  EMAIL = "ezra@engineyard.com"
16
16
  HOMEPAGE = "http://github.com/ezmobius/nanite"
@@ -19,8 +19,9 @@ SUMMARY = "self assembling fabric of ruby daemons"
19
19
  Dir.glob('tasks/*.rake').each { |r| Rake.application.add_import r }
20
20
 
21
21
  spec = Gem::Specification.new do |s|
22
+
22
23
  s.name = GEM
23
- s.version = ::VER
24
+ s.version = Nanite::VERSION
24
25
  s.platform = Gem::Platform::RUBY
25
26
  s.has_rdoc = true
26
27
  s.extra_rdoc_files = ["README.rdoc", "LICENSE", 'TODO']
@@ -46,7 +47,7 @@ end
46
47
  task :default => :spec
47
48
 
48
49
  task :install => [:package] do
49
- sh %{sudo gem install pkg/#{GEM}-#{VER}}
50
+ sh %{sudo gem install pkg/#{GEM}-#{Nanite::VERSION}}
50
51
  end
51
52
 
52
53
  desc "Run unit specs"
data/bin/nanite-agent CHANGED
@@ -37,6 +37,11 @@ opts = OptionParser.new do |opts|
37
37
  opts.on("--single-threaded", "Run all operations in one thread") do
38
38
  options[:single_threaded] = true
39
39
  end
40
+
41
+ opts.on("--threadpool COUNT", Integer, "Number of threads to run all operations in") do |tps|
42
+ options[:threadpool_size] = tps
43
+ end
44
+
40
45
  end
41
46
 
42
47
  opts.parse!
data/lib/nanite/actor.rb CHANGED
@@ -35,7 +35,14 @@ module Nanite
35
35
 
36
36
  def provides_for(prefix)
37
37
  return [] unless @exposed
38
- @exposed.map {|meth| "/#{prefix}/#{meth}".squeeze('/')}
38
+ @exposed.select do |meth|
39
+ if instance_methods.include?(meth.to_s) or instance_methods.include?(meth.to_sym)
40
+ true
41
+ else
42
+ Nanite::Log.warn("Exposing non-existing method #{meth} in actor #{name}")
43
+ false
44
+ end
45
+ end.map {|meth| "/#{prefix}/#{meth}".squeeze('/')}
39
46
  end
40
47
 
41
48
  def on_exception(proc = nil, &blk)
@@ -54,6 +61,10 @@ module Nanite
54
61
  def request(*args, &blk)
55
62
  MapperProxy.instance.request(*args, &blk)
56
63
  end
64
+
65
+ def push(*args)
66
+ MapperProxy.instance.push(*args)
67
+ end
57
68
  end # InstanceMethods
58
69
 
59
70
  end # Actor
data/lib/nanite/agent.rb CHANGED
@@ -8,8 +8,11 @@ module Nanite
8
8
  attr_reader :identity, :options, :serializer, :dispatcher, :registry, :amq, :tags
9
9
  attr_accessor :status_proc
10
10
 
11
- DEFAULT_OPTIONS = COMMON_DEFAULT_OPTIONS.merge({:user => 'nanite', :ping_time => 15,
12
- :default_services => []}) unless defined?(DEFAULT_OPTIONS)
11
+ DEFAULT_OPTIONS = COMMON_DEFAULT_OPTIONS.merge({
12
+ :user => 'nanite',
13
+ :ping_time => 15,
14
+ :default_services => []
15
+ }) unless defined?(DEFAULT_OPTIONS)
13
16
 
14
17
  # Initializes a new agent and establishes AMQP connection.
15
18
  # This must be used inside EM.run block or if EventMachine reactor
@@ -54,6 +57,8 @@ module Nanite
54
57
  #
55
58
  # single_threaded: Run all operations in one thread
56
59
  #
60
+ # threadpool_size: Number of threads to run operations in
61
+ #
57
62
  # Connection options:
58
63
  #
59
64
  # vhost : AMQP broker vhost that should be used
@@ -94,7 +99,7 @@ module Nanite
94
99
  Log.init(@identity, log_path)
95
100
  Log.level = @options[:log_level] if @options[:log_level]
96
101
  @serializer = Serializer.new(@options[:format])
97
- @status_proc = lambda { parse_uptime(`uptime`) rescue 'no status' }
102
+ @status_proc = lambda { parse_uptime(`uptime 2> /dev/null`) rescue 'no status' }
98
103
  pid_file = PidFile.new(@identity, @options)
99
104
  pid_file.check
100
105
  if @options[:daemonize]
@@ -169,7 +174,7 @@ module Nanite
169
174
  Nanite::Log.debug("RECV #{packet.to_s}")
170
175
  case packet
171
176
  when Advertise
172
- Nanite::Log.info("RECV #{packet.to_s}") unless Nanite::Log.level == Logger::DEBUG
177
+ Nanite::Log.info("RECV #{packet.to_s}") unless Nanite::Log.level == :debug
173
178
  advertise_services
174
179
  when Request, Push
175
180
  if @security && !@security.authorize(packet)
@@ -179,14 +184,14 @@ module Nanite
179
184
  amq.queue(packet.reply_to, :no_declare => options[:secure]).publish(serializer.dump(r))
180
185
  end
181
186
  else
182
- Nanite::Log.info("RECV #{packet.to_s([:from, :tags])}") unless Nanite::Log.level == Logger::DEBUG
187
+ Nanite::Log.info("RECV #{packet.to_s([:from, :tags])}") unless Nanite::Log.level == :debug
183
188
  dispatcher.dispatch(packet)
184
189
  end
185
190
  when Result
186
- Nanite::Log.info("RECV #{packet.to_s([])}") unless Nanite::Log.level == Logger::DEBUG
191
+ Nanite::Log.info("RECV #{packet.to_s([])}") unless Nanite::Log.level == :debug
187
192
  @mapper_proxy.handle_result(packet)
188
193
  when IntermediateMessage
189
- Nanite::Log.info("RECV #{packet.to_s([])}") unless Nanite::Log.level == Logger::DEBUG
194
+ Nanite::Log.info("RECV #{packet.to_s([])}") unless Nanite::Log.level == :debug
190
195
  @mapper_proxy.handle_intermediate_result(packet)
191
196
  end
192
197
  end
data/lib/nanite/amqp.rb CHANGED
@@ -39,9 +39,16 @@ end
39
39
  module Nanite
40
40
  module AMQPHelper
41
41
  def start_amqp(options)
42
- connection = AMQP.connect(:user => options[:user], :pass => options[:pass], :vhost => options[:vhost],
43
- :host => options[:host], :port => (options[:port] || ::AMQP::PORT).to_i, :insist => options[:insist] || false)
42
+ connection = AMQP.connect({
43
+ :user => options[:user],
44
+ :pass => options[:pass],
45
+ :vhost => options[:vhost],
46
+ :host => options[:host],
47
+ :port => (options[:port] || ::AMQP::PORT).to_i,
48
+ :insist => options[:insist] || false,
49
+ :retry => options[:retry] || 5
50
+ })
44
51
  MQ.new(connection)
45
52
  end
46
53
  end
47
- end
54
+ end
@@ -87,22 +87,26 @@ module Nanite
87
87
  # forward request coming from agent
88
88
  def handle_request(request)
89
89
  if @security.authorize_request(request)
90
- Nanite::Log.info("RECV #{request.to_s([:from, :target, :tags])}") unless Nanite::Log.level == Logger::DEBUG
90
+ Nanite::Log.info("RECV #{request.to_s([:from, :target, :tags])}") unless Nanite::Log.level == :debug
91
91
  Nanite::Log.debug("RECV #{request.to_s}")
92
-
93
- intm_handler = lambda do |result, job|
94
- result = IntermediateMessage.new(request.token, job.request.from, mapper.identity, nil, result)
95
- forward_response(result, request.persistent)
96
- end
92
+ case request
93
+ when Push
94
+ mapper.send_push(request)
95
+ else
96
+ intm_handler = lambda do |result, job|
97
+ result = IntermediateMessage.new(request.token, job.request.from, mapper.identity, nil, result)
98
+ forward_response(result, request.persistent)
99
+ end
97
100
 
98
- result = Result.new(request.token, request.from, nil, mapper.identity)
99
- ok = mapper.send_request(request, :intermediate_handler => intm_handler) do |res|
100
- result.results = res
101
- forward_response(result, request.persistent)
102
- end
103
-
104
- if ok == false
105
- forward_response(result, request.persistent)
101
+ result = Result.new(request.token, request.from, nil, mapper.identity)
102
+ ok = mapper.send_request(request, :intermediate_handler => intm_handler) do |res|
103
+ result.results = res
104
+ forward_response(result, request.persistent)
105
+ end
106
+
107
+ if ok == false
108
+ forward_response(result, request.persistent)
109
+ end
106
110
  end
107
111
  else
108
112
  Nanite::Log.warn("RECV NOT AUTHORIZED #{request.to_s}")
data/lib/nanite/config.rb CHANGED
@@ -1,7 +1,16 @@
1
1
  module Nanite
2
2
 
3
- COMMON_DEFAULT_OPTIONS = {:pass => 'testing', :vhost => '/nanite', :secure => false, :host => '0.0.0.0',
4
- :log_level => :info, :format => :marshal, :daemonize => false, :console => false, :root => Dir.pwd}
3
+ COMMON_DEFAULT_OPTIONS = {
4
+ :pass => 'testing',
5
+ :vhost => '/nanite',
6
+ :secure => false,
7
+ :host => '0.0.0.0',
8
+ :log_level => :info,
9
+ :format => :marshal,
10
+ :daemonize => false,
11
+ :console => false,
12
+ :root => Dir.pwd
13
+ }
5
14
 
6
15
  module CommonConfig
7
16
  def setup_mapper_options(opts, options)
@@ -10,6 +10,7 @@ module Nanite
10
10
  @identity = identity
11
11
  @options = options
12
12
  @evmclass = EM
13
+ @evmclass.threadpool_size = (@options[:threadpool_size] || 20).to_i
13
14
  end
14
15
 
15
16
  def dispatch(deliverable)
@@ -37,7 +38,7 @@ module Nanite
37
38
  r # For unit tests
38
39
  end
39
40
 
40
- if @options[:single_threaded]
41
+ if @options[:single_threaded] || @options[:thread_poolsize] == 1
41
42
  @evmclass.next_tick { callback.call(operation.call) }
42
43
  else
43
44
  @evmclass.defer(operation, callback)
@@ -88,4 +89,4 @@ module Nanite
88
89
  error
89
90
  end
90
91
  end
91
- end
92
+ end
@@ -18,10 +18,10 @@ module Nanite
18
18
  tags = tags.dup.flatten
19
19
  nanites = select { |name, state| state[:services].include?(service) }
20
20
  unless tags.empty?
21
- nanites.select { |a| !(a[1][:tags] & tags).empty? }
21
+ nanites.select { |a, b| !(b[:tags] & tags).empty? }
22
22
  else
23
23
  nanites
24
- end
24
+ end.to_a
25
25
  end
26
26
 
27
27
  private
data/lib/nanite/log.rb CHANGED
@@ -6,6 +6,13 @@ module Nanite
6
6
  class Log
7
7
 
8
8
  @logger = nil
9
+
10
+ # Map log levels symbols to values
11
+ LEVELS = { :debug => Logger::DEBUG,
12
+ :info => Logger::INFO,
13
+ :warn => Logger::WARN,
14
+ :error => Logger::ERROR,
15
+ :fatal => Logger::FATAL }
9
16
 
10
17
  class << self
11
18
  attr_accessor :logger, :level, :file #:nodoc
@@ -21,31 +28,25 @@ module Nanite
21
28
  end
22
29
  @logger = Logger.new(file)
23
30
  @logger.formatter = Nanite::Log::Formatter.new
24
- level = @log_level = :info
31
+ Log.level = :info
25
32
  end
26
33
 
27
34
  # Sets the level for the Logger by symbol or by command line argument.
28
35
  # Throws an ArgumentError if you feed it a bogus log level (that is not
29
- # one of :debug, :info, :warn, :error, :fatal or the corresponding strings)
36
+ # one of :debug, :info, :warn, :error, :fatal or the corresponding strings or a valid Logger level)
30
37
  def level=(loglevel)
31
38
  init() unless @logger
32
- loglevel = loglevel.intern if loglevel.is_a?(String)
33
- @logger.info("[setup] setting log level to #{loglevel.to_s.upcase}")
34
- @level = loglevel
35
- case loglevel
36
- when :debug
37
- @logger.level = Logger::DEBUG
38
- when :info
39
- @logger.level = Logger::INFO
40
- when :warn
41
- @logger.level = Logger::WARN
42
- when :error
43
- @logger.level = Logger::ERROR
44
- when :fatal
45
- @logger.level = Logger::FATAL
46
- else
47
- raise ArgumentError, "Log level must be one of :debug, :info, :warn, :error, or :fatal"
39
+ lvl = case loglevel
40
+ when String then loglevel.intern
41
+ when Integer then LEVELS.invert[loglevel]
42
+ else loglevel
43
+ end
44
+ unless LEVELS.include?(lvl)
45
+ raise(ArgumentError, 'Log level must be one of :debug, :info, :warn, :error, or :fatal')
48
46
  end
47
+ @logger.info("[setup] setting log level to #{lvl.to_s.upcase}")
48
+ @level = lvl
49
+ @logger.level = LEVELS[lvl]
49
50
  end
50
51
 
51
52
  # Passes any other method calls on directly to the underlying Logger object created with init. If
data/lib/nanite/mapper.rb CHANGED
@@ -23,9 +23,15 @@ module Nanite
23
23
 
24
24
  attr_reader :cluster, :identity, :job_warden, :options, :serializer, :amq
25
25
 
26
- DEFAULT_OPTIONS = COMMON_DEFAULT_OPTIONS.merge({:user => 'mapper', :identity => Identity.generate, :agent_timeout => 15,
27
- :offline_redelivery_frequency => 10, :persistent => false, :offline_failsafe => false,
28
- :callbacks => {} }) unless defined?(DEFAULT_OPTIONS)
26
+ DEFAULT_OPTIONS = COMMON_DEFAULT_OPTIONS.merge({
27
+ :user => 'mapper',
28
+ :identity => Identity.generate,
29
+ :agent_timeout => 15,
30
+ :offline_redelivery_frequency => 10,
31
+ :persistent => false,
32
+ :offline_failsafe => false,
33
+ :callbacks => {}
34
+ }) unless defined?(DEFAULT_OPTIONS)
29
35
 
30
36
  # Initializes a new mapper and establishes
31
37
  # AMQP connection. This must be used inside EM.run block or if EventMachine reactor
@@ -66,9 +72,15 @@ module Nanite
66
72
  #
67
73
  # persistent : true instructs the AMQP broker to save messages to persistent storage so that they aren't lost when the
68
74
  # broker is restarted. Default is false. Can be overriden on a per-message basis using the request and push methods.
69
- #
75
+ #
70
76
  # secure : use Security features of rabbitmq to restrict nanites to themselves
71
77
  #
78
+ # prefetch : Sets prefetch (only supported in RabbitMQ >= 1.6)
79
+ # callbacks : A set of callbacks to have code executed on specific events, supported events are :register,
80
+ # :unregister and :timeout. Parameter must be a hash with the corresponding events as keys and
81
+ # a block as value. The block will get the corresponding nanite's identity and a copy of the
82
+ # mapper
83
+ #
72
84
  # Connection options:
73
85
  #
74
86
  # vhost : AMQP broker vhost that should be used
@@ -106,7 +118,7 @@ module Nanite
106
118
  @options[:file_root] ||= File.join(@options[:root], 'files')
107
119
  @options.freeze
108
120
  end
109
-
121
+
110
122
  def run
111
123
  setup_logging
112
124
  @serializer = Serializer.new(@options[:format])
@@ -187,7 +199,7 @@ module Nanite
187
199
  false
188
200
  end
189
201
  end
190
-
202
+
191
203
  # Make a nanite request which does not expect a response.
192
204
  #
193
205
  # ==== Parameters
@@ -210,6 +222,10 @@ module Nanite
210
222
  # @api :public:
211
223
  def push(type, payload = '', opts = {})
212
224
  push = build_deliverable(Push, type, payload, opts)
225
+ send_push(push, opts)
226
+ end
227
+
228
+ def send_push(push, opts = {})
213
229
  targets = cluster.targets_for(push)
214
230
  if !targets.empty?
215
231
  cluster.route(push, targets)
@@ -221,7 +237,7 @@ module Nanite
221
237
  false
222
238
  end
223
239
  end
224
-
240
+
225
241
  private
226
242
 
227
243
  def build_deliverable(deliverable_type, type, payload, opts)
@@ -233,6 +249,10 @@ module Nanite
233
249
  end
234
250
 
235
251
  def setup_queues
252
+ if amq.respond_to?(:prefetch) && @options.has_key?(:prefetch)
253
+ amq.prefetch(@options[:prefetch])
254
+ end
255
+
236
256
  setup_offline_queue
237
257
  setup_message_queue
238
258
  end
@@ -264,14 +284,14 @@ module Nanite
264
284
  begin
265
285
  msg = serializer.load(msg)
266
286
  Nanite::Log.debug("RECV #{msg.to_s}")
267
- Nanite::Log.info("RECV #{msg.to_s([:from])}") unless Nanite::Log.level == Logger::DEBUG
287
+ Nanite::Log.info("RECV #{msg.to_s([:from])}") unless Nanite::Log.level == :debug
268
288
  job_warden.process(msg)
269
289
  rescue Exception => e
270
290
  Nanite::Log.error("RECV [result] #{e.message}")
271
291
  end
272
292
  end
273
293
  end
274
-
294
+
275
295
  def setup_logging
276
296
  log_path = false
277
297
  if @options[:daemonize]
@@ -280,7 +300,7 @@ module Nanite
280
300
  Nanite::Log.init(@identity, log_path)
281
301
  Nanite::Log.level = @options[:log_level] if @options[:log_level]
282
302
  end
283
-
303
+
284
304
  def setup_cluster
285
305
  @cluster = Cluster.new(@amq, @options[:agent_timeout], @options[:identity], @serializer, self, @options[:redis], @options[:callbacks])
286
306
  end
@@ -16,7 +16,7 @@ module Nanite
16
16
 
17
17
  # Accessor for actor
18
18
  def self.instance
19
- @@instance
19
+ @@instance if defined?(@@instance)
20
20
  end
21
21
 
22
22
  def initialize(id, opts)
@@ -40,6 +40,16 @@ module Nanite
40
40
  Nanite::Log.info("SEND #{request.to_s([:tags, :target])}")
41
41
  amqp.fanout('request', :no_declare => options[:secure]).publish(serializer.dump(request))
42
42
  end
43
+
44
+ def push(type, payload = '', opts = {})
45
+ raise "Mapper proxy not initialized" unless identity && options
46
+ push = Push.new(type, payload, opts)
47
+ push.from = identity
48
+ push.token = Identity.generate
49
+ push.persistent = opts.key?(:persistent) ? opts[:persistent] : options[:persistent]
50
+ Nanite::Log.info("SEND #{push.to_s([:tags, :target])}")
51
+ amqp.fanout('request', :no_declare => options[:secure]).publish(serializer.dump(push))
52
+ end
43
53
 
44
54
  # Handle intermediary result
45
55
  def handle_intermediate_result(res)
@@ -12,7 +12,7 @@ module Nanite
12
12
  def to_json(*a)
13
13
  js = {
14
14
  'json_class' => self.class.name,
15
- 'data' => instance_variables.inject({}) {|m,ivar| m[ivar.sub(/@/,'')] = instance_variable_get(ivar); m }
15
+ 'data' => instance_variables.inject({}) {|m,ivar| m[ivar.to_s.sub(/@/,'')] = instance_variable_get(ivar); m }
16
16
  }.to_json(*a)
17
17
  js = js.chop + ",\"size\":#{js.size}}"
18
18
  js
@@ -37,11 +37,6 @@ module Nanite
37
37
  end
38
38
  end
39
39
 
40
- # Wrap given string to given maximum number of characters per line
41
- def wrap(txt, col=120)
42
- txt.gsub(/(.{1,#{col}})( +|$\n?)|(.{1,#{col}})/, "\\1\\3\n").chomp
43
- end
44
-
45
40
  end
46
41
 
47
42
  # packet that means start of a file transfer
@@ -63,7 +58,7 @@ module Nanite
63
58
  end
64
59
 
65
60
  def to_s
66
- wrap("#{super} <#{token}> #{filename} to #{dest}")
61
+ "#{super} <#{token}> #{filename} to #{dest}"
67
62
  end
68
63
  end
69
64
 
@@ -85,7 +80,7 @@ module Nanite
85
80
  end
86
81
 
87
82
  def to_s
88
- wrap("#{super} <#{token}> meta #{meta}")
83
+ "#{super} <#{token}> meta #{meta}"
89
84
  end
90
85
  end
91
86
 
@@ -158,7 +153,7 @@ module Nanite
158
153
  log_msg += ", reply_to #{id_to_s(reply_to)}" if reply_to && (filter.nil? || filter.include?(:reply_to))
159
154
  log_msg += ", tags #{tags.inspect}" if tags && !tags.empty? && (filter.nil? || filter.include?(:tags))
160
155
  log_msg += ", payload #{payload.inspect}" if filter.nil? || filter.include?(:payload)
161
- wrap(log_msg)
156
+ log_msg
162
157
  end
163
158
 
164
159
  end
@@ -207,7 +202,7 @@ module Nanite
207
202
  log_msg += ", target #{id_to_s(target)}" if target && (filter.nil? || filter.include?(:target))
208
203
  log_msg += ", tags #{tags.inspect}" if tags && !tags.empty? && (filter.nil? || filter.include?(:tags))
209
204
  log_msg += ", payload #{payload.inspect}" if filter.nil? || filter.include?(:payload)
210
- wrap(log_msg)
205
+ log_msg
211
206
  end
212
207
  end
213
208
 
@@ -239,7 +234,7 @@ module Nanite
239
234
  log_msg += " from #{id_to_s(from)}" if filter.nil? || filter.include?(:from)
240
235
  log_msg += " to #{id_to_s(to)}" if filter.nil? || filter.include?(:to)
241
236
  log_msg += " results: #{results.inspect}" if filter.nil? || filter.include?(:results)
242
- wrap(log_msg)
237
+ log_msg
243
238
  end
244
239
  end
245
240
 
@@ -269,7 +264,7 @@ module Nanite
269
264
  end
270
265
 
271
266
  def to_s
272
- wrap("#{super} <#{token}> from #{id_to_s(from)}, key #{messagekey}")
267
+ "#{super} <#{token}> from #{id_to_s(from)}, key #{messagekey}"
273
268
  end
274
269
  end
275
270
 
@@ -300,7 +295,7 @@ module Nanite
300
295
  log_msg = "#{super} #{id_to_s(identity)}"
301
296
  log_msg += ", services: #{services.join(', ')}" if services && !services.empty?
302
297
  log_msg += ", tags: #{tags.join(', ')}" if tags && !tags.empty?
303
- wrap(log_msg)
298
+ log_msg
304
299
  end
305
300
  end
306
301
 
@@ -25,7 +25,7 @@ module Nanite
25
25
  # Initialize from encrypted data.
26
26
  def self.from_data(encrypted_data)
27
27
  doc = EncryptedDocument.allocate
28
- doc.instance_variable_set(:@pkcs7, OpenSSL::PKCS7::PKCS7.new(encrypted_data))
28
+ doc.instance_variable_set(:@pkcs7, Nanite::PKCS7.new(encrypted_data))
29
29
  doc
30
30
  end
31
31
 
@@ -51,6 +51,7 @@ module Nanite
51
51
  data = JSON.load(json)
52
52
  sig = Signature.from_data(data['signature'])
53
53
  certs = @store.get_signer(data['id'])
54
+ raise "Could not find a cert for signer #{data['id']}" unless certs
54
55
  certs = [ certs ] unless certs.respond_to?(:each)
55
56
  jsn = data['data'] if certs.any? { |c| sig.match?(c) }
56
57
  if jsn && @encrypt && data['encrypted']
@@ -1,3 +1,9 @@
1
+ if defined?(OpenSSL::PKCS7::PKCS7)
2
+ Nanite::PKCS7 = OpenSSL::PKCS7::PKCS7
3
+ else
4
+ Nanite::PKCS7 = OpenSSL::PKCS7
5
+ end
6
+
1
7
  module Nanite
2
8
 
3
9
  # Signature that can be validated against certificates
@@ -20,7 +26,7 @@ module Nanite
20
26
  # Load signature previously serialized via 'data'
21
27
  def self.from_data(data)
22
28
  sig = Signature.allocate
23
- sig.instance_variable_set(:@p7, OpenSSL::PKCS7::PKCS7.new(data))
29
+ sig.instance_variable_set(:@p7, Nanite::PKCS7.new(data))
24
30
  sig.instance_variable_set(:@store, OpenSSL::X509::Store.new)
25
31
  sig
26
32
  end
data/lib/nanite/state.rb CHANGED
@@ -39,7 +39,7 @@ module Nanite
39
39
 
40
40
  def log_redis_error(meth,&blk)
41
41
  blk.call
42
- rescue RedisError => e
42
+ rescue Exception => e
43
43
  Nanite::Log.info("redis error in method: #{meth}")
44
44
  raise e
45
45
  end
data/lib/nanite/util.rb CHANGED
@@ -30,22 +30,29 @@ class String
30
30
  end
31
31
 
32
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
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
48
56
  end
49
- ret
50
57
  end
51
58
  end
data/lib/nanite.rb CHANGED
@@ -39,7 +39,7 @@ require 'nanite/security/static_certificate_store'
39
39
  require 'nanite/serializer'
40
40
 
41
41
  module Nanite
42
- VERSION = '0.4.0' unless defined?(Nanite::VERSION)
42
+ VERSION = '0.4.1.3' unless defined?(Nanite::VERSION)
43
43
 
44
44
  class MapperNotRunning < StandardError; end
45
45
 
@@ -65,7 +65,10 @@ module Nanite
65
65
  end
66
66
 
67
67
  def ensure_mapper
68
- raise MapperNotRunning.new('A mapper needs to be started via Nanite.start_mapper') unless @mapper
68
+ @mapper ||= MapperProxy.instance
69
+ unless @mapper
70
+ raise MapperNotRunning.new('A mapper needs to be started via Nanite.start_mapper')
71
+ end
69
72
  end
70
73
  end
71
74
  end
@@ -2,30 +2,28 @@ require File.join(File.dirname(__FILE__), 'spec_helper')
2
2
 
3
3
  describe Nanite::ActorRegistry do
4
4
 
5
- before(:all) do
6
- class WebDocumentImporter
7
- include Nanite::Actor
8
- expose :import, :cancel
5
+ class ::WebDocumentImporter
6
+ include Nanite::Actor
7
+ expose :import, :cancel
9
8
 
10
- def import
11
- 1
12
- end
13
- def cancel
14
- 0
15
- end
9
+ def import
10
+ 1
11
+ end
12
+ def cancel
13
+ 0
16
14
  end
15
+ end
17
16
 
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
17
+ module ::Actors
18
+ class ComedyActor
19
+ include Nanite::Actor
20
+ expose :fun_tricks
21
+ def fun_tricks
22
+ :rabbit_in_the_hat
25
23
  end
26
24
  end
27
25
  end
28
-
26
+
29
27
  before(:each) do
30
28
  Nanite::Log.stub!(:info)
31
29
  @registry = Nanite::ActorRegistry.new
data/spec/actor_spec.rb CHANGED
@@ -1,33 +1,46 @@
1
1
  require File.join(File.dirname(__FILE__), 'spec_helper')
2
2
 
3
- class WebDocumentImporter
4
- include Nanite::Actor
5
- expose :import, :cancel
3
+ describe Nanite::Actor do
4
+ class ::WebDocumentImporter
5
+ include Nanite::Actor
6
+ expose :import, :cancel
6
7
 
7
- def import
8
- 1
9
- end
10
- def cancel
11
- 0
12
- end
13
- def continue
14
- 1
8
+ def import
9
+ 1
10
+ end
11
+ def cancel
12
+ 0
13
+ end
14
+ def continue
15
+ 1
16
+ end
15
17
  end
16
- end
17
18
 
18
- module Actors
19
- class ComedyActor
20
- include Nanite::Actor
21
- expose :fun_tricks
22
- def fun_tricks
23
- :rabbit_in_the_hat
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
24
26
  end
25
27
  end
26
- end
27
28
 
28
- describe Nanite::Actor do
29
+ class ::Actors::InvalidActor
30
+ include Nanite::Actor
31
+ expose :non_existing
32
+ end
29
33
 
30
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
+
31
44
  it "should single expose method only once" do
32
45
  3.times { WebDocumentImporter.expose(:continue) }
33
46
  WebDocumentImporter.provides_for("webfiles").should == ["/webfiles/import", "/webfiles/cancel", "/webfiles/continue"]
@@ -45,6 +58,7 @@ describe Nanite::Actor do
45
58
  before :each do
46
59
  @provides = Actors::ComedyActor.provides_for("money")
47
60
  end
61
+
48
62
  it "returns an array" do
49
63
  @provides.should be_kind_of(Array)
50
64
  end
@@ -55,5 +69,9 @@ describe Nanite::Actor do
55
69
  wdi_provides.should include("/webfiles/import")
56
70
  wdi_provides.should include("/webfiles/cancel")
57
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
58
76
  end
59
77
  end
data/spec/agent_spec.rb CHANGED
@@ -186,7 +186,12 @@ describe "Agent:" do
186
186
  agent.tags.should include("sample_tag_1")
187
187
  agent.tags.should include("sample_tag_2")
188
188
  end
189
-
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
+
190
195
  end
191
196
 
192
197
  describe "Security" do
data/spec/cluster_spec.rb CHANGED
@@ -94,17 +94,23 @@ describe Nanite::Cluster do
94
94
  end # Reaper
95
95
 
96
96
  describe "State" do
97
- require 'nanite/state'
98
- it "should use a local state by default" do
99
- cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper)
100
- cluster.nanites.instance_of?(Nanite::LocalState).should == true
97
+ begin
98
+ require 'nanite/state'
99
+ rescue LoadError
101
100
  end
101
+
102
+ if defined?(Redis)
103
+ it "should use a local state by default" do
104
+ cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper)
105
+ cluster.nanites.instance_of?(Nanite::LocalState).should == true
106
+ end
102
107
 
103
- it "should set up a redis state when requested" do
104
- state = Nanite::State.new("")
105
- Nanite::State.should_receive(:new).with("localhost:1234").and_return(state)
106
- cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper, "localhost:1234")
107
- cluster.nanites.instance_of?(Nanite::State).should == true
108
+ it "should set up a redis state when requested" do
109
+ state = Nanite::State.new("")
110
+ Nanite::State.should_receive(:new).with("localhost:1234").and_return(state)
111
+ cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper, "localhost:1234")
112
+ cluster.nanites.instance_of?(Nanite::State).should == true
113
+ end
108
114
  end
109
115
  end
110
116
  end # Intialization
@@ -410,6 +416,14 @@ describe Nanite::Cluster do
410
416
  @cluster_with_target.should_receive(:forward_response)
411
417
  @cluster_with_target.__send__(:handle_request, @request_with_target)
412
418
  end
419
+
420
+ describe "when getting push requests from an agent" do
421
+ it "should send the push message through the mapper" do
422
+ push = Nanite::Push.new(nil, nil)
423
+ @mapper_with_target.should_receive(:send_push).with(push)
424
+ @cluster_with_target.__send__(:handle_request, push)
425
+ end
426
+ end
413
427
  end # Agent Request Handling
414
428
 
415
429
  describe "Heartbeat" do
data/spec/log_spec.rb ADDED
@@ -0,0 +1,49 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Nanite::Log do
4
+ describe "Using log without initializing it first" do
5
+ before(:each) do
6
+ Nanite::Log.instance_variable_set(:@logger, nil)
7
+ end
8
+
9
+ it "should use standard out for logging" do
10
+ Nanite::Log.init
11
+ STDOUT.should_receive(:write) do |arg|
12
+ arg.include?("For your consideration").should == true
13
+ end
14
+ Nanite::Log.info("For your consideration")
15
+ end
16
+ end
17
+
18
+ describe "Initializing the log level" do
19
+ it "should default to :info" do
20
+ Nanite::Log.init
21
+ Nanite::Log.level.should == :info
22
+ end
23
+
24
+ it "should raise an error if level is incorrect" do
25
+ lambda { Nanite::Log.level = "fool" }.should raise_error
26
+ end
27
+
28
+ it "should succeed when using symbols" do
29
+ [ :debug, :info, :warn, :error, :fatal ].each do |level|
30
+ Nanite::Log.level = level
31
+ Nanite::Log.level.should == level
32
+ end
33
+ end
34
+
35
+
36
+ it "should succeed when using log levels" do
37
+ lvls = { Logger::DEBUG => :debug,
38
+ Logger::INFO => :info,
39
+ Logger::WARN => :warn,
40
+ Logger::ERROR => :error,
41
+ Logger::FATAL => :fatal }
42
+ lvls.keys.each do |level|
43
+ Nanite::Log.level = level
44
+ Nanite::Log.level.should == lvls[level]
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,72 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Nanite::MapperProxy do
4
+ describe "when fetching the instance" do
5
+ before(:each) do
6
+ if Nanite::MapperProxy.class_variables.include?('@@instance')
7
+ Nanite::MapperProxy.__send__(:remove_class_variable, :@@instance)
8
+ end
9
+ end
10
+
11
+ it "should return nil when the instance is undefined" do
12
+ Nanite::MapperProxy.instance.should == nil
13
+ end
14
+
15
+ it "should return the instance if defined" do
16
+ instance = mock
17
+ Nanite::MapperProxy.class_eval do
18
+ @@instance = "instance"
19
+ end
20
+
21
+ Nanite::MapperProxy.instance.should_not == nil
22
+ end
23
+ end
24
+
25
+ describe "when pushing a message" do
26
+ before do
27
+ AMQP.stub!(:connect)
28
+ MQ.stub!(:new)
29
+ Nanite::MapperProxy.new('mapperproxy', {})
30
+ @instance = Nanite::MapperProxy.instance
31
+ @fanout = stub(:fanout, :publish => true)
32
+ @instance.amqp.stub!(:fanout).and_return(@fanout)
33
+ end
34
+
35
+ it "should raise an error if mapper proxy is not initialized" do
36
+ lambda {
37
+ @instance.stub!(:identity).and_return nil
38
+ @instance.push('/welcome/aboard', 'iZac')
39
+ }.should raise_error("Mapper proxy not initialized")
40
+ end
41
+
42
+ it "should set correct attributes on the push message" do
43
+ @fanout.should_receive(:publish).with do |push|
44
+ push = @instance.serializer.load(push)
45
+ push.token.should_not == nil
46
+ push.persistent.should_not == true
47
+ push.from.should == 'mapperproxy'
48
+ end
49
+
50
+ @instance.push('/welcome/aboard', 'iZac')
51
+ end
52
+
53
+ it "should mark the message as persistent when the option is specified on the parameter" do
54
+ @fanout.should_receive(:publish).with do |push|
55
+ push = @instance.serializer.load(push)
56
+ push.persistent.should == true
57
+ end
58
+
59
+ @instance.push('/welcome/aboard', 'iZac', :persistent => true)
60
+ end
61
+
62
+ it "should mark the message as persistent when the option is set globally" do
63
+ @instance.options[:persistent] = true
64
+ @fanout.should_receive(:publish).with do |push|
65
+ push = @instance.serializer.load(push)
66
+ push.persistent.should == true
67
+ end
68
+
69
+ @instance.push('/welcome/aboard', 'iZac')
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,70 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Nanite::Mapper do
4
+ include SpecHelpers
5
+
6
+ describe "Initializing" do
7
+ before(:each) do
8
+ @mapper = Nanite::Mapper.new({})
9
+ end
10
+
11
+ it "should set the identity" do
12
+ @mapper.identity.should_not == nil
13
+ @mapper.identity.should =~ /mapper-.*/
14
+ end
15
+
16
+ it "should set the identity to a custom identity" do
17
+ @mapper = Nanite::Mapper.new({:identity => "bob"})
18
+ @mapper.identity.should == "mapper-bob"
19
+ end
20
+
21
+ it "should set the file root" do
22
+ @mapper.options[:file_root].should == File.expand_path("#{File.dirname(__FILE__)}/../files")
23
+ end
24
+ end
25
+
26
+ describe "Starting" do
27
+ before(:each) do
28
+ @mapper = Nanite::Mapper.new({:log_level => :debug})
29
+ @mapper.stub!(:setup_queues)
30
+ @mapper.stub!(:start_amqp)
31
+ end
32
+
33
+ it "should initialize the logger" do
34
+ @mapper.stub!(:setup_cluster)
35
+ run_in_em do
36
+ @mapper.run
37
+ Nanite::Log.logger.level.should == Logger::DEBUG
38
+ end
39
+ end
40
+
41
+ it "should set up the cluster" do
42
+ Nanite::Cluster.should_receive(:new).with(nil, 15, instance_of(String), instance_of(Nanite::Serializer), @mapper, nil, {})
43
+ run_in_em do
44
+ @mapper.run
45
+ end
46
+ end
47
+
48
+ it "should set the prefetch value" do
49
+ amqp = mock("AMQP")
50
+
51
+ mapper = Nanite::Mapper.new(:prefetch => 11)
52
+ mapper.stub!(:setup_offline_queue)
53
+ mapper.stub!(:setup_message_queue)
54
+ mapper.stub!(:start_amqp)
55
+ mapper.stub!(:setup_cluster)
56
+
57
+ mapper.stub!(:start_amqp).and_return(amqp)
58
+ amqp.should_receive(:prefetch).with(11)
59
+ mapper.run
60
+ end
61
+
62
+ it "should hand over callback options to the cluster" do
63
+ @mapper = Nanite::Mapper.new({:callbacks => {:register => lambda {|*args|}}})
64
+ @mapper.stub!(:setup_queues)
65
+ @mapper.stub!(:start_amqp)
66
+ Nanite::Cluster.should_receive(:new)
67
+ run_in_em {@mapper.run}
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,36 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Nanite do
4
+ describe "when ensuring a mapper exists" do
5
+ describe "with a configured mapper proxy" do
6
+ before(:each) do
7
+ Nanite.instance_variable_set(:@mapper, nil)
8
+ Nanite::MapperProxy.stub!(:instance).and_return(mock(:mapper_proxy))
9
+ end
10
+
11
+ it "should not raise an error" do
12
+ lambda {
13
+ Nanite.ensure_mapper
14
+ }.should_not raise_error
15
+ end
16
+
17
+ it "should set the mapper instance variable to the mapper proxy instance" do
18
+ Nanite.ensure_mapper
19
+ Nanite.mapper.should == Nanite::MapperProxy.instance
20
+ end
21
+ end
22
+
23
+ describe "when the mapper wasn't started yet" do
24
+ before do
25
+ Nanite.instance_variable_set(:@mapper, nil)
26
+ Nanite::MapperProxy.stub!(:instance).and_return(nil)
27
+ end
28
+
29
+ it "should raise an error" do
30
+ lambda {
31
+ Nanite.ensure_mapper
32
+ }.should raise_error(Nanite::MapperNotRunning)
33
+ end
34
+ end
35
+ end
36
+ end
data/spec/packet_spec.rb CHANGED
@@ -1,13 +1,15 @@
1
1
  require File.join(File.dirname(__FILE__), 'spec_helper')
2
2
 
3
+ class TestPacket < Nanite::Packet
4
+ @@cls_attr = "ignore"
5
+ def initialize(attr1)
6
+ @attr1 = attr1
7
+ end
8
+ end
9
+
3
10
  describe "Packet: Base class" do
11
+
4
12
  before(:all) do
5
- class TestPacket < Nanite::Packet
6
- @@cls_attr = "ignore"
7
- def initialize(attr1)
8
- @attr1 = attr1
9
- end
10
- end
11
13
  end
12
14
 
13
15
  it "should be an abstract class" do
data/spec/spec_helper.rb CHANGED
@@ -7,9 +7,6 @@ require 'nanite'
7
7
 
8
8
  module SpecHelpers
9
9
 
10
- # Initialize logger so it writes to file instead of STDOUT
11
- Nanite::Log.init('test', File.join(File.dirname(__FILE__)))
12
-
13
10
  # Create test certificate
14
11
  def issue_cert
15
12
  test_dn = { 'C' => 'US',
@@ -30,4 +27,4 @@ module SpecHelpers
30
27
  end
31
28
  end
32
29
 
33
- end
30
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rightscale-nanite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1.2
4
+ version: 0.4.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ezra Zygmuntowicz
@@ -86,9 +86,11 @@ files:
86
86
  - spec/certificate_cache_spec.rb
87
87
  - spec/cached_certificate_store_proxy_spec.rb
88
88
  - spec/dispatcher_spec.rb
89
+ - spec/log_spec.rb
89
90
  - spec/rsa_key_pair_spec.rb
90
91
  - spec/cluster_spec.rb
91
92
  - spec/spec_helper.rb
93
+ - spec/mapper_spec.rb
92
94
  - spec/actor_registry_spec.rb
93
95
  - spec/actor_spec.rb
94
96
  - spec/packet_spec.rb
@@ -96,9 +98,11 @@ files:
96
98
  - spec/static_certificate_store_spec.rb
97
99
  - spec/job_spec.rb
98
100
  - spec/signature_spec.rb
101
+ - spec/mapper_proxy_spec.rb
99
102
  - spec/secure_serializer_spec.rb
100
103
  - spec/serializer_spec.rb
101
104
  - spec/certificate_spec.rb
105
+ - spec/nanite_spec.rb
102
106
  - spec/distinguished_name_spec.rb
103
107
  has_rdoc: true
104
108
  homepage: http://github.com/ezmobius/nanite