rightscale-nanite 0.4.1.10 → 0.4.1.22

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
@@ -11,10 +11,10 @@ require 'rake/clean'
11
11
  require 'lib/nanite'
12
12
 
13
13
  GEM = "rightscale-nanite"
14
- AUTHOR = "Ezra Zygmuntowicz"
15
- EMAIL = "ezra@engineyard.com"
16
- HOMEPAGE = "http://github.com/ezmobius/nanite"
17
- SUMMARY = "self assembling fabric of ruby daemons"
14
+ AUTHOR = "RightScale, Inc."
15
+ EMAIL = "support@rightscale.com"
16
+ HOMEPAGE = "http://github.com/rightscale/nanite"
17
+ SUMMARY = "Self-assembling fabric of ruby daemons; fork of ezmobius/nanite."
18
18
 
19
19
  Dir.glob('tasks/*.rake').each { |r| Rake.application.add_import r }
20
20
 
@@ -73,6 +73,4 @@ task :docs => :rdoc do
73
73
  else
74
74
  sh 'firefox rdoc/index.html'
75
75
  end
76
- end
77
-
78
- require 'rightscale-nanite.rb'
76
+ end
@@ -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.1.10' unless defined?(Nanite::VERSION)
42
+ VERSION = '0.4.1.22' unless defined?(Nanite::VERSION)
43
43
 
44
44
  class MapperNotRunning < StandardError; end
45
45
 
@@ -57,15 +57,21 @@ module Nanite
57
57
  end # ClassMethods
58
58
 
59
59
  module InstanceMethods
60
- # send nanite request to another agent (through the mapper)
60
+ # Send nanite request to another agent (through the mapper)
61
61
  def request(*args, &blk)
62
62
  MapperProxy.instance.request(*args, &blk)
63
63
  end
64
64
 
65
+ # Send nanite push to another agent (through the mapper)
65
66
  def push(*args)
66
67
  MapperProxy.instance.push(*args)
67
68
  end
69
+
70
+ # Send nanite tag query to mapper
71
+ def query_tags(*args, &blk)
72
+ MapperProxy.instance.query_tags(*args, &blk)
73
+ end
68
74
  end # InstanceMethods
69
75
 
70
76
  end # Actor
71
- end # Nanite
77
+ end # Nanite
@@ -5,7 +5,7 @@ module Nanite
5
5
  include ConsoleHelper
6
6
  include DaemonizeHelper
7
7
 
8
- attr_reader :identity, :options, :serializer, :dispatcher, :registry, :amq, :tags
8
+ attr_reader :identity, :options, :serializer, :dispatcher, :registry, :amq, :tags, :callbacks
9
9
  attr_accessor :status_proc
10
10
 
