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.
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