smith 0.5.12 → 0.5.13.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.
- 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
|
|