11
11
  DEFAULT_OPTIONS = COMMON_DEFAULT_OPTIONS.merge({
@@ -74,6 +74,10 @@ module Nanite
74
74
  # this defaults to 5672, port used by some widely
75
75
  # used AMQP brokers (RabbitMQ and ZeroMQ)
76
76
  #
77
+ # callback : Hash of proc objects defining well known callbacks
78
+ # Currently only the :exception callback is supported
79
+ # This block gets called whenever a packet generates an exception
80
+ #
77
81
  # On start Nanite reads config.yml, so it is common to specify
78
82
  # options in the YAML file. However, when both Ruby code options
79
83
  # and YAML file specify option, Ruby code options take precedence.
@@ -95,7 +99,7 @@ module Nanite
95
99
  Log.init(@identity, @options[:log_path])
96
100
  Log.level = @options[:log_level] if @options[:log_level]
97
101
  @serializer = Serializer.new(@options[:format])
98
- @status_proc = lambda { parse_uptime(`uptime 2> /dev/null`) rescue 'no status' }
102
+ @status_proc ||= lambda { parse_uptime(`uptime 2> /dev/null`) rescue 'no status' }
99
103
  pid_file = PidFile.new(@identity, @options)
100
104
  pid_file.check
101
105
  if @options[:daemonize]
@@ -159,7 +163,12 @@ module Nanite
159
163
  if @options[:daemonize]
160
164
  @options[:log_path] = (@options[:log_dir] || @options[:root] || Dir.pwd)
161
165
  end
162
-
166
+
167
+ @callbacks = opts[:callbacks]
168
+ @status_proc = opts[:status_proc]
169
+
170
+ # note the return statement in case of identity being known; ensure all
171
+ # needed configurations occur before the the following line.
163
172
  return @identity = "nanite-#{@options[:identity]}" if @options[:identity]
164
173
  token = Identity.generate
165
174
  @identity = "nanite-#{token}"
@@ -224,6 +233,7 @@ module Nanite
224
233
  receive(serializer.load(msg))
225
234
  rescue Exception => e
226
235
  Nanite::Log.error("RECV #{e.message}")
236
+ callbacks[:exception].call(e, msg, self) rescue nil if callbacks && callbacks[:exception]
227
237
  end
228
238
  end
229
239
  end
@@ -18,9 +18,9 @@ module Nanite
18
18
  end
19
19
 
20
20
  # determine which nanites should receive the given request
21
- def targets_for(request)
21
+ def targets_for(request, include_timed_out)
22
22
  return [request.target] if request.target
23
- __send__(request.selector, request.from, request.type, request.tags).collect {|name, state| name }
23
+ __send__(request.selector, request, include_timed_out)
24
24
  end
25
25
 
26
26
  # adds nanite to nanites map: key is nanite's identity
@@ -33,7 +33,7 @@ module Nanite
33
33
  Nanite::Log.info("RECV #{reg.to_s}")
34
34
  nanites[reg.identity] = { :services => reg.services, :status => reg.status, :tags => reg.tags, :timestamp => Time.now.utc.to_i }
35
35
  reaper.register(reg.identity, agent_timeout + 1) { nanite_timed_out(reg.identity) }
36
- callbacks[:register].call(reg.identity, mapper) if callbacks[:register]
36
+ callbacks[:register].call(reg.identity, mapper) if callbacks && callbacks[:register]
37
37
  else
38
38
  Nanite::Log.warn("RECV NOT AUTHORIZED #{reg.to_s}")
39
39
  end
@@ -41,7 +41,7 @@ module Nanite
41
41
  Nanite::Log.info("RECV #{reg.to_s}")
42
42
  reaper.unregister(reg.identity)
43
43
  nanites.delete(reg.identity)
44
- callbacks[:unregister].call(reg.identity, mapper) if callbacks[:unregister]
44
+ callbacks[:unregister].call(reg.identity, mapper) if callbacks && callbacks[:unregister]
45
45
  when TagUpdate
46
46
  Nanite::Log.info("RECV #{reg.to_s}")
47
47
  nanites.update_tags(reg.identity, reg.new_tags, reg.obsolete_tags)
@@ -55,7 +55,7 @@ module Nanite
55
55
  if nanite && timed_out?(nanite)
56
56
  Nanite::Log.info("Nanite #{token} timed out")
57
57
  nanite = nanites.delete(token)
58
- callbacks[:timeout].call(token, mapper) if callbacks[:timeout]
58
+ callbacks[:timeout].call(token, mapper) if callbacks && callbacks[:timeout]
59
59
  true
60
60
  end
61
61
  end
@@ -71,10 +71,10 @@ module Nanite
71
71
  old_target = request.target
72
72
  request.target = target unless target == 'mapper-offline'
73
73
  if @security.authorize_request(request)
74
- Nanite::Log.info("SEND #{request.to_s([:from, :on_behalf, :tags, :target])}")
74
+ Nanite::Log.info("SEND #{request.to_s([:from, :scope, :tags, :target])}")
75
75
  amq.queue(target).publish(serializer.dump(request), :persistent => request.persistent)
76
76
  else
77
- Nanite::Log.warn("RECV NOT AUTHORIZED #{request.to_s}")
77
+ Nanite::Log.error("RECV NOT AUTHORIZED #{request.to_s}")
78
78
  end
79
79
  ensure
80
80
  request.target = old_target
@@ -100,12 +100,14 @@ module Nanite
100
100
 
101
101
  # forward request coming from agent
102
102
  def handle_request(request)
103
- Nanite::Log.info("RECV #{request.to_s([:from, :on_behalf, :target, :tags])}") unless Nanite::Log.level == :debug
103
+ Nanite::Log.info("RECV #{request.to_s([:from, :scope, :target, :tags])}") unless Nanite::Log.level == :debug
104
104
  Nanite::Log.debug("RECV #{request.to_s}")
105
105
  case request
106
106
  when Push
107
- mapper.send_push(request)
108
- else
107
+ if mapper.send_push(request) == false
108
+ Nanite::Log.info("RECV - No target found for #{request.to_s}")
109
+ end
110
+ when Request
109
111
  intm_handler = lambda do |result, job|
110
112
  result = IntermediateMessage.new(request.token, job.request.from, mapper.identity, nil, result)
111
113
  forward_response(result, request.persistent)
@@ -118,8 +120,23 @@ module Nanite
118
120
  end
119
121
 
120
122
  if ok == false
123
+ Nanite::Log.info("RECV - No target found for #{request.to_s}")
121
124
  forward_response(result, request.persistent)
122
125
  end
126
+ when TagQuery
127
+ results = {}
128
+ results = nanites.nanites_for(request) if request.tags && !request.tags.empty?
129
+ if request.agent_ids && !request.agent_ids.empty?
130
+ request.agent_ids.each do |nanite_id|
131
+ if !results.include?(nanite_id)
132
+ if nanite = nanites[nanite_id]
133
+ results[nanite_id] = nanite
134
+ end
135
+ end
136
+ end
137
+ end
138
+ result = Result.new(request.token, request.from, results, mapper.identity)
139
+ forward_response(result, request.persistent)
123
140
  end
124
141
  end
125
142
 
@@ -130,37 +147,38 @@ module Nanite
130
147
  end
131
148
 
132
149
  # returns least loaded nanite that provides given service
133
- def least_loaded(from, service, tags=[])
134
- candidates = nanites_providing(from, service, tags)
150
+ def least_loaded(request, include_timed_out)
151
+ candidates = nanites_providing(request, include_timed_out)
135
152
  return [] if candidates.empty?
136
-
137
- [candidates.min { |a,b| a[1][:status] <=> b[1][:status] }]
153
+ res = candidates.to_a.min { |a, b| a[1][:status] <=> b[1][:status] }
154
+ [res[0]]
138
155
  end
139
156
 
140
157
  # returns all nanites that provide given service
141
- def all(from, service, tags=[])
142
- nanites_providing(from, service,tags)
158
+ # potentially including timed out agents
159
+ def all(request, include_timed_out)
160
+ nanites_providing(request, include_timed_out).keys
143
161
  end
144
162
 
145
163
  # returns a random nanite
146
- def random(from, service, tags=[])
147
- candidates = nanites_providing(from, service,tags)
164
+ def random(request, include_timed_out)
165
+ candidates = nanites_providing(request, include_timed_out)
148
166
  return [] if candidates.empty?
149
-
150
- [candidates[rand(candidates.size)]]
167
+ [candidates.keys[rand(candidates.size)]]
151
168
  end
152
169
 
153
170
  # selects next nanite that provides given service
154
171
  # using round robin rotation
155
- def rr(from, service, tags=[])
172
+ def rr(request, include_timed_out)
156
173
  @last ||= {}
174
+ service = request.type
157
175
  @last[service] ||= 0
158
- candidates = nanites_providing(from, service,tags)
176
+ candidates = nanites_providing(request, include_timed_out)
159
177
  return [] if candidates.empty?
160
178
  @last[service] = 0 if @last[service] >= candidates.size
161
- candidate = candidates[@last[service]]
179
+ key = candidates.keys[@last[service]]
162
180
  @last[service] += 1
163
- [candidate]
181
+ [key]
164
182
  end
165
183
 
166
184
  def timed_out?(nanite)
@@ -168,13 +186,18 @@ module Nanite
168
186
  end
169
187
 
170
188
  # returns all nanites that provide the given service
171
- def nanites_providing(from, service, tags)
172
- nanites.nanites_for(from, service, tags).delete_if do |nanite|
173
- if res = timed_out?(nanite[1])
174
- Nanite::Log.debug("Ignoring timed out nanite #{nanite[0]} in target selection - last seen at #{nanite[1][:timestamp]}")
175
- end
176
- res
177
- end
189
+ def nanites_providing(request, include_timed_out)
190
+ # FIXME: Not filtering out results for now to alleviate issue with
191
+ # overloaded agents not pinging in a timely fashion
192
+ # Put that code back once agents behave properly
193
+ nanites.nanites_for(request)
194
+
195
+ # nanites.nanites_for(request).delete_if do |nanite, info|
196
+ # if res = !include_timed_out && timed_out?(info)
197
+ # Nanite::Log.debug("Ignoring timed out nanite #{nanite} in target selection - last seen at #{info[:timestamp]}")
198
+ # end
199
+ # res
200
+ # end
178
201
  end
179
202
 
180
203
  def setup_queues
@@ -191,7 +214,7 @@ module Nanite
191
214
  handle_ping(ping)
192
215
  rescue Exception => e
193
216
  Nanite::Log.error("RECV [ping] #{e.message}")
194
- callbacks[:exception].call(e, msg, mapper) rescue nil if callbacks[:exception]
217
+ callbacks[:exception].call(e, ping, mapper) rescue nil if callbacks && callbacks[:exception]
195
218
  end
196
219
  end
197
220
  hb_fanout = amq.fanout('heartbeat', :durable => true)
@@ -208,7 +231,7 @@ module Nanite
208
231
  register(serializer.load(msg))
209
232
  rescue Exception => e
210
233
  Nanite::Log.error("RECV [register] #{e.message}")
211
- callbacks[:exception].call(e, msg, mapper) rescue nil if callbacks[:exception]
234
+ callbacks[:exception].call(e, msg, mapper) rescue nil if callbacks && callbacks[:exception]
212
235
  end
213
236
  end
214
237
  reg_fanout = amq.fanout('registration', :durable => true)
@@ -225,7 +248,7 @@ module Nanite
225
248
  handle_request(serializer.load(msg))
226
249
  rescue Exception => e
227
250
  Nanite::Log.error("RECV [request] #{e.message}")
228
- callbacks[:exception].call(e, msg, mapper) rescue nil if callbacks[:exception]
251
+ callbacks[:exception].call(e, msg, mapper) rescue nil if callbacks && callbacks[:exception]
229
252
  end
230
253
  end
231
254
  req_fanout = amq.fanout('request', :durable => true)
@@ -6,28 +6,32 @@ module Nanite
6
6
  end
7
7
  end
8
8
 
9
- def all_services
10
- all(:services)
11
- end
12
-
13
- def all_tags
14
- all(:tags)
15
- end
9
+ def nanites_for(request)
10
+ tags = request.tags
11
+ service = request.type
12
+ if service
13
+ nanites = reject { |_, state| !state[:services].include?(service) }
14
+ else
15
+ nanites = self
16
+ end
16
17
 
17
- def nanites_for(from, service, tags)
18
- tags = tags.dup.flatten
19
- nanites = select { |name, state| state[:services].include?(service) }
20
- unless tags.empty?
21
- nanites.select { |a, b| !(b[:tags] & tags).empty? }
18
+ if tags.nil? || tags.empty?
19
+ service ? nanites : {}
22
20
  else
23
- nanites
24
- end.to_a
21
+ nanites.reject { |_, state| (state[:tags] & tags).empty? }
22
+ end
25
23
  end
26
24
 
27
25
  def update_status(name, status)
28
26
  self[name].update(:status => status, :timestamp => Time.now.utc.to_i)
29
27
  end
30
28
 
29
+ def update_tags(name, new_tags, obsolete_tags)
30
+ prev_tags = self[name] && self[name][:tags]
31
+ updated_tags = (new_tags || []) + (prev_tags || []) - (obsolete_tags || [])
32
+ self[name].update(:tags => updated_tags.uniq)
33
+ end
34
+
31
35
  private
32
36
 
33
37
  def all(key)
@@ -195,7 +195,7 @@ module Nanite
195
195
  def send_request(request, opts = {}, &blk)
196
196
  request.reply_to = identity
197
197
  intm_handler = opts.delete(:intermediate_handler)
198
- targets = cluster.targets_for(request)
198
+ targets = cluster.targets_for(request, include_timed_out=false)
199
199
  if !targets.empty?
200
200
  job = job_warden.new_job(request, targets, intm_handler, blk)
201
201
  cluster.route(request, job.targets)
@@ -235,7 +235,8 @@ module Nanite
235
235
  end
236
236
 
237
237
  def send_push(push, opts = {})
238
- targets = cluster.targets_for(push)
238
+ include_timed_out = push.selector == :all
239
+ targets = cluster.targets_for(push, include_timed_out)
239
240
  if !targets.empty?
240
241
  cluster.route(push, targets)
241
242
  true
@@ -270,7 +271,8 @@ module Nanite
270
271
  offline_queue = amq.queue('mapper-offline', :durable => true)
271
272
  offline_queue.subscribe(:ack => true) do |info, deliverable|
272
273
  deliverable = serializer.load(deliverable)
273
- targets = cluster.targets_for(deliverable)
274
+ include_timed_out = deliverable.is_a?(Push) && deliverable.selector == :all
275
+ targets = cluster.targets_for(deliverable, include_timed_out)
274
276
  unless targets.empty?
275
277
  info.ack
276
278
  if deliverable.kind_of?(Request)
@@ -297,7 +299,7 @@ module Nanite
297
299
  job_warden.process(msg)
298
300
  rescue Exception => e
299
301
  Nanite::Log.error("RECV [result] #{e.message}")
300
- callbacks[:exception].call(e, msg, mapper) rescue nil if callbacks[:exception]
302
+ callbacks[:exception].call(e, msg, mapper) rescue nil if callbacks && callbacks[:exception]
301
303
  end
302
304
  end
303
305
  end
@@ -41,6 +41,7 @@ module Nanite
41
41
  amqp.fanout('request', :no_declare => options[:secure]).publish(serializer.dump(request))
42
42
  end
43
43
 
44
+ # Send push to given agent through the mapper
44
45
  def push(type, payload = '', opts = {})
45
46
  raise "Mapper proxy not initialized" unless identity && options
46
47
  push = Push.new(type, payload, opts)
@@ -51,6 +52,17 @@ module Nanite
51
52
  amqp.fanout('request', :no_declare => options[:secure]).publish(serializer.dump(push))
52
53
  end
53
54
 
55
+ # Send tag query to mapper
56
+ def query_tags(opts, &blk)
57
+ raise "Mapper proxy not initialized" unless identity && options
58
+ tag_query = TagQuery.new(identity, opts)
59
+ tag_query.token = Identity.generate
60
+ tag_query.persistent = opts.key?(:persistent) ? opts[:persistent] : options[:persistent]
61
+ pending_requests[tag_query.token] = { :result_handler => blk }
62
+ Nanite::Log.info("SEND #{tag_query.to_s}")
63
+ amqp.fanout('request', :no_declare => options[:secure]).publish(serializer.dump(tag_query))
64
+ end
65
+
54
66
  # Update tags registered by mapper for agent
55
67
  def update_tags(new_tags, obsolete_tags)
56
68
  raise "Mapper proxy not initialized" unless identity && options
@@ -113,7 +113,7 @@ module Nanite
113
113
  #
114
114
  # Options:
115
115
  # from is sender identity
116
- # on_behalf is agent identity that should be used to authorize request
116
+ # scope define behavior that should be used to resolve tag based routing
117
117
  # token is a generated request id that mapper uses to identify replies
118
118
  # reply_to is identity of the node actor replies to, usually a mapper itself
119
119
  # selector is the selector used to route the request
@@ -121,7 +121,7 @@ module Nanite
121
121
  # persistent signifies if this request should be saved to persistent storage by the AMQP broker
122
122
  class Request < Packet
123
123
 
124
- attr_accessor :from, :on_behalf, :payload, :type, :token, :reply_to, :selector, :target, :persistent, :tags
124
+ attr_accessor :from, :scope, :payload, :type, :token, :reply_to, :selector, :target, :persistent, :tags
125
125
 
126
126
  DEFAULT_OPTIONS = {:selector => :least_loaded}
127
127
 
@@ -131,7 +131,7 @@ module Nanite
131
131
  @payload = payload
132
132
  @size = size
133
133
  @from = opts[:from]
134
- @on_behalf = opts[:on_behalf]
134
+ @scope = opts[:scope]
135
135
  @token = opts[:token]
136
136
  @reply_to = opts[:reply_to]
137
137
  @selector = opts[:selector]
@@ -142,9 +142,9 @@ module Nanite
142
142
 
143
143
  def self.json_create(o)
144
144
  i = o['data']
145
- new(i['type'], i['payload'], { :from => i['from'], :on_behalf => i['on_behalf'],
146
- :token => i['token'], :reply_to => i['reply_to'],
147
- :selector => i['selector'], :target => i['target'],
145
+ new(i['type'], i['payload'], { :from => i['from'], :scope => i['scope'],
146
+ :token => i['token'], :reply_to => i['reply_to'],
147
+ :selector => i['selector'], :target => i['target'],
148
148
  :persistent => i['persistent'], :tags => i['tags'] },
149
149
  o['size'])
150
150
  end
@@ -152,7 +152,7 @@ module Nanite
152
152
  def to_s(filter=nil)
153
153
  log_msg = "#{super} <#{token}> #{type}"
154
154
  log_msg += " from #{id_to_s(from)}" if filter.nil? || filter.include?(:from)
155
- log_msg += " on behalf of #{id_to_s(on_behalf)}" if on_behalf && (filter.nil? || filter.include?(:on_behalf))
155
+ log_msg += " with scope #{scope}" if scope && (filter.nil? || filter.include?(:scope))
156
156
  log_msg += " to #{id_to_s(target)}" if target && (filter.nil? || filter.include?(:target))
157
157
  log_msg += ", reply_to #{id_to_s(reply_to)}" if reply_to && (filter.nil? || filter.include?(:reply_to))
158
158
  log_msg += ", tags #{tags.inspect}" if tags && !tags.empty? && (filter.nil? || filter.include?(:tags))
@@ -170,14 +170,14 @@ module Nanite
170
170
  #
171
171
  # Options:
172
172
  # from is sender identity
173
- # on_behalf is agent identity that should be used to authorize request
173
+ # scope define behavior that should be used to resolve tag based routing
174
174
  # token is a generated request id that mapper uses to identify replies
175
175
  # selector is the selector used to route the request
176
176
  # target is the target nanite for the request
177
177
  # persistent signifies if this request should be saved to persistent storage by the AMQP broker
178
178
  class Push < Packet
179
179
 
180
- attr_accessor :from, :on_behalf, :payload, :type, :token, :selector, :target, :persistent, :tags
180
+ attr_accessor :from, :scope, :payload, :type, :token, :selector, :target, :persistent, :tags
181
181
 
182
182
  DEFAULT_OPTIONS = {:selector => :least_loaded}
183
183
 
@@ -187,7 +187,7 @@ module Nanite
187
187
  @payload = payload
188
188
  @size = size
189
189
  @from = opts[:from]
190
- @on_behalf = opts[:on_behalf]
190
+ @scope = opts[:scope]
191
191
  @token = opts[:token]
192
192
  @selector = opts[:selector]
193
193
  @target = opts[:target]
@@ -197,9 +197,9 @@ module Nanite
197
197
 
198
198
  def self.json_create(o)
199
199
  i = o['data']
200
- new(i['type'], i['payload'], { :from => i['from'], :on_behalf => i['on_behalf'],
201
- :token => i['token'], :selector => i['selector'],
202
- :target => i['target'], :persistent => i['persistent'],
200
+ new(i['type'], i['payload'], { :from => i['from'], :scope => i['scope'],
201
+ :token => i['token'], :selector => i['selector'],
202
+ :target => i['target'], :persistent => i['persistent'],
203
203
  :tags => i['tags'] },
204
204
  o['size'])
205
205
  end
@@ -207,7 +207,7 @@ module Nanite
207
207
  def to_s(filter=nil)
208
208
  log_msg = "#{super} <#{token}> #{type}"
209
209
  log_msg += " from #{id_to_s(from)}" if filter.nil? || filter.include?(:from)
210
- log_msg += " on behalf of #{id_to_s(on_behalf)}" if on_behalf && (filter.nil? || filter.include?(:on_behalf))
210
+ log_msg += " with scope #{scope}" if scope && (filter.nil? || filter.include?(:scope))
211
211
  log_msg += ", target #{id_to_s(target)}" if target && (filter.nil? || filter.include?(:target))
212
212
  log_msg += ", tags #{tags.inspect}" if tags && !tags.empty? && (filter.nil? || filter.include?(:tags))
213
213
  log_msg += ", payload #{payload.inspect}" if filter.nil? || filter.include?(:payload)
@@ -215,6 +215,42 @@ module Nanite
215
215
  end
216
216
  end
217
217
 
218
+ # Tag query: retrieve agent ids with associated tags that match given tags
219
+ #
220
+ # Options:
221
+ # from is sender identity
222
+ # opts Hash of options, two options are supported, at least one must be set:
223
+ # :tags is an array of tags defining a query that returned agents tags must match
224
+ # :agent_ids is an array of agents whose tags should be returned
225
+ class TagQuery < Packet
226
+
227
+ attr_accessor :from, :token, :agent_ids, :tags, :persistent
228
+
229
+ def initialize(from, opts, size=nil)
230
+ @from = from
231
+ @token = opts[:token]
232
+ @agent_ids = opts[:agent_ids]
233
+ @tags = opts[:tags]
234
+ @persistent = opts[:persistent]
235
+ @size = size
236
+ end
237
+
238
+ def self.json_create(o)
239
+ i = o['data']
240
+ new(i['from'], { :token => i['token'], :agent_ids => i['agent_ids'],
241
+ :tags => i['tags'], :persistent => i['persistent'] },
242
+ o['size'])
243
+ end
244
+
245
+ def to_s(filter=nil)
246
+ log_msg = "#{super} <#{token}>"
247
+ log_msg += " from #{id_to_s(from)}" if filter.nil? || filter.include?(:from)
248
+ log_msg += " agent_ids #{agent_ids.inspect}"
249
+ log_msg += " tags: #{tags.inspect}"
250
+ log_msg
251
+ end
252
+ end
253
+
218
254
  # packet that means a work result notification sent from actor to mapper
219
255
  #
220
256
  # from is sender identity
@@ -68,27 +68,13 @@ module Nanite
68
68
  @redis.set_members("tg-#{nanite}")
69
69
  end
70
70
 
71
- # Retrieve all agents services
72
- def all_services
73
- log_redis_error do
74
- @redis.set_members('naniteservices')
75
- end
76
- end
77
-
78
- # Retrieve all agents tags
79
- def all_tags
80
- log_redis_error do
81
- @redis.set_members('nanitetags')
82
- end
83
- end
84
-
85
71
  # Retrieve nanites implementing given service and exposing given tags
86
- def nanites_for(from, service, tags)
87
- keys = tags && tags.dup || []
88
- keys << service
89
- log_redis_error do
90
- @redis.set_intersect(keys.compact)
91
- end
72
+ def nanites_ids_for(request)
73
+ keys = request.tags ? request.tags.dup : []
74
+ keys << request.type if request.type
75
+ keys.compact!
76
+ return {} if keys.empty?
77
+ log_redis_error { @redis.set_intersect(keys) }
92
78
  end
93
79
 
94
80
  private
@@ -138,4 +124,4 @@ module Nanite
138
124
  end
139
125
 
140
126
  end
141
- end
127
+ end
@@ -27,7 +27,9 @@ module Nanite
27
27
 
28
28
  # Load certificate from file
29
29
  def self.load(file)
30
- from_data(File.new(file))
30
+ res = nil
31
+ File.open(file, 'r') { |f| res = from_data(f) }
32
+ res
31
33
  end
32
34
 
33
35
  # Initialize with raw certificate
@@ -28,13 +28,14 @@ module Nanite
28
28
  end
29
29
 
30
30
  # Serialize message and sign it using X.509 certificate
31
- def self.dump(obj)
31
+ def self.dump(obj, encrypt=nil)
32
32
  raise "Missing certificate identity" unless @identity
33
33
  raise "Missing certificate" unless @cert
34
34
  raise "Missing certificate key" unless @key
35
35
  raise "Missing certificate store" unless @store || !@encrypt
36
+ must_encrypt = encrypt.nil? ? @encrypt : encrypt
36
37
  json = obj.to_json
37
- if @encrypt
38
+ if must_encrypt
38
39
  certs = @store.get_recipients(obj)
39
40
  json = EncryptedDocument.new(json, certs).encrypted_data if certs
40
41
  end
@@ -44,24 +45,20 @@ module Nanite
44
45
 
45
46
  # Unserialize data using certificate store
46
47
  def self.load(json)
47
- begin
48
- raise "Missing certificate store" unless @store
49
- raise "Missing certificate" unless @cert || !@encrypt
50
- raise "Missing certificate key" unless @key || !@encrypt
51
- data = JSON.load(json)
52
- sig = Signature.from_data(data['signature'])
53
- certs = @store.get_signer(data['id'])
54
- raise "Could not find a cert for signer #{data['id']}" unless certs
55
- certs = [ certs ] unless certs.respond_to?(:each)
56
- jsn = data['data'] if certs.any? { |c| sig.match?(c) }
57
- if jsn && @encrypt && data['encrypted']
58
- jsn = EncryptedDocument.from_data(jsn).decrypted_data(@key, @cert)
59
- end
60
- JSON.load(jsn) if jsn
61
- rescue Exception => e
62
- Nanite::Log.error("Loading of secure packet failed: #{e.message}\n#{e.backtrace.join("\n")}")
63
- raise
48
+ raise "Missing certificate store" unless @store
49
+ raise "Missing certificate" unless @cert || !@encrypt
50
+ raise "Missing certificate key" unless @key || !@encrypt
51
+ data = JSON.load(json)
52
+ sig = Signature.from_data(data['signature'])
53
+ certs = @store.get_signer(data['id'])
54
+ raise "Could not find a cert for signer #{data['id']}" unless certs
55
+ certs = [ certs ] unless certs.respond_to?(:any?)
56
+ raise "Failed to check signature for signer #{data['id']}" unless certs.any? { |c| sig.match?(c) }
57
+ jsn = data['data']
58
+ if jsn && @encrypt && data['encrypted']
59
+ jsn = EncryptedDocument.from_data(jsn).decrypted_data(@key, @cert)
64
60
  end
61
+ JSON.load(jsn) if jsn
65
62
  end
66
63
 
67
64
  end
@@ -6,7 +6,7 @@ module Nanite
6
6
  def initialize(action, packet, serializers, msg = nil)
7
7
  @action, @packet = action, packet
8
8
  msg = ":\n#{msg}" if msg && !msg.empty?
9
- super("Could not #{action} #{packet.inspect} using #{serializers.inspect}#{msg}")
9
+ super("Could not #{action} packet using #{serializers.inspect}: #{msg}")
10
10
  end
11
11
  end # SerializationError
12
12
 
@@ -66,16 +66,6 @@ module Nanite
66
66
  end
67
67
  end
68
68
 
69
- # Return all services exposed by all agents
70
- def all_services
71
- @tag_store.all_services
72
- end
73
-
74
- # Return all tags exposed by all agents
75
- def all_tags
76
- @tag_store.all_tags
77
- end
78
-
79
69
  # Update status and timestamp for given agent
80
70
  def update_status(name, status)
81
71
  log_redis_error do
@@ -111,11 +101,11 @@ module Nanite
111
101
 
112
102
  # Return agents that implement given service and expose
113
103
  # all given tags
114
- def nanites_for(from, service, tags)
115
- res = []
116
- @tag_store.nanites_for(from, service, tags).each do |nanite_id|
104
+ def nanites_for(request)
105
+ res = {}
106
+ @tag_store.nanites_ids_for(request).each do |nanite_id|
117
107
  if nanite = self[nanite_id]
118
- res << [nanite_id, nanite]
108
+ res[nanite_id] = nanite
119
109
  end
120
110
  end
121
111
  res
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rightscale-nanite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1.10
4
+ version: 0.4.1.22
5
5
  platform: ruby
6
6
  authors:
7
- - Ezra Zygmuntowicz
7
+ - RightScale, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-09 00:00:00 -08:00
12
+ date: 2010-01-12 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,8 +22,8 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: 0.6.0
24
24
  version:
25
- description: self assembling fabric of ruby daemons
26
- email: ezra@engineyard.com
25
+ description: Self-assembling fabric of ruby daemons; fork of ezmobius/nanite.
26
+ email: support@rightscale.com
27
27
  executables:
28
28
  - nanite-agent
29
29
  - nanite-mapper
@@ -39,7 +39,6 @@ files:
39
39
  - README.rdoc
40
40
  - Rakefile
41
41
  - TODO
42
- - lib/nanite
43
42
  - lib/nanite/actor.rb
44
43
  - lib/nanite/actor_registry.rb
45
44
  - lib/nanite/admin.rb
@@ -52,7 +51,6 @@ files:
52
51
  - lib/nanite/identity.rb
53
52
  - lib/nanite/job.rb
54
53
  - lib/nanite/local_state.rb
55
- - lib/nanite/log
56
54
  - lib/nanite/log/formatter.rb
57
55
  - lib/nanite/log.rb
58
56
  - lib/nanite/mapper.rb
@@ -62,7 +60,6 @@ files:
62
60
  - lib/nanite/pid_file.rb
63
61
  - lib/nanite/reaper.rb
64
62
  - lib/nanite/redis_tag_store.rb
65
- - lib/nanite/security
66
63
  - lib/nanite/security/cached_certificate_store_proxy.rb
67
64
  - lib/nanite/security/certificate.rb
68
65
  - lib/nanite/security/certificate_cache.rb
@@ -82,7 +79,9 @@ files:
82
79
  - bin/nanite-agent
83
80
  - bin/nanite-mapper
84
81
  has_rdoc: true
85
- homepage: http://github.com/ezmobius/nanite
82
+ homepage: http://github.com/rightscale/nanite
83
+ licenses: []
84
+
86
85
  post_install_message:
87
86
  rdoc_options: []
88
87
 
@@ -103,9 +102,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
102
  requirements: []
104
103
 
105
104
  rubyforge_project:
106
- rubygems_version: 1.3.1
105
+ rubygems_version: 1.3.5
107
106
  signing_key:
108
- specification_version: 2
109
- summary: self assembling fabric of ruby daemons
107
+ specification_version: 3
108
+ summary: Self-assembling fabric of ruby daemons; fork of ezmobius/nanite.
110
109
  test_files: []
111
110