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
data/bin/agency CHANGED
@@ -1,13 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- encoding: utf-8 -*-
3
3
 
4
- require 'pp'
5
- require 'pathname'
6
-
7
- root_path = Pathname.new(__FILE__).dirname.join('..').expand_path
8
- $:.unshift(root_path.join('lib'))
4
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
9
5
 
6
+ require 'pp'
10
7
  require 'smith'
8
+ require 'smith/daemon'
11
9
  require 'smith/acl_compiler'
12
10
  require 'smith/application/agency'
13
11
 
@@ -16,40 +14,70 @@ module Smith
16
14
 
17
15
  include Logger
18
16
 
19
- def initialize(root)
17
+ AGENCY_NAME = 'agency'
18
+
19
+ def initialize(opts={})
20
20
 
21
- Smith.compile_acls
22
- Smith.load_acls
21
+ @options = opts
22
+ options_check
23
23
 
24
+ @daemon = Daemon.new(AGENCY_NAME, opts[:daemon], opts[:pid_dir])
24
25
  @agency = Agency.new(:paths => Smith.agent_paths)
25
26
 
27
+ Smith.shutdown_hook do
28
+ puts "Shutting down"
29
+ end
30
+
26
31
  # Setup signal handlers to clean up.
27
32
  %w{TERM INT QUIT}.each do |sig|
28
- trap sig, proc { puts "Shutting down"; @agency.stop }
33
+ trap sig, proc { @agency.stop }
29
34
  end
30
35
  end
31
36
 
32
37
  def run
33
- Smith.start do
38
+ if @daemon.running?
39
+ logger.fatal { "The agency is alredy running" }
40
+ else
41
+ @daemon.daemonise
34
42
 
35
- # This block is here so the that the shutdown hook added in
36
- # Smith.start runs last. Yes I know this is leaky but that's how
37
- # it is at the moment.
43
+ Smith.compile_acls
44
+ Smith.load_acls
38
45
 
39
- Smith.shutdown_hook do
40
- # TODO put this as a command line option
41
- if false
42
- @compiler.clear_cache
46
+ Smith.start do
47
+ # This block is here so the that the shutdown hook added in
48
+ # Smith.start runs last. Yes I know this is leaky but that's how
49
+ # it is at the moment.
50
+
51
+ Smith.shutdown_hook do
52
+ @daemon.unlink_pid_file
43
53
  end
54
+
55
+ logger.info { "Starting #{File.basename($0)}" }
56
+ @agency.setup_queues
57
+ @agency.start_monitoring
44
58
  end
59
+ end
60
+ end
45
61
 
46
- logger.info { "Starting #{File.basename($0)}" }
47
- @agency.setup_queues
48
- @agency.start_monitoring
62
+ private
63
+
64
+ def options_check
65
+ if @options[:daemon] && Regexp.new(/stdout|stderr/i).match(Smith.config.logging.appender[:type])
66
+ puts "Logger set to stdout and daemonise is true. Log messages will be sent to /dev/null."
49
67
  end
50
68
  end
51
69
  end
52
70
  end
53
71
 
54
- agency_runner = Smith::AgencyRunner.new(root_path)
72
+ parser = Trollop::Parser.new do
73
+ opt :daemon, "daemonise", :default => false, :short => :d
74
+ opt :pid, "pid file", :type => :string, :short => :p
75
+ end
76
+
77
+ opts = Trollop::with_standard_exception_handling parser do
78
+ #raise Trollop::HelpNeeded if ARGV.size < 2
79
+ parser.parse ARGV
80
+ end
81
+
82
+ agency_runner = Smith::AgencyRunner.new(opts)
55
83
  agency_runner.run
data/bin/smithctl CHANGED
@@ -1,6 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- encoding: utf-8 -*-
3
3
 
4
+ %w{TERM INT QUIT}.each do |sig|
5
+ trap sig, proc { exit }
6
+ end
7
+
8
+
4
9
  $:.unshift(File.dirname(__FILE__) + '/../lib')
5
10
 
6
11
  require 'smith'
@@ -10,58 +15,34 @@ module Smith
10
15
 
11
16
  include Logger
12
17
 
13
- AGENCY_TIMEOUT = 60
14
-
15
- def initialize
16
- log_level(:info)
18
+ def initialize(options={})
19
+ log_level((options[:log_level_given]) ? options[:log_level].to_sym : :info)
20
+ @timeout = (options[:timeout_given]) ? options[:timeout] : Smith.config.agency.timeout
17
21
  end
18
22
 
19
23
  def send_command(command, args, &blk)
20
24
  begin
21
25
  send("#{Command.command_type(command)}_command", command, args, &blk)
22
- rescue Smith::Command::UnkownCommandError => e
23
- puts e.message
24
- Smith.stop(true)
26
+ rescue Smith::Command::UnknownCommandError => e
27
+ blk.call(e.message)
25
28
  end
