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
@@ -1,43 +1,53 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require 'pp'
3
2
  require 'state_machine'
4
- require 'dm-observer'
3
+ require 'forwardable'
4
+
5
+ require 'protobuf/message'
5
6
 
6
7
  module Smith
7
8
 
8
9
  class AgentProcess
9
10
 
10
- include Logger
11
- include Extlib
12
- include DataMapper::Resource
13
-
14
- property :id, Serial
15
- property :path, String, :required => true
16
- property :name, String, :required => true
17
- property :state, String, :required => true
18
- property :pid, Integer
19
- property :started_at, Integer
20
- property :last_keep_alive, Integer
21
- property :metadata, String
22
- property :monitor, Boolean
23
- property :singleton, Boolean
24
-
25
- state_machine :initial => :null do
26
-
27
- before_transition do |transition|
11
+ include Smith::Logger
12
+ extend Forwardable
13
+
14
+ class AgentState < ::Protobuf::Message
15
+ required ::Protobuf::Field::StringField, :_state, 0
16
+ required ::Protobuf::Field::StringField, :name, 2
17
+ required ::Protobuf::Field::StringField, :uuid, 3
18
+ optional ::Protobuf::Field::Int32Field, :pid, 4
19
+ optional ::Protobuf::Field::Int32Field, :started_at, 5
20
+ optional ::Protobuf::Field::Int32Field, :last_keep_alive, 6
21
+ optional ::Protobuf::Field::BoolField, :singleton, 7
22
+ optional ::Protobuf::Field::StringField, :metadata, 8
23
+ optional ::Protobuf::Field::BoolField, :monitor, 9
24
+ end
25
+
26
+ def_delegators :@agent_state, :name, :uuid, :pid, :last_keep_alive, :metadata, :monitor, :singleton
27
+ def_delegators :@agent_state, :name=, :uuid=, :pid=, :last_keep_alive=, :metadata=, :monitor=, :singleton=
28
+
29
+ state_machine :initial => lambda {|o| o.send(:_state)}, :action => :save do
30
+
31
+ before_transition do |_, transition|
28
32
  logger.debug { "Transition [#{name}]: :#{transition.from} -> :#{transition.to}" }
29
33
  end
30
34
 
31
- after_failure do |transition|
35
+ after_failure do |_, transition|
32
36
  logger.debug { "Illegal state change [#{name}]: :#{transition.from} -> :#{transition.event}" }
33
37
  end
34
38
 
35
- event :instantiate do
36
- transition [:null] => :stopped
39
+ event :start do
40
+ transition [:null] => :checked
37
41
  end
38
42
 
39
- event :start do
40
- transition [:null, :stopped, :dead] => :starting
43
+ # I'm not terribly keen on this. On the one hand it means that the code
44
+ # for checking the existance of the agent is contained here on the other
45
+ # hand if the agent does not exist the state is checked which is not very
46
+ # indicative that a failure has occurred! It does work but not very nice.
47
+ # FIXME
48
+
49
+ event :check do
50
+ transition [:checked] => :starting
41
51
  end
42
52
 
43
53
  event :acknowledge_start do
@@ -49,7 +59,11 @@ module Smith
49
59
  end
50
60
 
51
61
  event :acknowledge_stop do
52
- transition [:stopping] => :stopped
62
+ transition [:stopping] => :null
63
+ end
64
+
65
+ event :null do
66
+ transition [:check, :stopped] => :null
53
67
  end
54
68
 
55
69
  event :no_process_running do
@@ -65,13 +79,25 @@ module Smith
65
79
  end
66
80
  end
67
81
 
82
+ def started_at
83
+ Time.at(@agent_state.started_at)
84
+ end
85
+
86
+ def started_at=(time)
87
+ @agent_state.started_at = time.to_i
88
+ end
89
+
68
90
  def add_callback(state, &blk)
69
91
  AgentProcess.state_machine do
70
- puts "changing callback"
92
+ puts "changing callback: :on => #{state}, :do => #{blk}"
71
93
  after_transition :on => state, :do => blk
