smith 0.5.8 → 0.5.10

Sign up to get free protection for your applications and to get access to all the features.
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