godhead 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile ADDED
@@ -0,0 +1,50 @@
1
+ h1. Godhead: Recipes for Monitoring with God
2
+
3
+ Godhead lets you configure God monitoring tasks to separate configuration of _the program being monitored_ from configuration of _the specific project it's used in_.
4
+
5
+ It also contains a collection of starting recipes gathered from around the web:
6
+
7
+ * Nginx
8
+ * Mongrel
9
+ * Thin
10
+ * Unicorn
11
+ * Beanstalkd
12
+ * Starling
13
+ * Memcached
14
+ * Tokyo Tyrant
15
+ * Generic worker daemons
16
+
17
+ There's skeleton files for delayed_job, workling
18
+
19
+ h2. Dependencies
20
+
21
+ Requires the gems for active_support and, of course, god. Optionally requires tlsmail (for Gmail or other secure email notification).
22
+
23
+ If you enjoy godhead you may also wish to find "Jesus,":http://github.com/dmathieu/jesus the "web interface for god to speak with mankind". Allelujah.
24
+
25
+ h2. Examples
26
+
27
+ The @examples/@ directory holds:
28
+ * a script to run god as a service.
29
+ ** @sudo cp [GODHEAD]/examples/etc/init.d/god /etc/init.d/god@
30
+ ** @sudo chmod a+x /etc/init.d/god@
31
+ ** @update-rc.d god defaults@
32
+ * a god.conf that loads God, sets up notifications, and then runs all .god files in /etc/god
33
+ * an example project script to put into /etc/god
34
+
35
+ h2. Our use case for godhead
36
+
37
+ At infochimps, we have a lot of boxen set up as "scrapers":http://github.com/mrflip/monkeyshines and as "feed endpoints.":http://github.com/mrflip/flood_monkey These typically talk to a lightweight database of some sort, pull or push to a queue, and are controlled through a simple sinatra admin panel.
38
+
39
+ With Godhead we describe the platonic form of, say, a "god config to monitor Tokyo Tyrant". The god files for a project using Tokyo Tyrant specifies only those few ways (port numbers, memory limits, etc) that its tasks differ from any other project's Tokyo Tyrant tasks. Configuration is still done in Ruby, through any combination of config params and subclassing.
40
+
41
+ h2. Links
42
+
43
+ * "God Monitoring homepage":http://god.rubyforge.org/
44
+ * "Railscast with God monitoring Workling and Starling":http://railscasts.com/episodes/130-monitoring-with-god
45
+ * "beanstalkd"
46
+ ** "article by topfunky":http://nubyonrails.com/articles/about-this-blog-beanstalk-messaging-queue
47
+ ** "god recipe":http://pastie.textmate.org/private/ovgxu2ihoicli2ktrwtbew
48
+ * "delayed_job":http://github.com/blog/229-dj-god
49
+ * "init.d/god script":http://www.johnwulff.com/2008/04/30/deploy-typo-using-capistrano
50
+ * backgroundRb: "this one":http://effectif.com/articles/monitoring-backgroundrb-with-god - or "this one":http://blog.jonathanrwallace.com/2008/08/monitoring-backgroundrb-workers-with-god/
data/Rakefile CHANGED
@@ -11,8 +11,8 @@ begin
11
11
  gem.homepage = "http://github.com/mrflip/godhead"
12
12
  gem.authors = ["Philip (flip) Kromer"]
13
13
  gem.add_development_dependency "rspec"
14
- gem.add_development_dependency "yard"
15
- gem.add_dependency 'extlib'
14
+ gem.add_dependency 'god'
15
+ gem.add_dependency 'activesupport'
16
16
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
17
  end
18
18
  rescue LoadError
@@ -32,14 +32,4 @@ Spec::Rake::SpecTask.new(:rcov) do |spec|
32
32
  end
33
33
 
34
34
  task :spec => :check_dependencies
35
-
36
35
  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 CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ # require 'active_support'