26
29
  end
27
30
 
28
31
  private
29
32
 
30
33
  def smithctl_command(command, args, &blk)
31
- Smith.channel.on_error do |ch,channel_close|
32
- case channel_close.reply_code
33
- when 404
34
- puts "No such queue: #{extract_queue(channel_close.reply_text)}"
35
- when 406
36
- puts "Queue in use: #{extract_queue(channel_close.reply_text)}"
37
- else
38
- puts channel_close.reply_text
39
- puts channel_close.reply_code
40
- end
41
- end
42
-
43
34
  responder = Messaging::Responder.new
44
35
  responder.callback do |v|
45
- puts v if v
46
- Smith.stop(true)
36
+ blk.call(v)
47
37
  end
48
38
 
49
39
  Command.run(command, args, :responder => responder)
50
40
  end
51
41
 
52
- def extract_queue(message)
53
- match = /.*?'(.*?)'.*$/.match(message) #[1]
54
- if match && match[1]
55
- match[1].sub(/smith\./, '')
56
- else
57
- message
58
- end
59
- end
60
-
61
42
  def agency_command(command, args, &blk)
62
- Messaging::Sender.new('agency.control', :auto_delete => true, :durable => false, :persistent => true, :strict => true).ready do |sender|
43
+ Messaging::Sender.new('agency.control', :auto_delete => false, :durable => false, :persistent => true, :strict => true).ready do |sender|
63
44
 
64
- sender.timeout(AGENCY_TIMEOUT) { puts "Timeout. Is the agency still running"; Smith.stop(true) }
45
+ sender.timeout(@timeout) { blk.call("Timeout. Is the agency still running?") }
65
46
 
66
47
  payload = ACL::Payload.new(:agency_command).content(:command => command, :args => args)
67
48
 
@@ -71,34 +52,56 @@ module Smith
71
52
  end
72
53
  end
73
54
 
74
- errback = proc do puts "Agency not running"
75
- Smith.stop(true)
55
+ errback = proc do
56
+ blk.call("Agency not running.")
76
57
  end
77
58
 
78
59
  sender.consumers?(callback, errback)
79
60
  end
80
61
  end
81
62
  end
82
- end
83
63
 
84
- command = ARGV.shift
85
- args = ARGV
64
+ parser = Trollop::Parser.new do
65
+ version Smith::VERSION
66
+ banner <<-EOS
86
67
 
87
- command || (puts "usage #{File.basename($0)} <command> opts]"; exit 2)
68
+ Command line interface to control Smith.
88
69
 
89
- trap 'INT', proc { (Smith.running?) ? Smith.stop(true) : exit}
70
+ Usage:
71
+ smithctl commands
72
+ smithctl <command> [options]
73
+ EOS
90
74
 
91
- Smith.load_acls
75
+ stop_on Command.commands
76
+ opt :log_level, "Set the log level of smithctl only.", :short => :l, :type => :string
77
+ opt :timeout, "Specify the timeout when communicating with the agency.", :short => :t, :type => :integer, :default => 60
78
+ end
92
79
 
93
- control = Smith::SmithControl.new
80
+ opts = Trollop::with_standard_exception_handling parser do
81
+ raise Trollop::HelpNeeded if ARGV.size < 1
82
+ parser.parse(ARGV).tap do
83
+ unless Command.commands.include?(ARGV.first)
84
+ puts "Unknown command: #{ARGV.first}"
85
+ exit 1
86
+ end
87
+ end
88
+ end
94
89
 
95
- Smith.on_error do
96
- Smith.stop(true)
97
- end
90
+ command = ARGV.shift
91
+ args = ARGV
98
92
 
99
- Smith.start(:quiet => true) do
100
- control.send_command(command, args) do |result|
101
- puts result if result && !result.empty?
102
- Smith.stop(true)
93
+ %w{TERM INT QUIT}.each do |sig|
94
+ trap sig, proc { (Smith.running?) ? Smith.stop(true) : exit}
95
+ end
96
+
97
+ Smith.load_acls
98
+
99
+ control = SmithControl.new(opts)
100
+
101
+ Smith.start do
102
+ control.send_command(command, args) do |result|
103
+ puts result if result && !result.empty?
104
+ Smith.stop(true)
105
+ end
103
106
  end
104
107
  end
data/lib/smith.rb CHANGED
@@ -26,8 +26,8 @@ module Smith
26
26
  @channel
27
27
  end
28
28
 
29
- def on_error=(handler)
30
- @handler = handler
29
+ def environment
30
+ ENV['SMITH_ENV'] || 'development'
31
31
  end
32
32
 
33
33
  def config
@@ -81,8 +81,15 @@ module Smith
81
81
  EM.reactor_running?
