smith 0.5.13.1 → 0.6.1

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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/bin/agency +19 -7
  3. data/bin/pry-smith +11 -0
  4. data/bin/smithctl +19 -21
  5. data/lib/smith/acl_compiler.rb +101 -56
  6. data/lib/smith/acl_parser.rb +75 -0
  7. data/lib/smith/agent.rb +28 -43
  8. data/lib/smith/agent_cache.rb +43 -17
  9. data/lib/smith/agent_monitoring.rb +1 -1
  10. data/lib/smith/agent_process.rb +148 -53
  11. data/lib/smith/application/agency.rb +44 -54
  12. data/lib/smith/bootstrap.rb +31 -17
  13. data/lib/smith/cache.rb +4 -0
  14. data/lib/smith/command.rb +1 -3
  15. data/lib/smith/commands/agency/kill.rb +22 -5
  16. data/lib/smith/commands/agency/list.rb +9 -4
  17. data/lib/smith/commands/agency/logger.rb +25 -12
  18. data/lib/smith/commands/agency/object_count.rb +19 -8
  19. data/lib/smith/commands/agency/start.rb +7 -10
  20. data/lib/smith/commands/agency/stop.rb +30 -12
  21. data/lib/smith/commands/common.rb +1 -1
  22. data/lib/smith/commands/smithctl/acl.rb +6 -3
  23. data/lib/smith/commands/smithctl/dump.rb +79 -0
  24. data/lib/smith/commands/smithctl/firehose.rb +2 -1
  25. data/lib/smith/commands/smithctl/push.rb +27 -12
  26. data/lib/smith/commands/smithctl/status.rb +27 -0
  27. data/lib/smith/config.rb +140 -28
  28. data/lib/smith/daemon.rb +16 -3
  29. data/lib/smith/exceptions.rb +6 -3
  30. data/lib/smith/logger.rb +12 -24
  31. data/lib/smith/messaging/acl/agent_keepalive.proto +2 -2
  32. data/lib/smith/messaging/acl/agent_lifecycle.proto +15 -9
  33. data/lib/smith/messaging/acl/agent_stats.proto +6 -5
  34. data/lib/smith/messaging/acl/default.rb +2 -7
  35. data/lib/smith/messaging/acl_type_cache.rb +77 -0
  36. data/lib/smith/messaging/factory.rb +29 -0
  37. data/lib/smith/messaging/queue.rb +12 -10
  38. data/lib/smith/messaging/queue_definition.rb +21 -4
  39. data/lib/smith/messaging/receiver.rb +55 -62
  40. data/lib/smith/messaging/requeue.rb +1 -5
  41. data/lib/smith/messaging/sender.rb +48 -43
  42. data/lib/smith/messaging/util.rb +0 -10
  43. data/lib/smith/queue_definitions.rb +7 -4
  44. data/lib/smith/version.rb +1 -1
  45. data/lib/smith.rb +57 -56
  46. metadata +77 -128
  47. data/lib/smith/messaging/payload.rb +0 -100
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 95209344632520fac5a790a8efa4cfd91a52ddb2
4
+ data.tar.gz: 562bb8a1c8bd5371fdf3d74765f8c069b88f0fa0
5
+ SHA512:
6
+ metadata.gz: 2a8202bf141e34265c7254ba6dfef92d7798b8d6bd9ff7b0bc903060a27d11d86d38ba8f1b1be7aede80932f10eee31f1fd21cdb1d22655e1ffc212b039543c2
7
+ data.tar.gz: a5e5d205a49c1fd10ada42bf317fe17bde5cf12d375746133b909ea87892ecd7b35462f6b6c9cb8e8267872503799a8ee99ab6acaa921cac287221369d383718
data/bin/agency CHANGED
@@ -1,11 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- encoding: utf-8 -*-
3
3
 
4
- $:.unshift(File.dirname(__FILE__) + '/../lib')
4
+ require 'pathname'
5
+
6
+ $:.unshift(Pathname.new(__FILE__).dirname.parent.join('lib').expand_path)
5
7
 
6
- require 'pp'
7
8
  require 'smith'
8
9
  require 'smith/daemon'
10
+ require 'smith/agent_cache'
9
11
  require 'smith/acl_compiler'
10
12
  require 'smith/application/agency'
