ezmobius-nanite 0.4.1.1 → 0.4.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -43,6 +43,8 @@ Rake::GemPackageTask.new(spec) do |pkg|
43
43
  pkg.gem_spec = spec
44
44
  end
45
45
 
46
+ task :default => :spec
47
+
46
48
  task :install => [:package] do
47
49
  sh %{sudo gem install pkg/#{GEM}-#{VER}}
48
50
  end
@@ -8,7 +8,9 @@ module Nanite
8
8
 
9
9
  def register(actor, prefix)
10
10
  raise ArgumentError, "#{actor.inspect} is not a Nanite::Actor subclass instance" unless Nanite::Actor === actor
11
- Nanite::Log.info("Registering #{actor.inspect} with prefix #{prefix.inspect}")
11
+ log_msg = "[actor] #{actor.class.to_s}"
12
+ log_msg += ", prefix #{prefix}" if prefix && !prefix.empty?
13
+ Nanite::Log.info(log_msg)
12
14
  prefix ||= actor.class.default_prefix
13
15
  actors[prefix.to_s] = actor
14
16
  end
data/lib/nanite/agent.rb CHANGED
@@ -158,7 +158,7 @@ module Nanite
158
158
  actors = @options[:actors]
159
159
  Dir["#{actors_dir}/*.rb"].each do |actor|
160
160
  next if actors && !actors.include?(File.basename(actor, ".rb"))
161
- Nanite::Log.info("loading actor: #{actor}")
161
+ Nanite::Log.info("[setup] loading #{actor}")
162
162
  require actor
163
163
  end
164
164
  init_path = @options[:initrb] || File.join(options[:root], 'init.rb')
@@ -166,25 +166,27 @@ module Nanite
166
166
  end
167
167
 
168
168
  def receive(packet)
169
+ Nanite::Log.debug("RECV #{packet.to_s}")
169
170
  case packet
170
171
  when Advertise
171
- Nanite::Log.debug("handling Advertise: #{packet.inspect}")
172
+ Nanite::Log.info("RECV #{packet.to_s}") unless Nanite::Log.level == Logger::DEBUG
172
173
  advertise_services
173
174
  when Request, Push
174
- Nanite::Log.debug("handling Request: #{packet.inspect}")
175
175
  if @security && !@security.authorize(packet)
176
+ Nanite::Log.warn("RECV NOT AUTHORIZED #{packet.to_s}")
176
177
  if packet.kind_of?(Request)
177
178
  r = Result.new(packet.token, packet.reply_to, @deny_token, identity)
178
179
  amq.queue(packet.reply_to, :no_declare => options[:secure]).publish(serializer.dump(r))
179
180
  end
180
181
  else
182
+ Nanite::Log.info("RECV #{packet.to_s([:from, :tags])}") unless Nanite::Log.level == Logger::DEBUG
181
183
  dispatcher.dispatch(packet)
182
184
  end
183
185
  when Result
184
- Nanite::Log.debug("handling Result: #{packet.inspect}")
186
+ Nanite::Log.info("RECV #{packet.to_s([])}") unless Nanite::Log.level == Logger::DEBUG
185
187
  @mapper_proxy.handle_result(packet)
186
188
  when IntermediateMessage
187
- Nanite::Log.debug("handling Intermediate Result: #{packet.inspect}")
189
+ Nanite::Log.info("RECV #{packet.to_s([])}") unless Nanite::Log.level == Logger::DEBUG
188
190
  @mapper_proxy.handle_intermediate_result(packet)
189
191
  end
190
192
  end
@@ -198,10 +200,9 @@ module Nanite
198
200
  amq.queue(identity, :durable => true).subscribe(:ack => true) do |info, msg|
199
201
  begin
200
202
  info.ack
201
- packet = serializer.load(msg)
202
- receive(packet)
203
+ receive(serializer.load(msg))
203
204
  rescue Exception => e
204
- Nanite::Log.error("Error handling packet: #{e.message}")
205
+ Nanite::Log.error("RECV #{e.message}")
205
206
  end
206
207
  end
207
208
  end
@@ -231,13 +232,15 @@ module Nanite
231
232
  def un_register
232
233
  unless @unregistered
233
234
  @unregistered = true
235
+ Nanite::Log.info("SEND [un_register]")
234
236
  amq.fanout('registration', :no_declare => options[:secure]).publish(serializer.dump(UnRegister.new(identity)))
235
237
  end
236
238
  end
237
239
 
238
240
  def advertise_services
239
- Nanite::Log.debug("advertise_services: #{registry.services.inspect}")
240
- amq.fanout('registration', :no_declare => options[:secure]).publish(serializer.dump(Register.new(identity, registry.services, status_proc.call, self.tags)))
241
+ reg = Register.new(identity, registry.services, status_proc.call, self.tags)
242
+ Nanite::Log.info("SEND #{reg.to_s}")
243
+ amq.fanout('registration', :no_declare => options[:secure]).publish(serializer.dump(reg))
241
244
  end
242
245
 
243
246
  def parse_uptime(up)
@@ -1,23 +1,17 @@
1
1
  module Nanite
2
2
  class Cluster
3
- attr_reader :agent_timeout, :nanites, :reaper, :serializer, :identity, :amq, :redis, :mapper
3
+ attr_reader :agent_timeout, :nanites, :reaper, :serializer, :identity, :amq, :redis, :mapper, :callbacks
4
4
 
5
- def initialize(amq, agent_timeout, identity, serializer, mapper, redis=nil)
5
+ def initialize(amq, agent_timeout, identity, serializer, mapper, state_configuration=nil, callbacks = {})
6
6
  @amq = amq
7
7
  @agent_timeout = agent_timeout
8
8
  @identity = identity
9
9
  @serializer = serializer
10
10
  @mapper = mapper
11
- @redis = redis
11
+ @state = state_configuration
12
12
  @security = SecurityProvider.get
13
- if redis
14
- Nanite::Log.info("using redis for state storage")
15
- require 'nanite/state'
16
- @nanites = ::Nanite::State.new(redis)
17
- else
18
- require 'nanite/local_state'
19
- @nanites = Nanite::LocalState.new
20
- end
13
+ @callbacks = callbacks
14
+ setup_state
21
15
  @reaper = Reaper.new(agent_timeout)
22
16
  setup_queues
23
17
  end
@@ -35,18 +29,27 @@ module Nanite
35
29
  case reg
36
30
  when Register
37
31
  if @security.authorize_registration(reg)
32
+ Nanite::Log.info("RECV #{reg.to_s}")
38
33
  nanites[reg.identity] = { :services => reg.services, :status => reg.status, :tags => reg.tags }
39
- reaper.timeout(reg.identity, agent_timeout + 1) { nanites.delete(reg.identity) }
40
- Nanite::Log.info("registered: #{reg.identity}, #{nanites[reg.identity].inspect}")
34
+ reaper.timeout(reg.identity, agent_timeout + 1) { nanite_timed_out(reg.identity) }
35
+ callbacks[:register].call(reg.identity, mapper) if callbacks[:register]
41
36
  else
42
- Nanite::Log.warning("registration of #{reg.inspect} not authorized")
37
+ Nanite::Log.warn("RECV NOT AUTHORIZED #{reg.to_s}")
43
38
  end
44
39
  when UnRegister
40
+ Nanite::Log.info("RECV #{reg.to_s}")
45
41
  nanites.delete(reg.identity)
46
- Nanite::Log.info("un-registering: #{reg.identity}")
42
+ callbacks[:unregister].call(reg.identity, mapper) if callbacks[:unregister]
43
+ else
44
+ Nanite::Log.warn("RECV [register] Invalid packet type: #{reg.class}")
47
45
  end
48
46
  end
49
47
 
48
+ def nanite_timed_out(token)
49
+ nanite = nanites.delete(token)
50
+ callbacks[:timeout].call(token, mapper) if callbacks[:timeout]
51
+ end
52
+
50
53
  def route(request, targets)
51
54
  EM.next_tick { targets.map { |target| publish(request, target) } }
52
55
  end
@@ -57,6 +60,7 @@ module Nanite
57
60
  begin
58
61
  old_target = request.target
59
62
  request.target = target unless target == 'mapper-offline'
63
+ Nanite::Log.info("SEND #{request.to_s([:from, :tags, :target])}")
60
64
  amq.queue(target).publish(serializer.dump(request), :persistent => request.persistent)
61
65
  ensure
62
66
  request.target = old_target
@@ -68,36 +72,46 @@ module Nanite
68
72
  # updates nanite information (last ping timestamps, status)
69
73
  # when heartbeat message is received
70
74
  def handle_ping(ping)
71
- if nanite = nanites[ping.identity]
72
- nanite[:status] = ping.status
73
- reaper.reset_with_autoregister_hack(ping.identity, agent_timeout + 1) { nanites.delete(ping.identity) }
74
- else
75
- amq.queue(ping.identity).publish(serializer.dump(Advertise.new))
75
+ begin
76
+ if nanite = nanites[ping.identity]
77
+ nanite[:status] = ping.status
78
+ reaper.reset_with_autoregister_hack(ping.identity, agent_timeout + 1) { nanite_timed_out(ping.identity) }
79
+ else
80
+ packet = Advertise.new
81
+ Nanite::Log.info("SEND #{packet.to_s} to #{ping.identity}")
82
+ amq.queue(ping.identity).publish(serializer.dump(packet))
83
+ end
76
84
  end
77
85
  end
78
86
 
79
87
  # forward request coming from agent
80
88
  def handle_request(request)
81
89
  if @security.authorize_request(request)
82
- result = Result.new(request.token, request.from, nil, mapper.identity)
83
- intm_handler = lambda do |res|
84
- result.results = res
90
+ Nanite::Log.info("RECV #{request.to_s([:from, :target, :tags])}") unless Nanite::Log.level == Logger::DEBUG
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)
85
95
  forward_response(result, request.persistent)