82
82
  end
83
83
 
84
- def on_error(&blk)
85
- @on_error = blk
84
+ # Define a channel error handler.
85
+ def on_error(chain=false, &blk)
86
+ # This strikes me as egregiously wrong but I don't know how to
87
+ # overwrite an already existing handler.
88
+ if chain
89
+ Smith.channel.callbacks[:error] << blk
90
+ else
91
+ Smith.channel.callbacks[:error] = [blk]
92
+ end
86
93
  end
87
94
 
88
95
  def start(opts={}, &block)
@@ -134,19 +141,14 @@ module Smith
134
141
  AMQP::Channel.new(connection) do |channel,ok|
135
142
  @channel = channel
136
143
  # Set up QOS. If you do not do this then the subscribe in receive_message
137
- # will get overwelmd and the whole thing will collapse in on itself.
144
+ # will get overwhelmed and the whole thing will collapse in on itself.
138
145
  channel.prefetch(1)
139
146
 
140
- if @on_error
141
- channel.on_error(&@on_error)
142
- else
143
- # Log the error and stop the agency when there are channel errors.
144
- # TODO Add recovery instead of stopping the agency.
145
- channel.on_error do |ch,channel_close|
146
- logger.fatal { "Channel level exception: #{channel_close.reply_text}. Class id: #{channel_close.class_id}, Method id: #{channel_close.method_id}, Status code : #{channel_close.reply_code}" }
147
- logger.fatal { "Agency is exiting" }
148
- Smith.stop(true)
149
- end
147
+ # Set up a default handler.
148
+ on_error do |ch,channel_close|
149
+ logger.fatal { "Channel level exception: #{channel_close.reply_code}: #{channel_close.reply_text}" }
150
+ logger.fatal { "Exiting" }
151
+ Smith.stop(true)
150
152
  end
151
153
 
152
154
  # Set up auto-recovery. This will ensure that the AMQP gem reconnects each
@@ -198,7 +200,7 @@ module Smith
198
200
  # Only display the following settings.
199
201
  s = settings.select { |k,v| [:user, :pass, :vhost, :host].include?(k) }
200
202
 
201
- logger.fatal { "Authenticaton failure." }
203
+ logger.fatal { "Authentication failure." }
202
204
  logger.info { "Details:" }
203
205
  s.each do |k,v|
204
206
  logger.info { " Setting: %-7s%s" % [k, v] }
@@ -227,12 +229,14 @@ require_relative 'smith/agent_process'
227
229
  require_relative 'smith/agent_monitoring'
228
230
  require_relative 'smith/command'
229
231
  require_relative 'smith/command_base'
232
+ require_relative 'smith/exceptions'
233
+ require_relative 'smith/version'
234
+
230
235
  require_relative 'smith/messaging/amqp_options'
231
236
  require_relative 'smith/messaging/queue_factory'
232
237
  require_relative 'smith/messaging/payload'
233
238
  require_relative 'smith/messaging/acl/default'
234
239
  require_relative 'smith/messaging/endpoint'
235
- require_relative 'smith/messaging/exceptions'
236
240
  require_relative 'smith/messaging/responder'
237
241
  require_relative 'smith/messaging/receiver'
238
242
  require_relative 'smith/messaging/sender'
@@ -19,10 +19,12 @@ module Smith
19
19
  @cache_path = Smith.acl_cache_path
20
20
  end
21
21
 
22
- # Compile any protocol buffer files. This checks the timestamp
23
- # to see if the file needs compiling.
22
+ # Compile any protocol buffer files. This checks the timestamp to see if
23
+ # the file needs compiling. This is done in a subprocess to stop the agency
24
+ # from dying. I think it's a problem with the class being loaded twice but
25
+ # I don't actually know that so this could be considered a bit brute force.
24
26
  def compile
25
- fork do
27
+ Process.fork do
26
28
  logger.debug { "Protocol buffer cache path: #{@cache_path}" }
27
29
  Smith.acl_path.each do |path|
28
30
  results = {}
@@ -35,6 +37,7 @@ module Smith
35
37
  end
36
38
  end
37
39
  end
40
+ Process.wait
38
41
  @cache_path
39
42
  end
40
43
 
data/lib/smith/agent.rb CHANGED
@@ -5,8 +5,6 @@ module Smith
5
5
 
6
6
  include Logger
7
7
 
8
- @@agent_options = Smith.config.agent
9
-
10
8
  attr_reader :factory, :name, :pid
11
9
 
12
10
  def initialize(options={})
@@ -36,7 +34,7 @@ module Smith
36
34
  start_keep_alive
37
35
  end
38
36
 
39
- # Overide this method to implement your own agent. You can use task but this may
37
+ # Override this method to implement your own agent. You can use task but this may
40
38
  # go away in the future. This method must not block.