11
13
 
@@ -21,16 +23,19 @@ module Smith
21
23
  @options = opts
22
24
  options_check
23
25
 
24
- @daemon = Daemon.new(AGENCY_NAME, opts[:daemon], opts[:pid_dir])
25
- @agency = Agency.new(:paths => Smith.agent_paths)
26
+ @daemon = Daemon.new(add_vhost(AGENCY_NAME), opts[:daemon], opts[:pid_dir])
27
+ @agency = Agency.new
26
28
 
27
29
  Smith.shutdown_hook do
28
- puts "Shutting down"
30
+ logger.info { "Shutting down" }
29
31
  end
30
32
 
31
33
  # Setup signal handlers to clean up.
32
34
  %w{TERM INT QUIT}.each do |sig|
33
- trap sig, proc { @agency.stop }
35
+ trap sig, proc {
36
+ logger.info { "Shutting down" }
37
+ @agency.stop
38
+ }
34
39
  end
35
40
  end
36
41
 
@@ -38,10 +43,12 @@ module Smith
38
43
  if @daemon.running?
39
44
  logger.fatal { "The agency is alredy running" }
40
45
  else
46
+ logger.info { "Using config file: #{Smith.config_path}" }
47
+ require 'smith/messaging/acl_type_cache'
48
+
41
49
  @daemon.daemonise
42
50
 
43
51
  Smith.compile_acls
44
- Smith.load_acls
45
52
 
46
53
  Smith.start do
47
54
  # This block is here so the that the shutdown hook added in
@@ -66,6 +73,11 @@ module Smith
66
73
  puts "Logger set to stdout and daemonise is true. Log messages will be sent to /dev/null."
67
74
  end
68
75
  end
76
+
77
+ def add_vhost(name)
78
+ suffix = Pathname.new(Smith.config.amqp.broker.vhost).basename
79
+ "%s.%s" % [name, (suffix.root?) ? 'root' : suffix.to_s]
80
+ end
69
81
  end
70
82
  end
71
83
 
data/bin/pry-smith ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- encoding: utf-8 -*-
3
+
4
+ require 'pathname'
5
+ $:.unshift(Pathname.new(__FILE__).dirname.parent.join('lib').expand_path)
6
+
7
+ require 'pry'
8
+ require 'smith'
9
+
10
+ Smith.compile_acls
11
+ Pry::CLI.parse_options
data/bin/smithctl CHANGED
@@ -5,20 +5,22 @@
5
5
  trap sig, proc { exit }
6
6
  end
7
7
 
8
-
9
- $:.unshift(File.dirname(__FILE__) + '/../lib')
8
+ require 'pathname'
9
+ $:.unshift(Pathname.new(__FILE__).dirname.parent.join('lib').expand_path)
10
10
 
11
11
  require 'smith'
12
+ require 'smith/command'
13
+
14
+ require 'trollop'
12
15
 
13
16
  include Smith::Logger
14
17
 
15
18
  module Smith
16
19
  class SmithControl
17
20
 
18
-
19
21
  def initialize(options={})
20
- log_level((options[:log_level_given]) ? options[:log_level].to_sym : :info)
21
- @timeout = (options[:timeout_given]) ? options[:timeout] : Smith.config.agency.timeout
22
+ log_level((options[:log_level_given]) ? options[:log_level].to_sym : :warn)
23
+ @timeout = (options[:timeout_given]) ? options[:timeout] : Smith.config.smith.timeout
22
24
  end
23
25
 
24
26
  def send_command(command, args, &blk)
@@ -37,23 +39,19 @@ module Smith
37
39
 
38
40
  def agency_command(command, args, &blk)
39
41
  Messaging::Sender.new(QueueDefinitions::Agency_control) do |sender|
42
+ sender.on_timeout(5) { |message_id| blk.call("Timeout. Is the agency still running?") }
40
43
 
41
- timeout = Messaging::Timeout.new(5) { |message_id| blk.call("Timeout. Is the agency still running?") }
42
-
43
- sender.on_reply(:timeout => timeout) do |reply_payload, r|
44
- r.ack
44
+ sender.on_reply(:auto_ack => true) do |reply_payload, r|
45
45
  blk.call(reply_payload[:response])
46
46
  end
47
47
 
