god 0.8.0 → 0.9.0
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/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
|
|