godhead 0.0.1

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
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.
@@ -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
@@ -0,0 +1,3 @@
1
+ module Godhead
2
+ autoload :RunsAsService, 'godhead/mixins/runs_as_service'
3
+ 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
@@ -0,0 +1,5 @@
1
+ require 'extlib'
2
+ require 'godhead/extensions'
3
+ require 'godhead/mixins'
4
+ require 'godhead/god_recipe'
5
+ require 'godhead/recipes'
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
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Godhead" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'godhead'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
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