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.
Files changed (46) hide show
  1. data/bin/smithctl +16 -19
  2. data/lib/smith.rb +25 -41
  3. data/lib/smith/agent.rb +96 -66
  4. data/lib/smith/agent_monitoring.rb +3 -4
  5. data/lib/smith/agent_process.rb +16 -9
  6. data/lib/smith/amqp_errors.rb +53 -0
  7. data/lib/smith/application/agency.rb +23 -20
  8. data/lib/smith/bootstrap.rb +3 -3
  9. data/lib/smith/command.rb +2 -2
  10. data/lib/smith/command_base.rb +4 -0
  11. data/lib/smith/commands/agency/agents.rb +19 -19
  12. data/lib/smith/commands/agency/kill.rb +6 -2
  13. data/lib/smith/commands/agency/list.rb +2 -4
  14. data/lib/smith/commands/agency/logger.rb +27 -28
  15. data/lib/smith/commands/agency/metadata.rb +1 -5
  16. data/lib/smith/commands/agency/object_count.rb +13 -11
  17. data/lib/smith/commands/agency/restart.rb +18 -9
  18. data/lib/smith/commands/agency/start.rb +34 -25
  19. data/lib/smith/commands/agency/stop.rb +58 -41
  20. data/lib/smith/commands/agency/version.rb +10 -10
  21. data/lib/smith/commands/common.rb +7 -4
  22. data/lib/smith/commands/smithctl/acl.rb +46 -37
  23. data/lib/smith/commands/smithctl/commands.rb +1 -1
  24. data/lib/smith/commands/smithctl/firehose.rb +30 -0
  25. data/lib/smith/commands/smithctl/pop.rb +39 -32
  26. data/lib/smith/commands/smithctl/push.rb +70 -51
  27. data/lib/smith/commands/smithctl/rm.rb +32 -9
  28. data/lib/smith/commands/smithctl/subscribe.rb +36 -0
  29. data/lib/smith/commands/smithctl/top.rb +1 -1
  30. data/lib/smith/exceptions.rb +2 -0
  31. data/lib/smith/messaging/acl/agency_command.proto +4 -0
  32. data/lib/smith/messaging/acl/default.rb +8 -1
  33. data/lib/smith/messaging/amqp_options.rb +2 -2
  34. data/lib/smith/messaging/message_counter.rb +21 -0
  35. data/lib/smith/messaging/payload.rb +47 -49
  36. data/lib/smith/messaging/queue.rb +50 -0
  37. data/lib/smith/messaging/queue_definition.rb +18 -0
  38. data/lib/smith/messaging/queue_factory.rb +20 -29
  39. data/lib/smith/messaging/receiver.rb +211 -173
  40. data/lib/smith/messaging/requeue.rb +91 -0
  41. data/lib/smith/messaging/sender.rb +184 -28
  42. data/lib/smith/messaging/util.rb +72 -0
  43. data/lib/smith/queue_definitions.rb +11 -0
  44. data/lib/smith/version.rb +1 -1
  45. metadata +18 -10
  46. data/lib/smith/messaging/endpoint.rb +0 -116
@@ -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).ready do |sender|
132
- callback = proc {|sender| sender.publish(ACL::Payload.new(:agent_command).content(:command => 'stop')) }
133
- errback = proc do
134
- logger.warn { "Agent is not listening. Setting state to dead." }
135
- agent_process.no_process_running
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('agency.control', :auto_delete => false, :durable => false, :strict => true).ready do |receiver|
19
- receiver.subscribe do |r|
20
- r.reply do |responder|
21
- # Add a logger proc to the responder chain.
22
- responder.callback { |ret| logger.debug { ret } if ret && !ret.empty? }
23
-
24
- begin
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('agent.lifecycle', :auto_delete => false, :durable => false).ready do |receiver|
34
- receiver.subscribe do |r|
35
- case r.payload.state
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(r.payload)
40
+ dead(payload)
38
41
  when 'acknowledge_start'
