rightscale-nanite 0.4.1.2 → 0.4.1.3

Sign up to get free protection for your applications and to get access to all the features.
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