86
96
  end
97
+
98
+ result = Result.new(request.token, request.from, nil, mapper.identity)
87
99
  ok = mapper.send_request(request, :intermediate_handler => intm_handler) do |res|
88
100
  result.results = res
89
101
  forward_response(result, request.persistent)
90
102
  end
103
+
91
104
  if ok == false
92
105
  forward_response(result, request.persistent)
93
106
  end
94
107
  else
95
- Nanite::Log.warning("request #{request.inspect} not authorized")
108
+ Nanite::Log.warn("RECV NOT AUTHORIZED #{request.to_s}")
96
109
  end
97
110
  end
98
111
 
99
112
  # forward response back to agent that originally made the request
100
113
  def forward_response(res, persistent)
114
+ Nanite::Log.info("SEND #{res.to_s([:to])}")
101
115
  amq.queue(res.to).publish(serializer.dump(res), :persistent => persistent)
102
116
  end
103
117
 
@@ -150,14 +164,14 @@ module Nanite
150
164
  handler = lambda do |ping|
151
165
  begin
152
166
  ping = serializer.load(ping)
153
- Nanite::Log.debug("got heartbeat from #{ping.identity}") if ping.respond_to?(:identity)
167
+ Nanite::Log.debug("RECV #{ping.to_s}") if ping.respond_to?(:to_s)
154
168
  handle_ping(ping)
155
169
  rescue Exception => e
156
- Nanite::Log.error("Error handling heartbeat: #{e.message}")
170
+ Nanite::Log.error("RECV [ping] #{e.message}")
157
171
  end
158
172
  end
159
173
  hb_fanout = amq.fanout('heartbeat', :durable => true)
160
- if @redis
174
+ if shared_state?
161
175
  amq.queue("heartbeat").bind(hb_fanout).subscribe &handler
162
176
  else
163
177
  amq.queue("heartbeat-#{identity}", :exclusive => true).bind(hb_fanout).subscribe &handler
@@ -167,15 +181,13 @@ module Nanite
167
181
  def setup_registration_queue
168
182
  handler = lambda do |msg|
169
183
  begin
170
- msg = serializer.load(msg)
171
- Nanite::Log.debug("got registration from #{msg.identity}")
172
- register(msg)
184
+ register(serializer.load(msg))
173
185
  rescue Exception => e
174
- Nanite::Log.error("Error handling registration: #{e.message}")
186
+ Nanite::Log.error("RECV [register] #{e.message}")
175
187
  end
176
188
  end
177
189
  reg_fanout = amq.fanout('registration', :durable => true)
178
- if @redis
190
+ if shared_state?
179
191
  amq.queue("registration").bind(reg_fanout).subscribe &handler
180
192
  else
181
193
  amq.queue("registration-#{identity}", :exclusive => true).bind(reg_fanout).subscribe &handler
@@ -185,19 +197,36 @@ module Nanite
185
197
  def setup_request_queue
