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
@@ -9,7 +9,12 @@ module Smith
9
9
  include Common
10
10
 
11
11
  def execute
12
+ start do |value|
13
+ responder.succeed(value)
14
+ end
15
+ end
12
16
 
17
+ def start(&blk)
13
18
  #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
14
19
  #!!!!!!!!!!!! See note about target at end of this file !!!!!!!!!!!!!!
15
20
  #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -20,41 +25,45 @@ module Smith
20
25
  begin
21
26
  agents_to_start = agent_group(options[:group])
22
27
  if agents_to_start.empty?
23
- responder.value("There are no agents in group: #{options[:group]}")
24
- return
28
+ blk.call("Agent group is empty. No agents started: #{options[:group]}")
29
+ else
30
+ start_agents(agents_to_start, &blk)
25
31
  end
26
32
  rescue RuntimeError => e
27
- responder.value(e.message)
28
- return
33
+ blk.call(e.message)
29
34
  end
30
35
  else
31
- agents_to_start = target
36
+ start_agents(target, &blk)
32
37
  end
38
+ end
33
39
 
34
- responder.value do
35
- if agents_to_start.empty?
36
- "Start what? No agent specified."
37
- else
38
- agents_to_start.map do |agent|
39
- agents[agent].name = agent
40
- if agents[agent].path
41
- if options[:kill]
42
- agents[agent].kill
43
- end
44
- agents[agent].start
45
- nil
46
- else
47
- "Unknown agent: #{agents[agent].name}".tap do |m|
48
- logger.error { m }
49
- end
40
+ private
41
+
42
+ def start_agents(agents_to_start, &blk)
43
+ if agents_to_start.empty?
44
+ blk.call("Start what? No agent specified.")
45
+ else
46
+ worker = ->(agent_name, iter) do
47
+ agents[agent_name].name = agent_name
48
+ if agents[agent_name].path
49
+ if options[:kill]
50
+ agents[agent_name].kill
50
51
  end
51
- end.compact.join("\n")
52
+ agents[agent_name].start
53
+ iter.return(agent_name)
54
+ else
55
+ iter.return("Unknown agent: #{agents[agent_name].name}")
56
+ end
57
+ end
58
+
59
+ done = ->(started_agents) do
60
+ blk.call(started_agents.compact.join("\n"))
52
61
  end
62
+
63
+ EM::Iterator.new(agents_to_start).map(worker, done)
53
64
  end
54
65
  end
55
66
 
56
- private
57
-
58
67
  def options_spec
59
68
  banner "Start an agent/agents or group of agents."
60
69
 
@@ -70,6 +79,6 @@ end
70
79
  #
71
80
  # Target is a method and if you assign something to it strange things happen --
72
81
  # 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
82
+ # think it's something to do with variable aliasing a method of the same
74
83
  # name. So even though the code isn't being run it gets compiled and that
75
84
  # somehow aliases the method. This looks like a bug in yarv to me.
@@ -9,65 +9,82 @@ module Smith
9
9
  include Common
10
10
 
11
11
  def execute
12
+ if target.first == 'agency' || target.first == 'all'
13
+ send("stop_#{target.first}") { |ret| responder.succeed(ret) }
14
+ else
15
+ stop_agent { |ret| responder.succeed(ret) }
16
+ end
17
+ end
12
18
 
13
- #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
14
- #!!!!!!!!!!!! See note about target at end of this file !!!!!!!!!!!!!!
15
- #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
19
+ private
16
20
 
17
- case target.first
18
- when 'agency'
19
- running_agents = agents.state(:running)
20
- if running_agents.empty?
21
- logger.info { "Agency shutting down." }
21
+ def stop_agency(&blk)
22
+ running_agents = agents.state(:running)
23
+ if running_agents.empty?
24
+ logger.info { "Agency shutting down." }
25
+ blk.call('')
26
+ Smith.stop
27
+ else
28
+ if options[:force]
29
+ blk.call('')
22
30
  Smith.stop
23
- responder.value
24
31
  else
25
32
  logger.warn { "Agents are still running: #{running_agents.map(&:name).join(", ")}." }
26
33
  logger.info { "Agency not shutting down. Use force_stop if you really want to shut it down." }
27
- responder.value("Not shutting down, agents are still running: #{running_agents.map(&:name).join(", ")}.")
34
+ blk.call("Not shutting down, agents are still running: #{running_agents.map(&:name).join(", ")}.")
28
35
  end