3
+ require 'extlib'
4
+ require 'godhead'
5
+ GOD_CONFIG = YAML.load(File.open('/etc/god/god_config.yaml')) rescue {}
6
+
7
+ # set up email
8
+ require 'godhead/notifications'
9
+ God.notify_by_gmail GOD_CONFIG[:email]
10
+ God.add_contact 'default', 'coders@example.com'
11
+ God.add_contact 'only_biff', 'biff@example.com'
12
+
13
+ # load in all god configs
14
+ God.load "/etc/god/*.god"
@@ -0,0 +1,10 @@
1
+ --- # -*- YAML -*-
2
+ #
3
+ # Save this file in your god dir (probably /etc/god), *then* change the settings
4
+ # below. Make sure your version control system is set to ignore the file.
5
+ #
6
+
7
+ :email:
8
+ :username: robot@mydomain.com
9
+ :password: password1your_mom
10
+ :smtp_domain: mydomain.com
@@ -0,0 +1,31 @@
1
+ YUPFRONT_ROOT = "/var/www/yuploader"
2
+
3
+ # ===========================================================================
4
+ #
5
+ # A worker daemon watching a starling queue
6
+ #
7
+ group_options = { :monitor_group => :yupshot, :stop_notify => :only_flip, :flapping_notify => :only_flip }
8
+
9
+ Godhead::MemcachedRecipe.create group_options.merge({
10
+ :user => 'www-data', :pid_file => '/tmp/mc.pid'})
11
+ Godhead::StarlingRecipe.create group_options
12
+ Godhead::GenericWorkerRecipe.create group_options.merge({
13
+ :runner_path => "#{YUPFRONT_ROOT}/yupshot/bin/yupshot_worker_daemon" })
14
+
15
+ # ===========================================================================
16
+ #
17
+ # The frontend server that accepts jobs and inserts them into the queue
18
+ #
19
+ group_options = { :monitor_group => :yupfront , :flapping_notify => :default }}
20
+
21
+ # nginx front end
22
+ # if nginx feeds backend servers for many different projects, move it to its own file
23
+ Godhead::NginxRunnerRecipe.create group_options.merge({ })
24
+
25
+ # thin runs clients
26
+ (5000..5003).each do |port|
27
+ Godhead::ThinRecipe.create(group_options.merge({
28
+ :port => port,
29
+ :rackup_file => File.join(YUPFRONT_ROOT, 'yupfront/config.ru'),
30
+ :runner_conf => File.join(YUPFRONT_ROOT, 'yupfront/production.yml') }))
31
+ end
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+
3
+ #
4
+ # God init.d (startup) script
5
+ #
6
+ # save this file as /etc/init.d/god
7
+ # make it executable:
8
+ # chmod +x /etc/init.d/god
9
+ # Tell the os to run the script at startup:
10
+ # sudo /usr/sbin/update-rc.d -f god defaults
11
+ #
12
+ # Also consider adding this line (kills god weekly) to your crontab (sudo crontab -e):
13
+ #
14
+ # # deicide is painless
15
+ # 0 1 * * 0 god quit; sleep 1; killall god; sleep 1; killall -9 god; sleep 1; /etc/init.d/god start
16
+ #
17
+
18
+ RETVAL=0
19
+ god_conf="/etc/god.conf"
20
+ god_pid_file="/var/run/god/god.pid" ; mkdir -p `dirname $god_pid_file`
21
+ god_log_file="/var/log/god/god.log" ; mkdir -p `dirname $god_log_file`
22
+ case "$1" in
23
+ start)
24
+ god -c "$god_conf" -P "$god_pid_file" -l "$god_log_file"
25
+ RETVAL=$?
26
+ echo "God started"
27
+ ;;
28
+ stop)
29
+ kill `cat $god_pid_file`
30
+ RETVAL=$?
31
+ echo "God stopped"
32
+ ;;
33
+ restart)
34
+ kill `cat $god_pid_file`
35
+ god -c "$god_conf" -P "$god_pid_file" -l "$god_log_file"
36
+ RETVAL=$?
37
+ echo "God restarted"
38
+ ;;
39
+ status)
40
+ RETVAL=$?
41
+ ;;
42
+ *)
43
+ echo "Usage: god {start|stop|restart|status}"
44
+ exit 1
45
+ ;;
46
+ esac
47
+
48
+ exit $RETVAL
data/godhead.gemspec CHANGED
@@ -1,47 +1,58 @@
1
1
  # Generated by jeweler
