smith 0.5.7
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.
- data/bin/agency +55 -0
- data/bin/smithctl +102 -0
- data/lib/smith.rb +237 -0
- data/lib/smith/acl_compiler.rb +74 -0
- data/lib/smith/agent.rb +207 -0
- data/lib/smith/agent_cache.rb +40 -0
- data/lib/smith/agent_config.rb +22 -0
- data/lib/smith/agent_monitoring.rb +52 -0
- data/lib/smith/agent_process.rb +181 -0
- data/lib/smith/application/agency.rb +126 -0
- data/lib/smith/bootstrap.rb +153 -0
- data/lib/smith/cache.rb +61 -0
- data/lib/smith/command.rb +128 -0
- data/lib/smith/commands/agency/agents.rb +28 -0
- data/lib/smith/commands/agency/common.rb +18 -0
- data/lib/smith/commands/agency/kill.rb +13 -0
- data/lib/smith/commands/agency/list.rb +65 -0
- data/lib/smith/commands/agency/logger.rb +56 -0
- data/lib/smith/commands/agency/metadata.rb +14 -0
- data/lib/smith/commands/agency/restart.rb +39 -0
- data/lib/smith/commands/agency/start.rb +62 -0
- data/lib/smith/commands/agency/state.rb +14 -0
- data/lib/smith/commands/agency/stop.rb +70 -0
- data/lib/smith/commands/agency/version.rb +23 -0
- data/lib/smith/commands/smithctl/cat.rb +70 -0
- data/lib/smith/commands/smithctl/pop.rb +76 -0
- data/lib/smith/commands/smithctl/rm.rb +36 -0
- data/lib/smith/commands/smithctl/smithctl_version.rb +23 -0
- data/lib/smith/commands/smithctl/top.rb +42 -0
- data/lib/smith/commands/template.rb +9 -0
- data/lib/smith/config.rb +32 -0
- data/lib/smith/logger.rb +91 -0
- data/lib/smith/messaging/acl/agency_command.proto +5 -0
- data/lib/smith/messaging/acl/agent_command.proto +5 -0
- data/lib/smith/messaging/acl/agent_config_request.proto +4 -0
- data/lib/smith/messaging/acl/agent_config_update.proto +5 -0
- data/lib/smith/messaging/acl/agent_keepalive.proto +6 -0
- data/lib/smith/messaging/acl/agent_lifecycle.proto +12 -0
- data/lib/smith/messaging/acl/agent_stats.proto +14 -0
- data/lib/smith/messaging/acl/default.rb +51 -0
- data/lib/smith/messaging/acl/search.proto +9 -0
- data/lib/smith/messaging/amqp_options.rb +55 -0
- data/lib/smith/messaging/endpoint.rb +116 -0
- data/lib/smith/messaging/exceptions.rb +7 -0
- data/lib/smith/messaging/payload.rb +102 -0
- data/lib/smith/messaging/queue_factory.rb +67 -0
- data/lib/smith/messaging/receiver.rb +237 -0
- data/lib/smith/messaging/responder.rb +15 -0
- data/lib/smith/messaging/sender.rb +61 -0
- metadata +239 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'yajl'
|
3
|
+
|
4
|
+
module Smith
|
5
|
+
module Commands
|
6
|
+
class Pop < Command
|
7
|
+
def execute
|
8
|
+
case target.size
|
9
|
+
when 0
|
10
|
+
responder.value("No queue specified. Please specify a queue.")
|
11
|
+
when 1
|
12
|
+
Messaging::Receiver.new(target.shift, :auto_ack => false).ready do |receiver|
|
13
|
+
callback = proc do
|
14
|
+
work = proc do |n,iter|
|
15
|
+
receiver.pop do |r|
|
16
|
+
iter.return(r)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
finished = proc do |result|
|
21
|
+
responder.value do
|
22
|
+
if options[:remove]
|
23
|
+
logger.debug { "Removing #{result.size} message from #{result.first.queue_name}" }
|
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")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
EM::Iterator.new(0..options[:number] - 1).map(work, finished)
|
42
|
+
end
|
43
|
+
|
44
|
+
errback = proc {responder.value(nil)}
|
45
|
+
|
46
|
+
receiver.messages?(callback, errback)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
responder.value("You can only specifiy one queue at a time")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def options_parser
|
54
|
+
Trollop::Parser.new do
|
55
|
+
banner Command.banner('pop-queue')
|
56
|
+
opt :print, "print the message", :short => :p
|
57
|
+
opt :json , "return the JSON representation of the message", :short => :j
|
58
|
+
opt :remove, "remove the message from the queue", :short => :r
|
59
|
+
opt :number, "the number of messages to remove", :default =>1, :short => :n
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def print_message(message)
|
66
|
+
if options[:print]
|
67
|
+
if options[:json]
|
68
|
+
message.as_json
|
69
|
+
else
|
70
|
+
message.inspect
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Smith
|
3
|
+
module Commands
|
4
|
+
class Rm < Command
|
5
|
+
def execute
|
6
|
+
case target.size
|
7
|
+
when 0
|
8
|
+
responder.value("No queue specified. Please specify a queue.")
|
9
|
+
else
|
10
|
+
target.each do |queue_name|
|
11
|
+
Smith.channel.queue("smith.#{queue_name}", :passive => true) do |queue|
|
12
|
+
queue_options = (options[:force]) ? {} : {:if_unused => true, :if_empty => true}
|
13
|
+
queue.delete(queue_options) do |delete_ok|
|
14
|
+
responder.value((options[:verbose]) ? delete_ok.message_count.to_s : nil)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def options_parser
|
22
|
+
Trollop::Parser.new do
|
23
|
+
banner Command.banner('remove-queue')
|
24
|
+
opt :force, "force the removal even if there are messages on the queue", :short => :f
|
25
|
+
opt :verbose, "print the number of messages deleted", :short => :v
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def format_output(message_count)
|
30
|
+
if options[:verbose]
|
31
|
+
responder.value("Queue deleted. #{message_count}s messages deleted.")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Smith
|
3
|
+
module Commands
|
4
|
+
class SmithctlVersion < Command
|
5
|
+
def execute
|
6
|
+
version_file = Smith.root_path.join('VERSION')
|
7
|
+
|
8
|
+
if options[:git] || !version_file.exist?
|
9
|
+
responder.value("#{(`git describe` rescue '').strip}")
|
10
|
+
else
|
11
|
+
responder.value(version_file.read.strip)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def options_parser
|
16
|
+
Trollop::Parser.new do
|
17
|
+
banner Command.banner('smithctl_version')
|
18
|
+
opt :git, "run git describe, assuming git is installed", :short => :g
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'curses'
|
4
|
+
|
5
|
+
module Smith
|
6
|
+
module Commands
|
7
|
+
class Top < Command
|
8
|
+
def execute
|
9
|
+
Curses.init_screen()
|
10
|
+
win = Curses::Window.new(Curses.lines, Curses.cols, 0, 0)
|
11
|
+
|
12
|
+
Smith::Messaging::Receiver.new('agent.stats', :durable => false, :auto_delete => false).ready do |receiver|
|
13
|
+
receiver.subscribe do |r|
|
14
|
+
payload = r.payload
|
15
|
+
win.setpos(0,0)
|
16
|
+
win.addstr("%s %12s %5s %5s" % ["Queue", "Pid", "RSS", "Time"])
|
17
|
+
win.setpos(2,0)
|
18
|
+
win.addstr(format(payload))
|
19
|
+
win.refresh
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def format(payload)
|
25
|
+
s = ""
|
26
|
+
s << "%s %.12s %5s %5s\n\n" % [payload.agent_name, payload.pid, payload.rss, payload.up_time]
|
27
|
+
s << payload.queues.map do |queue|
|
28
|
+
" %-26s %d" % [queue.name, queue.length]
|
29
|
+
end.join("\n")
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def format_queues(queue_stats)
|
34
|
+
queue_stats.inject([]) do |a,queue_stat|
|
35
|
+
a.tap do |acc|
|
36
|
+
acc << "#{queue_stat.name}:[#{queue_stat.length}]"
|
37
|
+
end
|
38
|
+
end.join(";")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/smith/config.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Smith
|
3
|
+
class Config
|
4
|
+
|
5
|
+
@@config = Optimism.new.tap do |o|
|
6
|
+
o.amqp do |a|
|
7
|
+
a.publish do |p|
|
8
|
+
p.ack = true
|
9
|
+
p.headers = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
a.pop.ack = true
|
13
|
+
a.subscribe.ack = true
|
14
|
+
end
|
15
|
+
o.logging do |l|
|
16
|
+
l.trace = false
|
17
|
+
l.level = :debug
|
18
|
+
l.appender = 'Stdout'
|
19
|
+
l.default_pattern = '%d [%5p] %7l - %34c:%-3L - %m\\n'
|
20
|
+
l.default_date_pattern = '%Y/%m/%d %H:%M:%S.%N'
|
21
|
+
end
|
22
|
+
end._merge!(Optimism.require('/etc/smith/smithrc', "#{ENV['HOME']}/.smithrc"))
|
23
|
+
|
24
|
+
# I'm sure there's a better way of doing this ...
|
25
|
+
@@config.agency['acl_cache_path'] = Pathname.new(@@config.agency.cache_path).join('acl')
|
26
|
+
@@config.agency['acl_path'] = "#{Pathname.new(__FILE__).dirname.join('messaging').join('acl')}#{File::PATH_SEPARATOR}#{@@config.agency['acl_path']}"
|
27
|
+
|
28
|
+
def self.get
|
29
|
+
@@config
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/smith/logger.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Smith
|
3
|
+
module Logger
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
|
7
|
+
if !Logging.const_defined?(:MAX_LEVEL_LENGTH)
|
8
|
+
Logging.init([:verbose, :debug, :info, :warn, :error, :fatal])
|
9
|
+
end
|
10
|
+
|
11
|
+
base.class_eval do
|
12
|
+
include Methods
|
13
|
+
extend Methods
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Methods
|
18
|
+
protected
|
19
|
+
|
20
|
+
@@__pattern = Smith::Config.get.logging.default_pattern
|
21
|
+
@@__date_pattern = Smith::Config.get.logging.default_date_pattern
|
22
|
+
@@__level = Smith::Config.get.logging.level
|
23
|
+
@@__trace = Smith::Config.get.logging.trace
|
24
|
+
@@__appender_type = Logging::Appenders.const_get(Smith::Config.get.logging.appender.to_s.to_sym)
|
25
|
+
@@__appender_filename = Smith::Config.get.logging.filename
|
26
|
+
@@__name = 'smith'
|
27
|
+
|
28
|
+
def log_level(level=nil)
|
29
|
+
if level
|
30
|
+
if Logging::LEVELS[level.to_s]
|
31
|
+
@@__level = level
|
32
|
+
@reload = true
|
33
|
+
else
|
34
|
+
raise ArgumentError, "Unknown level: #{level}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
Logging.logger.root.level = @@__level
|
38
|
+
end
|
39
|
+
|
40
|
+
def log_appender(opts={})
|
41
|
+
if @appender.nil? || !opts.empty?
|
42
|
+
@@__name = opts[:name] if opts[:name]
|
43
|
+
@@__appender_type = opts[:class] if opts[:class]
|
44
|
+
@appender = @@__appender_type.send(:new, @@__name, :filename => @@__appender_filename, :layout => log_pattern)
|
45
|
+
@reload = true
|
46
|
+
end
|
47
|
+
Logging.logger.root.appenders = @appender
|
48
|
+
end
|
49
|
+
|
50
|
+
def log_pattern(*pattern)
|
51
|
+
case pattern.size
|
52
|
+
when 1
|
53
|
+
@@__pattern = pattern.shift
|
54
|
+
@reload = true
|
55
|
+
when 2
|
56
|
+
@@__pattern = pattern.shift
|
57
|
+
@@__date_pattern = pattern.shift
|
58
|
+
@reload = true
|
59
|
+
end
|
60
|
+
|
61
|
+
Logging.layouts.pattern({:pattern => @@__pattern, :date_pattern => @@__date_pattern})
|
62
|
+
end
|
63
|
+
|
64
|
+
def log_trace(trace)
|
65
|
+
@@__trace = trace
|
66
|
+
@reload = true
|
67
|
+
end
|
68
|
+
|
69
|
+
def logger
|
70
|
+
__setup if @logger.nil?
|
71
|
+
__reload if @reload
|
72
|
+
@logger
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def __setup
|
78
|
+
self.log_pattern
|
79
|
+
self.log_appender
|
80
|
+
self.log_level
|
81
|
+
@reload = true
|
82
|
+
end
|
83
|
+
|
84
|
+
def __reload
|
85
|
+
@logger = Logging.logger[self || 'main']
|
86
|
+
@logger.trace = @@__trace
|
87
|
+
@reload = false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
package Smith.ACL;
|
2
|
+
|
3
|
+
// This should refactored into two: start and stop.
|
4
|
+
message AgentLifecycle {
|
5
|
+
required string state = 1;
|
6
|
+
required string name = 2;
|
7
|
+
optional string pid = 3;
|
8
|
+
optional bool monitor = 4;
|
9
|
+
optional bool singleton = 5;
|
10
|
+
optional string metadata = 6;
|
11
|
+
optional string started_at = 7;
|
12
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
package Smith.ACL;
|
2
|
+
message AgentStats {
|
3
|
+
message QueueStats {
|
4
|
+
required string name = 1;
|
5
|
+
required string type = 2;
|
6
|
+
required int32 length = 3;
|
7
|
+
}
|
8
|
+
|
9
|
+
required string agent_name = 1;
|
10
|
+
required int32 pid = 2;
|
11
|
+
optional int64 rss = 3;
|
12
|
+
required int64 up_time = 4;
|
13
|
+
repeated QueueStats queues = 5;
|
14
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Smith
|
3
|
+
module ACL
|
4
|
+
|
5
|
+
# Default message. This takes any objuct that can be marshalled. If
|
6
|
+
# no content is passed in on the constructor then an the message is
|
7
|
+
# assigned an empty Hash. method_missing is declared and will update
|
8
|
+
# the hash.
|
9
|
+
class Default
|
10
|
+
|
11
|
+
def initialize(message={})
|
12
|
+
@message = message
|
13
|
+
end
|
14
|
+
|
15
|
+
# Always return true. There is no validation.
|
16
|
+
def initialized?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def serialize_to_string
|
21
|
+
Marshal.dump(@message)
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_from_string(message)
|
25
|
+
Marshal.load(message)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
@message.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
"<#{self.class.to_s}> -> #{(self.respond_to?(:to_hash)) ? self.to_hash : self.to_s}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def as_json
|
37
|
+
Yajl.dump(@message)
|
38
|
+
end
|
39
|
+
|
40
|
+
def method_missing(method, args)
|
41
|
+
match = /(.*?)=$/.match(method.to_s)
|
42
|
+
if match && match[1]
|
43
|
+
index = match[1].to_sym
|
44
|
+
@message[index] = args
|
45
|
+
else
|
46
|
+
raise NoMethodError, "undefined method `#{method}' for #{self}", caller
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Smith
|
3
|
+
module Messaging
|
4
|
+
class AmqpOptions
|
5
|
+
include Logger
|
6
|
+
|
7
|
+
attr_accessor :queue_name
|
8
|
+
|
9
|
+
def initialize(options={})
|
10
|
+
@options = options
|
11
|
+
@options_map = {:strict => {:immediate => true, :mandatory => true}}
|
12
|
+
end
|
13
|
+
|
14
|
+
def exchange(*extra_opts)
|
15
|
+
merge(Smith.config.amqp.exchange._child, @options, extra_opts)
|
16
|
+
end
|
17
|
+
|
18
|
+
def queue(*extra_opts)
|
19
|
+
merge(Smith.config.amqp.queue._child, @options, extra_opts)
|
20
|
+
end
|
21
|
+
|
22
|
+
def pop(*extra_opts)
|
23
|
+
merge(Smith.config.amqp.pop._child, @options, extra_opts)
|
24
|
+
end
|
25
|
+
|
26
|
+
def publish(*extra_opts)
|
27
|
+
merge(Smith.config.amqp.publish._child, {:routing_key => queue_name}, extra_opts)
|
28
|
+
end
|
29
|
+
|
30
|
+
def subscribe(*extra_opts)
|
31
|
+
merge(Smith.config.amqp.subscribe._child, extra_opts)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def expand_options(opts)
|
37
|
+
options = opts.inject({}) do |a,(k,v)|
|
38
|
+
a.tap do |acc|
|
39
|
+
if @options_map.key?(k)
|
40
|
+
acc.merge!(@options_map[k])
|
41
|
+
else
|
42
|
+
acc[k] = v
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def merge(*hashes)
|
49
|
+
hashes.flatten.inject({}) do |acc,h|
|
50
|
+
acc.merge!(expand_options(h))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|