186
198
  handler = lambda do |msg|
187
199
  begin
188
- msg = serializer.load(msg)
189
- Nanite::Log.debug("got request from #{msg.from} of type #{msg.type}")
190
- handle_request(msg)
200
+ handle_request(serializer.load(msg))
191
201
  rescue Exception => e
192
- Nanite::Log.error("Error handling request: #{e.message}")
202
+ Nanite::Log.error("RECV [request] #{e.message}")
193
203
  end
194
204
  end
195
205
  req_fanout = amq.fanout('request', :durable => true)
196
- if @redis
206
+ if shared_state?
197
207
  amq.queue("request").bind(req_fanout).subscribe &handler
198
208
  else
199
209
  amq.queue("request-#{identity}", :exclusive => true).bind(req_fanout).subscribe &handler
200
210
  end
201
211
  end
212
+
213
+ def setup_state
214
+ case @state
215
+ when String
216
+ # backwards compatibility, we assume redis if the configuration option
217
+ # was a string
218
+ Nanite::Log.info("[setup] using redis for state storage")
219
+ require 'nanite/state'
220
+ @nanites = Nanite::State.new(@state)
221
+ when Hash
222
+ else
223
+ require 'nanite/local_state'
224
+ @nanites = Nanite::LocalState.new
225
+ end
226
+ end
227
+
228
+ def shared_state?
229
+ !@state.nil?
230
+ end
202
231
  end
203
232
  end
@@ -31,6 +31,7 @@ module Nanite
31
31
  callback = lambda do |r|
32
32
  if deliverable.kind_of?(Request)
33
33
  r = Result.new(deliverable.token, deliverable.reply_to, r, identity)
34
+ Nanite::Log.info("SEND #{r.to_s([])}")
34
35
  amq.queue(deliverable.reply_to, :no_declare => options[:secure]).publish(serializer.dump(r))
35
36
  end
36
37
  r # For unit tests
data/lib/nanite/job.rb CHANGED
@@ -14,8 +14,6 @@ module Nanite
14
14
  end
15
15
 
16
16
  def process(msg)
17
- Nanite::Log.debug("processing message: #{msg.inspect}")
18
-
19
17
  if job = jobs[msg.token]
20
18
  job.process(msg)
21
19
 
@@ -29,6 +27,8 @@ module Nanite
29
27
  handler = job.intermediate_handler_for_key(key)
30
28
  if handler
31
29
  case handler.arity
30
+ when 2
31
+ handler.call(job.intermediate_state[msg.from][key].last, job)
32
32
  when 3
33
33
  handler.call(key, msg.from, job.intermediate_state[msg.from][key].last)
34
34
  when 4
data/lib/nanite/log.rb CHANGED
@@ -13,7 +13,7 @@ module Nanite
13
13
  # Use Nanite::Logger.init when you want to set up the logger manually.
14
14
  # If this method is called with no arguments, it will log to STDOUT at the :info level.
15
15
  # It also configures the Logger instance it creates to use the custom Nanite::Log::Formatter class.
16
- def init(identity, path = false)
16
+ def init(identity = nil, path = false)
17
17
  if path
18
18
  @file = File.join(path, "nanite.#{identity}.log")
19
19
  else
@@ -30,7 +30,8 @@ module Nanite
30
30
  def level=(loglevel)
31
31
  init() unless @logger
32
32
  loglevel = loglevel.intern if loglevel.is_a?(String)
33
- @logger.info("Setting log level to #{loglevel.to_s.upcase}")
33
+ @logger.info("[setup] setting log level to #{loglevel.to_s.upcase}")
34
+ @level = loglevel
34
35
  case loglevel
35
36
  when :debug
36
37
  @logger.level = Logger::DEBUG
@@ -46,12 +47,12 @@ module Nanite
46
47
  raise ArgumentError, "Log level must be one of :debug, :info, :warn, :error, or :fatal"
47
48
  end
48
49
  end
49
-
50
+
50
51
  # Passes any other method calls on directly to the underlying Logger object created with init. If
51
52
  # this method gets hit before a call to Nanite::Logger.init has been made, it will call
52
53
  # Nanite::Logger.init() with no arguments.
53
54
  def method_missing(method_symbol, *args)
54
- init(identity) unless @logger
55
+ init unless @logger
55
56
  if args.length > 0
56
57
  @logger.send(method_symbol, *args)
57
58
  else
data/lib/nanite/mapper.rb CHANGED
@@ -24,7 +24,8 @@ module Nanite
24
24
  attr_reader :cluster, :identity, :job_warden, :options, :serializer, :amq
25
25
 
26
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}) unless defined?(DEFAULT_OPTIONS)
27
+ :offline_redelivery_frequency => 10, :persistent => false, :offline_failsafe => false,
28
+ :callbacks => {} }) unless defined?(DEFAULT_OPTIONS)
28
29
 
29
30
  # Initializes a new mapper and establishes
30
31
  # AMQP connection. This must be used inside EM.run block or if EventMachine reactor
@@ -65,8 +66,9 @@ module Nanite
65
66
  #
66
67
  # persistent : true instructs the AMQP broker to save messages to persistent storage so that they aren't lost when the
67
68
  # broker is restarted. Default is false. Can be overriden on a per-message basis using the request and push methods.
68
- #
69
+ #
69
70
  # secure : use Security features of rabbitmq to restrict nanites to themselves
71
+ # prefetch : Sets prefetch (only supported in RabbitMQ >= 1.6)
70
72
  #
71
73
  # Connection options:
72
74
  #
@@ -105,14 +107,9 @@ module Nanite
105
107
  @options[:file_root] ||= File.join(@options[:root], 'files')
106
108
  @options.freeze
107
109
  end
108
-
110
+
109
111
  def run
110
- log_path = false
111
- if @options[:daemonize]
112
- log_path = (@options[:log_dir] || @options[:root] || Dir.pwd)
113
- end
114
- Nanite::Log.init(@identity, log_path)
115
- Nanite::Log.level = @options[:log_level] if @options[:log_level]
112
+ setup_logging
116
113
  @serializer = Serializer.new(@options[:format])
117
114
  pid_file = PidFile.new(@identity, @options)
118
115
  pid_file.check
@@ -120,11 +117,13 @@ module Nanite
120
117
  daemonize
121
118
  pid_file.write
122
119
  at_exit { pid_file.remove }
120
+ else
121
+ trap("INT") {exit}
123
122
  end