48
- # callback = proc do |sender|
49
- sender.publish(Smith::ACL::Factory.create(:agency_command, :command => command, :args => args))
50
- # end
51
-
52
- # errback = proc do
53
- # blk.call("Agency not running.")
54
- # end
55
-
56
- # sender.consumers?(callback, errback)
48
+ sender.consumer_count do |count|
49
+ if count > 0
50
+ sender.publish(Smith::ACL::AgencyCommand.new(:command => command, :args => args))
51
+ else
52
+ blk.call("Agency not running.")
53
+ end
54
+ end
57
55
  end
58
56
  end
59
57
  end
@@ -84,14 +82,14 @@ Usage:
84
82
  end
85
83
  end
86
84
 
87
- command = ARGV.shift
88
- args = ARGV
85
+ command = ARGV.shift.dup
86
+ args = ARGV.map {|a| a.dup}
89
87
 
90
88
  %w{TERM INT QUIT}.each do |sig|
91
89
  trap sig, proc { (Smith.running?) ? Smith.stop(true) : exit}
92
90
  end
93
91
 
94
- Smith.load_acls
92
+ Smith.compile_acls
95
93
 
96
94
  control = SmithControl.new(opts)
97
95
 
@@ -1,89 +1,134 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require 'pp'
3
- require 'protobuf/compiler/compiler'
4
-
5
- # There doesn't seem to be a way to turn of printing. The code is generally run
6
- # like this: log_writing unless silent but there is no way of setting silent!
7
- # So monkey patch it.
8
- class Protobuf::Visitor::Base
9
- def log_writing(filename, message="writing..."); end
10
- end
2
+ require 'ffi'
3
+ require "tempfile"
4
+ require 'ruby_parser'
5
+ require 'smith/messaging/acl_type_cache'
11
6
 
12
7
  module Smith
13
8
  class ACLCompiler
14
9
 
15
10
  include Logger
11
+ extend FFI::Library
16
12
 
17
- def initialize(force=false)
18
- # TODO Add the force code.
19
- @cache_path = Smith.acl_cache_path
13
+ def self.find_so
14
+ $:.map{|p| Pathname.new(p).join("ruby_generator.so")}.detect{|so| so.exist? }
15
+ end
16
+
17
+ ffi_lib(find_so)
18
+ attach_function(:_rprotoc_extern, [:int, :pointer], :int32)
19
+
20
+ def initialize
21
+ @acl_type_cache = AclTypeCache.instance
22
+ @acl_parser = ACLParser.new
20
23
  end
21
24
 
22
- # Compile any protocol buffer files. This checks the timestamp to see if
23
- # the file needs compiling. This is done in a subprocess to stop the agency
24
- # from dying. I think it's a problem with the class being loaded twice but
25
- # I don't actually know that so this could be considered a bit brute force.
26
25
  def compile
27
- Process.fork do
28
- logger.debug { "Protocol buffer cache path: #{@cache_path}" }
29
- Smith.acl_path.each do |path|
30
- results = {}
31
- path_glob(path) do |p|
32
- if should_compile?(p)
33
- begin
34
- logger.info { "Compiling: #{p}" }
35
- Protobuf::Compiler.compile(p.basename, p.dirname, @cache_path)
36
- rescue Racc::ParseError => e
37
- logger.error { "Cannot parse: #{p}" }
38
- end
39
- end
40
- end
26
+ $LOAD_PATH << Smith.acl_cache_path
27
+
28
+ Smith.acl_path.each do |path|
29
+ $LOAD_PATH << path
30
+
31
+ acls_files = path_glob(path)
32
+ out_of_date_acls = path_glob(path).select { |p| should_compile?(p) }
33
+ if out_of_date_acls.size > 0
34
+ compile_on_path(path, acls_files, out_of_date_acls)
35
+ end
36
+
37
+ acls_files.each do |acl_file|
38
+ acl_class_path = acl_compiled_path(acl_file)
39
+ load_acl(acl_class_path)
40
+ add_to_type_cache(acl_class_path)
41
41
  end
42
42
  end
43
- Process.wait
44
- @cache_path
45
43
  end
46
44
 
47
- def cache_path
48
- @cache_path.to_s
49
- end
45
+ private
50
46
 
