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.
- checksums.yaml +7 -0
- data/bin/agency +19 -7
- data/bin/pry-smith +11 -0
- data/bin/smithctl +19 -21
- data/lib/smith/acl_compiler.rb +101 -56
- data/lib/smith/acl_parser.rb +75 -0
- data/lib/smith/agent.rb +28 -43
- data/lib/smith/agent_cache.rb +43 -17
- data/lib/smith/agent_monitoring.rb +1 -1
- data/lib/smith/agent_process.rb +148 -53
- data/lib/smith/application/agency.rb +44 -54
- data/lib/smith/bootstrap.rb +31 -17
- data/lib/smith/cache.rb +4 -0
- data/lib/smith/command.rb +1 -3
- data/lib/smith/commands/agency/kill.rb +22 -5
- data/lib/smith/commands/agency/list.rb +9 -4
- data/lib/smith/commands/agency/logger.rb +25 -12
- data/lib/smith/commands/agency/object_count.rb +19 -8
- data/lib/smith/commands/agency/start.rb +7 -10
- data/lib/smith/commands/agency/stop.rb +30 -12
- data/lib/smith/commands/common.rb +1 -1
- data/lib/smith/commands/smithctl/acl.rb +6 -3
- data/lib/smith/commands/smithctl/dump.rb +79 -0
- data/lib/smith/commands/smithctl/firehose.rb +2 -1
- data/lib/smith/commands/smithctl/push.rb +27 -12
- data/lib/smith/commands/smithctl/status.rb +27 -0
- data/lib/smith/config.rb +140 -28
- data/lib/smith/daemon.rb +16 -3
- data/lib/smith/exceptions.rb +6 -3
- data/lib/smith/logger.rb +12 -24
- data/lib/smith/messaging/acl/agent_keepalive.proto +2 -2
- data/lib/smith/messaging/acl/agent_lifecycle.proto +15 -9
- data/lib/smith/messaging/acl/agent_stats.proto +6 -5
- data/lib/smith/messaging/acl/default.rb +2 -7
- data/lib/smith/messaging/acl_type_cache.rb +77 -0
- data/lib/smith/messaging/factory.rb +29 -0
- data/lib/smith/messaging/queue.rb +12 -10
- data/lib/smith/messaging/queue_definition.rb +21 -4
- data/lib/smith/messaging/receiver.rb +55 -62
- data/lib/smith/messaging/requeue.rb +1 -5
- data/lib/smith/messaging/sender.rb +48 -43
- data/lib/smith/messaging/util.rb +0 -10
- data/lib/smith/queue_definitions.rb +7 -4
- data/lib/smith/version.rb +1 -1
- data/lib/smith.rb +57 -56
- metadata +77 -128
- data/lib/smith/messaging/payload.rb +0 -100
data/lib/smith/agent_process.rb
CHANGED
@@ -1,43 +1,53 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
require 'pp'
|
3
2
|
require 'state_machine'
|
4
|
-
require '
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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 :
|
36
|
-
transition [:null] => :
|
39
|
+
event :start do
|
40
|
+
transition [:null] => :checked
|
37
41
|
end
|
38
42
|
|
39
|
-
|
40
|
-
|
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] => :
|
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
|
91
|
-
|
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
|
-
|
158
|
+
module AgentProcessObserver
|
96
159
|
|
97
160
|
include Logger
|
98
|
-
include DataMapper::Observer
|
99
161
|
|
100
|
-
|
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.
|
105
|
-
|
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
|
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 =
|
191
|
+
bootstrapper = Pathname.new(__FILE__).dirname.join('bootstrap.rb').expand_path
|
126
192
|
|
127
|
-
|
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.
|
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::
|
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
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
logger.
|
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.
|
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.
|
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.
|
258
|
+
logger.warn { "Agent is still alive: #{agent_process.uuid}" }
|
176
259
|
else
|
177
|
-
logger.warn { "Agent is already dead: #{agent_process.
|
260
|
+
logger.warn { "Agent is already dead: #{agent_process.uuid}" }
|
178
261
|
end
|
179
262
|
end
|
180
263
|
end
|
181
264
|
|
182
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
24
|
-
responder.reply(Smith::ACL::
|
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
|
39
|
-
when
|
41
|
+
case payload
|
42
|
+
when Smith::ACL::AgentDead
|
40
43
|
dead(payload)
|
41
|
-
when
|
44
|
+
when Smith::ACL::AgentAcknowledgeStart
|
42
45
|
acknowledge_start(payload)
|
43
|
-
when
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
90
|
-
|
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
|
-
|
107
|
-
|
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
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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
|
data/lib/smith/bootstrap.rb
CHANGED
@@ -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
|
-
|
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(
|
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 =
|
25
|
-
@
|
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: #{@
|
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-#{@
|
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::
|
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
|
-
|
128
|
-
|
141
|
+
name = ARGV[0]
|
142
|
+
uuid = ARGV[1]
|
129
143
|
|
130
|
-
exit 1 if
|
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 = "#{
|
147
|
+
$0 = "#{name}"
|
134
148
|
|
135
|
-
#
|
136
|
-
Smith.
|
149
|
+
# Compile acls
|
150
|
+
Smith.compile_acls
|
137
151
|
|
138
|
-
bootstrapper = Smith::AgentBootstrap.new(
|
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
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 = ->(
|
7
|
-
agents
|
8
|
-
|
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).
|
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
|
-
|
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
|
35
|
+
a.map { |a| [a.uuid] }.join(sep)
|
31
36
|
end
|
32
37
|
|
33
38
|
def format_time(t)
|
34
|
-
(t) ?
|
39
|
+
(t) ? t.strftime("%Y/%m/%d %H:%M:%S") : ''
|
35
40
|
end
|
36
41
|
|
37
42
|
def tabulate(a, opts={})
|