124
123
  @amq = start_amqp(@options)
125
124
  @job_warden = JobWarden.new(@serializer)
126
- @cluster = Cluster.new(@amq, @options[:agent_timeout], @options[:identity], @serializer, self, @options[:redis])
127
- Nanite::Log.info('starting mapper')
125
+ setup_cluster
126
+ Nanite::Log.info('[setup] starting mapper')
128
127
  setup_queues
129
128
  start_console if @options[:console] && !@options[:daemonize]
130
129
  end
@@ -189,7 +188,7 @@ module Nanite
189
188
  false
190
189
  end
191
190
  end
192
-
191
+
193
192
  # Make a nanite request which does not expect a response.
194
193
  #
195
194
  # ==== Parameters
@@ -235,6 +234,10 @@ module Nanite
235
234
  end
236
235
 
237
236
  def setup_queues
237
+ if amq.respond_to?(:prefetch) && @options.has_key?(:prefetch)
238
+ amq.prefetch(@options[:prefetch])
239
+ end
240
+
238
241
  setup_offline_queue
239
242
  setup_message_queue
240
243
  end
@@ -264,14 +267,28 @@ module Nanite
264
267
  def setup_message_queue
265
268
  amq.queue(identity, :exclusive => true).bind(amq.fanout(identity)).subscribe do |msg|
266
269
  begin
267
- msg = serializer.load(msg)
268
- Nanite::Log.debug("got result from #{msg.from}: #{msg.results.inspect}")
270
+ msg = serializer.load(msg)
271
+ Nanite::Log.debug("RECV #{msg.to_s}")
272
+ Nanite::Log.info("RECV #{msg.to_s([:from])}") unless Nanite::Log.level == Logger::DEBUG
269
273
  job_warden.process(msg)
270
274
  rescue Exception => e
271
- Nanite::Log.error("Error handling result: #{e.message}")
275
+ Nanite::Log.error("RECV [result] #{e.message}")
272
276
  end
273
277
  end
274
278
  end
279
+
280
+ def setup_logging
281
+ log_path = false
282
+ if @options[:daemonize]
283
+ log_path = (@options[:log_dir] || @options[:root] || Dir.pwd)
284
+ end
285
+ Nanite::Log.init(@identity, log_path)
286
+ Nanite::Log.level = @options[:log_level] if @options[:log_level]
287
+ end
288
+
289
+ def setup_cluster
290
+ @cluster = Cluster.new(@amq, @options[:agent_timeout], @options[:identity], @serializer, self, @options[:redis], @options[:callbacks])
291
+ end
275
292
  end
276
293
  end
277
294
 
@@ -37,6 +37,7 @@ module Nanite
37
37
  request.persistent = opts.key?(:persistent) ? opts[:persistent] : options[:persistent]
38
38
  pending_requests[request.token] =
39
39
  { :intermediate_handler => opts[:intermediate_handler], :result_handler => blk }
40
+ Nanite::Log.info("SEND #{request.to_s([:tags, :target])}")
40
41
  amqp.fanout('request', :no_declare => options[:secure]).publish(serializer.dump(request))
41
42
  end
42
43
 
@@ -2,58 +2,111 @@ module Nanite
2
2
  # Base class for all Nanite packets,
3
3
  # knows how to dump itself to JSON
4
4
  class Packet
5
+
6
+ attr_accessor :size
7
+
5
8
  def initialize
6
9
  raise NotImplementedError.new("#{self.class.name} is an abstract class.")
7
10
  end
11
+
8
12
  def to_json(*a)
9
- {
13
+ js = {
10
14
  'json_class' => self.class.name,
11
15
  'data' => instance_variables.inject({}) {|m,ivar| m[ivar.sub(/@/,'')] = instance_variable_get(ivar); m }
12
16
  }.to_json(*a)
17
+ js = js.chop + ",\"size\":#{js.size}}"
18
+ js
19
+ end
20
+
21
+ # Log representation
22
+ def to_s(filter=nil)
23
+ res = "[#{ self.class.to_s.split('::').last.
24
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
25
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
26
+ downcase }]"
27
+ res += " (#{size.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,")} bytes)" if size && !size.to_s.empty?
28
+ res
29
+ end
30
+
31
+ # Log friendly name for given agent id
32
+ def id_to_s(id)
33
+ case id
34
+ when /^mapper-/ then 'mapper'
35
+ when /^nanite-(.*)/ then Regexp.last_match(1)
36
+ else id
37
+ end
38
+ end
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
13
43
  end
44
+
14
45
  end
15
46
 
16
47
  # packet that means start of a file transfer
17
48
  # operation
18
49
  class FileStart < Packet
50
+
19
51
  attr_accessor :filename, :token, :dest
20
- def initialize(filename, dest, token)
52
+
53
+ def initialize(filename, dest, token, size=nil)
21
54
  @filename = filename
22
55
  @dest = dest
23
56
  @token = token
57
+ @size = size
24
58
  end
25
59
 
26
60
  def self.json_create(o)
27
61
  i = o['data']
28
- new(i['filename'], i['dest'], i['token'])
62
+ new(i['filename'], i['dest'], i['token'], o['size'])
63
+ end
64
+
65
+ def to_s
66
+ wrap("#{super} <#{token}> #{filename} to #{dest}")
29
67
  end
30
68
  end
31
69
 
32
70
  # packet that means end of a file transfer
33
71
  # operation
34
72
  class FileEnd < Packet
73
+
35
74
  attr_accessor :token, :meta
36
- def initialize(token, meta)
75
+
76
+ def initialize(token, meta, size=nil)
37
77
  @token = token
38
78
  @meta = meta
79
+ @size = size
39
80
  end
40
81
 
41
82
  def self.json_create(o)
42
83
  i = o['data']
43
- new(i['token'], i['meta'])
84
+ new(i['token'], i['meta'], o['size'])
85
+ end
86
+
87
+ def to_s
88
+ wrap("#{super} <#{token}> meta #{meta}")
44
89
  end
45
90
  end
46
91
 
47
92
  # packet that carries data chunks during a file transfer
48
93
  class FileChunk < Packet
94
+
49
95
  attr_accessor :chunk, :token
50
- def initialize(token, chunk=nil)
96
+
97
+ def initialize(token, size=nil, chunk=nil)
51
98
  @chunk = chunk
52
99
  @token = token
100
+ @size = size
53
101
  end
102
+
54
103
  def self.json_create(o)
55
104
  i = o['data']
