smith 0.5.8 → 0.5.10
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.
- 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
|