72
94
  end
73
95
  end
74
96
 
97
+ def delete
98
+ @db.delete(@agent_state.uuid)
99
+ end
100
+
75
101
  # Check to see if the agent is alive.
76
102
  def alive?
77
103
  if self.pid
@@ -87,24 +113,64 @@ module Smith
87
113
  end
88
114
 
89
115
  # Return the agent control queue.
90
- def control_queue_name
91
- "agent.#{name.sub(/Agent$/, '').snake_case}.control"
116
+ def control_queue_def
117
+ QueueDefinitions::Agent_control.call(uuid)
118
+ end
119
+
120
+ def initialize(db, attributes={})
121
+ @db = db
122
+ if attributes.is_a?(String)
123
+ @agent_state = AgentState.new.parse_from_string(attributes)
124
+ else
125
+ raise ArgumentError, "missing uuid option" if attributes[:uuid].nil?
126
+ attr = attributes.merge(:_state => 'null')
127
+ @agent_state = AgentState.new(attr)
128
+ end
129
+
130
+ super()
131
+ end
132
+
133
+ def exists?
134
+ Smith.agent_paths.detect do |path|
135
+ Pathname.new(path).join("#{name.snake_case}.rb").exist?
136
+ end
137
+ end
138
+
139
+ def to_s
140
+ @agent_state.to_hash.tap do |h|
141
+ h[:state] = h.delete(:_state)
142
+ end
143
+ end
144
+
145
+ private
146
+
147
+ def _state
148
+ @agent_state._state || "null"
149
+ end
150
+
151
+ def save
152
+ @agent_state._state = state
153
+ # TODO This *must* change to uuid when I've worked out how to manage them.
154
+ @db[uuid] = @agent_state.to_s
92
155
  end
93
156
  end
94
157
 
95
- class AgentProcessObserver
158
+ module AgentProcessObserver
96
159
 
97
160
  include Logger
98
- include DataMapper::Observer
99
161
 
100
- observe AgentProcess
162
+ def self.start(agent_process)
163
+ if agent_process.exists?
164
+ agent_process.check
165
+ else
166
+ agent_process.delete
167
+ end
168
+ end
101
169
 
102
170
  # Start an agent. This forks and execs the bootstrapper class
103
171
  # which then becomes responsible for managing the agent process.
104
- def self.start(agent_process)
105
- agent_process.started_at = Time.now
106
- agent_process.pid = fork do
107
-
172
+ def self.start_process(agent_process)
173
+ fork do
108
174
  # Detach from the controlling terminal
109
175
  unless Process.setsid
110
176
  raise 'Cannot detach from controlling terminal'
@@ -113,7 +179,7 @@ module Smith
113
179
  # Close all file descriptors apart from stdin, stdout, stderr
114
180
  ObjectSpace.each_object(IO) do |io|
115
181
  unless [STDIN, STDOUT, STDERR].include?(io)
116
- io.close rescue nil
182
+ io.close unless io.closed?
117
183
  end
118
184
  end
119
185
 
@@ -122,23 +188,26 @@ module Smith
122
188
  STDIN.reopen("/dev/null")
123
189
  STDERR.reopen(STDOUT)
124
190
 
125
- bootstrapper = File.expand_path(File.join(File.dirname(__FILE__), 'bootstrap.rb'))
191
+ bootstrapper = Pathname.new(__FILE__).dirname.join('bootstrap.rb').expand_path
126
192
 
127
- exec('ruby', bootstrapper, agent_process.path, agent_process.name, Smith.acl_cache_path.to_s)
193
+ binary = Smith.config.ruby[agent_process.name]
194
+ logger.debug { "Launching #{agent_process.name} with: #{binary}" }
195
+ exec(binary, bootstrapper.to_s, agent_process.name, agent_process.uuid)
128
196
  end
129
197
 
130
198
  # We don't want any zombies.
131
199
  Process.detach(agent_process.pid)
132
200
  end
133
201
 