56
- new(i['token'], i['chunk'])
105
+ new(i['token'], o['size'], i['chunk'])
106
+ end
107
+
108
+ def to_s
109
+ "#{super} <#{token}>"
57
110
  end
58
111
  end
59
112
 
@@ -71,25 +124,43 @@ module Nanite
71
124
  # target is the target nanite for the request
72
125
  # persistent signifies if this request should be saved to persistent storage by the AMQP broker
73
126
  class Request < Packet
127
+
74
128
  attr_accessor :from, :payload, :type, :token, :reply_to, :selector, :target, :persistent, :tags
129
+
75
130
  DEFAULT_OPTIONS = {:selector => :least_loaded}
76
- def initialize(type, payload, opts={})
131
+
132
+ def initialize(type, payload, size=nil, opts={})
77
133
  opts = DEFAULT_OPTIONS.merge(opts)
78
- @type = type
79
- @payload = payload
80
- @from = opts[:from]
81
- @token = opts[:token]
82
- @reply_to = opts[:reply_to]
83
- @selector = opts[:selector]
84
- @target = opts[:target]
85
- @persistent = opts[:persistent]
86
- @tags = opts[:tags] || []
134
+ @type = type
135
+ @payload = payload
136
+ @size = size
137
+ @from = opts[:from]
138
+ @token = opts[:token]
139
+ @reply_to = opts[:reply_to]
140
+ @selector = opts[:selector]
141
+ @target = opts[:target]
142
+ @persistent = opts[:persistent]
143
+ @tags = opts[:tags] || []
87
144
  end
145
+
88
146
  def self.json_create(o)
89
147
  i = o['data']
90
- new(i['type'], i['payload'], {:from => i['from'], :token => i['token'], :reply_to => i['reply_to'], :selector => i['selector'],
91
- :target => i['target'], :persistent => i['persistent'], :tags => i['tags']})
148
+ new(i['type'], i['payload'], o['size'], { :from => i['from'], :token => i['token'],
149
+ :reply_to => i['reply_to'], :selector => i['selector'],
150
+ :target => i['target'], :persistent => i['persistent'],
151
+ :tags => i['tags'] })
152
+ end
153
+
154
+ def to_s(filter=nil)
155
+ log_msg = "#{super} <#{token}> #{type}"
156
+ log_msg += " from #{id_to_s(from)}" if filter.nil? || filter.include?(:from)
157
+ log_msg += " to #{id_to_s(target)}" if target && (filter.nil? || filter.include?(:target))
158
+ log_msg += ", reply_to #{id_to_s(reply_to)}" if reply_to && (filter.nil? || filter.include?(:reply_to))
159
+ log_msg += ", tags #{tags.inspect}" if tags && !tags.empty? && (filter.nil? || filter.include?(:tags))
160
+ log_msg += ", payload #{payload.inspect}" if filter.nil? || filter.include?(:payload)
161
+ wrap(log_msg)
92
162
  end
163
+
93
164
  end
94
165
 
95
166
  # packet that means a work push from mapper
@@ -105,23 +176,38 @@ module Nanite
105
176
  # target is the target nanite for the request
106
177
  # persistent signifies if this request should be saved to persistent storage by the AMQP broker
107
178
  class Push < Packet
179
+
108
180
  attr_accessor :from, :payload, :type, :token, :selector, :target, :persistent, :tags
181
+
109
182
  DEFAULT_OPTIONS = {:selector => :least_loaded}
110
- def initialize(type, payload, opts={})
183
+
184
+ def initialize(type, payload, size=nil, opts={})
111
185
  opts = DEFAULT_OPTIONS.merge(opts)
112
- @type = type
113
- @payload = payload
114
- @from = opts[:from]
115
- @token = opts[:token]
116
- @selector = opts[:selector]
117
- @target = opts[:target]
118
- @persistent = opts[:persistent]
119
- @tags = opts[:tags] || []
186
+ @type = type
187
+ @payload = payload
188
+ @size = size
189
+ @from = opts[:from]
190
+ @token = opts[:token]
191
+ @selector = opts[:selector]
192
+ @target = opts[:target]
193
+ @persistent = opts[:persistent]
194
+ @tags = opts[:tags] || []
120
195
  end
196
+
121
197
  def self.json_create(o)
122
198
  i = o['data']
123
- new(i['type'], i['payload'], {:from => i['from'], :token => i['token'], :selector => i['selector'],
124
- :target => i['target'], :persistent => i['persistent'], :tags => i['tags']})
199
+ new(i['type'], i['payload'], o['size'], { :from => i['from'], :token => i['token'],
200
+ :selector => i['selector'], :target => i['target'],
201
+ :persistent => i['persistent'], :tags => i['tags'] })
202
+ end
203
+
204
+ def to_s(filter=nil)
205
+ log_msg = "#{super} <#{token}> #{type}"
206
+ log_msg += " from #{id_to_s(from)}" if filter.nil? || filter.include?(:from)
207
+ log_msg += ", target #{id_to_s(target)}" if target && (filter.nil? || filter.include?(:target))
208
+ log_msg += ", tags #{tags.inspect}" if tags && !tags.empty? && (filter.nil? || filter.include?(:tags))
209
+ log_msg += ", payload #{payload.inspect}" if filter.nil? || filter.include?(:payload)
210
+ wrap(log_msg)
125
211
  end
126
212
  end
127
213
 
@@ -132,16 +218,28 @@ module Nanite
132
218
  # token is a generated request id that mapper uses to identify replies
133
219
  # to is identity of the node result should be delivered to
134
220
  class Result < Packet
221
+
135
222
  attr_accessor :token, :results, :to, :from
136
- def initialize(token, to, results, from)
223
+
224
+ def initialize(token, to, results, from, size=nil)
137
225
  @token = token
138
226
  @to = to
139
227
  @from = from
140
228
  @results = results
229
+ @size = size
141
230
  end
231
+
142
232
  def self.json_create(o)
143
233
  i = o['data']
144
- new(i['token'], i['to'], i['results'], i['from'])
234
+ new(i['token'], i['to'], i['results'], i['from'], o['size'])
235
+ end
236
+
237
+ def to_s(filter=nil)
238
+ log_msg = "#{super} <#{token}>"
239
+ log_msg += " from #{id_to_s(from)}" if filter.nil? || filter.include?(:from)
240
+ log_msg += " to #{id_to_s(to)}" if filter.nil? || filter.include?(:to)
241
+ log_msg += " results: #{results.inspect}" if filter.nil? || filter.include?(:results)
242
+ wrap(log_msg)
145
243
  end