51
- # Clears the Protocol Buffer cache. If acl_cache_path is
52
- # specified in the config then the directory itself won't be removed
53
- # but if it's not specified and a temporary directory was created then
54
- # the directory is removed as well.
55
- def clear_cache
56
- logger.info { "Clearing the Protocol Buffer cache: #{Smith.acl_cache_path}" }
47
+ def compile_on_path(path, acls, out_of_date_acls)
48
+ out_of_date_acls.each { |acl| logger.debug("Compiling acl: #{path.join(acl)}") }
57
49
 
58
- Pathname.glob(@cache_path.join("*")).each do |path|
59
- path.unlink
60
- end
50
+ unless acls.empty?
51
+ Dir.chdir(path) do
52
+ begin
53
+ GC.disable
54
+
55
+ args = ["rprotoc", "--ruby_out", Smith.acl_cache_path, "--proto_path", path].map {|a| ::FFI::MemoryPointer.from_string(a.to_s.dup) }
56
+
57
+ ffi_acls = acls.map do |acl|
58
+ FFI::MemoryPointer.from_string(acl.to_s.dup)
59
+ end
60
+ ffi_acls << nil
61
+
62
+ args += ffi_acls
63
+ argv = FFI::MemoryPointer.new(:pointer, args.size)
64
+
65
+ args.each_with_index { |p, index| argv[index].put_pointer(0, p) }
61
66
 
62
- unless Smith.config.agency._has_key?(:acl_cache_path)
63
- @cache_path.rmdir
67
+ errors = capture_stderr do
68
+ self._rprotoc_extern(args.compact.size, argv)
69
+ end.split("\n")
70
+
71
+ errors.each do |error|
72
+ logger.fatal { "Cannot compile ACLs: #{error}" }
73
+ raise RuntimeError, error
74
+ end
75
+ ensure
76
+ GC.enable
77
+ end
78
+ end
64
79
  end
65
80
  end
66
81
 
67
- private
68
-
69
82
  # Returns true if the .proto file is newer that the .pb.rb file
70
83
  def should_compile?(file)
71
- cached_file = @cache_path.join(file.basename).sub_ext(".pb.rb")
84
+ cached_file = Smith.acl_cache_path.join(file.basename).sub_ext(".pb.rb")
72
85
  if cached_file.exist?
73
- if file.mtime > cached_file.mtime
74
- true
75
- else
76
- false
77
- end
86
+ file.mtime > cached_file.mtime
78
87
  else
79
88
  true
80
89
  end
81
90
  end
82
91
 
83
92
  def path_glob(path)
84
- Pathname.glob("#{path.join("*.proto")}").map do |acl|
85
- yield acl.realpath
93
+ Pathname.glob(path.join("*.proto")).map { |acl| acl.realpath }
94
+ end
95
+
96
+ # This is not idea but I really don't know how else to do it. I cannot use
97
+ # $stderr = StringIO.new as this only seems to capture STDERR if you
98
+ # explicitly write to STDERR. In my case it's an ffi library call that's
99
+ # writing to STDERR.
100
+ def capture_stderr
101
+ org_stderr = STDERR.dup
102
+ begin
103
+ tmp = Tempfile.new('')
104
+ tmp.sync = true
105
+ STDERR.reopen(tmp)
106
+ yield
107
+ File.read(tmp.path)
108
+ ensure
109
+ STDERR.reopen(org_stderr)
86
110
  end
87
111
  end
112
+
113
+ def load_acl(path)
114
+ logger.verbose { "Loading ACL: #{path}" }
115
+ require path
116
+ end
117
+
118
+ def add_to_type_cache(path)
119
+ acl_class = File.read(path)
120
+ @acl_parser.go(acl_class)
121
+ @acl_parser.fully_qualified_classes.each do |clazz|
122
+ @acl_type_cache.add(to_class(clazz))
123
+ end
124
+ end
125
+
126
+ def to_class(type_as_array)
127
+ type_as_array.inject(Kernel) { |acc, t| acc.const_get(t) }
128
+ end
129
+
130
+ def acl_compiled_path(path)
131
+ "#{Smith.acl_cache_path.join(path.basename('.proto'))}.pb.rb"
132
+ end
88
133
  end
89
134
  end