29
- when 'all'
30
- agents.state(:running).each do |agent|
31
- agent.stop
32
- end
33
- responder.value
34
- else
35
- # Sort out any groups. If the group option is set it will override
36
- # any other specified agents.
37
- if options[:group]
38
- begin
39
- agents_to_stop = agent_group(options[:group])
40
- if agents_to_stop.empty?
41
- responder.value("There are no agents in group: #{options[:group]}")
42
- return
43
- end
44
- rescue RuntimeError => e
45
- responder.value(e)
46
- return
36
+ end
37
+ end
38
+
39
+ def stop_all(&blk)
40
+ agents.state(:running).each do |agent|
41
+ agent.stop
42
+ end
43
+ blk.call('')
44
+ end
45
+
46
+ def stop_agent(&blk)
47
+
48
+ #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
49
+ #!!!!!!!!!!!! See note about target at end of this file !!!!!!!!!!!!!!
50
+ #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
51
+
52
+ # Sort out any groups. If the group option is set it will override
53
+ # any other specified agents.
54
+ if options[:group]
55
+ begin
56
+ agents_to_stop = agent_group(options[:group])
57
+ if agents_to_stop.empty?
58
+ blk.call("There are no agents in group: #{options[:group]}")
47
59
  end
48
- else
49
- agents_to_stop = target
60
+ rescue RuntimeError => e
61
+ return blk.call(e.message)
50
62
  end
63
+ else
64
+ agents_to_stop = target
65
+ end
51
66
 
52
- ret = agents_to_stop.inject([]) do |acc,agent_name|
53
- acc << if agents[agent_name].running?
54
- agents[agent_name].stop
55
- nil
56
- else
57
- logger.warn { "Agent not running: #{agent_name}" }
58
- agent_name
59
- end
60
- end
61
- responder.value((ret.compact.empty?) ? nil : "Agent(s) not running: #{ret.compact.join(", ")}")
67
+ ret = agents_to_stop.inject([]) do |acc,agent_name|
68
+ acc << stop_if_running(agents[agent_name])
62
69
  end
70
+ blk.call((ret.compact.empty?) ? '' : "Agent(s) not running: #{ret.compact.join(", ")}")
63
71
  end
64
72
 
65
- private
73
+ def stop_if_running(agent)
74
+ if agent.running?
75
+ agent.stop
76
+ nil
77
+ else
78
+ logger.warn { "Agent not running: #{agent.name}" }
79
+ agent.name
80
+ end
81
+ end
66
82
 
67
83
  def options_spec
68
84
  banner "Stop an agent/agents."
69
85
 
70
86
  opt :group, "Stop everything in the specified group", :type => :string, :short => :g
87
+ opt :force, "If stopping the agency and there are agents running stop anyway"
71
88
  end
72
89
  end
73
90
  end
@@ -3,19 +3,19 @@ module Smith
3
3
  module Commands
4
4
  class Version < CommandBase
5
5
  def execute
6
- # FIXME Puts some error handling for when the agency isn't running in a git repo.
6
+ version do |v|
7
+ responder.succeed(v)
8
+ end
9
+ end
10
+
11
+ def version(&blk)
7
12
  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
13
+ # EM.system doesn't do any shell expansion so do it ourselves.
14
+ EM.system("sh -c 'git describe 2> /dev/null'") do |output,status|
15
+ blk.call((status.exitstatus == 0) ? output.strip : 'The agency is not running in a git repo.')
16
16
  end
17
17
  else
18
- responder.value(Smith::VERSION)
18
+ blk.call(Smith::VERSION)
19
19
  end
20
20
  end
21
21
 
@@ -3,15 +3,18 @@ module Smith
3
3
  module Commands
4
4
  module Common
5
5
  def agent_group(group)
6
- Smith.agent_paths.map do |path|
6
+ agents = Smith.agent_paths.map do |path|
7
7
  group_dir = path.join(group)
8
8
  if group_dir.exist? && group_dir.directory?
9
9
  agents = Pathname.glob("#{path.join(group)}/*_agent.rb")
10
- return agents.map {|a| Extlib::Inflection.camelize(a.basename(".rb").to_s)}
10
+ agents.map {|a| Extlib::Inflection.camelize(a.basename(".rb").to_s)}
11
11
  else
12
- raise RuntimeError, "Group does not exist: #{group}"
12
+ nil
13
13
  end
14
- end
14
+ end.uniq
15
+
16
+ raise RuntimeError, "Group does not exist: #{group}" if agents == [nil]
17
+ agents.compact.flatten
15
18
  end
16
19
  end
17
20
  end
