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
@@ -6,79 +6,98 @@ module Smith
|
|
6
6
|
module Commands
|
7
7
|
class Push < CommandBase
|
8
8
|
def execute
|
9
|
+
push do |ret|
|
10
|
+
responder.succeed(ret)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def push(&blk)
|
9
17
|
if target.size == 0
|
10
|
-
|
11
|
-
Smith.stop(true)
|
18
|
+
blk.call("No queue specified. Please specify a queue.")
|
12
19
|
else
|
13
20
|
begin
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
responder.value("--number option cannot be used with the --file option.") if options[:number_given]
|
19
|
-
|
20
|
-
file = Pathname.new(options[:file])
|
21
|
-
if file.exist?
|
22
|
-
file.read
|
23
|
-
else
|
24
|
-
responder.value("File does not exist: #{file.display}")
|
25
|
-
end
|
26
|
-
else
|
27
|
-
responder.value("--number option cannot be used when reading messages from standard in.") if options[:number_given]
|
28
|
-
STDIN.read
|
29
|
-
end
|
30
|
-
|
31
|
-
if messages.nil? || messages && messages.empty?
|
32
|
-
responder.value("Message must be empty.")
|
33
|
-
end
|
34
|
-
|
35
|
-
# This is starting to get a bit messy. The iterator is being used
|
36
|
-
# for two purposes: the first is to send multiple messages, the
|
37
|
-
# second is send the same message multiple times.
|
38
|
-
# TODO Clean this up.
|
39
|
-
|
40
|
-
Messaging::Sender.new(target.first, :auto_delete => options[:dynamic], :persistent => true, :nowait => false, :strict => true).ready do |sender|
|
41
|
-
work = proc do |message,iter|
|
42
|
-
m = (options[:number_given]) ? messages : message
|
43
|
-
|
44
|
-
sender.publish(json_to_payload(m, options[:type])) do
|
45
|
-
iter.next
|
21
|
+
Messaging::Sender.new(target.first, :auto_delete => options[:dynamic], :persistent => true, :nowait => false, :strict => true) do |sender|
|
22
|
+
if options[:reply]
|
23
|
+
timeout = Smith::Messaging::Timeout.new(options[:timeout]) do |message_id|
|
24
|
+
blk.call("Timed out after: #{options[:timeout]} seconds for message: #{options[:message]}: message_id: #{message_id}")
|
46
25
|
end
|
47
|
-
end
|
48
26
|
|
49
|
-
|
50
|
-
|
51
|
-
|
27
|
+
sender.on_reply(:timeout => timeout) { |payload| blk.call(payload.to_hash) }
|
28
|
+
sender.publish(json_to_payload(options[:message], options[:type]))
|
29
|
+
else
|
30
|
+
on_work = ->(message, iter) do
|
31
|
+
sender.publish(json_to_payload(message, options[:type])) do
|
32
|
+
iter.next
|
33
|
+
end
|
34
|
+
end
|
52
35
|
|
53
|
-
|
36
|
+
on_done = -> { blk.call("") }
|
54
37
|
|
55
|
-
|
38
|
+
iterator.each(on_work, on_done)
|
39
|
+
end
|
56
40
|
end
|
57
41
|
rescue MultiJson::DecodeError => e
|
58
|
-
|
59
|
-
Smith.stop
|
42
|
+
blk.call(e)
|
60
43
|
end
|
61
44
|
end
|
62
45
|
end
|
63
46
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
47
|
+
# Return a interator that can iterate over whatever the input is.
|
48
|
+
def iterator
|
49
|
+
case
|
50
|
+
when options[:message_given]
|
51
|
+
if options[:number_given]
|
52
|
+
EM::Iterator.new([options[:message]] * options[:number])
|
53
|
+
else
|
54
|
+
EM::Iterator.new([options[:message]])
|
70
55
|
end
|
56
|
+
when options[:file_given]
|
57
|
+
FileReader.new(options[:file])
|
58
|
+
else
|
59
|
+
raise ArgumentError, "--number option cannot be used when reading messages from standard in." if options[:number_given]
|
60
|
+
FileReader.new(STDIN)
|
71
61
|
end
|
72
62
|
end
|
73
63
|
|
64
|
+
def json_to_payload(data, type)
|
65
|
+
ACL::Factory.create(type, MultiJson.load(data, :symbolize_keys => true))
|
66
|
+
end
|
67
|
+
|
74
68
|
def options_spec
|
75
69
|
banner "Send a message to a queue. The ACL can also be specified."
|
76
70
|
|
77
71
|
opt :type, "message type", :type => :string, :default => 'default', :short => :t
|
78
|
-
opt :message, "the message, as json", :type => :string, :
|
79
|
-
opt :file, "read
|
80
|
-
opt :number, "the number of times to send the message", :type => :integer, :default => 1, :short => :n
|
72
|
+
opt :message, "the message, as json", :type => :string, :short => :m
|
73
|
+
opt :file, "read messages from the named file", :type => :string, :short => :f
|
74
|
+
opt :number, "the number of times to send the message", :type => :integer, :default => 1, :short => :n
|
75
|
+
opt :reply, "set a reply listener.", :short => :r
|
76
|
+
opt :timeout, "timeout when waiting for a reply", :type => :integer, :depends => :reply, :default => Smith.config.agency.timeout
|
81
77
|
opt :dynamic, "send message to a dynamic queue", :type => :boolean, :default => false, :short => :d
|
78
|
+
|
79
|
+
conflicts :reply, :number, :file
|
80
|
+
conflicts :message, :file
|
81
|
+
end
|
82
|
+
|
83
|
+
class FileReader
|
84
|
+
def initialize(file)
|
85
|
+
@file = (file.is_a?(IO)) ? file : File.open(file)
|
86
|
+
end
|
87
|
+
|
88
|
+
def each(on_work, on_completed)
|
89
|
+
on_done = proc do |message|
|
90
|
+
line = @file.readline rescue nil
|
91
|
+
if line
|
92
|
+
class << on_done; alias :next :call; end
|
93
|
+
on_work.call(line, on_done)
|
94
|
+
else
|
95
|
+
on_completed.call
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
EM.next_tick(&on_done)
|
100
|
+
end
|
82
101
|
end
|
83
102
|
end
|
84
103
|
end
|
@@ -5,24 +5,23 @@ module Smith
|
|
5
5
|
def execute
|
6
6
|
case target.size
|
7
7
|
when 0
|
8
|
-
responder.
|
8
|
+
responder.succeed("No queue specified. Please specify a queue.")
|
9
9
|
else
|
10
|
-
|
10
|
+
@on_error = proc do |ch,channel_close|
|
11
11
|
case channel_close.reply_code
|
12
12
|
when 404
|
13
|
-
responder.
|
13
|
+
responder.succeed("No such queue: [#{channel_close.reply_code}]: #{channel_close.reply_text}")
|
14
14
|
when 406
|
15
|
-
responder.
|
15
|
+
responder.succeed("Queue not empty: [#{channel_close.reply_code}]: #{channel_close.reply_text}.")
|
16
16
|
else
|
17
|
-
responder.
|
17
|
+
responder.succeed("Unknown error: [#{channel_close.reply_code}]: #{channel_close.reply_text}")
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
target.each do |queue_name|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
responder.value((options[:verbose]) ? delete_ok.message_count.to_s : nil)
|
22
|
+
delete_queue(queue_name) do |delete_ok|
|
23
|
+
delete_exchange(queue_name) do |delete_ok|
|
24
|
+
responder.succeed((options[:verbose]) ? delete_ok.message_count.to_s : nil)
|
26
25
|
end
|
27
26
|
end
|
28
27
|
end
|
@@ -38,6 +37,30 @@ module Smith
|
|
38
37
|
opt :verbose, "print the number of messages deleted", :short => :v
|
39
38
|
end
|
40
39
|
|
40
|
+
def delete_exchange(exchange_name, &blk)
|
41
|
+
AMQP::Channel.new(Smith.connection) do |channel,ok|
|
42
|
+
channel.on_error(&@on_error)
|
43
|
+
channel.direct("smith.#{exchange_name}", :passive => true) do |exchange|
|
44
|
+
exchange_options = (options[:force]) ? {} : {:if_unused => true}
|
45
|
+
exchange.delete(exchange_options) do |delete_ok|
|
46
|
+
blk.call(delete_ok)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def delete_queue(queue_name, &blk)
|
53
|
+
AMQP::Channel.new(Smith.connection) do |channel,ok|
|
54
|
+
channel.on_error(&@on_error)
|
55
|
+
channel.queue("smith.#{queue_name}", :passive => true) do |queue|
|
56
|
+
queue_options = (options[:force]) ? {} : {:if_unused => true, :if_empty => true}
|
57
|
+
queue.delete(queue_options) do |delete_ok|
|
58
|
+
blk.call(delete_ok)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
41
64
|
def extract_queue(message)
|
42
65
|
match = /.*?'(.*?)'.*$/.match(message) #[1]
|
43
66
|
if match && match[1]
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Smith
|
3
|
+
module Commands
|
4
|
+
class Subscribe < CommandBase
|
5
|
+
def execute
|
6
|
+
Messaging::Receiver.new(target.first, amqp_opts) do |receiver|
|
7
|
+
receiver.subscribe do |payload, r|
|
8
|
+
pp payload
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def amqp_opts
|
16
|
+
{}.tap do |amqp|
|
17
|
+
[:durable, :auto_delete, :header].each do |k|
|
18
|
+
if k == :header && !options[k].nil?
|
19
|
+
amqp[k] = eval(options[k])
|
20
|
+
else
|
21
|
+
amqp[k] = options[k]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def options_spec
|
28
|
+
banner "Subcribe to the named queue and print and received messages to stdout."
|
29
|
+
|
30
|
+
opt :durable, "amqp durable option", :default => false
|
31
|
+
opt :auto_delete, "amqp auto-delete option", :default => false
|
32
|
+
opt :header, "amqp headers as json", :type => :string
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -9,7 +9,7 @@ module Smith
|
|
9
9
|
Curses.init_screen()
|
10
10
|
win = Curses::Window.new(Curses.lines, Curses.cols, 0, 0)
|
11
11
|
|
12
|
-
Messaging::Receiver.new(
|
12
|
+
Messaging::Receiver.new(QueueDefinitions::Agent_stats) do |receiver|
|
13
13
|
receiver.subscribe do |r|
|
14
14
|
payload = r.payload
|
15
15
|
win.setpos(0,0)
|
data/lib/smith/exceptions.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'yajl'
|
4
|
+
|
2
5
|
module Smith
|
3
6
|
module ACL
|
4
7
|
|
@@ -29,11 +32,15 @@ module Smith
|
|
29
32
|
@message.to_s
|
30
33
|
end
|
31
34
|
|
35
|
+
def to_hash
|
36
|
+
@message && @message.to_hash
|
37
|
+
end
|
38
|
+
|
32
39
|
def inspect
|
33
40
|
"<#{self.class.to_s}> -> #{(self.respond_to?(:to_hash)) ? self.to_hash : self.to_s}"
|
34
41
|
end
|
35
42
|
|
36
|
-
def
|
43
|
+
def to_json
|
37
44
|
Yajl.dump(@message)
|
38
45
|
end
|
39
46
|
|
@@ -4,7 +4,7 @@ module Smith
|
|
4
4
|
class AmqpOptions
|
5
5
|
include Logger
|
6
6
|
|
7
|
-
attr_accessor :
|
7
|
+
attr_accessor :routing_key
|
8
8
|
|
9
9
|
def initialize(options={})
|
10
10
|
@options = options
|
@@ -24,7 +24,7 @@ module Smith
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def publish(*extra_opts)
|
27
|
-
merge(Smith.config.amqp.publish.to_hash, {:routing_key =>
|
27
|
+
merge(Smith.config.amqp.publish.to_hash, {:routing_key => routing_key, :persistent => true}, extra_opts)
|
28
28
|
end
|
29
29
|
|
30
30
|
def subscribe(*extra_opts)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Smith
|
3
|
+
module Messaging
|
4
|
+
class MessageCounter
|
5
|
+
|
6
|
+
def initialize(queue_name)
|
7
|
+
@message_counts = Hash.new(0)
|
8
|
+
@queue_name = queue_name
|
9
|
+
end
|
10
|
+
|
11
|
+
# Return the total number of messages sent or received for the named queue.
|
12
|
+
def counter
|
13
|
+
@message_counts[@queue_name]
|
14
|
+
end
|
15
|
+
|
16
|
+
def increment_counter(value=1)
|
17
|
+
@message_counts[@queue_name] += value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,101 +1,99 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
module Smith
|
3
|
-
module ACL
|
4
3
|
|
4
|
+
module ACL
|
5
5
|
module ACLInstanceMethods
|
6
6
|
def inspect
|
7
7
|
"<#{self.class.to_s}> -> #{self.to_hash}"
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
10
|
+
def to_json
|
11
11
|
Yajl.dump(self.to_hash)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
@@acl_classes ||= {:default => Default}
|
15
|
+
class Factory
|
16
|
+
include Logger
|
18
17
|
|
19
|
-
|
18
|
+
@@acl_classes = {:default => Default}
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
logger.
|
20
|
+
class << self
|
21
|
+
def create(type, content=nil, &blk)
|
22
|
+
type = type.to_s
|
23
|
+
|
24
|
+
unless @@acl_classes.include?(type)
|
25
|
+
logger.debug { "Loading ACL: #{type}" }
|
26
|
+
# decorate the ACL class
|
27
|
+
@@acl_classes[type] = clazz(type).send(:include, ACLInstanceMethods)
|
28
|
+
@@acl_classes[type].send(:define_method, :_type) { type }
|
29
|
+
end
|
30
|
+
|
31
|
+
if blk
|
32
|
+
if content.nil?
|
33
|
+
@@acl_classes[type].new.tap { |m| blk.call(m) }
|
34
|
+
else
|
35
|
+
raise ArgumentError, "You cannot give a content hash and a block."
|
36
|
+
end
|
27
37
|
else
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@@acl_classes[
|
38
|
+
if content.respond_to?(:serialize_to_string)
|
39
|
+
content
|
40
|
+
elsif content.nil?
|
41
|
+
@@acl_classes[type].new
|
42
|
+
else
|
43
|
+
@@acl_classes[type].new(content)
|
32
44
|
end
|
33
45
|
end
|
34
46
|
end
|
47
|
+
|
48
|
+
def clazz(type)
|
49
|
+
type.split(/::/).inject(ACL) do |a,m|
|
50
|
+
a.const_get(Extlib::Inflection.camelize(m))
|
51
|
+
end
|
52
|
+
end
|
35
53
|
end
|
36
54
|
end
|
37
55
|
|
38
56
|
class Payload
|
39
57
|
include Logger
|
40
58
|
|
41
|
-
include ClassMethods
|
42
|
-
extend ClassMethods
|
43
|
-
|
44
59
|
# content can be an existing ACL class.
|
45
|
-
def initialize(
|
46
|
-
if
|
47
|
-
@
|
48
|
-
@content = opts[:from]
|
49
|
-
else
|
50
|
-
@type = type
|
51
|
-
@clazz = content_class(type)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# Add content to the content or get the content from a payload
|
56
|
-
def content(*content, &block)
|
57
|
-
if content.empty?
|
58
|
-
if block.nil?
|
59
|
-
return @content
|
60
|
-
else
|
61
|
-
@content = @clazz.new
|
62
|
-
block.call(@content)
|
63
|
-
end
|
60
|
+
def initialize(acl, opts={})
|
61
|
+
if acl.respond_to?(:serialize_to_string)
|
62
|
+
@acl = acl
|
64
63
|
else
|
65
|
-
|
64
|
+
raise ArgumentError, "ACL does not have a serialize_to_string method."
|
66
65
|
end
|
67
|
-
self
|
68
66
|
end
|
69
67
|
|
70
68
|
# The type of content.
|
71
|
-
def
|
72
|
-
@
|
69
|
+
def _type
|
70
|
+
@acl._type
|
73
71
|
end
|
74
72
|
|
75
73
|
# Returns a hash of the payload.
|
76
74
|
def to_hash
|
77
|
-
@
|
75
|
+
@acl.to_hash
|
78
76
|
end
|
79
77
|
|
80
78
|
# Encode the content, returning the encoded data.
|
81
79
|
def encode
|
82
|
-
@
|
80
|
+
@acl.serialize_to_string
|
83
81
|
end
|
84
82
|
|
85
83
|
# Returns true if the payload has all its required fields set.
|
86
84
|
def initialized?
|
87
|
-
raise RuntimeError, "You probably forgot to call #content or give the :from option when instantiating the object." if @
|
88
|
-
@
|
85
|
+
raise RuntimeError, "You probably forgot to call #content or give the :from option when instantiating the object." if @acl.nil?
|
86
|
+
@acl.initialized?
|
89
87
|
end
|
90
88
|
|
91
89
|
# Convert the payload to a pretty string.
|
92
90
|
def to_s
|
93
|
-
@
|
91
|
+
@acl.inspect
|
94
92
|
end
|
95
93
|
|
96
94
|
# Decode the content using the specified decoder.
|
97
|
-
def self.decode(payload,
|
98
|
-
|
95
|
+
def self.decode(payload, type=:default)
|
96
|
+
Factory.create(type).parse_from_string(payload)
|
99
97
|
end
|
100
98
|
end
|
101
99
|
end
|