smith 0.5.8 → 0.5.10
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/agency +49 -21
- data/bin/smithctl +51 -48
- data/lib/smith.rb +21 -17
- data/lib/smith/acl_compiler.rb +6 -3
- data/lib/smith/agent.rb +13 -24
- data/lib/smith/agent_monitoring.rb +5 -5
- data/lib/smith/agent_process.rb +4 -4
- data/lib/smith/application/agency.rb +11 -7
- data/lib/smith/bootstrap.rb +4 -4
- data/lib/smith/command.rb +26 -5
- data/lib/smith/command_base.rb +2 -2
- data/lib/smith/commands/agency/list.rb +11 -26
- data/lib/smith/commands/agency/logger.rb +1 -1
- data/lib/smith/commands/agency/restart.rb +1 -1
- data/lib/smith/commands/agency/start.rb +20 -7
- data/lib/smith/commands/agency/stop.rb +23 -10
- data/lib/smith/commands/agency/version.rb +31 -0
- data/lib/smith/commands/smithctl/commands.rb +1 -13
- data/lib/smith/commands/smithctl/pop.rb +34 -28
- data/lib/smith/commands/smithctl/{cat.rb → push.rb} +3 -3
- data/lib/smith/commands/smithctl/rm.rb +20 -0
- data/lib/smith/commands/smithctl/top.rb +1 -1
- data/lib/smith/config.rb +15 -0
- data/lib/smith/daemon.rb +68 -0
- data/lib/smith/{messaging/exceptions.rb → exceptions.rb} +6 -0
- data/lib/smith/logger.rb +2 -3
- data/lib/smith/messaging/acl/agent_keepalive.proto +2 -2
- data/lib/smith/messaging/acl/agent_lifecycle.proto +2 -2
- data/lib/smith/messaging/acl/default.rb +1 -1
- data/lib/smith/messaging/endpoint.rb +5 -5
- data/lib/smith/messaging/receiver.rb +19 -19
- data/lib/smith/messaging/sender.rb +3 -3
- data/lib/smith/version.rb +3 -0
- metadata +52 -41
- data/lib/smith/commands/agency/agency_version.rb +0 -24
- data/lib/smith/commands/agency/state.rb +0 -20
- data/lib/smith/commands/smithctl/smithctl_version.rb +0 -22
@@ -3,14 +3,10 @@ module Smith
|
|
3
3
|
module Commands
|
4
4
|
class Commands < CommandBase
|
5
5
|
def execute
|
6
|
-
commands = (target.empty?) ?
|
6
|
+
commands = (target.empty?) ? Command.commands : target
|
7
7
|
responder.value(format(commands))
|
8
8
|
end
|
9
9
|
|
10
|
-
def list_commands(type)
|
11
|
-
list_command_files(type).map {|command| to_command_name(command) }
|
12
|
-
end
|
13
|
-
|
14
10
|
def format(commands)
|
15
11
|
if options[:long]
|
16
12
|
c = instantiate_commands(commands)
|
@@ -30,14 +26,6 @@ module Smith
|
|
30
26
|
|
31
27
|
private
|
32
28
|
|
33
|
-
def list_command_files(type)
|
34
|
-
Pathname.glob(Command.base_path.join(type).join("**/*.rb"))
|
35
|
-
end
|
36
|
-
|
37
|
-
def to_command_name(path)
|
38
|
-
path.basename(".rb").to_s
|
39
|
-
end
|
40
|
-
|
41
29
|
def instantiate_commands(commands)
|
42
30
|
commands.sort.inject({}) do |a, command|
|
43
31
|
a.tap do |acc|
|
@@ -9,36 +9,44 @@ module Smith
|
|
9
9
|
when 0
|
10
10
|
responder.value("No queue specified. Please specify a queue.")
|
11
11
|
when 1
|
12
|
-
|
12
|
+
|
13
|
+
queue = target.first
|
14
|
+
|
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
|
24
|
+
|
25
|
+
Messaging::Receiver.new(queue, :auto_ack => false, :passive => true).ready do |receiver|
|
13
26
|
callback = proc do
|
14
|
-
work = proc do |n,iter|
|
27
|
+
work = proc do |acc,n,iter|
|
15
28
|
receiver.pop do |r|
|
16
|
-
|
29
|
+
if options[:remove]
|
30
|
+
r.ack
|
31
|
+
else
|
32
|
+
r.reject(:requeue => true)
|
33
|
+
end
|
34
|
+
|
35
|
+
acc[:result] << print_message(r.payload) if options[:print]
|
36
|
+
acc[:count] += 1
|
37
|
+
|
38
|
+
iter.return(acc)
|
17
39
|
end
|
18
40
|
end
|
19
41
|
|
20
|
-
finished = proc do |
|
42
|
+
finished = proc do |acc|
|
21
43
|
responder.value do
|
22
|
-
|
23
|
-
|
24
|
-
result.inject([]) do |a,r|
|
25
|
-
a.tap do |acc|
|
26
|
-
r.ack
|
27
|
-
acc << print_message(r.payload)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
else
|
31
|
-
result.inject([]) do |a,r|
|
32
|
-
a.tap do |acc|
|
33
|
-
r.reject(:requeue => true)
|
34
|
-
acc << print_message(r.payload)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end.join("\n")
|
44
|
+
logger.debug { "Removing #{acc[:count]} message from #{receiver.queue_name}" }
|
45
|
+
acc[:result].join("\n")
|
38
46
|
end
|
39
47
|
end
|
40
48
|
|
41
|
-
EM::Iterator.new(0..options[:number] - 1).
|
49
|
+
EM::Iterator.new(0..options[:number] - 1).inject({:count => 0, :result => []}, work, finished)
|
42
50
|
end
|
43
51
|
|
44
52
|
errback = proc {responder.value(nil)}
|
@@ -46,19 +54,17 @@ module Smith
|
|
46
54
|
receiver.messages?(callback, errback)
|
47
55
|
end
|
48
56
|
else
|
49
|
-
responder.value("You can only
|
57
|
+
responder.value("You can only specify one queue at a time")
|
50
58
|
end
|
51
59
|
end
|
52
60
|
|
53
61
|
private
|
54
62
|
|
55
63
|
def print_message(message)
|
56
|
-
if options[:
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
message.inspect
|
61
|
-
end
|
64
|
+
if options[:json]
|
65
|
+
message.as_json
|
66
|
+
else
|
67
|
+
message.inspect
|
62
68
|
end
|
63
69
|
end
|
64
70
|
|
@@ -4,7 +4,7 @@ require 'multi_json'
|
|
4
4
|
|
5
5
|
module Smith
|
6
6
|
module Commands
|
7
|
-
class
|
7
|
+
class Push < CommandBase
|
8
8
|
def execute
|
9
9
|
if target.size == 0
|
10
10
|
responder.value("No queue specified. Please specify a queue.")
|
@@ -23,7 +23,7 @@ module Smith
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
|
26
|
+
Messaging::Sender.new(target.first, :auto_delete => options[:dynamic], :persistent => true, :nowait => false, :strict => true).ready do |sender|
|
27
27
|
|
28
28
|
work = proc do |n,iter|
|
29
29
|
sender.publish(json_to_payload(data, options[:type])) do
|
@@ -48,7 +48,7 @@ module Smith
|
|
48
48
|
private
|
49
49
|
|
50
50
|
def json_to_payload(data, type)
|
51
|
-
|
51
|
+
ACL::Payload.new(type.to_sym).content do |m|
|
52
52
|
MultiJson.load(data, :symbolize_keys => true).each do |k,v|
|
53
53
|
m.send("#{k}=".to_sym, v)
|
54
54
|
end
|
@@ -7,6 +7,17 @@ module Smith
|
|
7
7
|
when 0
|
8
8
|
responder.value("No queue specified. Please specify a queue.")
|
9
9
|
else
|
10
|
+
Smith.on_error do |ch,channel_close|
|
11
|
+
case channel_close.reply_code
|
12
|
+
when 404
|
13
|
+
responder.value("No such queue: #{extract_queue(channel_close.reply_text)}")
|
14
|
+
when 406
|
15
|
+
responder.value("Queue not empty: #{extract_queue(channel_close.reply_text)}. Use -f to force remove")
|
16
|
+
else
|
17
|
+
responder.value("Unknown error: #{channel_close.reply_text}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
10
21
|
target.each do |queue_name|
|
11
22
|
Smith.channel.queue("smith.#{queue_name}", :passive => true) do |queue|
|
12
23
|
queue_options = (options[:force]) ? {} : {:if_unused => true, :if_empty => true}
|
@@ -26,6 +37,15 @@ module Smith
|
|
26
37
|
opt :force, "force the removal even if there are messages on the queue", :short => :f
|
27
38
|
opt :verbose, "print the number of messages deleted", :short => :v
|
28
39
|
end
|
40
|
+
|
41
|
+
def extract_queue(message)
|
42
|
+
match = /.*?'(.*?)'.*$/.match(message) #[1]
|
43
|
+
if match && match[1]
|
44
|
+
match[1].sub(/smith\./, '')
|
45
|
+
else
|
46
|
+
message
|
47
|
+
end
|
48
|
+
end
|
29
49
|
end
|
30
50
|
end
|
31
51
|
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
|
-
|
12
|
+
Messaging::Receiver.new('agent.stats', :durable => false, :auto_delete => false).ready do |receiver|
|
13
13
|
receiver.subscribe do |r|
|
14
14
|
payload = r.payload
|
15
15
|
win.setpos(0,0)
|
data/lib/smith/config.rb
CHANGED
@@ -3,6 +3,16 @@ module Smith
|
|
3
3
|
class Config
|
4
4
|
|
5
5
|
@@config = Optimism.new.tap do |o|
|
6
|
+
o.agency do |a|
|
7
|
+
a.timeout = 30
|
8
|
+
end
|
9
|
+
|
10
|
+
o.agent do |a|
|
11
|
+
a.monitor = false
|
12
|
+
a.singleton = true
|
13
|
+
a.metadata = ''
|
14
|
+
end
|
15
|
+
|
6
16
|
o.amqp do |a|
|
7
17
|
a.publish do |p|
|
8
18
|
p.ack = true
|
@@ -12,6 +22,11 @@ module Smith
|
|
12
22
|
a.pop.ack = true
|
13
23
|
a.subscribe.ack = true
|
14
24
|
end
|
25
|
+
|
26
|
+
o.agency do |a|
|
27
|
+
a.timeout = 30
|
28
|
+
end
|
29
|
+
|
15
30
|
o.logging do |l|
|
16
31
|
l.trace = false
|
17
32
|
l.level = :debug
|
data/lib/smith/daemon.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'daemons/daemonize'
|
4
|
+
require 'daemons/pidfile'
|
5
|
+
|
6
|
+
module Smith
|
7
|
+
class Daemon
|
8
|
+
|
9
|
+
include Logger
|
10
|
+
|
11
|
+
def initialize(name, daemonise, dir=nil)
|
12
|
+
@name = name
|
13
|
+
@daemonise = daemonise
|
14
|
+
@pid = Daemons::PidFile.new(pid_dir(dir), @name)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Daemonise the process if the daemonise option is true, otherwise do nothing.
|
18
|
+
def daemonise
|
19
|
+
unlink_pid_file
|
20
|
+
|
21
|
+
if @daemonise
|
22
|
+
Daemonize::daemonize('/dev/null', @name)
|
23
|
+
else
|
24
|
+
$0 = @name
|
25
|
+
end
|
26
|
+
|
27
|
+
@pid.pid = Process.pid
|
28
|
+
logger.debug { "Pid file: #{@pid.filename}" }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Check to see if the program is running. This checks for the existance
|
32
|
+
# of a pid file and if there is checks to see if the pid exists.
|
33
|
+
def running?
|
34
|
+
pid_files = Daemons::PidFile.find_files(@pid.dir, @name)
|
35
|
+
|
36
|
+
if pid_files.empty?
|
37
|
+
false
|
38
|
+
else
|
39
|
+
pid = File.read(pid_files.first).to_i
|
40
|
+
pid > 0 && Daemons::Pid.running?(pid)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def unlink_pid_file
|
45
|
+
p = Pathname.new(@pid.filename)
|
46
|
+
if p.exist?
|
47
|
+
logger.verbose { "Removing pid file." }
|
48
|
+
p.unlink
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# Get the pid directory. This checks for the command line option,
|
55
|
+
# then the config and finally use the tmp directory.
|
56
|
+
def pid_dir(dir)
|
57
|
+
if dir
|
58
|
+
dir
|
59
|
+
else
|
60
|
+
if Smith.config.agency._has_key?(:pid_dir)
|
61
|
+
Smith.config.agency.pid_dir
|
62
|
+
else
|
63
|
+
Dir.tmpdir
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -1,5 +1,11 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
module Smith
|
3
|
+
class UnknownEnvironmentError < RuntimeError
|
4
|
+
def initialize(message=nil)
|
5
|
+
super("Invalid environment: #{message || Smith.environment}")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
3
9
|
module Messaging
|
4
10
|
class IncompletePayload < RuntimeError; end
|
5
11
|
class IncorrectPayloadType < RuntimeError; end
|
data/lib/smith/logger.rb
CHANGED
@@ -22,9 +22,8 @@ module Smith
|
|
22
22
|
@@__date_pattern = Smith::Config.get.logging.default_date_pattern
|
23
23
|
@@__level = Smith::Config.get.logging.level
|
24
24
|
@@__trace = Smith::Config.get.logging.trace
|
25
|
-
@@__appender = Smith::Config.get.logging.appender._data.
|
26
|
-
|
27
|
-
end
|
25
|
+
@@__appender = Smith::Config.get.logging.appender._data.clone
|
26
|
+
@@__appender[:type] = Logging::Appenders.const_get(Extlib::Inflection.camelize(Smith::Config.get.logging.appender.type))
|
28
27
|
|
29
28
|
def log_level(level=nil)
|
30
29
|
if level
|
@@ -4,9 +4,9 @@ package Smith.ACL;
|
|
4
4
|
message AgentLifecycle {
|
5
5
|
required string state = 1;
|
6
6
|
required string name = 2;
|
7
|
-
optional
|
7
|
+
optional int64 pid = 3;
|
8
8
|
optional bool monitor = 4;
|
9
9
|
optional bool singleton = 5;
|
10
10
|
optional string metadata = 6;
|
11
|
-
optional
|
11
|
+
optional int64 started_at = 7;
|
12
12
|
}
|
@@ -2,7 +2,7 @@
|
|
2
2
|
module Smith
|
3
3
|
module ACL
|
4
4
|
|
5
|
-
# Default message. This takes any
|
5
|
+
# Default message. This takes any object that can be marshalled. If
|
6
6
|
# no content is passed in on the constructor then an the message is
|
7
7
|
# assigned an empty Hash. method_missing is declared and will update
|
8
8
|
# the hash.
|
@@ -4,10 +4,10 @@ module Smith
|
|
4
4
|
class Endpoint
|
5
5
|
include Logger
|
6
6
|
|
7
|
-
attr_accessor :
|
7
|
+
attr_accessor :denormalized_queue_name, :queue_name
|
8
8
|
|
9
9
|
def initialize(queue_name, options)
|
10
|
-
@
|
10
|
+
@denormalized_queue_name = queue_name
|
11
11
|
@queue_name = normalise(queue_name)
|
12
12
|
@message_counts = Hash.new(0)
|
13
13
|
@options = options
|
@@ -22,7 +22,7 @@ module Smith
|
|
22
22
|
logger.error { "Properties: #{metadata.properties}" }
|
23
23
|
end
|
24
24
|
|
25
|
-
logger.verbose { "Creating queue: [queue]:#{
|
25
|
+
logger.verbose { "Creating queue: [queue]:#{denormalized_queue_name} [options]:#{options.queue}" }
|
26
26
|
|
27
27
|
Smith.channel.queue(queue_name, options.queue) do |queue|
|
28
28
|
@queue = queue
|
@@ -50,7 +50,7 @@ module Smith
|
|
50
50
|
@message_counts[queue_name]
|
51
51
|
end
|
52
52
|
|
53
|
-
def messages?(blk=nil, err=proc {logger.debug { "No messages on #{@
|
53
|
+
def messages?(blk=nil, err=proc {logger.debug { "No messages on #{@denormalized_queue_name}" } })
|
54
54
|
number_of_messages do |n|
|
55
55
|
if n > 0
|
56
56
|
if blk.respond_to? :call
|
@@ -64,7 +64,7 @@ module Smith
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
-
def consumers?(blk=nil, err=proc {logger.debug { "Nothing listening on #{@
|
67
|
+
def consumers?(blk=nil, err=proc {logger.debug { "Nothing listening on #{@denormalized_queue_name}" } })
|
68
68
|
number_of_consumers do |n|
|
69
69
|
if n > 0
|
70
70
|
if blk.respond_to? :call
|
@@ -21,7 +21,7 @@ module Smith
|
|
21
21
|
def subscribe(&block)
|
22
22
|
if !@queue.subscribed?
|
23
23
|
opts = options.subscribe
|
24
|
-
logger.verbose { "Subscribing to: [queue]:#{
|
24
|
+
logger.verbose { "Subscribing to: [queue]:#{denormalized_queue_name} [options]:#{opts}" }
|
25
25
|
queue.subscribe(opts) do |metadata,payload|
|
26
26
|
if payload
|
27
27
|
if @payload_type.empty? || @payload_type.include?(metadata.type.to_sym)
|
@@ -33,7 +33,7 @@ module Smith
|
|
33
33
|
raise IncorrectPayloadType, "This queue can only accept the following payload types: #{@payload_type.to_a.to_s}"
|
34
34
|
end
|
35
35
|
else
|
36
|
-
logger.verbose { "Received null message on: #{
|
36
|
+
logger.verbose { "Received null message on: #{denormalized_queue_name} [options]:#{opts}" }
|
37
37
|
end
|
38
38
|
end
|
39
39
|
else
|
@@ -68,8 +68,8 @@ module Smith
|
|
68
68
|
# to auto ack or not. This is because it can get called twice and we don't
|
69
69
|
# want to ack more than once or an error will be thrown.
|
70
70
|
def thread(reply, &block)
|
71
|
-
logger.verbose { "Threads: [queue]: #{
|
72
|
-
logger.verbose { "auto_ack: [queue]: #{
|
71
|
+
logger.verbose { "Threads: [queue]: #{denormalized_queue_name}: #{threading?}" }
|
72
|
+
logger.verbose { "auto_ack: [queue]: #{denormalized_queue_name}: #{auto_ack?}" }
|
73
73
|
if threading?
|
74
74
|
EM.defer do
|
75
75
|
block.call(reply)
|
@@ -81,7 +81,7 @@ module Smith
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
# I'm not terribly happy about this class. It's
|
84
|
+
# I'm not terribly happy about this class. It's publicly visible and it contains
|
85
85
|
# some gross violations of Ruby's protection mechanism. I suspect it's an indication
|
86
86
|
# of a more fundamental design flaw. I will leave it as is for the time being but
|
87
87
|
# this really needs to be reviewed. FIXME review this class.
|
@@ -100,10 +100,10 @@ module Smith
|
|
100
100
|
|
101
101
|
if undecoded_payload
|
102
102
|
@payload = ACL::Payload.decode(undecoded_payload, metadata.type)
|
103
|
-
logger.verbose { "Received content on: [queue]: #{
|
104
|
-
logger.verbose { "Payload content: [queue]: #{
|
103
|
+
logger.verbose { "Received content on: [queue]: #{denormalized_queue_name}." }
|
104
|
+
logger.verbose { "Payload content: [queue]: #{denormalized_queue_name}, [metadata type]: #{metadata.type}, [message]: #{payload.inspect}" }
|
105
105
|
else
|
106
|
-
logger.verbose { "Received nil content on: [queue]: #{
|
106
|
+
logger.verbose { "Received nil content on: [queue]: #{denormalized_queue_name}." }
|
107
107
|
@payload = nil
|
108
108
|
@nil_message = true
|
109
109
|
end
|
@@ -122,7 +122,7 @@ module Smith
|
|
122
122
|
else
|
123
123
|
# Null responder. If a call on the responder is made log a warning. Something is wrong.
|
124
124
|
responder.callback do |return_value|
|
125
|
-
logger.error { "You are responding to a message that has no reply_to on queue: #{
|
125
|
+
logger.error { "You are responding to a message that has no reply_to on queue: #{denormalized_queue_name}." }
|
126
126
|
logger.verbose { "Queue options: #{@metadata.exchange}." }
|
127
127
|
end
|
128
128
|
end
|
@@ -159,8 +159,8 @@ module Smith
|
|
159
159
|
o[:type] = metadata.type
|
160
160
|
end
|
161
161
|
|
162
|
-
logger.verbose { "Requeuing to: #{
|
163
|
-
logger.verbose { "Requeuing to: #{
|
162
|
+
logger.verbose { "Requeuing to: #{denormalized_queue_name}. [options]: #{opts}" }
|
163
|
+
logger.verbose { "Requeuing to: #{denormalized_queue_name}. [message]: #{ACL::Payload.decode(@undecoded_payload, metadata.type)}" }
|
164
164
|
|
165
165
|
@receiver.send(:exchange).publish(@undecoded_payload, opts)
|
166
166
|
end
|
@@ -184,7 +184,7 @@ module Smith
|
|
184
184
|
end
|
185
185
|
|
186
186
|
def queue_name
|
187
|
-
|
187
|
+
denormalized_queue_name
|
188
188
|
end
|
189
189
|
|
190
190
|
private
|
@@ -193,16 +193,16 @@ module Smith
|
|
193
193
|
if current_requeue_number < count
|
194
194
|
method = "#{strategy}_strategy".to_sym
|
195
195
|
if respond_to?(method, true)
|
196
|
-
|
197
|
-
@on_requeue.call(
|
198
|
-
EM.add_timer(
|
199
|
-
block.call(
|
196
|
+
cumulative_delay = send(method, delay)
|
197
|
+
@on_requeue.call(cumulative_delay, current_requeue_number + 1)
|
198
|
+
EM.add_timer(cumulative_delay) do
|
199
|
+
block.call(cumulative_delay, current_requeue_number + 1)
|
200
200
|
end
|
201
201
|
else
|
202
202
|
raise RuntimeError, "Unknown requeue strategy. #{method}"
|
203
203
|
end
|
204
204
|
else
|
205
|
-
@on_requeue_error.call(
|
205
|
+
@on_requeue_error.call(cumulative_delay, current_requeue_number)
|
206
206
|
end
|
207
207
|
end
|
208
208
|
|
@@ -218,8 +218,8 @@ module Smith
|
|
218
218
|
delay * (current_requeue_number + 1)
|
219
219
|
end
|
220
220
|
|
221
|
-
def
|
222
|
-
@receiver.
|
221
|
+
def denormalized_queue_name
|
222
|
+
@receiver.denormalized_queue_name
|
223
223
|
end
|
224
224
|
|
225
225
|
def normalised_queue_name
|