39
- acknowledge_start(r.payload)
42
+ acknowledge_start(payload)
40
43
  when 'acknowledge_stop'
41
- acknowledge_stop(r.payload)
44
+ acknowledge_stop(payload)
42
45
  else
43
- logger.warn { "Unknown command received on agent.lifecycle queue: #{r.payload.state}" }
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('agent.keepalive', :auto_delete => false, :durable => false).ready do |receiver|
49
- receiver.subscribe do |r|
50
- keep_alive(r.payload)
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
@@ -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('agent.lifecycle', :auto_delete => false, :durable => false).ready do |sender|
88
- sender.publish(ACL::Payload.new(:agent_lifecycle).content(:state => 'dead', :name => @agent_name))
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].value(clazz.format_help(:prefix => "Error: #{e.message}.\n"))
41
+ vars[:responder].succeed(clazz.format_help(:prefix => "Error: #{e.message}.\n"))
42
42
  rescue Trollop::HelpNeeded
43
- vars[:responder].value(clazz.format_help)
43
+ vars[:responder].succeed(clazz.format_help)
44
44
  end
45
45
  end
46
46
 
@@ -47,6 +47,10 @@ module Smith
47
47
  @parser.conflicts(*syms)
48
48
  end
49
49
 
50
+ def depends(*syms)
51
+ @parser.depends(*syms)
52
+ end
53
+
50
54
  def options_spec
51
55
  banner "You should really set a proper banner notice for this command."
52
56
  end
@@ -3,26 +3,26 @@ module Smith
3
3
  module Commands
4
4
  class Agents < CommandBase
5
5
  def execute
6
- responder.value do
7
- # FIXME make sure that if the path doesn't exist don't blow up.
8
- agent_paths = Smith.agent_paths.inject([]) do |path_acc,path|
9
- path_acc.tap do |a|
10
- if path.exist?
11
- a << path.each_child.inject([]) do |agent_acc,p|
12
- agent_acc.tap do |b|
13
- b << Extlib::Inflection.camelize(p.basename('.rb')) if p.file? && p.basename('.rb').to_s.end_with?("agent")
14
- end
15
- end.flatten
16
- else
17
- error_message = "Agent path doesn't exist: #{path}"
18
- responder.value(error_message)
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.flatten
23
- separator = (options[:one_column]) ? "\n" : " "
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
- target.each do |agent_name|
6
+ work = ->(agent_name, iter) do
7
7
  agents[agent_name].kill
8
+ iter.next
8
9
  end
9
- responder.value
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
- responder.value do
7
- a = (options[:all]) ? agents : agents.state(:running)
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.value do
7
- if options[:level].nil?
8
- "No log level. You must specify a log level and a target"
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
- case target.first
11
- when 'all'
12
- agents.state(:running).each do |agent|
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).ready do |sender|
43
- sender.publish(ACL::Payload.new(:agent_command).content(message))
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.value do
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
- responder.value do
8
- target.each do |agent|
9
- if agents[agent].running?
10
- send_agent_control_message(agents[agent], :command => 'object_count', :options => [options[:threshold].to_s])
11
- end
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
- private
18
-
19
- def send_agent_control_message(agent, message)
20
- Messaging::Sender.new(agent.control_queue_name, :durable => false, :auto_delete => true, :persistent => true, :strict => true).ready do |sender|
21
- sender.publish(ACL::Payload.new(:agent_command).content(message))
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('agency.control', :auto_delete => false, :durable => false, :strict => true).ready do |sender|
16
- payload = ACL::Payload.new(:agency_command).content(:command => 'stop', :args => target)
17
-
18
- sender.publish_and_receive(payload) do |r|
19
- payload = ACL::Payload.new(:agency_command).content(:command => 'start', :args => target)
20
- EM.add_timer(0.5) do
21
- sender.publish_and_receive(payload) do |r|
22
- responder.value
23
- end
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