@@ -6,52 +6,60 @@ module Smith
6
6
  module Commands
7
7
  class Acl < CommandBase
8
8
  def execute
9
- responder.value do
10
- if options[:show_given]
11
- if target.empty?
12
- "You must supply an ACL file name."
13
- else
14
- target.map do |acl|
15
- acls = Smith.acl_path.inject([]) do |a,path|
16
- a.tap do |acc|
17
- acl_file = path.join("#{acl.snake_case}.proto")
18
- if acl_file.exist?
19
- acc << acl_file
20
- end
21
- end
22
- end
9
+ responder.succeed(_acl)
10
+ end
23
11
 
24
- case acls.length
25
- when 0
26
- "ACL file does not exist."
27
- when 1
28
- if target.length == 1
29
- "\n#{indent_acl(acls.first.read)}\n"
30
- else
31
- "\n#{acl} ->\n#{indent_acl(acls.first.read)}"
32
- end
12
+ def _acl
13
+ if options[:show]
14
+ if target.empty?
15
+ "You must supply an ACL file name."
16
+ else
17
+ target.map do |acl|
18
+ if options[:source_given]
19
+ acls = find_acl(Smith.acl_cache_path, acl, 'pb.rb')
20
+ else
21
+ acls = find_acl(Smith.acl_path, acl, 'proto')
22
+ end
23
+
24
+ case acls.length
25
+ when 0
26
+ "ACL file does not exist."
27
+ when 1
28
+ if target.length == 1
29
+ "\n#{indent_acl(acls.first.read)}\n"
33
30
  else
34
- "There are multiple ACLs with the name: #{target}"
31
+ "\n#{acl} ->\n#{indent_acl(acls.first.read)}"
35
32
  end
36
- end.join("\n")
37
- end
38
- elsif options[:clean_given]
39
- Pathname.glob(Smith.acl_cache_path.join("*.pb.rb")).each {|p| p.unlink}
40
- ""
41
- elsif options[:compile_given]
42
- Pathname.glob(Smith.compile_acls)
43
- ""
44
- else
45
- join_string = (options[:long]) ? "\n" : " "
46
- Pathname.glob(Smith.acl_path.map {|p| "#{p}#{File::SEPARATOR}*"}).map do |p|
47
- p.basename(".proto")
48
- end.sort.join(join_string)
33
+ else
34
+ "There are multiple ACLs with the name: #{target}"
35
+ end
36
+ end.join("\n")
49
37
  end
38
+ elsif options[:clean_given]
39
+ Pathname.glob(Smith.acl_cache_path.join("*.pb.rb")).each {|p| p.unlink}
40
+ ""
41
+ elsif options[:compile_given]
42
+ Pathname.glob(Smith.compile_acls)
43
+ ""
44
+ else
45
+ join_string = (options[:long]) ? "\n" : " "
46
+ Pathname.glob(Smith.acl_path.map {|p| "#{p}#{File::SEPARATOR}*"}).map do |p|
47
+ p.basename(".proto")
48
+ end.sort.join(join_string)
50
49
  end
51
50
  end
52
51
 
53
52
  private
54
53
 
54
+ def find_acl(path, acl, ext)
55
+ [path].flatten.inject([]) do |a,path|
56
+ a.tap do |acc|
57
+ acl_file = path.join("#{acl.snake_case}.#{ext}")
58
+ acc << acl_file if acl_file.exist?
59
+ end
60
+ end
61
+ end
62
+
55
63
  def indent_acl(acl)
56
64
  acl.split("\n").map { |l| l.sub(/^/, " ") }.join("\n")
57
65
  end
@@ -61,6 +69,7 @@ module Smith
61
69
 
62
70
  opt :long, "format the listing", :short => :l
63
71
  opt :show, "show the contents of the acl file", :short => :s
72
+ opt :source, "show the contents of the generated acl file", :depends => :show
64
73
  opt :clean, "remove all compiled acls", :short => :none
65
74
  opt :compile, "compile all acls", :short => :none
66
75
  end
@@ -4,7 +4,7 @@ module Smith
4
4
  class Commands < CommandBase
5
5
  def execute
6
6
  commands = (target.empty?) ? Command.commands : target
7
- responder.value(format(commands))
7
+ responder.succeed(format(commands))
8
8
  end
9
9
 
