godhead 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +43 -0
- data/LICENSE +20 -0
- data/README-god.textile +54 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/lib/godhead/extensions/hash.rb +48 -0
- data/lib/godhead/extensions.rb +1 -0
- data/lib/godhead/god_recipe.rb +223 -0
- data/lib/godhead/mixins/runs_as_service.rb +19 -0
- data/lib/godhead/mixins.rb +3 -0
- data/lib/godhead/notification/gmail.rb +0 -0
- data/lib/godhead/notification.rb +45 -0
- data/lib/godhead/process_groups.rb +32 -0
- data/lib/godhead/recipes/beanstalkd_recipe.rb +36 -0
- data/lib/godhead/recipes/generic_worker_recipe.rb +40 -0
- data/lib/godhead/recipes/memcached_recipe.rb +21 -0
- data/lib/godhead/recipes/nginx_recipe.rb +69 -0
- data/lib/godhead/recipes/starling_recipe.rb +19 -0
- data/lib/godhead/recipes/thin_recipe.rb +36 -0
- data/lib/godhead/recipes/tyrant_recipe.rb +64 -0
- data/lib/godhead/recipes.rb +9 -0
- data/lib/godhead.rb +5 -0
- data/sample.god +36 -0
- data/spec/godhead_spec.rb +7 -0
- data/spec/spec_helper.rb +9 -0
- metadata +111 -0
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
\#*
|
2
|
+
.\#*
|
3
|
+
*~
|
4
|
+
.DS_Store
|
5
|
+
Icon?
|
6
|
+
REVISION
|
7
|
+
TAGS*
|
8
|
+
nohup.out
|
9
|
+
.bzr
|
10
|
+
.hg
|
11
|
+
.svn
|
12
|
+
|
13
|
+
a.out
|
14
|
+
*.o
|
15
|
+
*.pyc
|
16
|
+
*.so
|
17
|
+
*.stackdump
|
18
|
+
*.sw?
|
19
|
+
*.tmproj
|
20
|
+
*_flymake.*
|
21
|
+
.project
|
22
|
+
.pydevproject
|
23
|
+
.settings
|
24
|
+
.tasks-cache
|
25
|
+
.yardoc
|
26
|
+
|
27
|
+
/**/*DONTVERSION*
|
28
|
+
/**/*private*
|
29
|
+
/**/cache/*
|
30
|
+
/**/log/*
|
31
|
+
/**/tmp/*
|
32
|
+
/coverage
|
33
|
+
/doc/*
|
34
|
+
/pkg/*
|
35
|
+
/rdoc/*
|
36
|
+
|
37
|
+
/db/*.sqlite3
|
38
|
+
/db/sphinx
|
39
|
+
/config/*.sphinx.conf
|
40
|
+
/config/database.yml
|
41
|
+
/config/sphinx.yml
|
42
|
+
/public/stylesheets/compiled/*
|
43
|
+
/vendor/src/**/*
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Philip (flip) Kromer
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README-god.textile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
Keep running:
|
2
|
+
|
3
|
+
* Worker queue (beanstalkd)
|
4
|
+
* Backing store (ttserver)
|
5
|
+
* Request queue-fed Scrapers (list of scripts)
|
6
|
+
* Feed/Periodic scrapers
|
7
|
+
* Constant scrapers
|
8
|
+
|
9
|
+
* http://god.rubyforge.org/
|
10
|
+
* http://railscasts.com/episodes/130-monitoring-with-god
|
11
|
+
|
12
|
+
sudo gem install god
|
13
|
+
god -c config/mailit.god
|
14
|
+
god status
|
15
|
+
god terminate
|
16
|
+
god log mailit-workling
|
17
|
+
kill `cat log/workling.pid`
|
18
|
+
|
19
|
+
* http://nubyonrails.com/articles/about-this-blog-beanstalk-messaging-queue
|
20
|
+
** The "god.conf":http://pastie.textmate.org/private/ovgxu2ihoicli2ktrwtbew is
|
21
|
+
taken from there.
|
22
|
+
|
23
|
+
h2. Beanstalkd
|
24
|
+
|
25
|
+
*Usage*:
|
26
|
+
|
27
|
+
beanstalkd --help
|
28
|
+
Use: beanstalkd [OPTIONS]
|
29
|
+
|
30
|
+
Options:
|
31
|
+
-d detach
|
32
|
+
-l ADDR listen on address (default is 0.0.0.0)
|
33
|
+
-p PORT listen on port (default is 11300)
|
34
|
+
-u USER become user and group
|
35
|
+
-z SIZE set the maximum job size in bytes (default is 65535)
|
36
|
+
-v show version information
|
37
|
+
-h show this help
|
38
|
+
|
39
|
+
|
40
|
+
h2. Tokyo Tyrant
|
41
|
+
|
42
|
+
*Usage*:
|
43
|
+
|
44
|
+
ttserver --help
|
45
|
+
ttserver: the server of Tokyo Tyrant
|
46
|
+
|
47
|
+
usage:
|
48
|
+
ttserver [-host name] [-port num] [-thnum num] [-tout num] [-dmn]
|
49
|
+
[-pid path] [-kl] [-log path] [-ld|-le] [-ulog path]
|
50
|
+
[-ulim num] [-uas] [-sid num]
|
51
|
+
[-mhost name] [-mport num] [-rts path] [-rcc] [-skel name]
|
52
|
+
[-ext path] [-extpc name period] [-mask expr] [-unmask expr] [dbname]
|
53
|
+
|
54
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "godhead"
|
8
|
+
gem.summary = %Q{God recipes that separate configuration for processes, site policy and notifications; comes with many examples}
|
9
|
+
gem.description = %Q{Configure God monitored processes according to their concerns the servers (path and so forth), site policy (number, ports, etc), and notification (email groups, mailserver, etc).}
|
10
|
+
gem.email = "flip@infochimps.org"
|
11
|
+
gem.homepage = "http://github.com/mrflip/godhead"
|
12
|
+
gem.authors = ["Philip (flip) Kromer"]
|
13
|
+
gem.add_development_dependency "rspec"
|
14
|
+
gem.add_development_dependency "yard"
|
15
|
+
gem.add_dependency 'extlib'
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
end
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'spec/rake/spectask'
|
23
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
24
|
+
spec.libs << 'lib' << 'spec'
|
25
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
26
|
+
end
|
27
|
+
|
28
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
29
|
+
spec.libs << 'lib' << 'spec'
|
30
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
31
|
+
spec.rcov = true
|
32
|
+
end
|
33
|
+
|
34
|
+
task :spec => :check_dependencies
|
35
|
+
|
36
|
+
task :default => :spec
|
37
|
+
|
38
|
+
begin
|
39
|
+
require 'yard'
|
40
|
+
YARD::Rake::YardocTask.new
|
41
|
+
rescue LoadError
|
42
|
+
task :yardoc do
|
43
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
44
|
+
end
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Hash
|
2
|
+
# remove all key-value pairs where the value is nil
|
3
|
+
def compact
|
4
|
+
reject{|key,val| val.nil? }
|
5
|
+
end
|
6
|
+
# Replace the hash with its compacted self
|
7
|
+
def compact!
|
8
|
+
replace(compact)
|
9
|
+
end
|
10
|
+
|
11
|
+
# lambda for recursive merges
|
12
|
+
Hash::DEEP_MERGER = proc do |key,v1,v2|
|
13
|
+
if (v1.respond_to?(:merge) && v2.respond_to?(:merge))
|
14
|
+
v1.merge(v2.compact, &Hash::DEEP_MERGER)
|
15
|
+
else
|
16
|
+
(v2.nil? ? v1 : v2)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Merge hashes recursively.
|
22
|
+
# Nothing special happens to array values
|
23
|
+
#
|
24
|
+
# x = { :subhash => { 1 => :val_from_x, 222 => :only_in_x, 333 => :only_in_x }, :scalar => :scalar_from_x}
|
25
|
+
# y = { :subhash => { 1 => :val_from_y, 999 => :only_in_y }, :scalar => :scalar_from_y }
|
26
|
+
# x.deep_merge y
|
27
|
+
# => {:subhash=>{1=>:val_from_y, 222=>:only_in_x, 333=>:only_in_x, 999=>:only_in_y}, :scalar=>:scalar_from_y}
|
28
|
+
# y.deep_merge x
|
29
|
+
# => {:subhash=>{1=>:val_from_x, 222=>:only_in_x, 333=>:only_in_x, 999=>:only_in_y}, :scalar=>:scalar_from_x}
|
30
|
+
#
|
31
|
+
# Nil values always lose.
|
32
|
+
#
|
33
|
+
# x = {:subhash=>{:nil_in_x=>nil, 1=>:val1,}, :nil_in_x=>nil}
|
34
|
+
# y = {:subhash=>{:nil_in_x=>5}, :nil_in_x=>5}
|
35
|
+
# y.deep_merge x
|
36
|
+
# => {:subhash=>{1=>:val1, :nil_in_x=>5}, :nil_in_x=>5}
|
37
|
+
# x.deep_merge y
|
38
|
+
# => {:subhash=>{1=>:val1, :nil_in_x=>5}, :nil_in_x=>5}
|
39
|
+
#
|
40
|
+
def deep_merge hsh2
|
41
|
+
merge hsh2, &Hash::DEEP_MERGER
|
42
|
+
end
|
43
|
+
|
44
|
+
# Merge hashes recursively -- see #deep_merge
|
45
|
+
def deep_merge! hsh2
|
46
|
+
merge! hsh2, &Hash::DEEP_MERGER
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'godhead/extensions/hash'
|
@@ -0,0 +1,223 @@
|
|
1
|
+
module Godhead
|
2
|
+
class GodRecipe
|
3
|
+
DEFAULT_OPTIONS = {
|
4
|
+
:monitor_group => nil,
|
5
|
+
:uid => nil,
|
6
|
+
:gid => nil,
|
7
|
+
:start_notify => nil,
|
8
|
+
:restart_notify => nil,
|
9
|
+
:flapping_notify => nil,
|
10
|
+
:process_log_dir => '/var/log/god',
|
11
|
+
:pid_dir => "/var/run/god",
|
12
|
+
:start_grace_time => 10.seconds,
|
13
|
+
:restart_grace_time => nil, # will be start_grace_time+2 if left nil
|
14
|
+
:default_interval => 5.minutes,
|
15
|
+
:start_interval => 5.minutes,
|
16
|
+
:mem_usage_interval => 10.minutes,
|
17
|
+
:cpu_usage_interval => 10.minutes,
|
18
|
+
}
|
19
|
+
#
|
20
|
+
# Hash mapping recipe_name to default options
|
21
|
+
#
|
22
|
+
cattr_accessor :global_options
|
23
|
+
self.global_options = {}
|
24
|
+
#
|
25
|
+
# options for this instance
|
26
|
+
#
|
27
|
+
attr_accessor :options
|
28
|
+
|
29
|
+
#
|
30
|
+
# pass in options to override the class default_options
|
31
|
+
#
|
32
|
+
def initialize _options={}
|
33
|
+
self.options = self.class.default_options.deep_merge _options
|
34
|
+
end
|
35
|
+
|
36
|
+
def do_setup! options={}
|
37
|
+
self.options.merge! options
|
38
|
+
mkdirs!
|
39
|
+
God.watch do |watcher|
|
40
|
+
setup_watcher watcher
|
41
|
+
setup_start watcher
|
42
|
+
setup_restart watcher
|
43
|
+
setup_lifecycle watcher
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.create options={}
|
48
|
+
recipe = self.new options
|
49
|
+
recipe.do_setup!
|
50
|
+
recipe
|
51
|
+
end
|
52
|
+
|
53
|
+
# ===========================================================================
|
54
|
+
#
|
55
|
+
# Watcher setup
|
56
|
+
#
|
57
|
+
|
58
|
+
#
|
59
|
+
# Setup common to most watchers
|
60
|
+
#
|
61
|
+
def setup_watcher watcher
|
62
|
+
watcher.name = self.handle
|
63
|
+
watcher.group = monitor_group
|
64
|
+
watcher.start = start_command
|
65
|
+
watcher.stop = stop_command if stop_command
|
66
|
+
watcher.restart = restart_command if restart_command
|
67
|
+
watcher.pid_file = pid_file if pid_file
|
68
|
+
watcher.uid = options[:uid] if options[:uid]
|
69
|
+
watcher.gid = options[:gid] if options[:gid]
|
70
|
+
watcher.interval = options[:default_interval]
|
71
|
+
watcher.start_grace = options[:start_grace_time]
|
72
|
+
watcher.restart_grace = options[:restart_grace_time] || (options[:start_grace_time] + 2.seconds)
|
73
|
+
watcher.behavior(:clean_pid_file)
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Starts process
|
78
|
+
#
|
79
|
+
def setup_start watcher
|
80
|
+
watcher.start_if do |start|
|
81
|
+
start.condition(:process_running) do |c|
|
82
|
+
c.interval = options[:start_interval]
|
83
|
+
c.running = false
|
84
|
+
c.notify = options[:start_notify] if options[:start_notify]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
def setup_restart watcher
|
91
|
+
watcher.restart_if do |restart|
|
92
|
+
restart.condition(:memory_usage) do |c|
|
93
|
+
c.interval = options[:mem_usage_interval] if options[:mem_usage_interval]
|
94
|
+
c.above = options[:max_mem_usage] || 150.megabytes
|
95
|
+
c.times = [3, 5] # 3 out of 5 intervals
|
96
|
+
c.notify = options[:restart_notify] if options[:restart_notify]
|
97
|
+
end
|
98
|
+
restart.condition(:cpu_usage) do |c|
|
99
|
+
c.interval = options[:cpu_usage_interval] if options[:cpu_usage_interval]
|
100
|
+
c.above = options[:max_cpu_usage] || 50.percent
|
101
|
+
c.times = 5
|
102
|
+
c.notify = options[:restart_notify] if options[:restart_notify]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Define lifecycle
|
108
|
+
def setup_lifecycle watcher
|
109
|
+
watcher.lifecycle do |on|
|
110
|
+
on.condition(:flapping) do |c|
|
111
|
+
c.to_state = [:start, :restart]
|
112
|
+
c.times = 10
|
113
|
+
c.within = 15.minute
|
114
|
+
c.transition = :unmonitored
|
115
|
+
c.retry_in = 60.minutes
|
116
|
+
c.retry_times = 5
|
117
|
+
c.retry_within = 12.hours
|
118
|
+
c.notify = options[:flapping_notify] if options[:flapping_notify]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# ===========================================================================
|
124
|
+
#
|
125
|
+
# Configuration
|
126
|
+
#
|
127
|
+
|
128
|
+
#
|
129
|
+
# string holding name for this process type, eg 'mongrel' or 'starling'
|
130
|
+
#
|
131
|
+
# Unless told otherwise, uses the class_name -- MongrelRecipe
|
132
|
+
#
|
133
|
+
def self.recipe_name
|
134
|
+
@recipe_name ||= recipe_name_from_class_name
|
135
|
+
end
|
136
|
+
# calls back to self.class.recipe_name
|
137
|
+
def recipe_name() self.class.recipe_name ; end
|
138
|
+
|
139
|
+
def self.recipe_name_from_class_name
|
140
|
+
self.to_s.underscore.demodulize.
|
141
|
+
gsub(%r{.*\/+}, ''). # remove module portion
|
142
|
+
gsub(%r{_recipe}, '') # remove _recipe part
|
143
|
+
end
|
144
|
+
|
145
|
+
# unique label for this process
|
146
|
+
def handle
|
147
|
+
[recipe_name, options[:port]].compact.map(&:to_s).join('_')
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# Starts with the portion of the global_options dealing with this class then
|
152
|
+
# walks upwards through the inheritance tree, accumulating default options.
|
153
|
+
#
|
154
|
+
# Subclasses should override with something like
|
155
|
+
#
|
156
|
+
# def self.default_options
|
157
|
+
# super.deep_merge(ThisClass::DEFAULT_OPTIONS)
|
158
|
+
# end
|
159
|
+
#
|
160
|
+
def self.default_options
|
161
|
+
GodRecipe::DEFAULT_OPTIONS.deep_merge(global_options[recipe_name] || {})
|
162
|
+
end
|
163
|
+
|
164
|
+
#
|
165
|
+
# load a series of YAML-format options files
|
166
|
+
#
|
167
|
+
# Later files win out over earlier files
|
168
|
+
#
|
169
|
+
def self.options_from_files *options_filenames
|
170
|
+
options = {}
|
171
|
+
options_filenames.each do |options_filename|
|
172
|
+
options.deep_merge! YAML.load_file(options_filename)
|
173
|
+
end
|
174
|
+
options
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
# ===========================================================================
|
179
|
+
#
|
180
|
+
# helpers
|
181
|
+
#
|
182
|
+
|
183
|
+
# by default, groups by process -- you may want to instead group by project
|
184
|
+
def monitor_group
|
185
|
+
options[:monitor_group].to_s || recipe_name.pluralize
|
186
|
+
end
|
187
|
+
|
188
|
+
# by default, uses :pid_dir/:recipe_name_:port.pid
|
189
|
+
def pid_file
|
190
|
+
options[:pid_file] || File.join(options[:pid_dir], "#{recipe_name}_#{options[:port]}.pid")
|
191
|
+
end
|
192
|
+
|
193
|
+
# command to start the daemon
|
194
|
+
def start_command
|
195
|
+
options[:start_command]
|
196
|
+
end
|
197
|
+
# command to stop the daemon
|
198
|
+
# return nil to have god daemonize the process
|
199
|
+
def stop_command
|
200
|
+
options[:stop_command]
|
201
|
+
end
|
202
|
+
# command to restart
|
203
|
+
# if stop_command is nil, it lets god daemonize the process
|
204
|
+
# otherwise, by default it runs stop_command, pauses for 1 second, then runs start_command
|
205
|
+
def restart_command
|
206
|
+
return unless stop_command
|
207
|
+
[stop_command, "sleep 1", start_command].join(" && ")
|
208
|
+
end
|
209
|
+
|
210
|
+
# Default log filename
|
211
|
+
def process_log_file
|
212
|
+
File.join(options[:process_log_dir], handle+".log")
|
213
|
+
end
|
214
|
+
|
215
|
+
# create any directories required by the process
|
216
|
+
def mkdirs!
|
217
|
+
require 'fileutils'
|
218
|
+
FileUtils.mkdir_p File.dirname(process_log_file)
|
219
|
+
FileUtils.mkdir_p File.dirname(options[:pid_file]) unless options[:pid_file].blank?
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Godhead
|
2
|
+
# Uses the init.d "service" command for running
|
3
|
+
module RunsAsService
|
4
|
+
# "service #{recipe_name} start"
|
5
|
+
def start_command
|
6
|
+
"service #{recipe_name} start"
|
7
|
+
end
|
8
|
+
|
9
|
+
# "service #{recipe_name} stop"
|
10
|
+
def stop_command
|
11
|
+
"service #{recipe_name} stop"
|
12
|
+
end
|
13
|
+
|
14
|
+
# "service #{recipe_name} restart"
|
15
|
+
def restart_command
|
16
|
+
"service #{recipe_name} restart"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
File without changes
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module God
|
2
|
+
def self.setup_email options
|
3
|
+
God::Contacts::Email.message_settings = {
|
4
|
+
:from => options[:username], }
|
5
|
+
God.contact(:email) do |c|
|
6
|
+
c.name = options[:to_name]
|
7
|
+
c.email = options[:to]
|
8
|
+
c.group = options[:group] || 'default'
|
9
|
+
end
|
10
|
+
if options[:address] then delivery_by_smtp(options)
|
11
|
+
else delivery_by_gmail(options) end
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# GMail
|
16
|
+
#
|
17
|
+
# http://millarian.com/programming/ruby-on-rails/monitoring-thin-using-god-with-google-apps-notifications/
|
18
|
+
def self.delivery_by_gmail options
|
19
|
+
require 'tlsmail'
|
20
|
+
Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)
|
21
|
+
God::Contacts::Email.server_settings = {
|
22
|
+
:address => 'smtp.gmail.com',
|
23
|
+
:tls => 'true',
|
24
|
+
:port => 587,
|
25
|
+
:domain => options[:email_domain],
|
26
|
+
:user_name => options[:username],
|
27
|
+
:password => options[:password],
|
28
|
+
:authentication => :plain
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# SMTP email
|
34
|
+
#
|
35
|
+
def self.delivery_by_smtp options
|
36
|
+
God::Contacts::Email.server_settings = {
|
37
|
+
:address => options[:address],
|
38
|
+
:port => 25,
|
39
|
+
:domain => options[:email_domain],
|
40
|
+
:user_name => options[:username],
|
41
|
+
:password => options[:password],
|
42
|
+
:authentication => :plain,
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module God
|
2
|
+
#
|
3
|
+
# Given a base port number and an associative array
|
4
|
+
# [ [GodProcessSubclass, { :options => 'for factory methods', ... }],
|
5
|
+
# ..., }
|
6
|
+
# this creates each given service with incrementing port numbers.
|
7
|
+
#
|
8
|
+
# For example,
|
9
|
+
#
|
10
|
+
# God.service_group 12300, [
|
11
|
+
# [BeanstalkdGod, { :max_mem_usage => 2.gigabytes, }],
|
12
|
+
# [TyrantGod, { :db_dirname => EDAMAME_DB_DIR, :db_name => 'queue_jobs.tch' }],
|
13
|
+
# [TyrantGod, { :db_dirname => EDAMAME_DB_DIR, :db_name => 'fetched_urls.tch' }],
|
14
|
+
# [ThinGod, { :thin_config_yml => '/slice/www/edamame_monitor/current/config.yml' }],
|
15
|
+
# ]
|
16
|
+
#
|
17
|
+
# will create an edamame pair of beanstalkd queue on 123000 and tyrant DB on
|
18
|
+
# 12301, an app-specific DB on 12302, and a lightweight monitoring web app on
|
19
|
+
# 12303.
|
20
|
+
#
|
21
|
+
# It's up to you to choose the ports to not overlap with other groups, etc.
|
22
|
+
#
|
23
|
+
# If an explicit port is given, that port is used with no regard to the rest
|
24
|
+
# of the group, and its number is skipped.
|
25
|
+
#
|
26
|
+
def self.process_group base_port, services
|
27
|
+
services.each do |klass, options|
|
28
|
+
klass.create({ :port => base_port }.deep_merge(options))
|
29
|
+
base_port += 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Godhead
|
2
|
+
class BeanstalkdRecipe < GodRecipe
|
3
|
+
DEFAULT_OPTIONS = {
|
4
|
+
:listen_on => '0.0.0.0',
|
5
|
+
:port => 11300,
|
6
|
+
:max_job_size => '65535',
|
7
|
+
:max_cpu_usage => 20.percent,
|
8
|
+
:max_mem_usage => 500.megabytes,
|
9
|
+
:runner_path => '/usr/local/bin/beanstalkd',
|
10
|
+
}
|
11
|
+
def self.default_options() super.deep_merge(Godhead::BeanstalkdRecipe::DEFAULT_OPTIONS) ; end
|
12
|
+
|
13
|
+
def start_command
|
14
|
+
[
|
15
|
+
options[:runner_path],
|
16
|
+
"-l #{options[:listen_on]}",
|
17
|
+
"-p #{options[:port]}",
|
18
|
+
(options[:user] ? "-u #{options[:user]}" : nil),
|
19
|
+
"-z #{options[:max_job_size]}",
|
20
|
+
].flatten.compact.join(" ")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# $ beanstalkd -h
|
26
|
+
# Use: beanstalkd [OPTIONS]
|
27
|
+
#
|
28
|
+
# Options:
|
29
|
+
# -d detach
|
30
|
+
# -l ADDR listen on address (default is 0.0.0.0)
|
31
|
+
# -p PORT listen on port (default is 11300)
|
32
|
+
# -u USER become user and group
|
33
|
+
# -z SIZE set the maximum job size in bytes (default is 65535)
|
34
|
+
# -v show version information
|
35
|
+
# -h show this help
|
36
|
+
#
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Godhead
|
2
|
+
#
|
3
|
+
# Starling monitoring recipe
|
4
|
+
#
|
5
|
+
class GenericWorkerRecipe < GodRecipe
|
6
|
+
DEFAULT_OPTIONS = {
|
7
|
+
:max_cpu_usage => 20.percent,
|
8
|
+
:max_mem_usage => 50.megabytes,
|
9
|
+
#
|
10
|
+
:user => nil,
|
11
|
+
:runner_path => nil,
|
12
|
+
}
|
13
|
+
def self.default_options() super.deep_merge(DEFAULT_OPTIONS) ; end
|
14
|
+
|
15
|
+
def initialize _options={}
|
16
|
+
super _options
|
17
|
+
raise "need a runner path" unless options[:runner_path]
|
18
|
+
p options
|
19
|
+
end
|
20
|
+
|
21
|
+
# name the recipe after the worker script
|
22
|
+
def recipe_name
|
23
|
+
File.basename(options[:runner_path]).gsub(/\..*/, '')
|
24
|
+
end
|
25
|
+
|
26
|
+
# don't try to invent a pid_file -- by default god will find and make one
|
27
|
+
def pid_file
|
28
|
+
options[:pid_file]
|
29
|
+
end
|
30
|
+
|
31
|
+
def start_command
|
32
|
+
[
|
33
|
+
"sudo",
|
34
|
+
(options[:user] ? "-u #{options[:user]}" : nil),
|
35
|
+
options[:runner_path]
|
36
|
+
].flatten.compact.join(" ")
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Godhead
|
2
|
+
#
|
3
|
+
# Nginx monitoring recipe
|
4
|
+
#
|
5
|
+
class MemcachedRecipe < GodRecipe
|
6
|
+
DEFAULT_OPTIONS = {
|
7
|
+
:max_cpu_usage => 20.percent,
|
8
|
+
:max_mem_usage => 500.megabytes,
|
9
|
+
#
|
10
|
+
:pid_file => "/var/run/god/memcached.pid",
|
11
|
+
:port => 45000,
|
12
|
+
}
|
13
|
+
def self.default_options() super.deep_merge(DEFAULT_OPTIONS) ; end
|
14
|
+
include Godhead::RunsAsService
|
15
|
+
|
16
|
+
def start_command
|
17
|
+
"memcached -u #{options[:user]} -p #{options[:port]} -d -P #{options[:pid_file]}"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Godhead
|
2
|
+
#
|
3
|
+
# Nginx monitoring recipe
|
4
|
+
#
|
5
|
+
class GenericNginxRecipe < GodRecipe
|
6
|
+
DEFAULT_OPTIONS = {
|
7
|
+
:max_cpu_usage => 20.percent,
|
8
|
+
:max_mem_usage => 500.megabytes,
|
9
|
+
:pid_file => "/var/run/nginx/nginx.pid",
|
10
|
+
}
|
11
|
+
def self.default_options() super.deep_merge(DEFAULT_OPTIONS) ; end
|
12
|
+
|
13
|
+
def self.recipe_name
|
14
|
+
'nginx'
|
15
|
+
end
|
16
|
+
|
17
|
+
def mkdirs!
|
18
|
+
FileUtils.mkdir_p File.dirname(options[:pid_file])
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Recipe for nginx process
|
24
|
+
#
|
25
|
+
# uses 'service nginx start' and so forth for task management
|
26
|
+
class NginxRecipe < GenericNginxRecipe
|
27
|
+
include Godhead::RunsAsService
|
28
|
+
end
|
29
|
+
|
30
|
+
# Recipe for nginx process, use on OSX
|
31
|
+
#
|
32
|
+
# calls out to the runner directly, as defined by :runner_path and
|
33
|
+
#:runner_conf option
|
34
|
+
class NginxRunnerRecipe < GenericNginxRecipe
|
35
|
+
DEFAULT_OPTIONS = {
|
36
|
+
# runner options
|
37
|
+
:runner_path => '/usr/local/nginx/sbin/nginx',
|
38
|
+
:runner_conf => nil,
|
39
|
+
}
|
40
|
+
def self.default_options() super.deep_merge(DEFAULT_OPTIONS) ; end
|
41
|
+
|
42
|
+
# send signal to a master process: stop, quit, reopen, reload
|
43
|
+
def tell_runner action=nil
|
44
|
+
[
|
45
|
+
options[:runner_path],
|
46
|
+
(options[:runner_conf] ? "-c #{options[:runner_conf]}" : nil),
|
47
|
+
(!action.blank? ? "-s #{action}" : nil),
|
48
|
+
].flatten.compact.join(" ")
|
49
|
+
end
|
50
|
+
|
51
|
+
def start_command() tell_runner '' ; end
|
52
|
+
def stop_command() tell_runner 'stop' ; end
|
53
|
+
def restart_command() tell_runner 'restart' ; end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# nginx version: nginx/0.7.61
|
59
|
+
# Usage: nginx [-?hvVt] [-s signal] [-c filename] [-p prefix] [-g directives]
|
60
|
+
#
|
61
|
+
# Options:
|
62
|
+
# -?,-h : this help
|
63
|
+
# -v : show version and exit
|
64
|
+
# -V : show version and configure options then exit
|
65
|
+
# -t : test configuration and exit
|
66
|
+
# -s signal : send signal to a master process: stop, quit, reopen, reload
|
67
|
+
# -p prefix : set prefix path (default: /usr/local/nginx/)
|
68
|
+
# -c filename : set configuration file (default: /slice/etc/nginx/nginx.conf)
|
69
|
+
# -g directives : set global directives out of configuration file
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Godhead
|
2
|
+
#
|
3
|
+
# Starling monitoring recipe
|
4
|
+
#
|
5
|
+
class StarlingRecipe < GodRecipe
|
6
|
+
DEFAULT_OPTIONS = {
|
7
|
+
:max_cpu_usage => 20.percent,
|
8
|
+
:max_mem_usage => 50.megabytes,
|
9
|
+
#
|
10
|
+
:port => 22122,
|
11
|
+
}
|
12
|
+
def self.default_options() super.deep_merge(DEFAULT_OPTIONS) ; end
|
13
|
+
|
14
|
+
def start_command
|
15
|
+
"starling -d -p #{options[:port]} -d -P #{pid_file}"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Godhead
|
2
|
+
class ThinRecipe < GodRecipe
|
3
|
+
DEFAULT_OPTIONS = {
|
4
|
+
:port => 3000,
|
5
|
+
:runner_path => '/usr/bin/thin', # path to thin. Override this in the site config file.
|
6
|
+
:runner_conf => nil,
|
7
|
+
:pid_file => nil,
|
8
|
+
}
|
9
|
+
def self.default_options() super.deep_merge(DEFAULT_OPTIONS) ; end
|
10
|
+
|
11
|
+
# w.start = "thin -s 3 -C #{YUPFRONT_CONFIG} start"
|
12
|
+
def tell_thin action
|
13
|
+
[
|
14
|
+
options[:runner_path],
|
15
|
+
"--config=#{options[:runner_conf]}",
|
16
|
+
"--rackup=#{options[:rackup_file]}",
|
17
|
+
"--port=#{ options[:port]}",
|
18
|
+
"--pid=#{pid_file}",
|
19
|
+
action
|
20
|
+
].flatten.compact.join(" ")
|
21
|
+
end
|
22
|
+
|
23
|
+
def start_command
|
24
|
+
tell_thin :start
|
25
|
+
end
|
26
|
+
|
27
|
+
def restart_command
|
28
|
+
tell_thin :restart
|
29
|
+
end
|
30
|
+
|
31
|
+
def stop_command
|
32
|
+
tell_thin :stop
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Godhead
|
2
|
+
class TyrantRecipe < GodRecipe
|
3
|
+
DEFAULT_OPTIONS = {
|
4
|
+
:max_cpu_usage => 50.percent,
|
5
|
+
:max_mem_usage => 150.megabytes,
|
6
|
+
# runner-specific
|
7
|
+
:listen_on => '0.0.0.0',
|
8
|
+
:port => 1978,
|
9
|
+
:db_dirname => '/tmp',
|
10
|
+
:runner_path => '/usr/local/bin/ttserver',
|
11
|
+
}
|
12
|
+
def self.default_options()
|
13
|
+
super.deep_merge(Godhead::TyrantRecipe::DEFAULT_OPTIONS)
|
14
|
+
end
|
15
|
+
|
16
|
+
def dbname
|
17
|
+
basename = options[:db_name] || (handle+'.tct')
|
18
|
+
File.join(options[:db_dirname], basename)
|
19
|
+
end
|
20
|
+
|
21
|
+
# create any directories required by the process
|
22
|
+
def mkdirs!
|
23
|
+
FileUtils.mkdir_p File.dirname(dbname)
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def start_command
|
28
|
+
[
|
29
|
+
options[:runner_path],
|
30
|
+
"-host #{options[:listen_on]}",
|
31
|
+
"-port #{options[:port]}",
|
32
|
+
"-log #{process_log_file}",
|
33
|
+
dbname
|
34
|
+
].flatten.compact.join(" ")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# -host name : specify the host name or the address of the server. By default, every network address is bound.
|
41
|
+
# -port num : specify the port number. By default, it is 1978.
|
42
|
+
#
|
43
|
+
# -thnum num : specify the number of worker threads. By default, it is 8.
|
44
|
+
# -tout num : specify the timeout of each session in seconds. By default, no timeout is specified.
|
45
|
+
#
|
46
|
+
# -log path : output log messages into the file.
|
47
|
+
# -ld : log debug messages also.
|
48
|
+
# -le : log error messages only.
|
49
|
+
# -ulog path : specify the update log directory.
|
50
|
+
# -ulim num : specify the limit size of each update log file.
|
51
|
+
# -uas : use asynchronous I/O for the update log.
|
52
|
+
#
|
53
|
+
# -sid num : specify the server ID.
|
54
|
+
# -mhost name : specify the host name of the replication master server.
|
55
|
+
# -mport num : specify the port number of the replication master server.
|
56
|
+
# -rts path : specify the replication time stamp file.
|
57
|
+
# -rcc : check consistency of replication.
|
58
|
+
#
|
59
|
+
# -skel name : specify the name of the skeleton database library.
|
60
|
+
# -ext path : specify the script language extension file.
|
61
|
+
# -extpc name period : specify the function name and the calling period of a periodic command.
|
62
|
+
# -mask expr : specify the names of forbidden commands.
|
63
|
+
# -unmask expr : specify the names of allowed commands.
|
64
|
+
#
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Godhead
|
2
|
+
autoload :BeanstalkdRecipe, 'godhead/recipes/beanstalkd_recipe.rb'
|
3
|
+
autoload :GenericWorkerRecipe, 'godhead/recipes/generic_worker_recipe.rb'
|
4
|
+
autoload :NginxRecipe, 'godhead/recipes/nginx_recipe.rb'
|
5
|
+
autoload :ThinRecipe, 'godhead/recipes/thin_recipe.rb'
|
6
|
+
autoload :TyrantRecipe, 'godhead/recipes/tyrant_recipe.rb'
|
7
|
+
autoload :MemcachedRecipe, 'godhead/recipes/memcached_recipe.rb'
|
8
|
+
autoload :StarlingRecipe, 'godhead/recipes/starling_recipe.rb'
|
9
|
+
end
|
data/lib/godhead.rb
ADDED
data/sample.god
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'god'
|
4
|
+
require 'active_support'
|
5
|
+
$: << File.dirname(__FILE__)+'/lib'
|
6
|
+
require 'godhead'
|
7
|
+
|
8
|
+
YUPFRONT_ROOT = "/slice/www/apps/yuploader_static/yupfront"
|
9
|
+
|
10
|
+
# ===========================================================================
|
11
|
+
#
|
12
|
+
# Yupshot god monitoring: the backend processing chain for yuploader
|
13
|
+
#
|
14
|
+
group_options = { :monitor_group => :yupshot, }
|
15
|
+
|
16
|
+
Godhead::MemcachedRecipe.create group_options.merge({ :user => 'www-data', :pid_file => '/tmp/mc.pid'})
|
17
|
+
Godhead::StarlingRecipe.create group_options
|
18
|
+
Godhead::GenericWorkerRecipe.create group_options.merge({
|
19
|
+
:runner_path => "/slice/www/apps/yuploader_static/yupshot/bin/yupshot_worker_daemon" })
|
20
|
+
|
21
|
+
# ===========================================================================
|
22
|
+
#
|
23
|
+
# Yupfront god monitoring: the frontend processes for yuploader
|
24
|
+
#
|
25
|
+
group_options = { :monitor_group => :yupfront}
|
26
|
+
|
27
|
+
Godhead::NginxRecipe.create group_options.merge({ })
|
28
|
+
# replace with this one on OSX
|
29
|
+
# Godhead::NginxRunnerRecipe.create group_options.merge({ })
|
30
|
+
|
31
|
+
(5000..5003).each do |port|
|
32
|
+
Godhead::ThinRecipe.create(group_options.merge({
|
33
|
+
:port => port,
|
34
|
+
:rackup_file => File.join(YUPFRONT_ROOT, 'config.ru'),
|
35
|
+
:runner_conf => File.join(YUPFRONT_ROOT, 'production.yml') }))
|
36
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: godhead
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Philip (flip) Kromer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-07 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: yard
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: extlib
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
description: Configure God monitored processes according to their concerns the servers (path and so forth), site policy (number, ports, etc), and notification (email groups, mailserver, etc).
|
46
|
+
email: flip@infochimps.org
|
47
|
+
executables: []
|
48
|
+
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files:
|
52
|
+
- LICENSE
|
53
|
+
- README-god.textile
|
54
|
+
files:
|
55
|
+
- .document
|
56
|
+
- .gitignore
|
57
|
+
- LICENSE
|
58
|
+
- README-god.textile
|
59
|
+
- Rakefile
|
60
|
+
- VERSION
|
61
|
+
- lib/godhead.rb
|
62
|
+
- lib/godhead/extensions.rb
|
63
|
+
- lib/godhead/extensions/hash.rb
|
64
|
+
- lib/godhead/god_recipe.rb
|
65
|
+
- lib/godhead/mixins.rb
|
66
|
+
- lib/godhead/mixins/runs_as_service.rb
|
67
|
+
- lib/godhead/notification.rb
|
68
|
+
- lib/godhead/notification/gmail.rb
|
69
|
+
- lib/godhead/process_groups.rb
|
70
|
+
- lib/godhead/recipes.rb
|
71
|
+
- lib/godhead/recipes/beanstalkd_recipe.rb
|
72
|
+
- lib/godhead/recipes/generic_worker_recipe.rb
|
73
|
+
- lib/godhead/recipes/memcached_recipe.rb
|
74
|
+
- lib/godhead/recipes/nginx_recipe.rb
|
75
|
+
- lib/godhead/recipes/starling_recipe.rb
|
76
|
+
- lib/godhead/recipes/thin_recipe.rb
|
77
|
+
- lib/godhead/recipes/tyrant_recipe.rb
|
78
|
+
- sample.god
|
79
|
+
- spec/godhead_spec.rb
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
has_rdoc: true
|
82
|
+
homepage: http://github.com/mrflip/godhead
|
83
|
+
licenses: []
|
84
|
+
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options:
|
87
|
+
- --charset=UTF-8
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
version:
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: "0"
|
101
|
+
version:
|
102
|
+
requirements: []
|
103
|
+
|
104
|
+
rubyforge_project:
|
105
|
+
rubygems_version: 1.3.5
|
106
|
+
signing_key:
|
107
|
+
specification_version: 3
|
108
|
+
summary: God recipes that separate configuration for processes, site policy and notifications; comes with many examples
|
109
|
+
test_files:
|
110
|
+
- spec/godhead_spec.rb
|
111
|
+
- spec/spec_helper.rb
|