134
- def self.acknowledge_start(agent_process)
202
+ def self.acknowledge_start(agent_process, &blk)
203
+ logger.info { "Agent started: #{agent_process.uuid}" }
135
204
  end
136
205
 
137
206
  def self.stop(agent_process)
138
- Messaging::Sender.new(agent_process.control_queue_name, :durable => false, :auto_delete => true) do |sender|
207
+ Messaging::Sender.new(agent_process.control_queue_def) do |sender|
139
208
  sender.consumer_count do |count|
140
209
  if count > 0
141
- sender.publish(ACL::Factory.create(:agent_command, :command => 'stop'))
210
+ sender.publish(ACL::AgentCommand.new(:command => 'stop'))
142
211
  else
143
212
  logger.warn { "Agent is not listening. Setting state to dead." }
144
213
  agent_process.no_process_running
@@ -147,21 +216,35 @@ module Smith
147
216
  end
148
217
  end
149
218
 
219
+ def self.no_process_running(agent_process)
220
+ agent_process.delete
221
+ end
222
+
223
+
150
224
  def self.acknowledge_stop(agent_process)
151
- pp :stopped
225
+ agent_process.delete
226
+ logger.info { "Agent stopped: #{agent_process.uuid}" }
152
227
  end
153
228
 
229
+ # This needs to use the PID class to verify if an agent is still running.
230
+ # FIXME
154
231
  def self.kill(agent_process)
155
232
  if agent_process.pid
156
- logger.info { "Sending kill signal: #{agent_process.name}(#{agent_process.pid})" }
157
- begin
158
- Process.kill('TERM', agent_process.pid)
159
- rescue
160
- logger.error { "Process does not exist. PID is stale: #{agent_process.pid}: #{agent_process.name}" }
233
+ if agent_process.pid == 0
234
+ logger.info { "Agent's pid is 0. The agent probably didn't start correctly. Cleaning up." }
235
+ agent_process.delete
236
+ else
237
+ logger.info { "Sending kill signal: #{agent_process.pid}: #{agent_process.uuid}" }
238
+ begin
239
+ Process.kill('TERM', agent_process.pid)
240
+ rescue
241
+ logger.error { "Process does not exist. PID is stale: #{agent_process.pid}: #{agent_process.uuid}" }
242
+ end
161
243
  end
162
244
  else
163
- logger.error { "Not sending kill signal, agent pid is not set: #{agent_process.name}" }
245
+ logger.error { "Not sending kill signal, agent pid is not set: #{agent_process.uuid}" }
164
246
  end
247
+ agent_process.delete
165
248
  end
166
249
 
167
250
  # If an agent is in an unknown state then this will check to see
@@ -170,19 +253,31 @@ module Smith
170
253
  # quite worked out what I'm going to do with it so I'll leave it
171
254
  # as is
172
255
  def self.reap_agent(agent_process)
173
- logger.info { "Reaping agent: #{agent_process.name}" }
256
+ logger.info { "Reaping agent: #{agent_process.uuid}" }
174
257
  if Pathname.new('/proc').join(agent_process.pid.to_s).exist?
175
- logger.warn { "Agent is still alive: #{agent_process.name}" }
258
+ logger.warn { "Agent is still alive: #{agent_process.uuid}" }
176
259
  else
177
- logger.warn { "Agent is already dead: #{agent_process.name}" }
260
+ logger.warn { "Agent is already dead: #{agent_process.uuid}" }
178
261
  end
179
262
  end
180
263
  end
181
264
 
182
- AgentProcess.state_machine do
265
+ module LoggeMethods
266
+ include Logger
267
+ end
268
+
269
+ AgentProcess.state_machine do |state_machine|
270
+ # Make sure the state machine gets a logger.
271
+ # This doesn't work with JRuby.
272
+ state_machine.extend LoggeMethods
273
+
183
274
  after_transition :on => :start, :do => AgentProcessObserver.method(:start)
275
+ after_transition :on => :check, :do => AgentProcessObserver.method(:start_process)
184
276
  after_transition :on => :stop, :do => AgentProcessObserver.method(:stop)
