smith 0.5.8 → 0.5.10
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/agency +49 -21
- data/bin/smithctl +51 -48
- data/lib/smith.rb +21 -17
- data/lib/smith/acl_compiler.rb +6 -3
- data/lib/smith/agent.rb +13 -24
- data/lib/smith/agent_monitoring.rb +5 -5
- data/lib/smith/agent_process.rb +4 -4
- data/lib/smith/application/agency.rb +11 -7
- data/lib/smith/bootstrap.rb +4 -4
- data/lib/smith/command.rb +26 -5
- data/lib/smith/command_base.rb +2 -2
- data/lib/smith/commands/agency/list.rb +11 -26
- data/lib/smith/commands/agency/logger.rb +1 -1
- data/lib/smith/commands/agency/restart.rb +1 -1
- data/lib/smith/commands/agency/start.rb +20 -7
- data/lib/smith/commands/agency/stop.rb +23 -10
- data/lib/smith/commands/agency/version.rb +31 -0
- data/lib/smith/commands/smithctl/commands.rb +1 -13
- data/lib/smith/commands/smithctl/pop.rb +34 -28
- data/lib/smith/commands/smithctl/{cat.rb → push.rb} +3 -3
- data/lib/smith/commands/smithctl/rm.rb +20 -0
- data/lib/smith/commands/smithctl/top.rb +1 -1
- data/lib/smith/config.rb +15 -0
- data/lib/smith/daemon.rb +68 -0
- data/lib/smith/{messaging/exceptions.rb → exceptions.rb} +6 -0
- data/lib/smith/logger.rb +2 -3
- data/lib/smith/messaging/acl/agent_keepalive.proto +2 -2
- data/lib/smith/messaging/acl/agent_lifecycle.proto +2 -2
- data/lib/smith/messaging/acl/default.rb +1 -1
- data/lib/smith/messaging/endpoint.rb +5 -5
- data/lib/smith/messaging/receiver.rb +19 -19
- data/lib/smith/messaging/sender.rb +3 -3
- data/lib/smith/version.rb +3 -0
- metadata +52 -41
- data/lib/smith/commands/agency/agency_version.rb +0 -24
- data/lib/smith/commands/agency/state.rb +0 -20
- data/lib/smith/commands/smithctl/smithctl_version.rb +0 -22
@@ -17,26 +17,26 @@ module Smith
|
|
17
17
|
when 'running'
|
18
18
|
if agent_process.last_keep_alive
|
19
19
|
if agent_process.last_keep_alive > agent_process.started_at
|
20
|
-
if (Time.now.
|
20
|
+
if (Time.now.to_i - agent_process.last_keep_alive) > 10
|
21
21
|
logger.fatal { "Agent not responding: #{agent_process.name}" }
|
22
22
|
agent_process.no_process_running
|
23
23
|
end
|
24
24
|
else
|
25
|
-
logger.warn { "Discarding
|
25
|
+
logger.warn { "Discarding keepalives with timestamp before agent started: #{Time.at(agent_process.started_at)} > #{Time.at(agent_process.last_keep_alive)}" }
|
26
26
|
end
|
27
27
|
end
|
28
28
|
when 'starting'
|
29
|
-
if (Time.now.
|
29
|
+
if (Time.now.to_i - agent_process.started_at) > 10
|
30
30
|
logger.error { "No response from agent for > 10 seconds. Agent probably didn't start" }
|
31
31
|
agent_process.not_responding
|
32
32
|
else
|
33
33
|
logger.debug { "no keep alive from #{agent_process.name}" }
|
34
34
|
end
|
35
|
-
when '
|
35
|
+
when 'stopping'
|
36
36
|
logger.info { "Agent is shutting down: #{agent_process.name}" }
|
37
37
|
when 'dead'
|
38
38
|
logger.info { "Restarting dead agent: #{agent_process.name}" }
|
39
|
-
Messaging::Sender.new('agency.control', :auto_delete =>
|
39
|
+
Messaging::Sender.new('agency.control', :auto_delete => false, :durable => false, :strict => true).ready do |sender|
|
40
40
|
sender.publish_and_receive(ACL::Payload.new(:agency_command).content(:command => 'start', :args => [agent_process.name])) do |r|
|
41
41
|
logger.debug { "Agent restart message acknowledged: #{agent_process.name}" }
|
42
42
|
end
|
data/lib/smith/agent_process.rb
CHANGED
@@ -15,7 +15,7 @@ module Smith
|
|
15
15
|
property :path, String, :required => true
|
16
16
|
property :name, String, :required => true
|
17
17
|
property :state, String, :required => true
|
18
|
-
property :pid,
|
18
|
+
property :pid, Integer
|
19
19
|
property :started_at, Integer
|
20
20
|
property :last_keep_alive, Integer
|
21
21
|
property :metadata, String
|
@@ -95,7 +95,7 @@ module Smith
|
|
95
95
|
# Start an agent. This forks and execs the bootstrapper class
|
96
96
|
# which then becomes responsible for managing the agent process.
|
97
97
|
def self.start(agent_process)
|
98
|
-
agent_process.started_at = Time.now
|
98
|
+
agent_process.started_at = Time.now
|
99
99
|
agent_process.pid = fork do
|
100
100
|
|
101
101
|
# Detach from the controlling terminal
|
@@ -115,9 +115,9 @@ module Smith
|
|
115
115
|
STDIN.reopen("/dev/null")
|
116
116
|
STDERR.reopen(STDOUT)
|
117
117
|
|
118
|
-
|
118
|
+
bootstrapper = File.expand_path(File.join(File.dirname(__FILE__), 'bootstrap.rb'))
|
119
119
|
|
120
|
-
exec('ruby',
|
120
|
+
exec('ruby', bootstrapper, agent_process.path, agent_process.name, Smith.acl_cache_path.to_s)
|
121
121
|
end
|
122
122
|
|
123
123
|
# We don't want any zombies.
|
@@ -15,7 +15,7 @@ module Smith
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def setup_queues
|
18
|
-
Messaging::Receiver.new('agency.control', :auto_delete =>
|
18
|
+
Messaging::Receiver.new('agency.control', :auto_delete => false, :durable => false, :strict => true).ready do |receiver|
|
19
19
|
receiver.subscribe do |r|
|
20
20
|
r.reply do |responder|
|
21
21
|
# Add a logger proc to the responder chain.
|
@@ -23,14 +23,14 @@ module Smith
|
|
23
23
|
|
24
24
|
begin
|
25
25
|
Command.run(r.payload.command, r.payload.args, :agency => self, :agents => @agent_processes, :responder => responder)
|
26
|
-
rescue Command::
|
26
|
+
rescue Command::UnknownCommandError => e
|
27
27
|
responder.value("Unknown command: #{r.payload.command}")
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
Messaging::Receiver.new('agent.lifecycle', :auto_delete =>
|
33
|
+
Messaging::Receiver.new('agent.lifecycle', :auto_delete => false, :durable => false).ready do |receiver|
|
34
34
|
receiver.subscribe do |r|
|
35
35
|
case r.payload.state
|
36
36
|
when 'dead'
|
@@ -40,12 +40,12 @@ module Smith
|
|
40
40
|
when 'acknowledge_stop'
|
41
41
|
acknowledge_stop(r.payload)
|
42
42
|
else
|
43
|
-
logger.warn { "
|
43
|
+
logger.warn { "Unknown command received on agent.lifecycle queue: #{r.payload.state}" }
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
Messaging::Receiver.new('agent.keepalive', :auto_delete =>
|
48
|
+
Messaging::Receiver.new('agent.keepalive', :auto_delete => false, :durable => false).ready do |receiver|
|
49
49
|
receiver.subscribe do |r|
|
50
50
|
keep_alive(r.payload)
|
51
51
|
end
|
@@ -59,8 +59,12 @@ module Smith
|
|
59
59
|
|
60
60
|
# Stop the agency. This will wait for one second to ensure
|
61
61
|
# that any messages are flushed.
|
62
|
-
def stop
|
63
|
-
|
62
|
+
def stop(&blk)
|
63
|
+
if blk
|
64
|
+
Smith.stop(true, &blk)
|
65
|
+
else
|
66
|
+
Smith.stop(true)
|
67
|
+
end
|
64
68
|
end
|
65
69
|
|
66
70
|
private
|
data/lib/smith/bootstrap.rb
CHANGED
@@ -84,7 +84,7 @@ module Smith
|
|
84
84
|
|
85
85
|
def send_dead_message
|
86
86
|
logger.debug { "Sending dead message to agency: #{@agent_name}" }
|
87
|
-
Messaging::Sender.new('agent.lifecycle', :auto_delete =>
|
87
|
+
Messaging::Sender.new('agent.lifecycle', :auto_delete => false, :durable => false).ready do |sender|
|
88
88
|
sender.publish(ACL::Payload.new(:agent_lifecycle).content(:state => 'dead', :name => @agent_name))
|
89
89
|
end
|
90
90
|
end
|
@@ -137,10 +137,10 @@ Smith.load_acls
|
|
137
137
|
|
138
138
|
bootstrapper = Smith::AgentBootstrap.new(path, agent_name)
|
139
139
|
|
140
|
-
# I've tried putting the exception handling in the main reactor
|
141
|
-
# but it doesn't do anything. I know
|
140
|
+
# I've tried putting the exception handling in the main reactor log
|
141
|
+
# but it doesn't do anything. I know there's a reason for this but I
|
142
142
|
# don't what it is at the moment. Just beware that whenever there
|
143
|
-
# is an exception the
|
143
|
+
# is an exception the reactor is not going going to be running.
|
144
144
|
begin
|
145
145
|
Smith.start do
|
146
146
|
bootstrapper.load_agent
|
data/lib/smith/command.rb
CHANGED
@@ -4,7 +4,7 @@ require 'trollop'
|
|
4
4
|
|
5
5
|
module Smith
|
6
6
|
class Command
|
7
|
-
class
|
7
|
+
class UnknownCommandError < RuntimeError; end
|
8
8
|
|
9
9
|
include Logger
|
10
10
|
|
@@ -17,12 +17,12 @@ module Smith
|
|
17
17
|
def self.run(command, args, vars)
|
18
18
|
# Change _ to - underscores look so ugly as a command name.
|
19
19
|
command = command.gsub(/-/, '_')
|
20
|
-
logger.debug { "Agency command: #{command}#{(args.empty?) ? '' : " #{args.join(', ')}"}." }
|
21
|
-
|
22
20
|
load_command(command)
|
23
21
|
|
24
22
|
clazz = Commands.const_get(Extlib::Inflection.camelize(command)).new
|
25
23
|
|
24
|
+
logger.debug { "#{(Command.agency?) ? 'Agency' : 'Smithctl'} command: #{command}#{(args.empty?) ? '' : " #{args.join(', ')}"}." }
|
25
|
+
|
26
26
|
begin
|
27
27
|
|
28
28
|
clazz.parse_options(args)
|
@@ -62,17 +62,34 @@ module Smith
|
|
62
62
|
when smithctl_command?(command)
|
63
63
|
:smithctl
|
64
64
|
else
|
65
|
-
raise
|
65
|
+
raise UnknownCommandError, "Unknown command: #{command}"
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
-
|
69
|
+
def self.commands(type=:all)
|
70
|
+
types = case type
|
71
|
+
when :all
|
72
|
+
['agency', 'smithctl']
|
73
|
+
when :agency
|
74
|
+
types = ['agency']
|
75
|
+
when :smithctl
|
76
|
+
types = ['smithctl']
|
77
|
+
else
|
78
|
+
raise ArgumentError, "Unknown command type"
|
79
|
+
end
|
80
|
+
|
81
|
+
types.map do |type|
|
82
|
+
Pathname.glob(base_path.join(type).join("**/*.rb"))
|
83
|
+
end.flatten.map {|p| to_command_name(p) }
|
84
|
+
end
|
70
85
|
|
71
86
|
# Check to see if the command is an agency or smithctl command.
|
72
87
|
def self.agency?
|
73
88
|
Smith.constants.include?(:Agency)
|
74
89
|
end
|
75
90
|
|
91
|
+
private
|
92
|
+
|
76
93
|
# Return the full path of the ruby class.
|
77
94
|
def self.command_path(command)
|
78
95
|
send("#{command_type(command)}_path").join(command)
|
@@ -98,6 +115,10 @@ module Smith
|
|
98
115
|
base_path.join('smithctl')
|
99
116
|
end
|
100
117
|
|
118
|
+
def self.to_command_name(path)
|
119
|
+
path.basename(".rb").to_s
|
120
|
+
end
|
121
|
+
|
101
122
|
# Return the command base path.
|
102
123
|
def self.base_path
|
103
124
|
@c64a6f4f ||= Smith.root_path.join('lib').join("smith").join('commands')
|
data/lib/smith/command_base.rb
CHANGED
@@ -4,41 +4,26 @@ module Smith
|
|
4
4
|
class List < CommandBase
|
5
5
|
def execute
|
6
6
|
responder.value do
|
7
|
-
|
8
|
-
|
9
|
-
"No agents running."
|
10
|
-
else
|
11
|
-
if options[:long]
|
12
|
-
tabulate(long_format(agents), :header => "total #{agents.count}")
|
13
|
-
else
|
14
|
-
short_format(agents)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
else
|
18
|
-
running_agents = agents.state(:running)
|
19
|
-
if running_agents.empty?
|
20
|
-
"No agents running."
|
21
|
-
else
|
22
|
-
if options[:long]
|
23
|
-
tabulate(long_format(running_agents), :header => "total #{running_agents.count}")
|
24
|
-
else
|
25
|
-
short_format(running_agents)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
7
|
+
a = (options[:all]) ? agents : agents.state(:running)
|
8
|
+
(a.empty?) ? nil : format(a, options[:long])
|
29
9
|
end
|
30
10
|
end
|
31
11
|
|
32
12
|
private
|
33
13
|
|
34
|
-
def
|
35
|
-
|
14
|
+
def format(a, long)
|
15
|
+
a = (target.empty?) ? a : a.select {|z| target.detect {|y| z.name == y } }.flatten
|
16
|
+
(long) ? tabulate(long_format(a), :header => "total #{a.count}") : short_format(a)
|
17
|
+
end
|
18
|
+
|
19
|
+
def long_format(a)
|
20
|
+
a.map do |a|
|
36
21
|
[a.state, a.pid, (a.started_at) ? format_time(a.started_at) : '', (!(a.stopped? || a.null?) && !a.alive?) ? '(agent dead)' : "", a.name]
|
37
22
|
end
|
38
23
|
end
|
39
24
|
|
40
|
-
def short_format(
|
41
|
-
|
25
|
+
def short_format(a)
|
26
|
+
a.map(&:name).sort.join(" ")
|
42
27
|
end
|
43
28
|
|
44
29
|
def format_time(t)
|
@@ -39,7 +39,7 @@ module Smith
|
|
39
39
|
private
|
40
40
|
|
41
41
|
def send_agent_control_message(agent, message)
|
42
|
-
Messaging::Sender.new(agent.control_queue_name, :durable => false, :auto_delete => true).ready do |sender|
|
42
|
+
Messaging::Sender.new(agent.control_queue_name, :durable => false, :auto_delete => true, :persistent => true, :strict => true).ready do |sender|
|
43
43
|
sender.publish(ACL::Payload.new(:agent_command).content(message))
|
44
44
|
end
|
45
45
|
end
|
@@ -12,7 +12,7 @@ module Smith
|
|
12
12
|
include Common
|
13
13
|
|
14
14
|
def execute
|
15
|
-
Messaging::Sender.new('agency.control', :auto_delete =>
|
15
|
+
Messaging::Sender.new('agency.control', :auto_delete => false, :durable => false, :strict => true).ready do |sender|
|
16
16
|
payload = ACL::Payload.new(:agency_command).content(:command => 'stop', :args => target)
|
17
17
|
|
18
18
|
sender.publish_and_receive(payload) do |r|
|
@@ -9,15 +9,17 @@ module Smith
|
|
9
9
|
include Common
|
10
10
|
|
11
11
|
def execute
|
12
|
+
|
13
|
+
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
14
|
+
#!!!!!!!!!!!! See not about target at end of this file !!!!!!!!!!!!!!
|
15
|
+
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
16
|
+
|
12
17
|
# Sort out any groups. If the group option is set it will override
|
13
18
|
# any other specified agents.
|
14
19
|
if options[:group]
|
15
20
|
begin
|
16
|
-
|
17
|
-
|
18
|
-
# variable is used instead of the method. TODO work out why and fix.
|
19
|
-
self.target = agent_group(options[:group])
|
20
|
-
if target.empty?
|
21
|
+
agents_to_start = agent_group(options[:group])
|
22
|
+
if agents_to_start.empty?
|
21
23
|
responder.value("There are no agents in group: #{options[:group]}")
|
22
24
|
return
|
23
25
|
end
|
@@ -25,13 +27,15 @@ module Smith
|
|
25
27
|
responder.value(e.message)
|
26
28
|
return
|
27
29
|
end
|
30
|
+
else
|
31
|
+
agents_to_start = target
|
28
32
|
end
|
29
33
|
|
30
34
|
responder.value do
|
31
|
-
if
|
35
|
+
if agents_to_start.empty?
|
32
36
|
"Start what? No agent specified."
|
33
37
|
else
|
34
|
-
|
38
|
+
agents_to_start.map do |agent|
|
35
39
|
agents[agent].name = agent
|
36
40
|
if agents[agent].path
|
37
41
|
if options[:kill]
|
@@ -60,3 +64,12 @@ module Smith
|
|
60
64
|
end
|
61
65
|
end
|
62
66
|
end
|
67
|
+
|
68
|
+
|
69
|
+
# A note about target.
|
70
|
+
#
|
71
|
+
# Target is a method and if you assign something to it strange things happen --
|
72
|
+
# even if the code doesn't get run! I'm not strictly sure what's going on but I
|
73
|
+
# think it's something to do with the a variable aliasing a method of the same
|
74
|
+
# name. So even though the code isn't being run it gets compiled and that
|
75
|
+
# somehow aliases the method. This looks like a bug in yarv to me.
|
@@ -9,6 +9,11 @@ module Smith
|
|
9
9
|
include Common
|
10
10
|
|
11
11
|
def execute
|
12
|
+
|
13
|
+
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
14
|
+
#!!!!!!!!!!!! See not about target at end of this file !!!!!!!!!!!!!!
|
15
|
+
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
16
|
+
|
12
17
|
case target.first
|
13
18
|
when 'agency'
|
14
19
|
running_agents = agents.state(:running)
|
@@ -31,11 +36,8 @@ module Smith
|
|
31
36
|
# any other specified agents.
|
32
37
|
if options[:group]
|
33
38
|
begin
|
34
|
-
|
35
|
-
|
36
|
-
# variable is used instead of the method. TODO work out why and fix.
|
37
|
-
self.target = agent_group(options[:group])
|
38
|
-
if target.empty?
|
39
|
+
agents_to_stop = agent_group(options[:group])
|
40
|
+
if agents_to_stop.empty?
|
39
41
|
responder.value("There are no agents in group: #{options[:group]}")
|
40
42
|
return
|
41
43
|
end
|
@@ -43,16 +45,18 @@ module Smith
|
|
43
45
|
responder.value(e)
|
44
46
|
return
|
45
47
|
end
|
48
|
+
else
|
49
|
+
agents_to_stop = target
|
46
50
|
end
|
47
51
|
|
48
|
-
ret =
|
52
|
+
ret = agents_to_stop.inject([]) do |acc,agent_name|
|
49
53
|
acc << if agents[agent_name].running?
|
50
54
|
agents[agent_name].stop
|
51
55
|
nil
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
+
else
|
57
|
+
logger.warn { "Agent not running: #{agent_name}" }
|
58
|
+
agent_name
|
59
|
+
end
|
56
60
|
end
|
57
61
|
responder.value((ret.compact.empty?) ? nil : "Agent(s) not running: #{ret.compact.join(", ")}")
|
58
62
|
end
|
@@ -68,3 +72,12 @@ module Smith
|
|
68
72
|
end
|
69
73
|
end
|
70
74
|
end
|
75
|
+
|
76
|
+
|
77
|
+
# A note about target.
|
78
|
+
#
|
79
|
+
# Target is a method and if you assign something to it strange things happen --
|
80
|
+
# even if the code doesn't get run! I'm not strictly sure what's going on but I
|
81
|
+
# think it's something to do with the a variable aliasing a method of the same
|
82
|
+
# name. So even though the code isn't being run it gets compiled and that
|
83
|
+
# somehow aliases the method. This looks like a bug in yarv to me.
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Smith
|
3
|
+
module Commands
|
4
|
+
class Version < CommandBase
|
5
|
+
def execute
|
6
|
+
# FIXME Puts some error handling for when the agency isn't running in a git repo.
|
7
|
+
if options[:git]
|
8
|
+
EM.system('git describe') do |output,status|
|
9
|
+
responder.value do
|
10
|
+
if status.exitstatus == 0
|
11
|
+
output.strip
|
12
|
+
else
|
13
|
+
'The agency is not running in a git repo.'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
else
|
18
|
+
responder.value(Smith::VERSION)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def options_spec
|
25
|
+
banner "Display the agency version."
|
26
|
+
|
27
|
+
opt :git, "run git describe, assuming git is installed", :short => :g
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|