@@ -0,0 +1,75 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'set'
4
+ require 'sexp_processor'
5
+ require 'ruby_parser'
6
+ require 'optparse'
7
+ require 'timeout'
8
+
9
+ module Smith
10
+ class ACLParser < SexpProcessor
11
+
12
+ def initialize
13
+ super()
14
+ @class_stack = []
15
+ self.auto_shift_type = true
16
+ self.reset
17
+ end
18
+
19
+ def go(ruby)
20
+ @parser = RubyParser.new
21
+ process(@parser.process(ruby))
22
+ end
23
+
24
+ # Adds name to the class stack, for the duration of the block
25
+ def in_class(name)
26
+ @class_stack.unshift(name)
27
+ yield
28
+ @class_stack.shift
29
+ end
30
+
31
+ # Returns the first class in the list, or :main
32
+ def class_name
33
+ if @class_stack.any?
34
+ @class_stack.reverse
35
+ else
36
+ :main
37
+ end
38
+ end
39
+
40
+ # Process each element of #exp in turn.
41
+ def process_until_empty(exp)
42
+ process(exp.shift) until exp.empty?
43
+ end
44
+
45
+ def fully_qualified_classes
46
+ @classes.delete(:main)
47
+ @classes.inject([]) do |a, class_method|
48
+ a.tap do |acc|
49
+ acc << class_method
50
+ end
51
+ end
52
+ end
53
+
54
+ # Reset @classes data
55
+ def reset
56
+ @classes = Set.new
57
+ end
58
+
59
+ # Process Class method
60
+ def process_class(exp)
61
+ in_class(exp.shift) do
62
+ process_until_empty exp
63
+ @classes << class_name
64
+ end
65
+ s()
66
+ end
67
+
68
+ def process_module(exp)
69
+ in_class exp.shift do
70
+ process_until_empty exp
71
+ end
72
+ s()
73
+ end
74
+ end
75
+ end
data/lib/smith/agent.rb CHANGED
@@ -6,11 +6,12 @@ module Smith
6
6
  include Logger
7
7
  include Smith::ObjectCount
8
8
 
9
- attr_reader :factory, :name, :pid
9
+ attr_reader :name, :pid, :uuid
10
10
 
11
- def initialize(options={})
11
+ def initialize(uuid)
12
12
  @name = self.class.to_s
13
13
  @pid = $$
14
+ @uuid = uuid
14
15
 
15
16
  @factory = QueueFactory.new
16
17
 
@@ -67,16 +68,9 @@ module Smith
67
68
  @on_running = blk
68
69
  end
69
70
 
70
- # Override this method to implement your own agent. You can use task but this may
71
- # go away in the future. This method must not block.
71
+ # Override this method to implement your own agent.
72
72
  def run
73
- raise ArgumentError, "You need to call Agent.task(&block)" if @@task.nil?
74
-
75
- logger.debug { "Setting up default queue: #{default_queue_name}" }
76
-
77
- subscribe(default_queue_name, :auto_delete => false) do |r|
78
- @@task.call(r.payload)
79
- end
73
+ raise ArgumentError, "You must override this method"
80
74
  end
81
75
 
82
76
  def install_signal_handler(signal, position=:end, &blk)
@@ -107,16 +101,14 @@ module Smith
107
101
  end
108
102
 
109
103
  class << self
110
- def task(opts={}, &blk)
111
- @@task = blk
112
- end
113
-
114
104
  # Options supported:
115
105
  # :monitor, the agency will monitor the agent & if dies restart.
116
106
  # :singleton, only every have one agent. If this is set to false
117
107
  # multiple agents are allowed.
118
108
  def options(opts)
119
- Smith.config.agent._merge!(opts)
109
+ opts.each do |k, v|
110
+ Smith.config.agent.send("#{k}=", v)
111
+ end
120
112
  end
121
113
  end
122
114
 
@@ -128,9 +120,9 @@ module Smith
128
120
  end
129
121
 
130
122
  def setup_control_queue
131
- logger.debug { "Setting up control queue: #{control_queue_name}" }
123
+ logger.debug { "Setting up control queue: #{control_queue_def.denormalise}" }
132
124
 
133
- Messaging::Receiver.new(control_queue_name, :durable => false, :auto_delete => true) do |receiver|
125
+ Messaging::Receiver.new(control_queue_def) do |receiver|
134
126
  receiver.subscribe do |payload|
