smith 0.5.13.1 → 0.6.1

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