185
277
  after_transition :on => :kill, :do => AgentProcessObserver.method(:kill)
186
278
  after_transition :on => :not_responding, :do => AgentProcessObserver.method(:reap_agent)
279
+ after_transition :on => :acknowledge_start, :do => AgentProcessObserver.method(:acknowledge_start)
280
+ after_transition :on => :acknowledge_stop, :do => AgentProcessObserver.method(:acknowledge_stop)
281
+ after_transition :on => :no_process_running, :do => AgentProcessObserver.method(:no_process_running)
187
282
  end
188
283
  end
@@ -1,5 +1,10 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require 'dm-core'
2
+
3
+ require 'daemons/pidfile'
4
+
5
+ require 'smith/agent_process'
6
+ require 'smith/agent_monitoring'
7
+ require 'smith/command'
3
8
 
4
9
  module Smith
5
10
  class Agency
@@ -9,19 +14,17 @@ module Smith
9
14
  attr_reader :agents, :agent_processes
10
15
 
11
16
  def initialize(opts={})
12
- DataMapper.setup(:default, "yaml:///#{Smith.config.agency.cache_path}")
13
-
14
- @agent_processes = AgentCache.new(:paths => opts.delete(:paths))
17
+ @agent_processes = AgentCache.new
15
18
  end
16
19
 
17
20
  def setup_queues
18
- Messaging::Receiver.new(QueueDefinitions::Agency_control) do |receiver|
21
+ Messaging::Receiver.new(QueueDefinitions::Agency_control, :auto_ack => false) do |receiver|
19
22
  receiver.subscribe do |payload, responder|
20
23
 
21
24
  completion = EM::Completion.new.tap do |c|
22
25
  c.completion do |value|
23
- pp value
24
- responder.reply(Smith::ACL::Factory.create(:agency_command_response, :response => value))
26
+ responder.ack
27
+ responder.reply(Smith::ACL::AgencyCommandResponse.new(:response => value))
25
28
  end
26
29
  end
27
30
 
@@ -35,12 +38,12 @@ module Smith
35
38
 
36
39
  Messaging::Receiver.new(QueueDefinitions::Agent_lifecycle) do |receiver|
37
40
  receiver.subscribe do |payload, r|
38
- case payload.state
39
- when 'dead'
41
+ case payload
42
+ when Smith::ACL::AgentDead
40
43
  dead(payload)
41
- when 'acknowledge_start'
44
+ when Smith::ACL::AgentAcknowledgeStart
42
45
  acknowledge_start(payload)
43
- when 'acknowledge_stop'
46
+ when Smith::ACL::AgentAcknowledgeStop
44
47
  acknowledge_stop(payload)
45
48
  else
46
49
  logger.warn { "Unknown command received on #{QueueDefinitions::Agent_lifecycle.name} queue: #{payload.state}" }
@@ -56,8 +59,8 @@ module Smith
56
59
  end
57
60
 
58
61
  def start_monitoring
59
- @agent_monitor = AgentMonitoring.new(@agent_processes)
60
- @agent_monitor.start_monitoring
62
+ # @agent_monitor = AgentMonitoring.new(@agent_processes)
63
+ # @agent_monitor.start_monitoring
61
64
  end
62
65
 
63
66
  # Stop the agency. This will wait for one second to ensure
@@ -73,61 +76,48 @@ module Smith
73
76
  private
74
77
 
75
78
  def acknowledge_start(agent_data)
76
- @agent_processes[agent_data.name].tap do |agent_process|
77
- if agent_data.pid == agent_process.pid
78
- agent_process.monitor = agent_data.monitor
79
- agent_process.singleton = agent_data.singleton
80
- agent_process.metadata = agent_data.metadata
81
- agent_process.acknowledge_start
82
- else
83
- logger.error { "Agent reports different pid during acknowledge_start: #{agent_data.name}" }
84
- end
79
+ agent_exists?(agent_data.uuid) do |agent_process|
80
+ agent_process.pid = agent_data.pid
81
+ agent_process.started_at = agent_data.started_at
82
+ agent_process.singleton = agent_data.singleton
83
+ agent_process.monitor = agent_data.monitor
84
+ agent_process.metadata = agent_data.metadata
85
+ agent_process.acknowledge_start
85
86
  end