135
127
  logger.debug { "Command received on agent control queue: #{payload.command} #{payload.options}" }
136
128
 
@@ -142,7 +134,7 @@ module Smith
142
134
  when 'log_level'
143
135
  begin
144
136
  level = payload.options.first
145
- logger.info { "Setting log level to #{level} for: #{name}" }
137
+ logger.info { "Setting log level to #{level} for: #{name} (#{uuid})" }
146
138
  log_level(level)
147
139
  rescue ArgumentError => e
148
140
  logger.error { "Incorrect log level: #{level}" }
@@ -155,20 +147,18 @@ module Smith
155
147
  end
156
148
 
157
149
  def setup_stats_queue
158
- puts "setting stats"
159
- # instantiate this queue without using the factory so it doesn't show
160
- # up in the stats.
161
150
  Messaging::Sender.new(QueueDefinitions::Agent_stats) do |stats_queue|
162
151
  EventMachine.add_periodic_timer(2) do
163
152
  stats_queue.number_of_consumers do |consumers|
164
153
  if consumers > 0
165
- payload = ACL::Factory.create(:agent_stats) do |p|
154
+ payload = ACL::AgentStats.new.tap do |p|
155
+ p.uuid = uuid
166
156
  p.agent_name = self.name
167
157
  p.pid = self.pid
168
158
  p.rss = (File.read("/proc/#{pid}/statm").split[1].to_i * 4) / 1024 # This assumes the page size is 4K & is MB
169
159
  p.up_time = (Time.now - @start_time).to_i
170
- factory.each_queue do |q|
171
- p.queues << ACL::Factory.create('agent_stats::queue_stats', :name => q.denormalised_queue_name, :type => q.class.to_s, :length => q.counter)
160
+ queues.each_queue do |q|
161
+ p.queues << ACL::AgentStats::QueueStats.new(:name => q.name, :type => q.class.to_s, :length => q.counter)
172
162
  end
173
163
  end
174
164
 
@@ -180,24 +170,23 @@ module Smith
180
170
  end
181
171
 
182
172
  def acknowledge_start(&blk)
183
- Messaging::Sender.new(QueueDefinitions::Agent_lifecycle) do |ack_start_queue|
184
- payload = ACL::Factory.create(:agent_lifecycle) do |p|
185
- p.state = 'acknowledge_start'
173
+ Messaging::Sender.new(QueueDefinitions::Agent_lifecycle) do |queue|
174
+ payload = ACL::AgentAcknowledgeStart.new.tap do |p|
175
+ p.uuid = uuid
186
176
  p.pid = $$
187
- p.name = self.class.to_s
188
- p.metadata = Smith.config.agent.metadata
189
- p.monitor = Smith.config.agent.monitor
190
177
  p.singleton = Smith.config.agent.singleton
191
178
  p.started_at = Time.now.to_i
179
+ p.metadata = Smith.config.agent.metadata
180
+ p.monitor = Smith.config.agent.monitor
192
181
  end
193
- ack_start_queue.publish(payload, &blk)
182
+ queue.publish(payload)
194
183
  end
195
184
  end
196
185
 
197
186
  def acknowledge_stop(&blk)
198
- Messaging::Sender.new(QueueDefinitions::Agent_lifecycle) do |ack_stop_queue|
187
+ Messaging::Sender.new(QueueDefinitions::Agent_lifecycle) do |queue|
199
188
  message = {:state => 'acknowledge_stop', :pid => $$, :name => self.class.to_s}
200
- ack_stop_queue.publish(ACL::Factory.create(:agent_lifecycle, message), &blk)
189
+ queue.publish(ACL::AgentAcknowledgeStop.new(:uuid => uuid), &blk)
201
190
  end
202
191
  end
203
192
 
@@ -205,10 +194,10 @@ module Smith
205
194
  if Smith.config.agent.monitor
206
195
  EventMachine::add_periodic_timer(1) do
207
196
  Messaging::Sender.new(QueueDefinitions::Agent_keepalive) do |queue|
208
- message = {:name => self.class.to_s, :pid => $$, :time => Time.now.to_i}
209
- keep_alive_queue.consumers do |consumers|
197
+ message = {:name => self.class.to_s, :uuid => uuid, :time => Time.now.to_i}
198
+ queue.consumers do |consumers|
210
199
  if consumers > 0
