god 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/Manifest.txt +37 -0
- data/README.txt +42 -0
- data/Rakefile +28 -0
- data/bin/god +26 -0
- data/examples/gravatar.god +41 -0
- data/examples/local.god +60 -0
- data/lib/god.rb +35 -0
- data/lib/god/base.rb +13 -0
- data/lib/god/behavior.rb +67 -0
- data/lib/god/behaviors/clean_pid_file.rb +23 -0
- data/lib/god/condition.rb +48 -0
- data/lib/god/conditions/always.rb +11 -0
- data/lib/god/conditions/cpu_usage.rb +44 -0
- data/lib/god/conditions/memory_usage.rb +44 -0
- data/lib/god/conditions/process_not_running.rb +22 -0
- data/lib/god/conditions/timeline.rb +17 -0
- data/lib/god/errors.rb +12 -0
- data/lib/god/meddle.rb +54 -0
- data/lib/god/reporter.rb +25 -0
- data/lib/god/server.rb +28 -0
- data/lib/god/system/process.rb +56 -0
- data/lib/god/timer.rb +78 -0
- data/lib/god/watch.rb +151 -0
- data/test/configs/real.rb +64 -0
- data/test/helper.rb +93 -0
- data/test/suite.rb +6 -0
- data/test/test_behavior.rb +13 -0
- data/test/test_condition.rb +26 -0
- data/test/test_god.rb +15 -0
- data/test/test_meddle.rb +46 -0
- data/test/test_reporter.rb +18 -0
- data/test/test_server.rb +24 -0
- data/test/test_system_process.rb +42 -0
- data/test/test_timeline.rb +24 -0
- data/test/test_timer.rb +43 -0
- data/test/test_watch.rb +123 -0
- metadata +111 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
bin/god
|
6
|
+
examples/gravatar.god
|
7
|
+
examples/local.god
|
8
|
+
lib/god.rb
|
9
|
+
lib/god/base.rb
|
10
|
+
lib/god/behavior.rb
|
11
|
+
lib/god/behaviors/clean_pid_file.rb
|
12
|
+
lib/god/condition.rb
|
13
|
+
lib/god/conditions/always.rb
|
14
|
+
lib/god/conditions/cpu_usage.rb
|
15
|
+
lib/god/conditions/memory_usage.rb
|
16
|
+
lib/god/conditions/process_not_running.rb
|
17
|
+
lib/god/conditions/timeline.rb
|
18
|
+
lib/god/errors.rb
|
19
|
+
lib/god/meddle.rb
|
20
|
+
lib/god/reporter.rb
|
21
|
+
lib/god/server.rb
|
22
|
+
lib/god/system/process.rb
|
23
|
+
lib/god/timer.rb
|
24
|
+
lib/god/watch.rb
|
25
|
+
test/configs/real.rb
|
26
|
+
test/helper.rb
|
27
|
+
test/suite.rb
|
28
|
+
test/test_behavior.rb
|
29
|
+
test/test_condition.rb
|
30
|
+
test/test_god.rb
|
31
|
+
test/test_meddle.rb
|
32
|
+
test/test_reporter.rb
|
33
|
+
test/test_server.rb
|
34
|
+
test/test_system_process.rb
|
35
|
+
test/test_timeline.rb
|
36
|
+
test/test_timer.rb
|
37
|
+
test/test_watch.rb
|
data/README.txt
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
god
|
2
|
+
by Tom Preston-Werner
|
3
|
+
http://god.rubyforge.org
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
God is an easy to configure, easy to extend monitoring framework written in Ruby.
|
8
|
+
|
9
|
+
Keeping your server processes and tasks running should be a simple part of your deployment process. God aims to be the simplest, most powerful monitoring application available.
|
10
|
+
|
11
|
+
== DOCUMENTATION:
|
12
|
+
|
13
|
+
See online documentation at http://god.rubyforge.org
|
14
|
+
|
15
|
+
== INSTALL:
|
16
|
+
|
17
|
+
$ sudo gem install god
|
18
|
+
|
19
|
+
== LICENSE:
|
20
|
+
|
21
|
+
(The MIT License)
|
22
|
+
|
23
|
+
Copyright (c) 2007 FIX
|
24
|
+
|
25
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
26
|
+
a copy of this software and associated documentation files (the
|
27
|
+
'Software'), to deal in the Software without restriction, including
|
28
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
29
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
30
|
+
permit persons to whom the Software is furnished to do so, subject to
|
31
|
+
the following conditions:
|
32
|
+
|
33
|
+
The above copyright notice and this permission notice shall be
|
34
|
+
included in all copies or substantial portions of the Software.
|
35
|
+
|
36
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
37
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
38
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
39
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
40
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
41
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
42
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/god.rb'
|
6
|
+
|
7
|
+
Hoe.new('god', God::VERSION) do |p|
|
8
|
+
p.rubyforge_name = 'god'
|
9
|
+
p.author = 'Tom Preston-Werner'
|
10
|
+
p.email = 'tom@rubyisawesome.com'
|
11
|
+
p.url = 'http://god.rubyforge.org/'
|
12
|
+
p.summary = 'Like monit, only awesome'
|
13
|
+
p.description = "God is an easy to configure, easy to extend monitoring framework written in Ruby."
|
14
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
15
|
+
p.extra_deps << ['daemons', '>=1.0.7']
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Open an irb session preloaded with this library"
|
19
|
+
task :console do
|
20
|
+
sh "irb -rubygems -r ./lib/god.rb"
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Upload site to Rubyforge"
|
24
|
+
task :site do
|
25
|
+
sh "scp -r site/* mojombo@god.rubyforge.org:/var/www/gforge-projects/god"
|
26
|
+
end
|
27
|
+
|
28
|
+
# vim: syntax=Ruby
|
data/bin/god
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'daemons'
|
7
|
+
require 'optparse'
|
8
|
+
require 'god'
|
9
|
+
|
10
|
+
options = {}
|
11
|
+
OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: god command [options]"
|
13
|
+
|
14
|
+
opts.on("-cCONFIG", "--config-file CONFIG", "Configuration file") do |x|
|
15
|
+
options[:config] = x
|
16
|
+
end
|
17
|
+
end.parse!
|
18
|
+
|
19
|
+
options[:config] = File.expand_path(options[:config]) if options[:config]
|
20
|
+
|
21
|
+
# p options
|
22
|
+
# p ARGV
|
23
|
+
|
24
|
+
Daemons.run_proc('god') do
|
25
|
+
load options[:config]
|
26
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# This is the actual config file used to keep the mongrels of
|
2
|
+
# gravatar.com running.
|
3
|
+
|
4
|
+
RAILS_ROOT = "/var/www/gravatar2/current"
|
5
|
+
|
6
|
+
God.meddle do |god|
|
7
|
+
%w{8200 8201 8202}.each do |port|
|
8
|
+
god.watch do |w|
|
9
|
+
w.name = "gravatar2-mongrel-#{port}"
|
10
|
+
w.interval = 30 # seconds
|
11
|
+
w.start = "mongrel_rails cluster::start --only #{port} -c #{RAILS_ROOT}"
|
12
|
+
w.stop = "mongrel_rails cluster::stop --only #{port} -c #{RAILS_ROOT}"
|
13
|
+
|
14
|
+
pid_file = File.join(RAILS_ROOT, "log/mongrel.#{port}.pid")
|
15
|
+
|
16
|
+
w.behavior(:clean_pid_file) do |b|
|
17
|
+
b.pid_file = pid_file
|
18
|
+
end
|
19
|
+
|
20
|
+
w.start_if do |start|
|
21
|
+
start.condition(:process_not_running) do |c|
|
22
|
+
c.pid_file = pid_file
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
w.restart_if do |restart|
|
27
|
+
restart.condition(:memory_usage) do |c|
|
28
|
+
c.pid_file = pid_file
|
29
|
+
c.above = (150 * 1024) # 150mb
|
30
|
+
c.times = [3, 5] # 3 out of 5 intervals
|
31
|
+
end
|
32
|
+
|
33
|
+
restart.condition(:cpu_usage) do |c|
|
34
|
+
c.pid_file = pid_file
|
35
|
+
c.above = 50 # percent
|
36
|
+
c.times = 5
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/examples/local.god
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# This example shows how you might keep a local development Rails server up
|
2
|
+
# and running on your Mac.
|
3
|
+
|
4
|
+
# Run with:
|
5
|
+
# god local.god
|
6
|
+
|
7
|
+
RAILS_ROOT = "/Users/tom/dev/powerset/querytopia"
|
8
|
+
|
9
|
+
God.meddle do |god|
|
10
|
+
god.interval = 5 # seconds
|
11
|
+
|
12
|
+
god.watch do |w|
|
13
|
+
w.name = "local-3000"
|
14
|
+
w.start = "mongrel_rails start -P ./log/mongrel.pid -c #{RAILS_ROOT} -d"
|
15
|
+
w.stop = "mongrel_rails stop -P ./log/mongrel.pid -c #{RAILS_ROOT}"
|
16
|
+
w.grace = 5
|
17
|
+
|
18
|
+
pid_file = File.join(RAILS_ROOT, "log/mongrel.pid")
|
19
|
+
|
20
|
+
# clean pid files before start if necessary
|
21
|
+
w.behavior(:clean_pid_file) do |b|
|
22
|
+
b.pid_file = pid_file
|
23
|
+
end
|
24
|
+
|
25
|
+
# start if process is not running
|
26
|
+
w.start_if do |start|
|
27
|
+
start.condition(:process_not_running) do |c|
|
28
|
+
c.pid_file = pid_file
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# restart if memory or cpu is too high
|
33
|
+
w.restart_if do |restart|
|
34
|
+
restart.condition(:memory_usage) do |c|
|
35
|
+
c.pid_file = pid_file
|
36
|
+
c.above = (50 * 1024) # 50mb
|
37
|
+
c.times = [3, 5]
|
38
|
+
end
|
39
|
+
|
40
|
+
restart.condition(:cpu_usage) do |c|
|
41
|
+
c.pid_file = pid_file
|
42
|
+
c.above = 10 # percent
|
43
|
+
c.times = [3, 5]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# clear old session files
|
49
|
+
god.watch do |w|
|
50
|
+
w.name = "local-session-cleanup"
|
51
|
+
w.cwd = File.join(RAILS_ROOT, 'tmp/sessions')
|
52
|
+
w.start = lambda do
|
53
|
+
Dir['ruby_sess.*'].select { |f| File.mtime(f) < Time.now - (7 * 24 * 60 * 60) }.each { |f| File.delete(f) }
|
54
|
+
end
|
55
|
+
|
56
|
+
w.start_if do |start|
|
57
|
+
start.condition(:always)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/god.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
|
2
|
+
|
3
|
+
# internal requires
|
4
|
+
require 'god/base'
|
5
|
+
require 'god/errors'
|
6
|
+
|
7
|
+
require 'god/system/process'
|
8
|
+
|
9
|
+
require 'god/behavior'
|
10
|
+
require 'god/behaviors/clean_pid_file'
|
11
|
+
|
12
|
+
require 'god/condition'
|
13
|
+
require 'god/conditions/timeline'
|
14
|
+
require 'god/conditions/process_not_running'
|
15
|
+
require 'god/conditions/memory_usage'
|
16
|
+
require 'god/conditions/cpu_usage'
|
17
|
+
require 'god/conditions/always'
|
18
|
+
|
19
|
+
require 'god/reporter'
|
20
|
+
require 'god/server'
|
21
|
+
require 'god/timer'
|
22
|
+
|
23
|
+
require 'god/watch'
|
24
|
+
require 'god/meddle'
|
25
|
+
|
26
|
+
module God
|
27
|
+
VERSION = '0.1.0'
|
28
|
+
|
29
|
+
def self.meddle(options = {})
|
30
|
+
m = Meddle.new(options)
|
31
|
+
yield m
|
32
|
+
m.monitor
|
33
|
+
m.timer.join
|
34
|
+
end
|
35
|
+
end
|
data/lib/god/base.rb
ADDED
data/lib/god/behavior.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
module God
|
2
|
+
|
3
|
+
class Behavior < Base
|
4
|
+
# Generate a Behavior of the given kind. The proper class if found by camel casing the
|
5
|
+
# kind (which is given as an underscored symbol).
|
6
|
+
# +kind+ is the underscored symbol representing the class (e.g. foo_bar for God::Behaviors::FooBar)
|
7
|
+
def self.generate(kind)
|
8
|
+
sym = kind.to_s.capitalize.gsub(/_(.)/){$1.upcase}.intern
|
9
|
+
God::Behaviors.const_get(sym).new
|
10
|
+
rescue NameError
|
11
|
+
raise NoSuchBehaviorError.new("No Behavior found with the class name God::Behaviors::#{sym}")
|
12
|
+
end
|
13
|
+
|
14
|
+
# Override this method in your Behaviors (optional)
|
15
|
+
#
|
16
|
+
# Called once after the Condition has been sent to the block and attributes have been
|
17
|
+
# set. Do any post-processing on attributes here
|
18
|
+
def prepare
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# Override this method in your Behaviors (optional)
|
23
|
+
#
|
24
|
+
# Called once during evaluation of the config file. Return true if valid, false otherwise
|
25
|
+
#
|
26
|
+
# A convenience method 'complain' is available that will print out a message and return false,
|
27
|
+
# making it easy to report multiple validation errors:
|
28
|
+
#
|
29
|
+
# def valid?
|
30
|
+
# valid = true
|
31
|
+
# valid &= complain("You must specify the 'pid_file' attribute for :memory_usage") if self.pid_file.nil?
|
32
|
+
# valid &= complain("You must specify the 'above' attribute for :memory_usage") if self.above.nil?
|
33
|
+
# valid
|
34
|
+
# end
|
35
|
+
def valid?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
#######
|
40
|
+
|
41
|
+
def before_start
|
42
|
+
end
|
43
|
+
|
44
|
+
def after_start
|
45
|
+
end
|
46
|
+
|
47
|
+
def before_restart
|
48
|
+
end
|
49
|
+
|
50
|
+
def after_restart
|
51
|
+
end
|
52
|
+
|
53
|
+
def before_stop
|
54
|
+
end
|
55
|
+
|
56
|
+
def after_stop
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
def complain(text)
|
62
|
+
puts text
|
63
|
+
false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module God
|
2
|
+
module Behaviors
|
3
|
+
|
4
|
+
class CleanPidFile < Behavior
|
5
|
+
attr_accessor :pid_file
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
self.pid_file = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid?
|
12
|
+
valid = true
|
13
|
+
valid &= complain("You must specify the 'pid_file' attribute for :clean_pid_file") if self.pid_file.nil?
|
14
|
+
valid
|
15
|
+
end
|
16
|
+
|
17
|
+
def before_start
|
18
|
+
File.delete(self.pid_file) rescue nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module God
|
2
|
+
|
3
|
+
class Condition < Behavior
|
4
|
+
# Generate a Condition of the given kind. The proper class if found by camel casing the
|
5
|
+
# kind (which is given as an underscored symbol).
|
6
|
+
# +kind+ is the underscored symbol representing the class (e.g. foo_bar for God::Conditions::FooBar)
|
7
|
+
def self.generate(kind)
|
8
|
+
sym = kind.to_s.capitalize.gsub(/_(.)/){$1.upcase}.intern
|
9
|
+
cond = God::Conditions.const_get(sym).new
|
10
|
+
|
11
|
+
unless cond.kind_of?(PollCondition) || cond.kind_of?(EventCondition)
|
12
|
+
abort "Condition '#{cond.class.name}' must subclass either God::PollCondition or God::EventCondition"
|
13
|
+
end
|
14
|
+
|
15
|
+
cond
|
16
|
+
rescue NameError
|
17
|
+
raise NoSuchConditionError.new("No Condition found with the class name God::Conditions::#{sym}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class PollCondition < Condition
|
22
|
+
# all poll conditions can specify a poll interval
|
23
|
+
attr_accessor :interval
|
24
|
+
|
25
|
+
# Override this method in your Conditions (optional)
|
26
|
+
def before
|
27
|
+
end
|
28
|
+
|
29
|
+
# Override this method in your Conditions (mandatory)
|
30
|
+
#
|
31
|
+
# Return true if the test passes (everything is ok)
|
32
|
+
# Return false otherwise
|
33
|
+
def test
|
34
|
+
raise AbstractMethodNotOverriddenError.new("Condition#test must be overridden in subclasses")
|
35
|
+
end
|
36
|
+
|
37
|
+
# Override this method in your Conditions (optional)
|
38
|
+
def after
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class EventCondition < Condition
|
43
|
+
def register
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|