86
87
  end
87
88
 
88
89
  def acknowledge_stop(agent_data)
89
- @agent_processes[agent_data.name].tap do |agent_process|
90
- if agent_data.pid == agent_process.pid
91
- agent_process.pid = nil
92
- agent_process.monitor = nil
93
- agent_process.singleton = nil
94
- agent_process.started_at = nil
95
- agent_process.last_keep_alive = nil
96
- agent_process.acknowledge_stop
97
- else
98
- if agent_process.pid
99
- logger.error { "Agent reports different pid during acknowledge_stop: #{agent_data.name}" }
100
- end
101
- end
90
+ agent_exists?(agent_data.uuid) do |agent_process|
91
+ agent_process.acknowledge_stop
102
92
  end
103
93
  end
104
94
 
105
95
  def dead(agent_data)
106
- @agent_processes[agent_data.name].no_process_running
107
- logger.fatal { "Agent is dead: #{agent_data.name}" }
96
+ agent_exists?(agent_data.uuid, ->{}) do |agent_process|
97
+ if agent_process.no_process_running
98
+ logger.fatal { "Agent is dead: #{agent_data.uuid}" }
99
+ end
100
+ end
108
101
  end
109
102
 
110
103
  def keep_alive(agent_data)
111
- @agent_processes[agent_data.name].tap do |agent_process|
112
- if agent_data.pid == agent_process.pid
113
- agent_process.last_keep_alive = agent_data.time
114
- logger.verbose { "Agent keep alive: #{agent_data.name}: #{agent_data.time}" }
115
-
116
- # We need to call save explicitly here as the keep alive is not part of
117
- # the state_machine which is the thing that writes the state to disc.
118
- agent_process.save
119
- else
120
- if agent_process.pid
121
- logger.error { "Agent reports different pid during acknowledge_stop: #{agent_data.name}" }
122
- end
123
- end
104
+ agent_exists?(agent_data.uuid) do |agent_process|
105
+ agent_process.last_keep_alive = agent_data.time
106
+ logger.verbose { "Agent keep alive: #{agent_data.uuid}: #{agent_data.time}" }
107
+
108
+ # We need to call save explicitly here as the keep alive is not part of
109
+ # the state_machine which is the thing that writes the state to disc.
110
+ agent_process.save
124
111
  end
125
112
  end
126
113
 
127
- # FIXME this doesn't work.
128
- def delete_agent_process(agent_pid)
129
- @agent_processes.invalidate(agent_pid)
130
- AgentProcess.first('pid' => agent_pid).destroy!
114
+ def agent_exists?(uuid, error_proc=nil, &blk)
115
+ agent = @agent_processes[uuid]
116
+ if agent
117
+ blk.call(agent)
118
+ else
119
+ error_proc.call
120
+ end
131
121
  end
132
122
  end
133
123
  end
@@ -3,7 +3,8 @@
3
3
  # This should never be run directly it should only ever be run by
4
4
  # the agency.
5
5
 
6
- $: << File.dirname(__FILE__) + '/..'
6
+ require 'pathname'
7
+ $:.unshift(Pathname.new(__FILE__).dirname.parent.expand_path)
7
8
 
8
9
  require 'smith'
9
10
  require 'smith/agent'
@@ -15,31 +16,44 @@ module Smith
15
16
 
16
17
  include Logger
17
18
 
18
- def initialize(path, agent_name)
19
+ def initialize(name, uuid)
20
+ Dir.chdir('/')
21
+
19
22
  # FIXME
20
23
  # This doesn't do what I think it should. If an exception is
21
24
  # thrown in setup_control_queue, for example, it just kills
22
25
  # the agent without it actually raising the exception.
