rightscale-nanite 0.4.1.10 → 0.4.1.22

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