god 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +14 -0
- data/Rakefile +42 -26
- data/ext/god/kqueue_handler.c +6 -4
- data/ext/god/netlink_handler.c +6 -6
- data/god.gemspec +127 -151
- data/lib/god.rb +26 -5
- data/lib/god/cli/command.rb +1 -1
- data/lib/god/cli/run.rb +5 -5
- data/lib/god/compat19.rb +36 -0
- data/lib/god/contacts/prowl.rb +77 -0
- data/lib/god/contacts/scout.rb +64 -0
- data/lib/god/driver.rb +5 -0
- data/lib/god/logger.rb +8 -29
- data/lib/god/process.rb +8 -2
- data/lib/god/simple_logger.rb +7 -1
- data/lib/god/socket.rb +13 -2
- data/lib/god/sys_logger.rb +45 -0
- data/lib/god/watch.rb +2 -1
- data/test/configs/contact/contact.god +7 -1
- data/test/configs/task/logs/.placeholder +0 -0
- data/test/helper.rb +2 -1
- data/test/test_behavior.rb +1 -1
- data/test/test_logger.rb +1 -1
- data/test/test_prowl.rb +15 -0
- metadata +6 -27
- data/.gitignore +0 -8
- data/VERSION.yml +0 -5
- data/ideas/execve/execve.c +0 -29
- data/ideas/execve/extconf.rb +0 -11
- data/ideas/execve/go.rb +0 -8
- data/ideas/future.god +0 -82
- data/init/god +0 -42
- data/init/lsb_compliant_god +0 -109
- data/site/images/banner.jpg +0 -0
- data/site/images/bg.gif +0 -0
- data/site/images/bg_grey.gif +0 -0
- data/site/images/bullet.jpg +0 -0
- data/site/images/corner_green.gif +0 -0
- data/site/images/corner_green.psd +0 -0
- data/site/images/corner_pink.gif +0 -0
- data/site/images/god_logo.png +0 -0
- data/site/images/header_bg.gif +0 -0
- data/site/images/header_bg.jpg +0 -0
- data/site/images/red_dot.gif +0 -0
- data/site/images/top_bg.gif +0 -0
- data/site/index.html +0 -563
- data/site/install.html +0 -2
- data/site/javascripts/code_highlighter.js +0 -188
- data/site/javascripts/ruby.js +0 -18
- data/site/stylesheets/layout.css +0 -174
data/lib/god.rb
CHANGED
@@ -63,6 +63,11 @@ begin
|
|
63
63
|
require 'god/contacts/campfire'
|
64
64
|
rescue LoadError
|
65
65
|
end
|
66
|
+
begin
|
67
|
+
require 'god/contacts/prowl'
|
68
|
+
rescue LoadError
|
69
|
+
end
|
70
|
+
|
66
71
|
|
67
72
|
require 'god/socket'
|
68
73
|
require 'god/driver'
|
@@ -145,11 +150,13 @@ class Module
|
|
145
150
|
end
|
146
151
|
|
147
152
|
module God
|
153
|
+
VERSION = '0.9.0'
|
148
154
|
LOG_BUFFER_SIZE_DEFAULT = 100
|
149
155
|
PID_FILE_DIRECTORY_DEFAULTS = ['/var/run/god', '~/.god/pids']
|
150
156
|
DRB_PORT_DEFAULT = 17165
|
151
157
|
DRB_ALLOW_DEFAULT = ['127.0.0.1']
|
152
158
|
LOG_LEVEL_DEFAULT = :info
|
159
|
+
TERMINATE_TIMEOUT_DEFAULT = 10
|
153
160
|
|
154
161
|
class << self
|
155
162
|
# user configurable
|
@@ -161,7 +168,11 @@ module God
|
|
161
168
|
:pid_file_directory,
|
162
169
|
:log_file,
|
163
170
|
:log_level,
|
164
|
-
:use_events
|
171
|
+
:use_events,
|
172
|
+
:terminate_timeout,
|
173
|
+
:socket_user,
|
174
|
+
:socket_group,
|
175
|
+
:socket_perms
|
165
176
|
|
166
177
|
# internal
|
167
178
|
attr_accessor :inited,
|
@@ -184,6 +195,10 @@ module God
|
|
184
195
|
self.log_buffer_size = nil
|
185
196
|
self.pid_file_directory = nil
|
186
197
|
self.log_level = nil
|
198
|
+
self.terminate_timeout = nil
|
199
|
+
self.socket_user = nil
|
200
|
+
self.socket_group = nil
|
201
|
+
self.socket_perms = 0755
|
187
202
|
|
188
203
|
# Initialize internal data.
|
189
204
|
#
|
@@ -205,6 +220,7 @@ module God
|
|
205
220
|
self.port ||= DRB_PORT_DEFAULT
|
206
221
|
self.allow ||= DRB_ALLOW_DEFAULT
|
207
222
|
self.log_level ||= LOG_LEVEL_DEFAULT
|
223
|
+
self.terminate_timeout ||= TERMINATE_TIMEOUT_DEFAULT
|
208
224
|
|
209
225
|
# additional setup
|
210
226
|
self.setup
|
@@ -441,7 +457,7 @@ module God
|
|
441
457
|
end
|
442
458
|
end
|
443
459
|
|
444
|
-
|
460
|
+
terminate_timeout.times do
|
445
461
|
return true unless self.watches.map { |name, w| w.alive? }.any?
|
446
462
|
sleep 1
|
447
463
|
end
|
@@ -592,6 +608,12 @@ module God
|
|
592
608
|
end
|
593
609
|
end
|
594
610
|
|
611
|
+
if God::Logger.syslog
|
612
|
+
LOG.info("Syslog enabled.")
|
613
|
+
else
|
614
|
+
LOG.info("Syslog disabled.")
|
615
|
+
end
|
616
|
+
|
595
617
|
applog(nil, :info, "Using pid file directory: #{self.pid_file_directory}")
|
596
618
|
end
|
597
619
|
|
@@ -602,7 +624,7 @@ module God
|
|
602
624
|
self.internal_init
|
603
625
|
|
604
626
|
# instantiate server
|
605
|
-
self.server = Socket.new(self.port)
|
627
|
+
self.server = Socket.new(self.port, self.socket_user, self.socket_group, self.socket_perms)
|
606
628
|
|
607
629
|
# start monitoring any watches set to autostart
|
608
630
|
self.watches.values.each { |w| w.monitor if w.autostart? }
|
@@ -625,8 +647,7 @@ module God
|
|
625
647
|
end
|
626
648
|
|
627
649
|
def self.version
|
628
|
-
|
629
|
-
"#{yml[:major]}.#{yml[:minor]}.#{yml[:patch]}"
|
650
|
+
God::VERSION
|
630
651
|
end
|
631
652
|
|
632
653
|
# To be called on program exit to start god
|
data/lib/god/cli/command.rb
CHANGED
data/lib/god/cli/run.rb
CHANGED
@@ -12,6 +12,10 @@ module God
|
|
12
12
|
# have at_exit start god
|
13
13
|
$run = true
|
14
14
|
|
15
|
+
if @options[:syslog]
|
16
|
+
require 'god/sys_logger'
|
17
|
+
end
|
18
|
+
|
15
19
|
# run
|
16
20
|
if @options[:daemonize]
|
17
21
|
run_daemonized
|
@@ -36,7 +40,7 @@ module God
|
|
36
40
|
def default_run
|
37
41
|
# make sure we have STDIN/STDOUT redirected immediately
|
38
42
|
setup_logging
|
39
|
-
|
43
|
+
|
40
44
|
# start attached pid watcher if necessary
|
41
45
|
if @options[:attach]
|
42
46
|
self.attach
|
@@ -93,10 +97,6 @@ module God
|
|
93
97
|
God.pid = @options[:pid]
|
94
98
|
end
|
95
99
|
|
96
|
-
unless @options[:syslog]
|
97
|
-
Logger.syslog = false
|
98
|
-
end
|
99
|
-
|
100
100
|
default_run
|
101
101
|
|
102
102
|
unless God::EventHandler.loaded?
|
data/lib/god/compat19.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
require 'monitor'
|
3
|
+
|
4
|
+
|
5
|
+
# Taken from http://redmine.ruby-lang.org/repositories/entry/ruby-19/lib/monitor.rb
|
6
|
+
|
7
|
+
module MonitorMixin
|
8
|
+
class ConditionVariable
|
9
|
+
def wait(timeout = nil)
|
10
|
+
@monitor.__send__(:mon_check_owner)
|
11
|
+
count = @monitor.__send__(:mon_exit_for_cond)
|
12
|
+
begin
|
13
|
+
@cond.wait(@monitor.instance_variable_get("@mon_mutex"), timeout)
|
14
|
+
return true
|
15
|
+
ensure
|
16
|
+
@monitor.__send__(:mon_enter_for_cond, count)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# Taken from http://redmine.ruby-lang.org/repositories/entry/ruby-19/lib/thread.rb
|
24
|
+
|
25
|
+
class ConditionVariable
|
26
|
+
def wait(mutex, timeout=nil)
|
27
|
+
begin
|
28
|
+
# TODO: mutex should not be used
|
29
|
+
@waiters_mutex.synchronize do
|
30
|
+
@waiters.push(Thread.current)
|
31
|
+
end
|
32
|
+
mutex.sleep timeout
|
33
|
+
end
|
34
|
+
self
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# For Prowl notifications you need the 'prowly' gem
|
2
|
+
# (gem install prowly)
|
3
|
+
#
|
4
|
+
# Configure your watches like this:
|
5
|
+
#
|
6
|
+
# God.contact(:prowl) do |c|
|
7
|
+
# c.name = 'georgette'
|
8
|
+
# c.apikey = 'ffffffffffffffffffffffffffffffffffffffff'
|
9
|
+
# c.group = 'developers'
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
#
|
13
|
+
# God.contact(:prowl) do |c|
|
14
|
+
# c.name = 'johnny'
|
15
|
+
# c.apikey = 'ffffffffffffffffffffffffffffffffffffffff'
|
16
|
+
# c.group = 'developers'
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
#
|
20
|
+
# Define a transition for the process running event
|
21
|
+
#
|
22
|
+
# w.transition(:up, :start) do |on|
|
23
|
+
# on.condition(:process_running) do |c|
|
24
|
+
# c.running = true
|
25
|
+
# c.notify = 'developers'
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
|
29
|
+
require 'prowly'
|
30
|
+
|
31
|
+
module God
|
32
|
+
module Contacts
|
33
|
+
class Prowl < Contact
|
34
|
+
|
35
|
+
attr_accessor :apikey
|
36
|
+
|
37
|
+
def valid?
|
38
|
+
valid = true
|
39
|
+
valid &= complain("Attribute 'apikey' must be specified", self) if self.apikey.nil?
|
40
|
+
valid
|
41
|
+
end
|
42
|
+
|
43
|
+
def notify(message, time, priority, category, host)
|
44
|
+
begin
|
45
|
+
result = Prowly.notify do |n|
|
46
|
+
n.apikey = self.apikey
|
47
|
+
n.priority = map_priority(priority.to_i)
|
48
|
+
n.application = category || "God"
|
49
|
+
n.event = "on " + host.to_s
|
50
|
+
n.description = message.to_s + " at " + time.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
if result.succeeded?
|
54
|
+
self.info = "sent prowl notification to #{self.name}"
|
55
|
+
else
|
56
|
+
self.info = "failed to send prowl notification to #{self.name}: #{result.message}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
rescue Object => e
|
60
|
+
self.info = "failed to send prowl notification to #{self.name}: #{e.message}"
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def map_priority(priority)
|
66
|
+
case priority
|
67
|
+
when 1 then Prowly::Notification::Priority::EMERGENCY
|
68
|
+
when 2 then Prowly::Notification::Priority::HIGH
|
69
|
+
when 3 then Prowly::Notification::Priority::NORMAL
|
70
|
+
when 4 then Prowly::Notification::Priority::MODERATE
|
71
|
+
when 5 then Prowly::Notification::Priority::VERY_LOW
|
72
|
+
else Prowly::Notification::Priority::NORMAL
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Configure your Scout client key:
|
2
|
+
#
|
3
|
+
# God::Contacts::Scout.client_key = '1qpw29ie38ur37yt5
|
4
|
+
#
|
5
|
+
# A client key is configured per god process. Inside this God process,
|
6
|
+
# you can create multiple Scout 'contacts' - which are actually Scout
|
7
|
+
# plugins. This allows you to use Scout's UI to configure who gets
|
8
|
+
# notifications for each plugin, and to disable notifications when you
|
9
|
+
# go on vacation, etc.
|
10
|
+
#
|
11
|
+
# God.contact(:scout) do |c|
|
12
|
+
# c.name = 'scout_delayed_job_plugin'
|
13
|
+
# c.plugin_id = '12345
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# God.contact(:scout) do |c|
|
17
|
+
# c.name = 'scout_apache_plugin'
|
18
|
+
# c.plugin_id = '54312
|
19
|
+
# end
|
20
|
+
|
21
|
+
require 'net/http'
|
22
|
+
require 'uri'
|
23
|
+
|
24
|
+
module God
|
25
|
+
module Contacts
|
26
|
+
class Scout < Contact
|
27
|
+
class << self
|
28
|
+
attr_accessor :client_key, :format
|
29
|
+
end
|
30
|
+
attr_accessor :plugin_id
|
31
|
+
|
32
|
+
self.format = lambda do |message, priority, category, host|
|
33
|
+
text = "Message: #{message}\n"
|
34
|
+
text += "Host: #{host}\n" if host
|
35
|
+
text += "Priority: #{priority}\n" if priority
|
36
|
+
text += "Category: #{category}\n" if category
|
37
|
+
return text
|
38
|
+
end
|
39
|
+
|
40
|
+
def valid?
|
41
|
+
valid = true
|
42
|
+
end
|
43
|
+
|
44
|
+
def notify(message, time, priority, category, host)
|
45
|
+
begin
|
46
|
+
data = {
|
47
|
+
:client_key => Scout.client_key,
|
48
|
+
:plugin_id => plugin_id,
|
49
|
+
:format => 'xml',
|
50
|
+
'alert[subject]' => message,
|
51
|
+
'alert[body]' => Scout.format.call(message, priority, category, host)
|
52
|
+
}
|
53
|
+
|
54
|
+
uri = URI.parse('http://scoutapp.com/alerts/create')
|
55
|
+
Net::HTTP.post_form(uri, data)
|
56
|
+
|
57
|
+
self.info = "sent scout alert to plugin ##{plugin_id}"
|
58
|
+
rescue => e
|
59
|
+
self.info = "failed to send scout alert to plugin ##{plugin_id}: #{e.message}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/god/driver.rb
CHANGED
data/lib/god/logger.rb
CHANGED
@@ -1,19 +1,14 @@
|
|
1
1
|
module God
|
2
2
|
|
3
3
|
class Logger < SimpleLogger
|
4
|
-
|
5
|
-
:error => :err,
|
6
|
-
:warn => :debug,
|
7
|
-
:info => :debug,
|
8
|
-
:debug => :debug}
|
9
|
-
|
4
|
+
|
10
5
|
attr_accessor :logs
|
11
6
|
|
12
7
|
class << self
|
13
8
|
attr_accessor :syslog
|
14
9
|
end
|
15
10
|
|
16
|
-
self.syslog
|
11
|
+
self.syslog = defined?(Syslog)
|
17
12
|
|
18
13
|
# Instantiate a new Logger object
|
19
14
|
def initialize(io = $stdout)
|
@@ -25,28 +20,12 @@ module God
|
|
25
20
|
@templogio = StringIO.new
|
26
21
|
@templog = SimpleLogger.new(@templogio)
|
27
22
|
@templog.level = Logger::INFO
|
28
|
-
load_syslog
|
29
23
|
end
|
30
24
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
def load_syslog
|
36
|
-
return unless Logger.syslog
|
37
|
-
|
38
|
-
begin
|
39
|
-
require 'syslog'
|
40
|
-
|
41
|
-
# Ensure that Syslog is open
|
42
|
-
begin
|
43
|
-
Syslog.open('god')
|
44
|
-
rescue RuntimeError
|
45
|
-
Syslog.reopen('god')
|
46
|
-
end
|
47
|
-
rescue Exception
|
48
|
-
Logger.syslog = false
|
49
|
-
end
|
25
|
+
|
26
|
+
def level=(lev)
|
27
|
+
SysLogger.level = SimpleLogger::CONSTANT_TO_SYMBOL[lev] if Logger.syslog
|
28
|
+
super(lev)
|
50
29
|
end
|
51
30
|
|
52
31
|
# Log a message
|
@@ -80,7 +59,7 @@ module God
|
|
80
59
|
self.send(level, text)
|
81
60
|
|
82
61
|
# send to syslog
|
83
|
-
|
62
|
+
SysLogger.log(level, text) if Logger.syslog
|
84
63
|
end
|
85
64
|
|
86
65
|
# Get all log output for a given Watch since a certain Time.
|
@@ -127,4 +106,4 @@ module God
|
|
127
106
|
end
|
128
107
|
end
|
129
108
|
|
130
|
-
end
|
109
|
+
end
|
data/lib/god/process.rb
CHANGED
@@ -2,7 +2,7 @@ module God
|
|
2
2
|
class Process
|
3
3
|
WRITES_PID = [:start, :restart]
|
4
4
|
|
5
|
-
attr_accessor :name, :uid, :gid, :log, :log_cmd, :start, :stop, :restart,
|
5
|
+
attr_accessor :name, :uid, :gid, :log, :log_cmd, :err_log, :err_log_cmd, :start, :stop, :restart,
|
6
6
|
:unix_socket, :chroot, :env, :dir
|
7
7
|
|
8
8
|
def initialize
|
@@ -300,7 +300,13 @@ module God
|
|
300
300
|
else
|
301
301
|
STDOUT.reopen file_in_chroot(self.log), "a"
|
302
302
|
end
|
303
|
-
|
303
|
+
if err_log_cmd
|
304
|
+
STDERR.reopen IO.popen(err_log_cmd, "a")
|
305
|
+
elsif err_log && (log_cmd || err_log != log)
|
306
|
+
STDERR.reopen file_in_chroot(err_log), "a"
|
307
|
+
else
|
308
|
+
STDERR.reopen STDOUT
|
309
|
+
end
|
304
310
|
|
305
311
|
# close any other file descriptors
|
306
312
|
3.upto(256){|fd| IO::new(fd).close rescue nil}
|
data/lib/god/simple_logger.rb
CHANGED
@@ -13,6 +13,12 @@ module God
|
|
13
13
|
ERROR => 'ERROR',
|
14
14
|
FATAL => 'FATAL'}
|
15
15
|
|
16
|
+
CONSTANT_TO_SYMBOL = { DEBUG => :debug,
|
17
|
+
INFO => :info,
|
18
|
+
WARN => :warn,
|
19
|
+
ERROR => :error,
|
20
|
+
FATAL => :fatal }
|
21
|
+
|
16
22
|
attr_accessor :datetime_format, :level
|
17
23
|
|
18
24
|
def initialize(io)
|
@@ -26,7 +32,7 @@ module God
|
|
26
32
|
|
27
33
|
time = Time.now.strftime(self.datetime_format)
|
28
34
|
label = SEV_LABEL[level]
|
29
|
-
@io.
|
35
|
+
@io.puts("#{label[0..0]} [#{time}] #{label.rjust(5)}: #{msg}")
|
30
36
|
end
|
31
37
|
|
32
38
|
def fatal(msg)
|
data/lib/god/socket.rb
CHANGED
@@ -38,8 +38,11 @@ module God
|
|
38
38
|
|
39
39
|
# Create a new Server and star the DRb server
|
40
40
|
# +port+ is the port on which to start the DRb service (default nil)
|
41
|
-
def initialize(port = nil)
|
42
|
-
@port
|
41
|
+
def initialize(port = nil, user = nil, group = nil, perm = nil)
|
42
|
+
@port = port
|
43
|
+
@user = user
|
44
|
+
@group = group
|
45
|
+
@perm = perm
|
43
46
|
start
|
44
47
|
end
|
45
48
|
|
@@ -90,6 +93,14 @@ module God
|
|
90
93
|
applog(nil, :info, "Started on #{DRb.uri}")
|
91
94
|
end
|
92
95
|
end
|
96
|
+
|
97
|
+
if File.exists?(self.socket_file)
|
98
|
+
uid = Etc.getpwnam(@user).uid if @user
|
99
|
+
gid = Etc.getgrnam(@group).gid if @group
|
100
|
+
|
101
|
+
File.chmod(Integer(@perm), socket_file) if @perm
|
102
|
+
File.chown(uid, gid, socket_file) if uid or gid
|
103
|
+
end
|
93
104
|
end
|
94
105
|
end
|
95
106
|
|