10
10
  def format(commands)
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Smith
4
+ module Commands
5
+ class Firehose < CommandBase
6
+ def execute
7
+ queue_name = target.first
8
+ AMQP::Channel.new(Smith.connection) do |channel,ok|
9
+ channel.topic('amq.rabbitmq.trace', :durable => true) do |exchange|
10
+ channel.queue('smith.firehose', :durable => true) do |queue|
11
+ queue.bind(exchange, :routing_key => "publish.#{Smith.config.smith.namespace}.#{queue_name}").subscribe do |m,p|
12
+ message = ACL::Payload.decode(p, m.headers['properties']['type'])
13
+ puts (options[:json_given]) ? message.to_json : message.inspect
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ def options_spec
21
+ b = ["Listens to the rabbitmq firehose for the named queue and prints decoded message to stdout.",
22
+ " Be warned it can produce vast amounts of outpu.\n",
23
+ " You _must_ run 'rabbitmqctl trace_on' for this to work."]
24
+ banner b.join("\n")
25
+
26
+ opt :json , "return the JSON representation of the message", :short => :j
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,60 +1,67 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  require 'yajl'
3
3
 
4
+ require 'smith/messaging/queue'
5
+
4
6
  module Smith
5
7
  module Commands
6
8
  class Pop < CommandBase
7
9
  def execute
10
+ pop
11
+ end
12
+
13
+ def pop
8
14
  case target.size
9
15
  when 0
10
- responder.value("No queue specified. Please specify a queue.")
16
+ "No queue specified. Please specify a queue."
11
17
  when 1
12
18
 
13
19
  queue = target.first
14
20
 
15
- # What to do if there is a channel error.
16
- Smith.on_error do |ch,channel_close|
17
- case channel_close.reply_code
18
- when 404
19
- responder.value("Queue does not exist: #{queue}")
20
- else
21
- responder.value("Unknown error: #{channel_close.reply_text}")
22
- end
23
- end
21
+ Messaging::Queue.number_of_messages(queue) do |queue_length|
24
22
 
25
- Messaging::Receiver.new(queue, :auto_ack => false, :passive => true).ready do |receiver|
26
- callback = proc do
27
- work = proc do |acc,n,iter|
28
- receiver.pop do |r|
29
- if options[:remove]
30
- r.ack
31
- else
32
- r.reject(:requeue => true)
33
- end
23
+ # Number of messages on the queue.
24
+ number_to_remove = (options[:number] > queue_length) ? queue_length : options[:number]
25
+
26
+ Messaging::Receiver.new(queue, :auto_ack => false, :prefetch => number_to_remove, :passive => true) do |receiver|
34
27
 
35
- acc[:result] << print_message(r.payload) if options[:print]
36
- acc[:count] += 1
28
+ receiver.on_error do |ch,channel_close|
29
+ case channel_close.reply_code
30
+ when 404
31
+ responder.succeed("Queue does not exist: #{queue}")
32
+ else
33
+ responder.succeed("Unknown error: #{channel_close.reply_text}")
34
+ end
35
+ end
37
36
 
37
+ worker = proc do |acc, n, iter|
38
+ receiver.pop do |payload,r|
39
+ if payload
40
+ acc[:result] << print_message(payload) if options[:print]
41
+ acc[:count] += 1
42
+
43
+ if n == number_to_remove - 1
44
+ if options[:remove]
45
+ r.ack(true)
46
+ else
47
+ r.reject(:requeue => true)
48
+ end
49
+ end
50
+ end
38
51
  iter.return(acc)
39
52
  end
40
53
  end
41
54
 
42
55
  finished = proc do |acc|
43
- responder.value do
44
- logger.debug { "Removing #{acc[:count]} message from #{receiver.queue_name}" }
45
- acc[:result].join("\n")
46
- end
56
+ logger.debug { "Removing #{acc[:count]} message from #{receiver.queue_name}" }
57
+ responder.succeed(acc[:result].join("\n"))
47
58
  end
48
59
 
49
- EM::Iterator.new(0..options[:number] - 1).inject({:count => 0, :result => []}, work, finished)
60
+ EM::Iterator.new(0...number_to_remove).inject({:count => 0, :result => [], :ack => nil}, worker, finished)
50
61
  end
51
-
52
- errback = proc {responder.value(nil)}
53
-
54
- receiver.messages?(callback, errback)
55
62
  end
56
63
  else
57
- responder.value("You can only specify one queue at a time")
64
+ "You can only specify one queue at a time"
58
65
  end
59
66
  end
60
67
 
@@ -62,7 +69,7 @@ module Smith
62
69
 
63
70
  def print_message(message)
64
71
  if options[:json]
65
- message.as_json
72
+ message.to_json
66
73
  else
67
74
  message.inspect
68
75
  end