146
244
  end
147
245
 
@@ -153,17 +251,25 @@ module Nanite
153
251
  # token is a generated request id that mapper uses to identify replies
154
252
  # to is identity of the node result should be delivered to
155
253
  class IntermediateMessage < Packet
254
+
156
255
  attr_accessor :token, :messagekey, :message, :to, :from
157
- def initialize(token, to, from, messagekey, message)
158
- @token = token
159
- @to = to
160
- @from = from
256
+
257
+ def initialize(token, to, from, messagekey, message, size=nil)
258
+ @token = token
259
+ @to = to
260
+ @from = from
161
261
  @messagekey = messagekey
162
- @message = message
262
+ @message = message
263
+ @size = size
163
264
  end
265
+
164
266
  def self.json_create(o)
165
267
  i = o['data']
166
- new(i['token'], i['to'], i['from'], i['messagekey'], i['message'])
268
+ new(i['token'], i['to'], i['from'], i['messagekey'], i['message'], o['size'])
269
+ end
270
+
271
+ def to_s
272
+ wrap("#{super} <#{token}> from #{id_to_s(from)}, key #{messagekey}")
167
273
  end
168
274
  end
169
275
 
@@ -174,16 +280,27 @@ module Nanite
174
280
  # status is a load of the node by default, but may be any criteria
175
281
  # agent may use to report it's availability, load, etc
176
282
  class Register < Packet
283
+
177
284
  attr_accessor :identity, :services, :status, :tags
178
- def initialize(identity, services, status, tags)
179
- @status = status
180
- @tags = tags
285
+
286
+ def initialize(identity, services, status, tags, size=nil)
287
+ @status = status
288
+ @tags = tags
181
289
  @identity = identity
182
290
  @services = services
291
+ @size = size
183
292
  end
293
+
184
294
  def self.json_create(o)
185
295
  i = o['data']
186
- new(i['identity'], i['services'], i['status'], i['tags'])
296
+ new(i['identity'], i['services'], i['status'], i['tags'], o['size'])
297
+ end
298
+
299
+ def to_s
300
+ log_msg = "#{super} #{id_to_s(identity)}"
301
+ log_msg += ", services: #{services.join(', ')}" if services && !services.empty?
302
+ log_msg += ", tags: #{tags.join(', ')}" if tags && !tags.empty?
303
+ wrap(log_msg)
187
304
  end
188
305
  end
189
306
 
@@ -191,13 +308,21 @@ module Nanite
191
308
  #
192
309
  # from is sender identity
193
310
  class UnRegister < Packet
311
+
194
312
  attr_accessor :identity
195
- def initialize(identity)
313
+
314
+ def initialize(identity, size=nil)
196
315
  @identity = identity
316
+ @size = size
197
317
  end
318
+
198
319
  def self.json_create(o)
199
320
  i = o['data']
200
- new(i['identity'])
321
+ new(i['identity'], o['size'])
322
+ end
323
+
324
+ def to_s
325
+ "#{super} #{id_to_s(identity)}"
201
326
  end
202
327
  end
203
328
 
@@ -206,26 +331,40 @@ module Nanite
206
331
  # identity is sender's identity
207
332
  # status is sender's status (see Register packet documentation)
208
333
  class Ping < Packet
334
+
209
335
  attr_accessor :identity, :status
210
- def initialize(identity, status)
211
- @status = status
336
+
337
+ def initialize(identity, status, size=nil)
338
+ @status = status
212
339
  @identity = identity
340
+ @size = size
213
341
  end
342
+
214
343
  def self.json_create(o)
215
344
  i = o['data']
216
- new(i['identity'], i['status'])
345
+ new(i['identity'], i['status'], o['size'])
346
+ end
347
+
348
+ def to_s
349
+ "#{super} #{id_to_s(identity)} status #{status}"
217
350
  end
351
+
218
352
  end
219
353
 
220
354
  # packet that is sent by workers to the mapper
221
355
  # when worker initially comes online to advertise
222
356
  # it's services
223
357
  class Advertise < Packet
224
- def initialize
358
+
359
+ def initialize(size=nil)
360
+ @size = size
225
361
  end
362
+
226
363
  def self.json_create(o)
227
- new
364
+ new(o['size'])
228
365
  end
366
+
229
367
  end
368
+
230
369
  end
231
370
 
data/lib/nanite/state.rb CHANGED
@@ -30,7 +30,7 @@ module Nanite
30
30
  # of these two service tags
31
31
 
32
32
  def initialize(redis)
33
- Nanite::Log.info("initializing redis state: #{redis}")
33
+ Nanite::Log.info("[setup] initializing redis state: #{redis}")
34
34
  host, port = redis.split(':')
35
35
  host ||= '127.0.0.1'
36
36
  port ||= '6379'
@@ -49,14 +49,14 @@ describe Nanite::ActorRegistry do
49
49
 
50
50
  it "should log info message that actor was registered" do
51
51
  importer = WebDocumentImporter.new
52
- Nanite::Log.should_receive(:info).with("Registering #{importer.inspect} with prefix nil")
52
+ Nanite::Log.should_receive(:info).with("[actor] #{importer.class.to_s}")
53
53
  @registry.register(importer, nil)
54
54
  end
55
55
 
56
56
  it "should handle actors registered with a custom prefix" do
57
57
  importer = WebDocumentImporter.new
58
58
  @registry.register(importer, 'monkey')
59
- @registry.actors['monkey'].should == importer
59
+ @registry.actor_for('monkey').should == importer
60
60
  end
61
61
 
62
62
  end # Nanite::ActorRegistry
data/spec/cluster_spec.rb CHANGED
@@ -2,6 +2,8 @@ require File.join(File.dirname(__FILE__), 'spec_helper')
2
2
 
3
3
  describe Nanite::Cluster do
4
4
 
5
+ include SpecHelpers
6
+
5
7
  describe "Intialization" do
6
8
 
7
9
  before(:each) do
@@ -91,6 +93,26 @@ describe Nanite::Cluster do
91
93
 
92
94
  end # Reaper
93
95
 
96
+ describe "State" do
97
+ begin
98
+ require 'nanite/state'
99
+ rescue LoadError
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
107
+
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
114
+ end
115
+ end
94
116
  end # Intialization
95
117
 
96
118
 
