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.
- 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
|