smith 0.5.8 → 0.5.10
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/agency +49 -21
- data/bin/smithctl +51 -48
- data/lib/smith.rb +21 -17
- data/lib/smith/acl_compiler.rb +6 -3
- data/lib/smith/agent.rb +13 -24
- data/lib/smith/agent_monitoring.rb +5 -5
- data/lib/smith/agent_process.rb +4 -4
- data/lib/smith/application/agency.rb +11 -7
- data/lib/smith/bootstrap.rb +4 -4
- data/lib/smith/command.rb +26 -5
- data/lib/smith/command_base.rb +2 -2
- data/lib/smith/commands/agency/list.rb +11 -26
- data/lib/smith/commands/agency/logger.rb +1 -1
- data/lib/smith/commands/agency/restart.rb +1 -1
- data/lib/smith/commands/agency/start.rb +20 -7
- data/lib/smith/commands/agency/stop.rb +23 -10
- data/lib/smith/commands/agency/version.rb +31 -0
- data/lib/smith/commands/smithctl/commands.rb +1 -13
- data/lib/smith/commands/smithctl/pop.rb +34 -28
- data/lib/smith/commands/smithctl/{cat.rb → push.rb} +3 -3
- data/lib/smith/commands/smithctl/rm.rb +20 -0
- data/lib/smith/commands/smithctl/top.rb +1 -1
- data/lib/smith/config.rb +15 -0
- data/lib/smith/daemon.rb +68 -0
- data/lib/smith/{messaging/exceptions.rb → exceptions.rb} +6 -0
- data/lib/smith/logger.rb +2 -3
- data/lib/smith/messaging/acl/agent_keepalive.proto +2 -2
- data/lib/smith/messaging/acl/agent_lifecycle.proto +2 -2
- data/lib/smith/messaging/acl/default.rb +1 -1
- data/lib/smith/messaging/endpoint.rb +5 -5
- data/lib/smith/messaging/receiver.rb +19 -19
- data/lib/smith/messaging/sender.rb +3 -3
- data/lib/smith/version.rb +3 -0
- metadata +52 -41
- data/lib/smith/commands/agency/agency_version.rb +0 -24
- data/lib/smith/commands/agency/state.rb +0 -20
- 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
|
-
|
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
|
-
|
17
|
+
AGENCY_NAME = 'agency'
|
18
|
+
|
19
|
+
def initialize(opts={})
|
20
20
|
|
21
|
-
|
22
|
-
|
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 {
|
33
|
+
trap sig, proc { @agency.stop }
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
32
37
|
def run
|
33
|
-
|
38
|
+
if @daemon.running?
|
39
|
+
logger.fatal { "The agency is alredy running" }
|
40
|
+
else
|
41
|
+
@daemon.daemonise
|
34
42
|
|
35
|
-
|
36
|
-
|
37
|
-
# it is at the moment.
|
43
|
+
Smith.compile_acls
|
44
|
+
Smith.load_acls
|
38
45
|
|
39
|
-
Smith.
|
40
|
-
#
|
41
|
-
|
42
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
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::
|
23
|
-
|
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
|
-
|
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 =>
|
43
|
+
Messaging::Sender.new('agency.control', :auto_delete => false, :durable => false, :persistent => true, :strict => true).ready do |sender|
|
63
44
|
|
64
|
-
sender.timeout(
|
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
|
75
|
-
|
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
|
-
|
85
|
-
|
64
|
+
parser = Trollop::Parser.new do
|
65
|
+
version Smith::VERSION
|
66
|
+
banner <<-EOS
|
86
67
|
|
87
|
-
|
68
|
+
Command line interface to control Smith.
|
88
69
|
|
89
|
-
|
70
|
+
Usage:
|
71
|
+
smithctl commands
|
72
|
+
smithctl <command> [options]
|
73
|
+
EOS
|
90
74
|
|
91
|
-
|
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
|
-
|
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
|
-
|
96
|
-
|
97
|
-
end
|
90
|
+
command = ARGV.shift
|
91
|
+
args = ARGV
|
98
92
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
30
|
-
|
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
|
-
|
85
|
-
|
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
|
144
|
+
# will get overwhelmed and the whole thing will collapse in on itself.
|
138
145
|
channel.prefetch(1)
|
139
146
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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 { "
|
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'
|
data/lib/smith/acl_compiler.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
#
|
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
|
91
|
+
# multiple agents are allowed.
|
94
92
|
def options(opts)
|
95
|
-
|
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
|
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.
|
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
|
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 =>
|
164
|
-
message = {:state => 'acknowledge_start', :pid =>
|
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 =>
|
171
|
-
message = {:state => 'acknowledge_stop', :pid =>
|
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 =>
|
180
|
-
message = {:name => self.class.to_s, :pid =>
|
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
|
-
|
185
|
+
Smith.config.agent._child
|
197
186
|
end
|
198
187
|
|
199
188
|
def control_queue_name
|