2
- # DO NOT EDIT THIS FILE
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{godhead}
8
- s.version = "0.0.2"
8
+ s.version = "0.0.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Philip (flip) Kromer"]
12
- s.date = %q{2009-12-07}
12
+ s.date = %q{2010-02-11}
13
13
  s.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).}
14
14
  s.email = %q{flip@infochimps.org}
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE",
17
- "README-god.textile"
17
+ "README.textile"
18
18
  ]
19
19
  s.files = [
20
20
  ".document",
21
21
  ".gitignore",
22
22
  "LICENSE",
23
- "README-god.textile",
23
+ "README.textile",
24
24
  "Rakefile",
25
25
  "VERSION",
26
+ "examples/etc/god.conf",
27
+ "examples/etc/god/god_config.yaml",
28
+ "examples/etc/god/sample_project.god",
29
+ "examples/etc/init.d/god",
26
30
  "godhead.gemspec",
27
31
  "lib/godhead.rb",
28
32
  "lib/godhead/extensions.rb",
29
33
  "lib/godhead/extensions/hash.rb",
34
+ "lib/godhead/extensions/string.rb",
35
+ "lib/godhead/god_email.rb",
30
36
  "lib/godhead/god_recipe.rb",
31
37
  "lib/godhead/mixins.rb",
32
38
  "lib/godhead/mixins/runs_as_service.rb",
33
- "lib/godhead/notification.rb",
34
- "lib/godhead/notification/gmail.rb",
39
+ "lib/godhead/notifications.rb",
35
40
  "lib/godhead/process_groups.rb",
36
41
  "lib/godhead/recipes.rb",
37
42
  "lib/godhead/recipes/beanstalkd_recipe.rb",
43
+ "lib/godhead/recipes/delayed_job_recipe.rb",
38
44
  "lib/godhead/recipes/generic_worker_recipe.rb",
39
45
  "lib/godhead/recipes/memcached_recipe.rb",
46
+ "lib/godhead/recipes/mongrel_recipe.rb",
47
+ "lib/godhead/recipes/mysql_recipe.rb",
40
48
  "lib/godhead/recipes/nginx_recipe.rb",
49
+ "lib/godhead/recipes/resque_recipe.rb",
41
50
  "lib/godhead/recipes/starling_recipe.rb",
42
51
  "lib/godhead/recipes/thin_recipe.rb",
52
+ "lib/godhead/recipes/thinking_sphinx.rb",
43
53
  "lib/godhead/recipes/tyrant_recipe.rb",
44
- "sample.god",
54
+ "lib/godhead/recipes/unicorn_recipe.rb",
55
+ "lib/godhead/recipes/workling_recipe.rb",
45
56
  "spec/godhead_spec.rb",
46
57
  "spec/spec_helper.rb"
47
58
  ]
@@ -61,16 +72,17 @@ Gem::Specification.new do |s|
61
72
 
62
73
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
63
74
  s.add_development_dependency(%q<rspec>, [">= 0"])
64
- s.add_development_dependency(%q<yard>, [">= 0"])
65
- s.add_runtime_dependency(%q<extlib>, [">= 0"])
75
+ s.add_runtime_dependency(%q<god>, [">= 0"])
76
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
66
77
  else
67
78
  s.add_dependency(%q<rspec>, [">= 0"])