23
26
  Thread.abort_on_exception = true
24
- @agent_name = agent_name
25
- @agent_filename = Pathname.new(path).join("#{agent_name.snake_case}.rb").expand_path
27
+ @agent_name = name
28
+ @agent_uuid = uuid
29
+ @agent_filename = agent_path(name)
26
30
  end
27
31
 
28
32
  def signal_handlers
29
33
  logger.debug { "Installing default signal handlers" }
30
34
  %w{TERM INT QUIT}.each do |sig|
31
35
  @agent.install_signal_handler(sig) do |sig|
32
- logger.error { "Agent received: signal #{sig}: #{agent.name}" }
36
+ logger.error { "Agent received: signal #{sig}: #{agent.name} (#{agent.uuid})" }
33
37
  terminate!
34
38
  end
35
39
  end
36
40
  end
37
41
 
38
42
  def load_agent
43
+ @agent_filename = agent_path(@agent_name)
39
44
  logger.debug { "Loading #{@agent_name} from: #{@agent_filename.dirname}" }
40
45
  add_agent_load_path
41
46
  load @agent_filename
42
- @agent = Kernel.const_get(@agent_name).new
47
+ @agent = Kernel.const_get(@agent_name).new(@agent_uuid)
48
+ end
49
+
50
+ def agent_path(name)
51
+ file_name = "#{name.snake_case}.rb"
52
+ Smith.agent_paths.each do |path|
53
+ p = Pathname.new(path).join(file_name)
54
+ return p if p.exist?
55
+ end
56
+ return nil
43
57
  end
44
58
 
45
59
  def start!
@@ -53,7 +67,7 @@ module Smith
53
67
  # See the note at the in main.
54
68
  def terminate!(exception=nil)
55
69
  logger.error { format_exception(exception) } if exception
56
- logger.error { "Terminating: #{@agent_name}." }
70
+ logger.error { "Terminating: #{@agent_uuid}." }
57
71
 
58
72
  if Smith.running?
59
73
  send_dead_message
@@ -78,14 +92,14 @@ module Smith
78
92
  private
79
93
 
80
94
  def write_pid_file
81
- @pid = Daemons::PidFile.new(Daemons::Pid.dir(:normal, Dir::tmpdir, nil), ".rubymas-#{@agent_name.snake_case}", true)
95
+ @pid = Daemons::PidFile.new(Daemons::Pid.dir(:normal, Dir::tmpdir, nil), ".rubymas-#{@agent_uuid.snake_case}", true)
82
96
  @pid.pid = Process.pid
83
97
  end
84
98
 
85
99
  def send_dead_message
86
- logger.debug { "Sending dead message to agency: #{@agent_name}" }
100
+ logger.debug { "Sending dead message to agency: #{@agent_name} (#{@agent_uuid})" }
87
101
  Messaging::Sender.new(QueueDefinitions::Agent_lifecycle) do |sender|
88
- sender.publish(ACL::Factory.create(:agent_lifecycle, :state => 'dead', :name => @agent_name))
102
+ sender.publish(ACL::AgentDead.new(:uuid => @agent_uuid))
89
103
  end
90
104
  end
91
105
 
@@ -124,18 +138,18 @@ module Smith
124
138
  end
125
139
  end
126
140
 
127
- path = ARGV[0]
128
- agent_name = ARGV[1]
141
+ name = ARGV[0]
142
+ uuid = ARGV[1]
129
143
 
130
- exit 1 if agent_name.nil? || path.nil?
144
+ exit 1 if name.nil? || uuid.nil?
131
145
 
132
146
  # Set the running instance name to the name of the agent.
133
- $0 = "#{agent_name}"
147
+ $0 = "#{name}"
134
148
 
135
- # load the acls
136
- Smith.load_acls
149
+ # Compile acls
150
+ Smith.compile_acls
137
151
 
138
- bootstrapper = Smith::AgentBootstrap.new(path, agent_name)
152
+ bootstrapper = Smith::AgentBootstrap.new(name, uuid)
139
153
 