41
39
  def run
42
40
  raise ArgumentError, "You need to call Agent.task(&block)" if @@task.nil?
@@ -90,19 +88,10 @@ module Smith
90
88
  # Options supported:
91
89
  # :monitor, the agency will monitor the agent & if dies restart.
92
90
  # :singleton, only every have one agent. If this is set to false
93
- # multiple agents are allow.
91
+ # multiple agents are allowed.
94
92
  def options(opts)
95
- opts.each { |k,v| merge_options(k, v) }
96
- end
97
-
98
- def merge_options(option, value)
99
- if @@agent_options[option]
100
- @@agent_options[option] = value
101
- else
102
- raise ArgumentError, "Unknown option: #{option}"
103
- end
93
+ Smith.config.agent._merge!(opts)
104
94
  end
105
- private :merge_options
106
95
  end
107
96
 
108
97
  protected
@@ -143,32 +132,32 @@ module Smith
143
132
  payload = ACL::Payload.new(:agent_stats).content do |p|
144
133
  p.agent_name = self.name
145
134
  p.pid = self.pid
146
- p.rss = (File.read("/proc/#{pid}/statm").split[1].to_i * 4) / 1024 # This assums the page size is 4K & is MB
135
+ p.rss = (File.read("/proc/#{pid}/statm").split[1].to_i * 4) / 1024 # This assumes the page size is 4K & is MB
147
136
  p.up_time = (Time.now - @start_time).to_i
148
137
  factory.each_queue do |q|
149
- p.queues << ACL::AgentStats::QueueStats.new(:name => q.denomalized_queue_name, :type => q.class.to_s, :length => q.counter)
138
+ p.queues << ACL::AgentStats::QueueStats.new(:name => q.denormalized_queue_name, :type => q.class.to_s, :length => q.counter)
150
139
  end
151
140
  end
152
141
 
153
142
  stats_queue.publish(payload)
154
143
  end
155
144
 
156
- # The errback argument is set to nil so as to suppres the default message.
145
+ # The errback argument is set to nil so as to suppress the default message.
157
146
  stats_queue.consumers?(callback, nil)
158
147
  end
159
148
  end
160
149
  end
161
150
 
162
151
  def acknowledge_start
163
- sender('agent.lifecycle', :auto_delete => true, :durable => false, :dont_cache => true) do |ack_start_queue|
164
- message = {:state => 'acknowledge_start', :pid => $$.to_s, :name => self.class.to_s, :metadata => agent_options[:metadata], :started_at => Time.now.utc.to_i.to_s}
152
+ sender('agent.lifecycle', :auto_delete => false, :durable => false, :dont_cache => true) do |ack_start_queue|
153
+ message = {:state => 'acknowledge_start', :pid => $$, :name => self.class.to_s, :metadata => agent_options[:metadata], :started_at => Time.now.to_i}
165
154
  ack_start_queue.publish(ACL::Payload.new(:agent_lifecycle).content(agent_options.merge(message)))
166
155
  end
167
156
  end
168
157
 
169
158
  def acknowledge_stop(&block)
170
- sender('agent.lifecycle', :auto_delete => true, :durable => false, :dont_cache => true) do |ack_stop_queue|
171
- message = {:state => 'acknowledge_stop', :pid => $$.to_s, :name => self.class.to_s}
159
+ sender('agent.lifecycle', :auto_delete => false, :durable => false, :dont_cache => true) do |ack_stop_queue|
160
+ message = {:state => 'acknowledge_stop', :pid => $$, :name => self.class.to_s}
172
161
  ack_stop_queue.publish(ACL::Payload.new(:agent_lifecycle).content(message), &block)
173
162
  end
174
163
  end
@@ -176,8 +165,8 @@ module Smith
176
165
  def start_keep_alive
177
166
  if agent_options[:monitor]
178
167
  EventMachine::add_periodic_timer(1) do
179
- sender('agent.keepalive', :auto_delete => true, :durable => false, :dont_cache => true) do |keep_alive_queue|
180
- message = {:name => self.class.to_s, :pid => $$.to_s, :time => Time.now.utc.to_i.to_s}
168
+ sender('agent.keepalive', :auto_delete => false, :durable => false, :dont_cache => true) do |keep_alive_queue|
169
+ message = {:name => self.class.to_s, :pid => $$, :time => Time.now.to_i}
181
170
  keep_alive_queue.consumers? do |sender|
182
171
  keep_alive_queue.publish(ACL::Payload.new(:agent_keepalive).content(message))
183
172
  end
@@ -193,7 +182,7 @@ module Smith
193
182
  end
194
183
 
195
184
  def agent_options
196
- @@agent_options._child
185
+ Smith.config.agent._child
197
186
  end
198
187
 
199
188
  def control_queue_name