smith 0.5.12 → 0.5.13.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/smithctl +16 -19
- data/lib/smith.rb +25 -41
- data/lib/smith/agent.rb +96 -66
- data/lib/smith/agent_monitoring.rb +3 -4
- data/lib/smith/agent_process.rb +16 -9
- data/lib/smith/amqp_errors.rb +53 -0
- data/lib/smith/application/agency.rb +23 -20
- data/lib/smith/bootstrap.rb +3 -3
- data/lib/smith/command.rb +2 -2
- data/lib/smith/command_base.rb +4 -0
- data/lib/smith/commands/agency/agents.rb +19 -19
- data/lib/smith/commands/agency/kill.rb +6 -2
- data/lib/smith/commands/agency/list.rb +2 -4
- data/lib/smith/commands/agency/logger.rb +27 -28
- data/lib/smith/commands/agency/metadata.rb +1 -5
- data/lib/smith/commands/agency/object_count.rb +13 -11
- data/lib/smith/commands/agency/restart.rb +18 -9
- data/lib/smith/commands/agency/start.rb +34 -25
- data/lib/smith/commands/agency/stop.rb +58 -41
- data/lib/smith/commands/agency/version.rb +10 -10
- data/lib/smith/commands/common.rb +7 -4
- data/lib/smith/commands/smithctl/acl.rb +46 -37
- data/lib/smith/commands/smithctl/commands.rb +1 -1
- data/lib/smith/commands/smithctl/firehose.rb +30 -0
- data/lib/smith/commands/smithctl/pop.rb +39 -32
- data/lib/smith/commands/smithctl/push.rb +70 -51
- data/lib/smith/commands/smithctl/rm.rb +32 -9
- data/lib/smith/commands/smithctl/subscribe.rb +36 -0
- data/lib/smith/commands/smithctl/top.rb +1 -1
- data/lib/smith/exceptions.rb +2 -0
- data/lib/smith/messaging/acl/agency_command.proto +4 -0
- data/lib/smith/messaging/acl/default.rb +8 -1
- data/lib/smith/messaging/amqp_options.rb +2 -2
- data/lib/smith/messaging/message_counter.rb +21 -0
- data/lib/smith/messaging/payload.rb +47 -49
- data/lib/smith/messaging/queue.rb +50 -0
- data/lib/smith/messaging/queue_definition.rb +18 -0
- data/lib/smith/messaging/queue_factory.rb +20 -29
- data/lib/smith/messaging/receiver.rb +211 -173
- data/lib/smith/messaging/requeue.rb +91 -0
- data/lib/smith/messaging/sender.rb +184 -28
- data/lib/smith/messaging/util.rb +72 -0
- data/lib/smith/queue_definitions.rb +11 -0
- data/lib/smith/version.rb +1 -1
- metadata +18 -10
- data/lib/smith/messaging/endpoint.rb +0 -116
data/lib/smith/agent_process.rb
CHANGED
@@ -65,6 +65,13 @@ module Smith
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
+
def add_callback(state, &blk)
|
69
|
+
AgentProcess.state_machine do
|
70
|
+
puts "changing callback"
|
71
|
+
after_transition :on => state, :do => blk
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
68
75
|
# Check to see if the agent is alive.
|
69
76
|
def alive?
|
70
77
|
if self.pid
|
@@ -128,18 +135,20 @@ module Smith
|
|
128
135
|
end
|
129
136
|
|
130
137
|
def self.stop(agent_process)
|
131
|
-
Messaging::Sender.new(agent_process.control_queue_name, :durable => false, :auto_delete => true)
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
138
|
+
Messaging::Sender.new(agent_process.control_queue_name, :durable => false, :auto_delete => true) do |sender|
|
139
|
+
sender.consumer_count do |count|
|
140
|
+
if count > 0
|
141
|
+
sender.publish(ACL::Factory.create(:agent_command, :command => 'stop'))
|
142
|
+
else
|
143
|
+
logger.warn { "Agent is not listening. Setting state to dead." }
|
144
|
+
agent_process.no_process_running
|
145
|
+
end
|
136
146
|
end
|
137
|
-
|
138
|
-
sender.consumers?(callback, errback)
|
139
147
|
end
|
140
148
|
end
|
141
149
|
|
142
150
|
def self.acknowledge_stop(agent_process)
|
151
|
+
pp :stopped
|
143
152
|
end
|
144
153
|
|
145
154
|
def self.kill(agent_process)
|
@@ -172,10 +181,8 @@ module Smith
|
|
172
181
|
|
173
182
|
AgentProcess.state_machine do
|
174
183
|
after_transition :on => :start, :do => AgentProcessObserver.method(:start)
|
175
|
-
after_transition :on => :acknowledge_start, :do => AgentProcessObserver.method(:acknowledge_start)
|
176
184
|
after_transition :on => :stop, :do => AgentProcessObserver.method(:stop)
|
177
185
|
after_transition :on => :kill, :do => AgentProcessObserver.method(:kill)
|
178
|
-
after_transition :on => :acknowledge_stop, :do => AgentProcessObserver.method(:acknowledge_stop)
|
179
186
|
after_transition :on => :not_responding, :do => AgentProcessObserver.method(:reap_agent)
|
180
187
|
end
|
181
188
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Smith
|
4
|
+
module Messaging
|
5
|
+
module AmqpErrors
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def error_message(code, text, &blk)
|
10
|
+
details = error_lookup(code)
|
11
|
+
message = "#{details[:error_class]} exception: #{code} (#{details[:name]}). #{details[:description]}"
|
12
|
+
|
13
|
+
case code
|
14
|
+
when 404
|
15
|
+
diagnosis = "looks like the queue has been deleted."
|
16
|
+
when 406
|
17
|
+
case text
|
18
|
+
when /.*(unknown delivery tag [0-9]+).*/
|
19
|
+
diagnosis = "#{$1} - you've probably already acknowledged the message."
|
20
|
+
end
|
21
|
+
else
|
22
|
+
end
|
23
|
+
blk.call(message, diagnosis)
|
24
|
+
end
|
25
|
+
|
26
|
+
def error_lookup(code)
|
27
|
+
errors[code]
|
28
|
+
end
|
29
|
+
|
30
|
+
def errors
|
31
|
+
@errors ||= {
|
32
|
+
311 => {:name => "content-too-large", :error_class => "Channel", :description => "The client attempted to transfer content larger than the server could accept at the present time. The client may retry at a later time."},
|
33
|
+
313 => {:name => "no-consumers", :error_class => "Channel", :description => "When the exchange cannot deliver to a consumer when the immediate flag is set. As a result of pending data on the queue or the absence of any consumers of the queue."},
|
34
|
+
320 => {:name => "connection-forced", :error_class => "Connection", :description => "An operator intervened to close the Connection for some reason. The client may retry at some later date."},
|
35
|
+
402 => {:name => "invalid-path", :error_class => "Connection", :description => "The client tried to work with an unknown virtual host."},
|
36
|
+
403 => {:name => "access-refused", :error_class => "Channel", :description => "The client attempted to work with a server entity to which it has no access due to security settings."},
|
37
|
+
404 => {:name => "not-found", :error_class => "Channel", :description => "The client attempted to work with a server entity that does not exist."},
|
38
|
+
405 => {:name => "resource-locked", :error_class => "Channel", :description => "The client attempted to work with a server entity to which it has no access because another client is working with it."},
|
39
|
+
406 => {:name => "precondition-failed", :error_class => "Channel", :description => "The client requested a method that was not allowed because some precondition failed."},
|
40
|
+
501 => {:name => "frame-error", :error_class => "Connection", :description => "The sender sent a malformed frame that the recipient could not decode. This strongly implies a programming error in the sending peer."},
|
41
|
+
502 => {:name => "syntax-error", :error_class => "Connection", :description => "The sender sent a frame that contained illegal values for one or more fields. This strongly implies a programming error in the sending peer."},
|
42
|
+
503 => {:name => "command-invalid", :error_class => "Connection", :description => "The client sent an invalid sequence of frames, attempting to perform an operation that was considered invalid by the server. This usually implies a programming error in the client."},
|
43
|
+
504 => {:name => "channel-error", :error_class => "Connection", :description => "The client attempted to work with a Channel that had not been correctly opened. This most likely indicates a fault in the client layer."},
|
44
|
+
505 => {:name => "unexpected-frame", :error_class => "Connection", :description => "The peer sent a frame that was not expected, usually in the context of a content header and body. This strongly indicates a fault in the peer's content processing."},
|
45
|
+
506 => {:name => "resource-error", :error_class => "Connection", :description => "The server could not complete the method because it lacked sufficient resources. This may be due to the client creating too many of some type of entity."},
|
46
|
+
530 => {:name => "not-allowed", :error_class => "Connection", :description => "The client tried to work with some entity in a manner that is prohibited by the server, due to security settings or by some other criteria."},
|
47
|
+
540 => {:name => "not-implemented", :error_class => "Connection", :description => "The client tried to use functionality that is not implemented in the server."},
|
48
|
+
541 => {:name => "internal-error", :error_class => "Connection", :description => "The server could not complete the method because of an internal error. The server may require intervention by an operator in order to resume normal operations."}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -15,39 +15,42 @@ module Smith
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def setup_queues
|
18
|
-
Messaging::Receiver.new(
|
19
|
-
receiver.subscribe do |
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
Command.run(r.payload.command, r.payload.args, :agency => self, :agents => @agent_processes, :responder => responder)
|
26
|
-
rescue Command::UnknownCommandError => e
|
27
|
-
responder.value("Unknown command: #{r.payload.command}")
|
18
|
+
Messaging::Receiver.new(QueueDefinitions::Agency_control) do |receiver|
|
19
|
+
receiver.subscribe do |payload, responder|
|
20
|
+
|
21
|
+
completion = EM::Completion.new.tap do |c|
|
22
|
+
c.completion do |value|
|
23
|
+
pp value
|
24
|
+
responder.reply(Smith::ACL::Factory.create(:agency_command_response, :response => value))
|
28
25
|
end
|
29
26
|
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
Command.run(payload.command, payload.args, :agency => self, :agents => @agent_processes, :responder => completion)
|
30
|
+
rescue Command::UnknownCommandError => e
|
31
|
+
responder.reply("Unknown command: #{payload.command}")
|
32
|
+
end
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
33
|
-
Messaging::Receiver.new(
|
34
|
-
receiver.subscribe do |r|
|
35
|
-
case
|
36
|
+
Messaging::Receiver.new(QueueDefinitions::Agent_lifecycle) do |receiver|
|
37
|
+
receiver.subscribe do |payload, r|
|
38
|
+
case payload.state
|
36
39
|
when 'dead'
|
37
|
-
dead(
|
40
|
+
dead(payload)
|
38
41
|
when 'acknowledge_start'
|
39
|
-
acknowledge_start(
|
42
|
+
acknowledge_start(payload)
|
40
43
|
when 'acknowledge_stop'
|
41
|
-
acknowledge_stop(
|
44
|
+
acknowledge_stop(payload)
|
42
45
|
else
|
43
|
-
logger.warn { "Unknown command received on
|
46
|
+
logger.warn { "Unknown command received on #{QueueDefinitions::Agent_lifecycle.name} queue: #{payload.state}" }
|
44
47
|
end
|
45
48
|
end
|
46
49
|
end
|
47
50
|
|
48
|
-
Messaging::Receiver.new(
|
49
|
-
receiver.subscribe do |r|
|
50
|
-
keep_alive(
|
51
|
+
Messaging::Receiver.new(QueueDefinitions::Agent_keepalive) do |receiver|
|
52
|
+
receiver.subscribe do |payload, r|
|
53
|
+
keep_alive(payload)
|
51
54
|
end
|
52
55
|
end
|
53
56
|
end
|
data/lib/smith/bootstrap.rb
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
$: << File.dirname(__FILE__) + '/..'
|
7
7
|
|
8
8
|
require 'smith'
|
9
|
+
require 'smith/agent'
|
9
10
|
|
10
11
|
module Smith
|
11
12
|
class AgentBootstrap
|
@@ -44,7 +45,6 @@ module Smith
|
|
44
45
|
def start!
|
45
46
|
write_pid_file
|
46
47
|
@agent.run
|
47
|
-
@agent.started
|
48
48
|
end
|
49
49
|
|
50
50
|
# Exceptional shutdown of the agent. Note. Whenever this is
|
@@ -84,8 +84,8 @@ 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(
|
88
|
-
sender.publish(ACL::
|
87
|
+
Messaging::Sender.new(QueueDefinitions::Agent_lifecycle) do |sender|
|
88
|
+
sender.publish(ACL::Factory.create(:agent_lifecycle, :state => 'dead', :name => @agent_name))
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
data/lib/smith/command.rb
CHANGED
@@ -38,9 +38,9 @@ module Smith
|
|
38
38
|
clazz.execute
|
39
39
|
|
40
40
|
rescue Trollop::CommandlineError => e
|
41
|
-
vars[:responder].
|
41
|
+
vars[:responder].succeed(clazz.format_help(:prefix => "Error: #{e.message}.\n"))
|
42
42
|
rescue Trollop::HelpNeeded
|
43
|
-
vars[:responder].
|
43
|
+
vars[:responder].succeed(clazz.format_help)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
data/lib/smith/command_base.rb
CHANGED
@@ -3,26 +3,26 @@ module Smith
|
|
3
3
|
module Commands
|
4
4
|
class Agents < CommandBase
|
5
5
|
def execute
|
6
|
-
responder.
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
6
|
+
responder.succeed(_agents)
|
7
|
+
end
|
8
|
+
|
9
|
+
def _agents
|
10
|
+
# FIXME make sure that if the path doesn't exist don't blow up.
|
11
|
+
separator = (options[:one_column]) ? "\n" : " "
|
12
|
+
|
13
|
+
Smith.agent_paths.inject([]) do |path_acc,path|
|
14
|
+
path_acc.tap do |a|
|
15
|
+
if path.exist?
|
16
|
+
a << path.each_child.inject([]) do |agent_acc,p|
|
17
|
+
agent_acc.tap do |b|
|
18
|
+
b << Extlib::Inflection.camelize(p.basename('.rb')) if p.file? && p.basename('.rb').to_s.end_with?("agent")
|
19
|
+
end
|
20
|
+
end.flatten
|
21
|
+
else
|
22
|
+
return "Agent path doesn't exist: #{path}"
|
21
23
|
end
|
22
|
-
end
|
23
|
-
|
24
|
-
(agent_paths.empty?) ? "" : agent_paths.sort.join(separator)
|
25
|
-
end
|
24
|
+
end
|
25
|
+
end.flatten.sort.join(separator)
|
26
26
|
end
|
27
27
|
|
28
28
|
private
|
@@ -3,10 +3,14 @@ module Smith
|
|
3
3
|
module Commands
|
4
4
|
class Kill < CommandBase
|
5
5
|
def execute
|
6
|
-
|
6
|
+
work = ->(agent_name, iter) do
|
7
7
|
agents[agent_name].kill
|
8
|
+
iter.next
|
8
9
|
end
|
9
|
-
|
10
|
+
|
11
|
+
done = -> { responder.succeed('') }
|
12
|
+
|
13
|
+
EM::Iterator.new(target).each(work, done)
|
10
14
|
end
|
11
15
|
|
12
16
|
private
|
@@ -3,10 +3,8 @@ module Smith
|
|
3
3
|
module Commands
|
4
4
|
class List < CommandBase
|
5
5
|
def execute
|
6
|
-
|
7
|
-
|
8
|
-
(a.empty?) ? nil : format(a, options[:long])
|
9
|
-
end
|
6
|
+
selected_agents = (options[:all]) ? agents : agents.state(:running)
|
7
|
+
responder.succeed((selected_agents.empty?) ? '' : format(selected_agents, options[:long]))
|
10
8
|
end
|
11
9
|
|
12
10
|
private
|
@@ -3,44 +3,43 @@ module Smith
|
|
3
3
|
module Commands
|
4
4
|
class Logger < CommandBase
|
5
5
|
def execute
|
6
|
-
responder.
|
7
|
-
|
8
|
-
|
6
|
+
responder.succeed(_logger)
|
7
|
+
end
|
8
|
+
|
9
|
+
def _logger(&blk)
|
10
|
+
if options[:level].nil?
|
11
|
+
"No log level. You must specify a log level and a target"
|
12
|
+
else
|
13
|
+
case target.first
|
14
|
+
when 'all'
|
15
|
+
agents.state(:running).each do |agent|
|
16
|
+
send_agent_control_message(agent, :command => 'log_level', :options => options[:level])
|
17
|
+
end
|
18
|
+
when 'agency'
|
19
|
+
begin
|
20
|
+
logger.info { "Setting agency log level to: #{options[:level]}" }
|
21
|
+
log_level(options[:level])
|
22
|
+
rescue ArgumentError
|
23
|
+
logger.error { "Incorrect log level: #{options[:level]}" }
|
24
|
+
end
|
25
|
+
when nil
|
26
|
+
"No target. You must specify one of the following: 'agency', 'all' or a list of agents"
|
9
27
|
else
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
send_agent_control_message(agent, :command => 'log_level', :options => options[:level])
|
14
|
-
end
|
15
|
-
nil
|
16
|
-
when 'agency'
|
17
|
-
begin
|
18
|
-
logger.info { "Setting agency log level to: #{options[:level]}" }
|
19
|
-
log_level(options[:level])
|
20
|
-
nil
|
21
|
-
rescue ArgumentError
|
22
|
-
logger.error { "Incorrect log level: #{options[:level]}" }
|
23
|
-
nil
|
24
|
-
end
|
25
|
-
when nil
|
26
|
-
"No target. You must specify one of the following: 'agency', 'all' or a list of agents"
|
27
|
-
else
|
28
|
-
target.each do |agent|
|
29
|
-
if agents[agent].running?
|
30
|
-
send_agent_control_message(agents[agent], :command => 'log_level', :options => [options[:level]])
|
31
|
-
end
|
28
|
+
target.each do |agent|
|
29
|
+
if agents[agent].running?
|
30
|
+
send_agent_control_message(agents[agent], :command => 'log_level', :options => [options[:level]])
|
32
31
|
end
|
33
|
-
nil
|
34
32
|
end
|
35
33
|
end
|
36
34
|
end
|
35
|
+
''
|
37
36
|
end
|
38
37
|
|
39
38
|
private
|
40
39
|
|
41
40
|
def send_agent_control_message(agent, message)
|
42
|
-
Messaging::Sender.new(agent.control_queue_name, :durable => false, :auto_delete => true, :persistent => true, :strict => true)
|
43
|
-
sender.publish(ACL::
|
41
|
+
Messaging::Sender.new(agent.control_queue_name, :durable => false, :auto_delete => true, :persistent => true, :strict => true) do |sender|
|
42
|
+
sender.publish(ACL::Factory.create(:agent_command, message))
|
44
43
|
end
|
45
44
|
end
|
46
45
|
|
@@ -3,11 +3,7 @@ module Smith
|
|
3
3
|
module Commands
|
4
4
|
class Metadata < CommandBase
|
5
5
|
def execute
|
6
|
-
responder.
|
7
|
-
target.inject([]) do |acc,agent_name|
|
8
|
-
acc.tap { |a| a << ["#{agent_name}: #{agents[agent_name].metadata}"] }
|
9
|
-
end.join("\n")
|
10
|
-
end
|
6
|
+
responder.succeed(target.inject([]) { |acc,agent_name| acc.tap { |a| a << ["#{agent_name}: #{agents[agent_name].metadata}"] } }.join("\n"))
|
11
7
|
end
|
12
8
|
|
13
9
|
private
|
@@ -4,24 +4,26 @@ module Smith
|
|
4
4
|
module Commands
|
5
5
|
class ObjectCount < CommandBase
|
6
6
|
def execute
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
if target.size > 1
|
8
|
+
responder.succeed("You can only specify one agent at at time.")
|
9
|
+
else
|
10
|
+
object_count(target.first) do |objects|
|
11
|
+
responder.succeed(objects)
|
12
12
|
end
|
13
|
-
nil
|
14
13
|
end
|
15
14
|
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
def object_count(agent, &blk)
|
17
|
+
if agents[agent].running?
|
18
|
+
Messaging::Sender.new(agent.control_queue_name, :durable => false, :auto_delete => true, :persistent => true, :strict => true) do |sender|
|
19
|
+
sender.on_reply(blk)
|
20
|
+
sender.publish(ACL::Payload.new(:agent_command, :command => 'object_count', :options => [options[:threshold].to_s]))
|
21
|
+
end
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
private
|
26
|
+
|
25
27
|
def options_spec
|
26
28
|
banner "Dump the ruby ObjectSpace stats. This is purely for debuging purposes only."
|
27
29
|
|
@@ -12,17 +12,26 @@ module Smith
|
|
12
12
|
include Common
|
13
13
|
|
14
14
|
def execute
|
15
|
-
Messaging::Sender.new(
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
15
|
+
Messaging::Sender.new(QueueDefinitions::Agency_control) do |sender|
|
16
|
+
@sender = sender
|
17
|
+
|
18
|
+
# agent is a method and as such I cannot pass it into the block.
|
19
|
+
# This just assigns it to a local method making it all work.
|
20
|
+
|
21
|
+
lagents = agents
|
22
|
+
# lagents.each do |agent_name|
|
23
|
+
worker = ->(agent_name, iter) do
|
24
|
+
lagents[agent_name].stop
|
25
|
+
lagents[agent_name].add_callback(:acknowledge_stop) do
|
26
|
+
lagents.invalidate(agent_name)
|
27
|
+
lagents[agent_name].start
|
24
28
|
end
|
29
|
+
iter.next
|
25
30
|
end
|
31
|
+
|
32
|
+
done = -> { responder.succeed('') }
|
33
|
+
|
34
|
+
EM::Iterator.new(target).each(worker, done)
|
26
35
|
end
|
27
36
|
end
|
28
37
|
|