68
- s.add_dependency(%q<yard>, [">= 0"])
69
- s.add_dependency(%q<extlib>, [">= 0"])
79
+ s.add_dependency(%q<god>, [">= 0"])
80
+ s.add_dependency(%q<activesupport>, [">= 0"])
70
81
  end
71
82
  else
72
83
  s.add_dependency(%q<rspec>, [">= 0"])
73
- s.add_dependency(%q<yard>, [">= 0"])
74
- s.add_dependency(%q<extlib>, [">= 0"])
84
+ s.add_dependency(%q<god>, [">= 0"])
85
+ s.add_dependency(%q<activesupport>, [">= 0"])
75
86
  end
76
87
  end
88
+
@@ -1 +1,2 @@
1
1
  require 'godhead/extensions/hash'
2
+ require 'godhead/extensions/string'
@@ -0,0 +1,9 @@
1
+ class String
2
+ [:camelize, :underscore, :demodulize].each do |inflection|
3
+ next if self.respond_to?(inflection)
4
+ self.class_eval %Q{
5
+ def #{inflection}
6
+ Extlib::Inflection.#{inflection} self
7
+ end}
8
+ end
9
+ end
@@ -0,0 +1,45 @@
1
+ module God
2
+ #
3
+ # Sets up
4
+ #
5
+ def self.add_simple_contact to_address, group=nil, &block
6
+ God.contact(:email) do |c|
7
+ c.email = to_address
8
+ c.group = group || 'default'
9
+ block.call(c) if block
10
+ end
11
+ end
12
+
13
+ #
14
+ # GMail
15
+ #
16
+ # Sends mail using a gmail or google apps account.
17
+ #
18
+ # http://millarian.com/programming/ruby-on-rails/monitoring-thin-using-god-with-google-apps-notifications/
19
+ def self.notify_by_gmail options
20
+ require 'tlsmail'
21
+ Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)
22
+ notify_by_smtp options.reverse_merge({
23
+ :address => 'smtp.gmail.com',
24
+ :tls => 'true',
25
+ :port => 587,
26
+ })
27
+ end
28
+
29
+ #
30
+ # SMTP email
31
+ #
32
+ # Assumes that the 'from' account and the smtp server login name are identical.
33
+ def self.notify_by_smtp options
34
+ God::Contacts::Email.message_settings = {
35
+ :from => options[:username], }
36
+ God::Contacts::Email.server_settings = {
37
+ :address => options[:address],
38
+ :port => options[:port] || 25,
39
+ :user_name => options[:username],
40
+ :password => options[:password],
41
+ :domain => options[:domain],
42
+ :authentication => :plain
43
+ }
44
+ end
45
+ end
@@ -4,16 +4,18 @@ module Godhead
4
4
  :monitor_group => nil,
5
5
  :uid => nil,
6
6
  :gid => nil,
7
- :start_notify => nil,
7
+ :crash_notify => nil,
8
8
  :restart_notify => nil,
9
9
  :flapping_notify => nil,
10
10
  :process_log_dir => '/var/log/god',
11
- :pid_dir => "/var/run/god",
11
+ :pid_dir => '/var/run/god',
12
12
  :start_grace_time => 10.seconds,
13
13
  :restart_grace_time => nil, # will be start_grace_time+2 if left nil
14
14
  :default_interval => 5.minutes,
15
- :start_interval => 5.minutes,
15
+ :start_interval => nil, # uses default_interval if unset
16
+ :max_mem_usage => nil, # doesn't monitor mem usage if nil
16
17
  :mem_usage_interval => 10.minutes,
18
+ :max_cpu_usage => 50.percent, # doesn't monitor cpu usage if nil
17
19
  :cpu_usage_interval => 10.minutes,
18
20
  }
19
21
  #
@@ -79,9 +81,13 @@ module Godhead
79
81
  def setup_start watcher
80
82
  watcher.start_if do |start|