140
154
  # I've tried putting the exception handling in the main reactor log
141
155
  # but it doesn't do anything. I know there's a reason for this but I
data/lib/smith/cache.rb CHANGED
@@ -46,6 +46,10 @@ module Smith
46
46
  !@cache[name].nil?
47
47
  end
48
48
 
49
+ def delete(name)
50
+ @cache.delete(name)
51
+ end
52
+
49
53
  def size
50
54
  @cache.size
51
55
  end
data/lib/smith/command.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  require 'trollop'
4
+ require 'smith/command_base'
4
5
 
5
6
  module Smith
6
7
  class Command
@@ -21,10 +22,7 @@ module Smith
21
22
 
22
23
  clazz = Commands.const_get(Extlib::Inflection.camelize(command)).new
23
24
 
24
- logger.debug { "#{(Command.agency?) ? 'Agency' : 'Smithctl'} command: #{command}#{(args.empty?) ? '' : " #{args.join(', ')}"}." }
25
-
26
25
  begin
27
-
28
26
  clazz.parse_options(args)
29
27
 
30
28
  vars.each do |k,v|
@@ -3,18 +3,35 @@ module Smith
3
3
  module Commands
4
4
  class Kill < CommandBase
5
5
  def execute
6
- work = ->(agent_name, iter) do
7
- agents[agent_name].kill
8
- iter.next
6
+ work = ->(acc, uuid, iter) do
7
+ if agents.exist?(uuid)
8
+ agents[uuid].kill
9
+ else
10
+ acc << uuid
11
+ end
12
+
13
+ iter.return(acc)
9
14
  end
10
15
 
11
- done = -> { responder.succeed('') }
16
+ done = ->(errors) { responder.succeed(format_error_message(errors)) }
12
17
 
13
- EM::Iterator.new(target).each(work, done)
18
+ EM::Iterator.new(target).inject([], work, done)
14
19
  end
15
20
 
16
21
  private
17
22
 
23
+ def format_error_message(errors)
24
+ errors = errors.compact
25
+ case errors.size
26
+ when 0
27
+ ''
28
+ when 1
29
+ "Agent does not exist: #{errors.first}"
30
+ else
31
+ "Agents do not exist: #{errors.join(", ")}"
32
+ end
33
+ end
34
+
18
35
  def options_spec
19
36
  banner "Kill an agent/agents."
20
37
  end
@@ -3,7 +3,12 @@ module Smith
3
3
  module Commands
4
4
  class List < CommandBase
5
5
  def execute
6
- selected_agents = (options[:all]) ? agents : agents.state(:running)
6
+ if target.size > 0
7
+ selected_agents = agents.find_by_name(target)
8
+ else
9
+ selected_agents = (options[:all]) ? agents.to_a : agents.state(:running)
10
+ end
11
+
7
12
  responder.succeed((selected_agents.empty?) ? '' : format(selected_agents, options[:long]))
8
13
  end
9
14
 
@@ -22,16 +27,16 @@ module Smith
22
27
 
23
28
  def long_format(a)
24
29
  a.map do |a|
25
- [a.state, a.pid, (a.started_at) ? format_time(a.started_at) : '', (!(a.stopped? || a.null?) && !a.alive?) ? '(agent dead)' : "", a.name]
30
+ [a.state, a.uuid, a.pid, (a.started_at) ? format_time(a.started_at) : '', (!(a.stopped? || a.null?) && !a.alive?) ? '(agent dead)' : "", a.name]
26
31
  end
27
32
  end
28
33
 
29
34
  def short_format(a, sep=' ')
30
- a.map(&:name).sort.join(sep)
35
+ a.map { |a| [a.uuid] }.join(sep)
31
36
  end
32
37
 
33
38
  def format_time(t)
34
- (t) ? Time.at(t).strftime("%Y/%m/%d %H:%M:%S") : ''
39
+ (t) ? t.strftime("%Y/%m/%d %H:%M:%S") : ''
35
40
  end
36
41
 
37
42
  def tabulate(a, opts={})