@@ -189,9 +211,94 @@ describe Nanite::Cluster do
189
211
  @cluster.register(@register_packet)
190
212
  end
191
213
 
214
+ describe "with registered callbacks" do
215
+ before(:each) do
216
+ @register_callback = lambda {|request, mapper|}
217
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper, nil, :register => @register_callback)
218
+ end
219
+
220
+ it "should call the registration callback" do
221
+ @register_callback.should_receive(:call).with("nanite_id", @mapper)
222
+ @cluster.register(@register_packet)
223
+ end
224
+ end
225
+
226
+ describe "when sending an invalid packet to the registration queue" do
227
+ it "should log a message statement" do
228
+ Nanite::Log.logger.should_receive(:warn).with("RECV [register] Invalid packet type: Nanite::Ping")
229
+ @cluster.register(Nanite::Ping.new(nil, nil))
230
+ end
231
+ end
192
232
  end # Nanite Registration
193
233
 
194
-
234
+ describe "Unregister" do
235
+ before(:each) do
236
+ @fanout = mock("fanout")
237
+ @binding = mock("binding", :subscribe => true)
238
+ @queue = mock("queue", :bind => @binding)
239
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
240
+ @serializer = mock("Serializer")
241
+ @reaper = mock("Reaper", :timeout => true)
242
+ Nanite::Log.stub!(:info)
243
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
244
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
245
+ @cluster.nanites["nanite_id"] = "nanite_id"
246
+ @unregister_packet = Nanite::UnRegister.new("nanite_id")
247
+ end
248
+
249
+ it "should delete the nanite" do
250
+ @cluster.register(@unregister_packet)
251
+ @cluster.nanites["nanite_id"].should == nil
252
+ end
253
+
254
+ describe "with registered callbacks" do
255
+ before(:each) do
256
+ @unregister_callback = lambda {|request, mapper| }
257
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper, nil, :unregister => @unregister_callback)
258
+ @cluster.nanites["nanite_id"] = "nanite_id"
259
+ end
260
+
261
+ it "should call the unregister callback" do
262
+ @unregister_callback.should_receive(:call).with("nanite_id", @mapper)
263
+ @cluster.register(@unregister_packet)
264
+ end
265
+ end
266
+ end
267
+
268
+ describe "Nanite timed out" do
269
+ before(:each) do
270
+ @fanout = mock("fanout")
271
+ @binding = mock("binding", :subscribe => true)
272
+ @queue = mock("queue", :bind => @binding)
273
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
274
+ @serializer = mock("Serializer")
275
+ Nanite::Log.stub!(:info)
276
+ @register_packet = Nanite::Register.new("nanite_id", ["the_nanite_services"], "nanite_status",[])
277
+ end
278
+
279
+ it "should remove the nanite when timed out" do
280
+ EM.run do
281
+ @cluster = Nanite::Cluster.new(@amq, 0.01, "the_identity", @serializer, @mapper)
282
+ @cluster.register(@register_packet)
283
+ EM.add_timer(1.1) {
284
+ @cluster.nanites["nanite_id"].should == nil
285
+ EM.stop_event_loop
286
+ }
287
+ end
288
+ end
289
+
290
+ it "should call the timed out callback handler when registered" do
291
+ EM.run do
292
+ @cluster = Nanite::Cluster.new(@amq, 0.01, "the_identity", @serializer, @mapper)
293
+ @cluster.register(@register_packet)
294
+ EM.add_timer(1.1) {
295
+ @cluster.nanites["nanite_id"].should == nil
296
+ EM.stop_event_loop
297
+ }
298
+ end
299
+ end
300
+ end
301
+
195
302
  describe "Route" do
196
303
 
197
304
  before(:each) do
@@ -231,7 +338,7 @@ describe Nanite::Cluster do
231
338
  @reaper = mock("Reaper")
232
339
  Nanite::Reaper.stub!(:new).and_return(@reaper)
233
340
  @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
234
- @request = mock("Request", :persistent => true)
341
+ @request = mock("Request", :persistent => true, :target => nil, :target= => nil, :to_s => nil)
235
342
  @target = mock("Target of Request")
236
343
  end
237
344
 
@@ -274,9 +381,10 @@ describe Nanite::Cluster do
274
381
  @reaper = mock("Reaper")
275
382
  Nanite::Reaper.stub!(:new).and_return(@reaper)
276
383
  @request_without_target = mock("Request", :target => nil, :token => "Token",
277
- :reply_to => "Reply To", :from => "From", :persistent => true, :identity => "Identity")
384
+ :reply_to => "Reply To", :from => "From", :persistent => true, :identity => "Identity",
385
+ :payload => "Payload", :to_s => nil)
278
386
  @request_with_target = mock("Request", :target => "Target", :token => "Token",
279
- :reply_to => "Reply To", :from => "From", :persistent => true)
387
+ :reply_to => "Reply To", :from => "From", :persistent => true, :payload => "Payload", :to_s => nil)
280
388
  @mapper_with_target = mock("Mapper", :identity => "id")
281
389
  @mapper_without_target = mock("Mapper", :request => false, :identity => @request_without_target.identity)
282
390
  @cluster_with_target = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper_with_target)
@@ -292,9 +400,78 @@ describe Nanite::Cluster do
292
400
  it "should reply back with nil results for requests with no target when offline queue is disabled" do
293
401
  @mapper_without_target.should_receive(:send_request).with(@request_without_target, anything())
294
402
  Nanite::Result.should_receive(:new).with(@request_without_target.token, @request_without_target.from, nil, @request_without_target.identity)
295
- @cluster_without_target.__send__(:handle_request, @request_without_target)
403
+ @cluster_without_target.__send__(:handle_request, @request_without_target)
296
404
  end
297
405
 
406
+ it "should hand in an intermediate handler" do
407
+ @mapper_with_target.should_receive(:send_request) do |request, opts|
408
+ opts[:intermediate_handler].should be_instance_of(Proc)
409
+ end
410
+
411
+ @cluster_with_target.__send__(:handle_request, @request_with_target)
412
+ end
413
+
414
+ it "should forward the message when send_request failed" do
415
+ @mapper_with_target.stub!(:send_request).and_return(false)
416
+ @cluster_with_target.should_receive(:forward_response)
417
+ @cluster_with_target.__send__(:handle_request, @request_with_target)
418
+ end
298
419
  end # Agent Request Handling
299
420
 
