smith 0.5.8 → 0.5.10

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 (37) hide show
  1. data/bin/agency +49 -21
  2. data/bin/smithctl +51 -48
  3. data/lib/smith.rb +21 -17
  4. data/lib/smith/acl_compiler.rb +6 -3
  5. data/lib/smith/agent.rb +13 -24
  6. data/lib/smith/agent_monitoring.rb +5 -5
  7. data/lib/smith/agent_process.rb +4 -4
  8. data/lib/smith/application/agency.rb +11 -7
  9. data/lib/smith/bootstrap.rb +4 -4
  10. data/lib/smith/command.rb +26 -5
  11. data/lib/smith/command_base.rb +2 -2
  12. data/lib/smith/commands/agency/list.rb +11 -26
  13. data/lib/smith/commands/agency/logger.rb +1 -1
  14. data/lib/smith/commands/agency/restart.rb +1 -1
  15. data/lib/smith/commands/agency/start.rb +20 -7
  16. data/lib/smith/commands/agency/stop.rb +23 -10
  17. data/lib/smith/commands/agency/version.rb +31 -0
  18. data/lib/smith/commands/smithctl/commands.rb +1 -13
  19. data/lib/smith/commands/smithctl/pop.rb +34 -28
  20. data/lib/smith/commands/smithctl/{cat.rb → push.rb} +3 -3
  21. data/lib/smith/commands/smithctl/rm.rb +20 -0
  22. data/lib/smith/commands/smithctl/top.rb +1 -1
  23. data/lib/smith/config.rb +15 -0
  24. data/lib/smith/daemon.rb +68 -0
  25. data/lib/smith/{messaging/exceptions.rb → exceptions.rb} +6 -0
  26. data/lib/smith/logger.rb +2 -3
  27. data/lib/smith/messaging/acl/agent_keepalive.proto +2 -2
  28. data/lib/smith/messaging/acl/agent_lifecycle.proto +2 -2
  29. data/lib/smith/messaging/acl/default.rb +1 -1
  30. data/lib/smith/messaging/endpoint.rb +5 -5
  31. data/lib/smith/messaging/receiver.rb +19 -19
  32. data/lib/smith/messaging/sender.rb +3 -3
  33. data/lib/smith/version.rb +3 -0
  34. metadata +52 -41
  35. data/lib/smith/commands/agency/agency_version.rb +0 -24
  36. data/lib/smith/commands/agency/state.rb +0 -20
  37. 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?) ? list_commands('agency') + list_commands('smithctl') : target
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
- Messaging::Receiver.new(target.shift, :auto_ack => false).ready do |receiver|
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
- iter.return(r)
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 |result|
42
+ finished = proc do |acc|
21
43
  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")
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).map(work, finished)
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 specifiy one queue at a time")
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[:print]
57
- if options[:json]
58
- message.as_json
59
- else
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 Cat < CommandBase
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
- Smith::Messaging::Sender.new(target.first, :auto_delete => options[:dynamic], :persistent => true, :nowait => false, :strict => true).ready do |sender|
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
- Smith::ACL::Payload.new(type.to_sym).content do |m|
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
- Smith::Messaging::Receiver.new('agent.stats', :durable => false, :auto_delete => false).ready do |receiver|
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
@@ -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.tap do |appender|
26
- appender[:type] = Logging::Appenders.const_get(Extlib::Inflection.camelize(appender.delete(:type)))
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
@@ -1,6 +1,6 @@
1
1
  package Smith.ACL;
2
2
  message AgentKeepalive {
3
3
  required string name = 1;
4
- optional string pid = 2;
5
- optional string time = 3;
4
+ optional int64 pid = 2;
5
+ optional int64 time = 3;
6
6
  }
@@ -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 string pid = 3;
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 string started_at = 7;
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 objuct that can be marshalled. If
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 :denomalized_queue_name, :queue_name
7
+ attr_accessor :denormalized_queue_name, :queue_name
8
8
 
9
9
  def initialize(queue_name, options)
10
- @denomalized_queue_name = queue_name
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]:#{denomalized_queue_name} [options]:#{options.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 #{@denomalized_queue_name}" } })
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 #{@denomalized_queue_name}" } })
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]:#{denomalized_queue_name} [options]:#{opts}" }
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: #{denomalized_queue_name} [options]:#{opts}" }
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]: #{denomalized_queue_name}: #{threading?}" }
72
- logger.verbose { "auto_ack: [queue]: #{denomalized_queue_name}: #{auto_ack?}" }
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 publicaly visable and it contains
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]: #{denomalized_queue_name}." }
104
- logger.verbose { "Payload content: [queue]: #{denomalized_queue_name}, [metadata type]: #{metadata.type}, [message]: #{payload.inspect}" }
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]: #{denomalized_queue_name}." }
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: #{denomalized_queue_name}." }
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: #{denomalized_queue_name}. [options]: #{opts}" }
163
- logger.verbose { "Requeuing to: #{denomalized_queue_name}. [message]: #{ACL::Payload.decode(@undecoded_payload, metadata.type)}" }
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
- denomalized_queue_name
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
- cummulative_delay = send(method, delay)
197
- @on_requeue.call(cummulative_delay, current_requeue_number + 1)
198
- EM.add_timer(cummulative_delay) do
199
- block.call(cummulative_delay, current_requeue_number + 1)
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(cummulative_delay, current_requeue_number)
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 denomalized_queue_name
222
- @receiver.denomalized_queue_name
221
+ def denormalized_queue_name
222
+ @receiver.denormalized_queue_name
223
223
  end
224
224
 
225
225
  def normalised_queue_name