81
83
  start.condition(:process_running) do |c|
82
- c.interval = options[:start_interval]
84
+ c.interval = options[:start_interval] || options[:default_interval]
83
85
  c.running = false
84
- c.notify = options[:start_notify] if options[:start_notify]
86
+ end
87
+ end
88
+ watcher.transition(:up, :start) do |on|
89
+ on.condition(:process_exits) do |c|
90
+ c.notify = options[:crash_notify] if options[:crash_notify]
85
91
  end
86
92
  end
87
93
  end
@@ -91,16 +97,16 @@ module Godhead
91
97
  watcher.restart_if do |restart|
92
98
  restart.condition(:memory_usage) do |c|
93
99
  c.interval = options[:mem_usage_interval] if options[:mem_usage_interval]
94
- c.above = options[:max_mem_usage] || 150.megabytes
100
+ c.above = options[:max_mem_usage]
95
101
  c.times = [3, 5] # 3 out of 5 intervals
96
102
  c.notify = options[:restart_notify] if options[:restart_notify]
97
- end
103
+ end if options[:max_mem_usage]
98
104
  restart.condition(:cpu_usage) do |c|
99
105
  c.interval = options[:cpu_usage_interval] if options[:cpu_usage_interval]
100
- c.above = options[:max_cpu_usage] || 50.percent
106
+ c.above = options[:max_cpu_usage]
101
107
  c.times = 5
102
108
  c.notify = options[:restart_notify] if options[:restart_notify]
103
- end
109
+ end if options[:max_cpu_usage]
104
110
  end
105
111
  end
106
112
 
@@ -109,12 +115,12 @@ module Godhead
109
115
  watcher.lifecycle do |on|
110
116
  on.condition(:flapping) do |c|
111
117
  c.to_state = [:start, :restart]
112
- c.times = 10
113
- c.within = 15.minute
118
+ c.times = 5
119
+ c.within = options[:flapping_window] || 15.minute
114
120
  c.transition = :unmonitored
115
- c.retry_in = 60.minutes
121
+ c.retry_in = options[:flapping_retry_in] || 30.minutes
116
122
  c.retry_times = 5
117
- c.retry_within = 12.hours
123
+ c.retry_within = 4.hours
118
124
  c.notify = options[:flapping_notify] if options[:flapping_notify]
119
125
  end
120
126
  end
@@ -187,6 +193,7 @@ module Godhead
187
193
 
188
194
  # by default, uses :pid_dir/:recipe_name_:port.pid
189
195
  def pid_file
196
+ return nil if (options[:pid_file] == false)
190
197
  options[:pid_file] || File.join(options[:pid_dir], "#{recipe_name}_#{options[:port]}.pid")
191
198
  end
192
199
 
@@ -0,0 +1,43 @@
1
+ module God
2
+ #
3
+ # Adds a notification contact
4
+ # pass in a group name and a valid email address.
5
+ #
6
+ def self.add_contact group, to_address
7
+ God.contact(:email) do |c|
8
+ c.group = group
9
+ c.email = to_address
10
+ c.name = "God monitoring #{group} notification"
11
+ 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.notify_by_gmail options
19
+ require 'tlsmail'
20
+ Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)
21
+ notify_by_smtp options.reverse_merge({
22
+ :address => 'smtp.gmail.com',
23
+ :tls => 'true',
24
+ :port => 587,
25
+ })
26
+ end
27
+
28
+ #
29
+ # SMTP email
30
+ #
31
+ def self.notify_by_smtp options
32
+ God::Contacts::Email.message_settings = {
33
+ :from => options[:username], }
34
+ God::Contacts::Email.server_settings = {
35
+ :address => options[:address],
36
+ :port => options[:port] || 25,
37
+ :domain => options[:smtp_domain],
38
+ :user_name => options[:username],
39
+ :password => options[:password],
40
+ :authentication => :plain,
41
+ }
42
+ end
43
+ end