421
+ describe "Heartbeat" do
422
+ before(:each) do
423
+ @fanout = mock("fanout")
424
+ @binding = mock("binding", :subscribe => true)
425
+ @queue = mock("queue", :bind => @binding, :publish => true)
426
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
427
+ @serializer = mock("Serializer", :dump => "dumped_value")
428
+ Nanite::Log.stub!(:info)
429
+ @ping = stub("ping", :status => 0.3, :identity => "nanite_id")
430
+ end
431
+
432
+ it "should update the nanite status" do
433
+ run_in_em do
434
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
435
+ @cluster.nanites["nanite_id"] = {:status => "nanite_status"}
436
+ @cluster.send :handle_ping, @ping
437
+ @cluster.nanites["nanite_id"][:status].should == 0.3
438
+ end
439
+ end
440
+
441
+ it "should reset the agent time out" do
442
+ run_in_em do
443
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
444
+ @cluster.reaper.should_receive(:reset_with_autoregister_hack).with("nanite_id", 33)
445
+ @cluster.nanites["nanite_id"] = {:status => "nanite_status"}
446
+ @cluster.send :handle_ping, @ping
447
+ end
448
+ end
449
+
450
+ describe "when timing out after a heartbeat" do
451
+ it "should remove the nanite" do
452
+ run_in_em(false) do
453
+ @cluster = Nanite::Cluster.new(@amq, 0.1, "the_identity", @serializer, @mapper)
454
+ @cluster.nanites["nanite_id"] = {:status => "nanite_status"}
455
+ @cluster.send :handle_ping, @ping
456
+ EM.add_timer(1.5) do
457
+ @cluster.nanites["nanite_id"].should == nil
458
+ EM.stop_event_loop
459
+ end
460
+ end
461
+ end
462
+
463
+ it "should call the timeout callback when defined" do
464
+ run_in_em(false) do
465
+ @timeout_callback = lambda {|nanite, mapper| }
466
+ @timeout_callback.should_receive(:call).with("nanite_id", @mapper)
467
+ @cluster = Nanite::Cluster.new(@amq, 0.1, "the_identity", @serializer, @mapper, nil, :timeout => @timeout_callback)
468
+ @cluster.nanites["nanite_id"] = {:status => "nanite_status"}
469
+ @cluster.send :handle_ping, @ping
470
+ EM.add_timer(1.5) do
471
+ EM.stop_event_loop
472
+ end
473
+ end
474
+ end
475
+ end
476
+ end
300
477
  end # Nanite::Cluster
data/spec/job_spec.rb CHANGED
@@ -33,7 +33,44 @@ describe Nanite::JobWarden do
33
33
 
34
34
  end # Creating a new Job
35
35
 
36
-
36
+ describe "Processing an intermediate message" do
37
+ before(:each) do
38
+ @intm_handler = lambda {|arg1, arg2, arg3| puts 'ehlo'}
39
+ @message = mock("Message", :token => "3faba24fcc", :from => 'nanite-agent')
40
+ @serializer = mock("Serializer", :load => @message)
41
+ @warden = Nanite::JobWarden.new(@serializer)
42
+ @job = Nanite::Job.new(stub("request", :token => "3faba24fcc"), [], @intm_handler)
43
+ @job.instance_variable_set(:@pending_keys, ["defaultkey"])
44
+ @job.instance_variable_set(:@intermediate_state, {"nanite-agent" => {"defaultkey" => [1]}})
45
+ @warden.jobs[@job.token] = @job
46
+ Nanite::Log.stub!(:debug)
47
+ Nanite::Log.stub!(:error)
48
+ end
49
+
50
+ it "should call the intermediate handler with three parameters" do
51
+ @intm_handler.should_receive(:call).with("defaultkey", "nanite-agent", 1)
52
+ @warden.process(@message)
53
+ end
54
+
55
+ it "should call the intermediate handler with four parameters" do
56
+ @intm_handler.stub!(:arity).and_return(4)
57
+ @intm_handler.should_receive(:call).with("defaultkey", "nanite-agent", 1, @job)
58
+ @warden.process(@message)
59
+ end
60
+
61
+ it "should not call the intermediate handler when it can't be found for the specified key" do
62
+ @intm_handler.should_not_receive(:call)
63
+ @job.instance_variable_set(:@intermediate_handler, nil)
64
+ @warden.process(@message)
65
+ end
66
+
67
+ it "should call the intermediate handler with one parameter which needs to be the result" do
68
+ @intm_handler.should_receive(:call).with(1, @job)
69
+ @intm_handler.stub!(:arity).and_return(2)
70
+ @warden.process(@message)
71
+ end
72
+ end
73
+
37
74
  describe "Processing a Message" do
38
75
 
39
76
  before(:each) do
@@ -44,11 +81,6 @@ describe Nanite::JobWarden do
44
81
  Nanite::Log.stub!(:debug)
45
82
  end
46
83
 
47
- it "should log debug message about message to be processed" do
48
- Nanite::Log.should_receive(:debug)
49
- @warden.process(@message)
50
- end
51
-
52
84
  it "should hand over processing to job" do
53
85
  Nanite::Job.stub!(:new).and_return(@job)
54
86
  @job.should_receive(:process).with(@message)
data/spec/spec_helper.rb CHANGED
@@ -6,7 +6,10 @@ require 'spec'
6
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
+
10
13
  # Create test certificate
11
14
  def issue_cert
12
15
  test_dn = { 'C' => 'US',
@@ -20,4 +23,11 @@ module SpecHelpers
20
23
  [ Nanite::Certificate.new(key, dn, dn), key ]
21
24
  end
22
25
 
26
+ def run_in_em(stop_event_loop = true)
27
+ EM.run do
28
+ yield
29
+ EM.stop_event_loop if stop_event_loop
30
+ end
31
+ end
32
+
23
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ezmobius-nanite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1.1
4
+ version: 0.4.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ezra Zygmuntowicz
@@ -102,6 +102,7 @@ files:
102
102
  - spec/distinguished_name_spec.rb
103
103
  has_rdoc: true
104
104
  homepage: http://github.com/ezmobius/nanite
105
+ licenses:
105
106
  post_install_message:
106
107
  rdoc_options: []
107
108
 
@@ -122,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
123
  requirements: []
123
124
 
124
125
  rubyforge_project:
125
- rubygems_version: 1.2.0
126
+ rubygems_version: 1.3.5
126
127
  signing_key:
127
128
  specification_version: 2
128
129
  summary: self assembling fabric of ruby daemons