211
- keep_alive_queue.publish(ACL::Factory.create(:agent_keepalive, message))
200
+ queue.publish(ACL::AgentKeepalive, message)
212
201
  end
213
202
  end
214
203
  end
@@ -222,12 +211,8 @@ module Smith
222
211
  @factory
223
212
  end
224
213
 
225
- def control_queue_name
226
- "#{default_queue_name}.control"
227
- end
228
-
229
- def default_queue_name
230
- "agent.#{name.sub(/Agent$/, '').snake_case}"
214
+ def control_queue_def
215
+ @control_queue_def ||= QueueDefinitions::Agent_control.call(uuid)
231
216
  end
232
217
  end
233
218
  end
@@ -1,40 +1,66 @@
1
1
  # -*- encoding: utf-8 -*-
2
+ require 'tdb'
3
+ require 'securerandom'
4
+
2
5
  module Smith
3
- class AgentCache < Cache
6
+ class AgentCache
7
+
8
+ include Enumerable
4
9
 
5
10
  attr_accessor :path
6
11
 
7
12
  def initialize(opts={})
8
- super()
9
- @paths = opts[:paths]
13
+ # @db = LevelDB::DB.make(Smith.cache_path.join('agent_state').to_s, :error_if_exists => false, :create_if_missing => true)
14
+ @db = TDB.new(Smith.cache_path.join('agent_state.tdb').to_s)
15
+ end
10
16
 
11
- operator ->(agent_name, options={}) { AgentProcess.first(:name => agent_name) || AgentProcess.new(:name => agent_name, :path => agent_path(agent_name)) }
17
+ def create(name)
18
+ AgentProcess.new(@db, :name => name, :uuid => SecureRandom.uuid)
19
+ end
12
20
 
13
- populate
21
+ def alive?(uuid)
22
+ (@db.include?(uuid)) ? instantiate(@db[uuid]).alive? : false
14
23
  end
15
24
 
16
- def alive?(name)
17
- (exist?(name)) ? entry(name).alive? : false
25
+ def exist?(uuid)
26
+ @db.include?(uuid)
18
27
  end
19
28
 
29
+ def find_by_name(*names)
30
+ inject([]) do |a, agent|
31
+ a.tap do |acc|
32
+ names.flatten.each do |name|
33
+ acc << agent if name == agent.name
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ # select {|a| a.name == name.to_s }
40
+ # end
41
+
20
42
  def state(state)
21
43
  select {|a| a.state == state.to_s }
22
44
  end
23
45
 
24
- alias names :entries
25
- alias :[] :entry
46
+ def delete(uuid)
47
+ @db.delete(uuid)
48
+ end
26
49
 
27
- private
50
+ def entry(uuid)
51
+ (uuid) ? instantiate(@db[uuid]) : nil
52
+ end
53
+
54
+ alias :[] :entry
28
55
 
29
- # When we start load any new data from the db.
30
- def populate
31
- AgentProcess.all.each { |a| update(a.name, a) }
56
+ def each(&blk)
57
+ @db.each {|k,v| blk.call(instantiate(v)) }
32
58
  end
33
59
 
34
- def agent_path(agent_name)
35
- @paths.detect do |path|
36
- Pathname.new(path).join("#{agent_name.snake_case}.rb").exist?
37
- end
60
+ private
61
+
62
+ def instantiate(state)
63
+ (state) ? AgentProcess.new(@db, state) : nil
38
64
  end
39
65
  end
40
66
  end
@@ -38,7 +38,7 @@ module Smith
38
38
  logger.info { "Restarting dead agent: #{agent_process.name}" }
39
39
  Messaging::Sender.new(QueueDefinitions::Agency_control) do |sender|
40
40
  sender.on_reply { |p, r| logger.debug { "Agent restart message acknowledged: #{agent_process.name}" } }
41
- sender.publish(ACL::Factory.create(:agency_command).content(:command => 'start', :args => [agent_process.name]))
41
+ sender.publish(ACL::AgencyCommand.new(:command => 'start', :args => [agent_process.name]))
42
42
  end
43
43
  when 'unknown'
44
44
  logger.info { "Agent is in an unknown state: #{agent_process.name}" }