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.
Files changed (50) hide show
  1. data/bin/agency +55 -0
  2. data/bin/smithctl +102 -0
  3. data/lib/smith.rb +237 -0
  4. data/lib/smith/acl_compiler.rb +74 -0
  5. data/lib/smith/agent.rb +207 -0
  6. data/lib/smith/agent_cache.rb +40 -0
  7. data/lib/smith/agent_config.rb +22 -0
  8. data/lib/smith/agent_monitoring.rb +52 -0
  9. data/lib/smith/agent_process.rb +181 -0
  10. data/lib/smith/application/agency.rb +126 -0
  11. data/lib/smith/bootstrap.rb +153 -0
  12. data/lib/smith/cache.rb +61 -0
  13. data/lib/smith/command.rb +128 -0
  14. data/lib/smith/commands/agency/agents.rb +28 -0
  15. data/lib/smith/commands/agency/common.rb +18 -0
  16. data/lib/smith/commands/agency/kill.rb +13 -0
  17. data/lib/smith/commands/agency/list.rb +65 -0
  18. data/lib/smith/commands/agency/logger.rb +56 -0
  19. data/lib/smith/commands/agency/metadata.rb +14 -0
  20. data/lib/smith/commands/agency/restart.rb +39 -0
  21. data/lib/smith/commands/agency/start.rb +62 -0
  22. data/lib/smith/commands/agency/state.rb +14 -0
  23. data/lib/smith/commands/agency/stop.rb +70 -0
  24. data/lib/smith/commands/agency/version.rb +23 -0
  25. data/lib/smith/commands/smithctl/cat.rb +70 -0
  26. data/lib/smith/commands/smithctl/pop.rb +76 -0
  27. data/lib/smith/commands/smithctl/rm.rb +36 -0
  28. data/lib/smith/commands/smithctl/smithctl_version.rb +23 -0
  29. data/lib/smith/commands/smithctl/top.rb +42 -0
  30. data/lib/smith/commands/template.rb +9 -0
  31. data/lib/smith/config.rb +32 -0
  32. data/lib/smith/logger.rb +91 -0
  33. data/lib/smith/messaging/acl/agency_command.proto +5 -0
  34. data/lib/smith/messaging/acl/agent_command.proto +5 -0
  35. data/lib/smith/messaging/acl/agent_config_request.proto +4 -0
  36. data/lib/smith/messaging/acl/agent_config_update.proto +5 -0
  37. data/lib/smith/messaging/acl/agent_keepalive.proto +6 -0
  38. data/lib/smith/messaging/acl/agent_lifecycle.proto +12 -0
  39. data/lib/smith/messaging/acl/agent_stats.proto +14 -0
  40. data/lib/smith/messaging/acl/default.rb +51 -0
  41. data/lib/smith/messaging/acl/search.proto +9 -0
  42. data/lib/smith/messaging/amqp_options.rb +55 -0
  43. data/lib/smith/messaging/endpoint.rb +116 -0
  44. data/lib/smith/messaging/exceptions.rb +7 -0
  45. data/lib/smith/messaging/payload.rb +102 -0
  46. data/lib/smith/messaging/queue_factory.rb +67 -0
  47. data/lib/smith/messaging/receiver.rb +237 -0
  48. data/lib/smith/messaging/responder.rb +15 -0
  49. data/lib/smith/messaging/sender.rb +61 -0
  50. 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
@@ -0,0 +1,9 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Smith
3
+ module Commands
4
+ class Template < Command
5
+ def execute
6
+ end
7
+ end
8
+ end
9
+ end
@@ -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
@@ -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,5 @@
1
+ package Smith.ACL;
2
+ message AgencyCommand {
3
+ required string command = 1;
4
+ repeated string args = 2;
5
+ }
@@ -0,0 +1,5 @@
1
+ package Smith.ACL;
2
+ message AgentCommand {
3
+ required string command = 1;
4
+ repeated string options = 2;
5
+ }
@@ -0,0 +1,4 @@
1
+ package Smith.ACL;
2
+ message AgentConfigRequest {
3
+ required string agent = 1;
4
+ }
@@ -0,0 +1,5 @@
1
+ package Smith.ACL;
2
+ message AgentConfigUpdate {
3
+ required string agent = 1;
4
+ required string value = 2;
5
+ }
@@ -0,0 +1,6 @@
1
+ package Smith.ACL;
2
+ message AgentKeepalive {
3
+ required string name = 1;
4
+ optional string pid = 2;
5
+ optional string time = 3;
6
+ }
@@ -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,9 @@
1
+ package Smith.ACL;
2
+ message SearchResponse {
3
+ message Result {
4
+ required string url = 1;
5
+ optional string title = 2;
6
+ repeated string snippets = 3;
7
+ }
8
+ repeated Result result = 1;
9
+ }
@@ -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