mrflip-edamame 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +8 -0
- data/.gitignore +31 -0
- data/LICENSE.textile +21 -0
- data/README.textile +178 -0
- data/Rakefile +75 -0
- data/VERSION +1 -0
- data/app/edamame_san/config.ru +4 -0
- data/app/edamame_san/config.yml +17 -0
- data/app/edamame_san/config/.gitignore +1 -0
- data/app/edamame_san/edamame_san.rb +71 -0
- data/app/edamame_san/public/favicon.ico +0 -0
- data/app/edamame_san/public/images/edamame_logo.icns +0 -0
- data/app/edamame_san/public/images/edamame_logo.ico +0 -0
- data/app/edamame_san/public/images/edamame_logo.png +0 -0
- data/app/edamame_san/public/images/edamame_logo_2.icns +0 -0
- data/app/edamame_san/public/javascripts/application.js +8 -0
- data/app/edamame_san/public/javascripts/jquery/jquery-ui.js +8694 -0
- data/app/edamame_san/public/javascripts/jquery/jquery.js +4376 -0
- data/app/edamame_san/public/stylesheets/application.css +32 -0
- data/app/edamame_san/public/stylesheets/layout.css +88 -0
- data/app/edamame_san/views/layout.haml +13 -0
- data/app/edamame_san/views/load.haml +37 -0
- data/app/edamame_san/views/root.haml +25 -0
- data/bin/edamame-ps +2 -0
- data/bin/empty_all.rb +15 -0
- data/bin/stats.rb +13 -0
- data/bin/sync.rb +15 -0
- data/bin/test_run.rb +14 -0
- data/edamame.gemspec +110 -0
- data/lib/edamame.rb +193 -0
- data/lib/edamame/job.rb +134 -0
- data/lib/edamame/queue.rb +6 -0
- data/lib/edamame/queue/beanstalk.rb +132 -0
- data/lib/edamame/rescheduled.rb +89 -0
- data/lib/edamame/scheduling.rb +69 -0
- data/lib/edamame/store.rb +8 -0
- data/lib/edamame/store/base.rb +62 -0
- data/lib/edamame/store/tyrant_store.rb +50 -0
- data/lib/methods.txt +94 -0
- data/spec/edamame_spec.rb +7 -0
- data/spec/spec_helper.rb +9 -0
- data/utils/god/README-god.textile +54 -0
- data/utils/god/beanstalkd_god.rb +34 -0
- data/utils/god/edamame.god +30 -0
- data/utils/god/god-etc-init-dot-d-example +40 -0
- data/utils/god/god_email.rb +45 -0
- data/utils/god/god_process.rb +140 -0
- data/utils/god/god_site_config.rb +4 -0
- data/utils/god/sinatra_god.rb +36 -0
- data/utils/god/tyrant_god.rb +67 -0
- data/utils/simulation/Add Percent Variation.vi +0 -0
- data/utils/simulation/Harmonic Average.vi +0 -0
- data/utils/simulation/Rescheduling Simulation.aliases +3 -0
- data/utils/simulation/Rescheduling Simulation.lvlps +3 -0
- data/utils/simulation/Rescheduling Simulation.lvproj +22 -0
- data/utils/simulation/Rescheduling.vi +0 -0
- data/utils/simulation/Weighted Average.vi +0 -0
- metadata +135 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'tokyotyrant'
|
2
|
+
module Edamame
|
3
|
+
module Store
|
4
|
+
|
5
|
+
#
|
6
|
+
# Implementation of KeyStore with a Local TokyoCabinet table database (TDB)
|
7
|
+
#
|
8
|
+
class TyrantStore < Edamame::Store::Base
|
9
|
+
attr_accessor :db_host, :db_port
|
10
|
+
|
11
|
+
# pass in the host:port uri of the key store.
|
12
|
+
def initialize options
|
13
|
+
self.db_host, self.db_port = options[:uri].to_s.split(':')
|
14
|
+
super options
|
15
|
+
end
|
16
|
+
|
17
|
+
def db
|
18
|
+
return @db if @db
|
19
|
+
@db ||= TokyoTyrant::RDBTBL.new
|
20
|
+
@db.open(db_host, db_port) or raise("Can't open DB #{db_host}:#{db_port}. Pass in host:port' #{@db.ecode}: #{@db.errmsg(@db.ecode)}")
|
21
|
+
@db
|
22
|
+
end
|
23
|
+
|
24
|
+
def close
|
25
|
+
@db.close if @db
|
26
|
+
@db = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# Save the value into the database without waiting for a response.
|
30
|
+
def set_nr(key, val)
|
31
|
+
db.putnr key, val if val
|
32
|
+
end
|
33
|
+
|
34
|
+
def size()
|
35
|
+
db.rnum
|
36
|
+
end
|
37
|
+
|
38
|
+
def stats
|
39
|
+
{ :size => size }
|
40
|
+
end
|
41
|
+
|
42
|
+
def include? *args
|
43
|
+
db.has_key? *args
|
44
|
+
end
|
45
|
+
|
46
|
+
end #class
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
data/lib/methods.txt
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
See also:
|
2
|
+
|
3
|
+
http://github.com/kr/beanstalkd/blob/master/doc/protocol.txt
|
4
|
+
|
5
|
+
== Connection ==
|
6
|
+
|
7
|
+
connection.rb: def initialize(addr, default_tube=nil)
|
8
|
+
connection.rb: def connect
|
9
|
+
connection.rb: def close
|
10
|
+
|
11
|
+
connection.rb: def put(body, pri=65536, delay=0, ttr=120)
|
12
|
+
connection.rb: def yput(obj, pri=65536, delay=0, ttr=120)
|
13
|
+
connection.rb: def reserve(timeout=nil)
|
14
|
+
connection.rb: def delete(id)
|
15
|
+
connection.rb: def release(id, pri, delay)
|
16
|
+
connection.rb: def bury(id, pri)
|
17
|
+
|
18
|
+
connection.rb: def use(tube)
|
19
|
+
connection.rb: def watch(tube)
|
20
|
+
connection.rb: def ignore(tube)
|
21
|
+
|
22
|
+
connection.rb: def peek_job(id)
|
23
|
+
connection.rb: def peek_ready()
|
24
|
+
connection.rb: def peek_delayed()
|
25
|
+
connection.rb: def peek_buried()
|
26
|
+
|
27
|
+
connection.rb: def stats()
|
28
|
+
connection.rb: def job_stats(id)
|
29
|
+
|
30
|
+
connection.rb: def stats_tube(tube)
|
31
|
+
connection.rb: def list_tubes()
|
32
|
+
connection.rb: def list_tube_used()
|
33
|
+
connection.rb: def list_tubes_watched(cached=false)
|
34
|
+
|
35
|
+
== Pool ==
|
36
|
+
|
37
|
+
connection.rb: def initialize(addrs, default_tube=nil)
|
38
|
+
connection.rb: def connect()
|
39
|
+
connection.rb: def open_connections()
|
40
|
+
connection.rb: def last_server
|
41
|
+
connection.rb: def put(body, pri=65536, delay=0, ttr=120)
|
42
|
+
connection.rb: def yput(obj, pri=65536, delay=0, ttr=120)
|
43
|
+
connection.rb: def reserve(timeout=nil)
|
44
|
+
connection.rb: def use(tube)
|
45
|
+
connection.rb: def watch(tube)
|
46
|
+
connection.rb: def ignore(tube)
|
47
|
+
connection.rb: def raw_stats()
|
48
|
+
connection.rb: def stats()
|
49
|
+
connection.rb: def raw_stats_tube(tube)
|
50
|
+
connection.rb: def stats_tube(tube)
|
51
|
+
connection.rb: def list_tubes()
|
52
|
+
connection.rb: def list_tube_used()
|
53
|
+
connection.rb: def list_tubes_watched(*args)
|
54
|
+
connection.rb: def remove(conn)
|
55
|
+
connection.rb: def close
|
56
|
+
connection.rb: def peek_ready()
|
57
|
+
connection.rb: def peek_delayed()
|
58
|
+
connection.rb: def peek_buried()
|
59
|
+
connection.rb: def peek_job(id)
|
60
|
+
connection.rb: def call_wrap(c, *args)
|
61
|
+
connection.rb: def retry_wrap(*args)
|
62
|
+
connection.rb: def send_to_each_conn_first_res(*args)
|
63
|
+
connection.rb: def send_to_rand_conn(*args)
|
64
|
+
connection.rb: def send_to_all_conns(*args)
|
65
|
+
connection.rb: def pick_connection()
|
66
|
+
connection.rb: def make_hash(pairs)
|
67
|
+
connection.rb: def map_hash(h)
|
68
|
+
connection.rb: def compact_hash(hash)
|
69
|
+
connection.rb: def sum_hashes(hs)
|
70
|
+
connection.rb: def combine_stats(k, a, b)
|
71
|
+
|
72
|
+
|
73
|
+
== Job ==
|
74
|
+
|
75
|
+
job.rb: def [](name)
|
76
|
+
job.rb: def []=(name, val)
|
77
|
+
job.rb: def ybody()
|
78
|
+
job.rb: def initialize(conn, id, body)
|
79
|
+
job.rb: def delete()
|
80
|
+
job.rb: def put_back(pri=self.pri, delay=0, ttr=self.ttr)
|
81
|
+
job.rb: def release(newpri=pri, delay=0)
|
82
|
+
job.rb: def bury(newpri=pri)
|
83
|
+
job.rb: def stats()
|
84
|
+
job.rb: def timeouts() stats['timeouts'] end
|
85
|
+
job.rb: def time_left() stats['time-left'] end
|
86
|
+
job.rb: def age() stats['age'] end
|
87
|
+
job.rb: def state() stats['state'] end
|
88
|
+
job.rb: def delay() stats['delay'] end
|
89
|
+
job.rb: def pri() stats['pri'] end
|
90
|
+
job.rb: def ttr() stats['ttr'] end
|
91
|
+
job.rb: def server()
|
92
|
+
job.rb: def decay(d=([1, delay].max * 1.3).ceil)
|
93
|
+
job.rb: def to_s
|
94
|
+
job.rb: def inspect
|
data/spec/spec_helper.rb
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
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class BeanstalkdGod < GodProcess
|
2
|
+
BeanstalkdGod::CONFIG_DEFAULTS = {
|
3
|
+
:listen_on => '0.0.0.0',
|
4
|
+
:port => 11300,
|
5
|
+
:user => nil,
|
6
|
+
:max_job_size => '65535',
|
7
|
+
:max_cpu_usage => 50.percent,
|
8
|
+
:max_mem_usage => 500.megabytes,
|
9
|
+
:monitor_group => 'beanstalkds',
|
10
|
+
:beanstalkd_exe => '/usr/local/bin/beanstalkd',
|
11
|
+
}
|
12
|
+
def initialize *args
|
13
|
+
super *args
|
14
|
+
self.config = BeanstalkdGod::CONFIG_DEFAULTS.compact.merge(self.config)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.kind
|
18
|
+
:beanstalkd
|
19
|
+
end
|
20
|
+
|
21
|
+
def start_command
|
22
|
+
[
|
23
|
+
config[:beanstalkd_exe],
|
24
|
+
"-l #{config[:listen_on]}",
|
25
|
+
"-p #{config[:port]}",
|
26
|
+
"-z #{config[:max_job_size]}",
|
27
|
+
config[:user] ? "-u #{config[:user]}" : "",
|
28
|
+
].flatten.compact.join(" ")
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.are_you_there_god_its_me_beanstalkd *args
|
32
|
+
create *args
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
$: << File.dirname(__FILE__)
|
2
|
+
require 'god_process'
|
3
|
+
require 'god_email'
|
4
|
+
require 'beanstalkd_god'
|
5
|
+
require 'tyrant_god'
|
6
|
+
require 'sinatra_god'
|
7
|
+
require 'god_site_config'
|
8
|
+
|
9
|
+
EDAMAME_DB_DIR = '/data/distdb'
|
10
|
+
|
11
|
+
#
|
12
|
+
# For debugging:
|
13
|
+
#
|
14
|
+
# sudo god -c /Users/flip/ics/edamame/utils/god/edamame.god -D
|
15
|
+
#
|
16
|
+
# (for production, use the etc/initc.d script in this directory)
|
17
|
+
#
|
18
|
+
# TODO: define an EdamameDirector that lets us name these collections.
|
19
|
+
#
|
20
|
+
THE_FAITHFUL = [
|
21
|
+
[BeanstalkdGod, { :port => 11210 }],
|
22
|
+
[TyrantGod, { :port => 11212, :db_dirname => EDAMAME_DB_DIR, :db_name => 'flat_delay_queue.tct' }],
|
23
|
+
[TyrantGod, { :port => 11213, :db_dirname => EDAMAME_DB_DIR, :db_name => 'fetched_urls.tch' }],
|
24
|
+
# [SinatraGod, { :port => 11219, :app_dirname => File.dirname(__FILE__)+'/../../app/edamame_san' }],
|
25
|
+
]
|
26
|
+
|
27
|
+
THE_FAITHFUL.each do |klass, config|
|
28
|
+
proc = klass.create(config.merge :flapping_notify => 'default')
|
29
|
+
proc.mkdirs!
|
30
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
#
|
3
|
+
# God
|
4
|
+
#
|
5
|
+
# chkconfig: - 85 15
|
6
|
+
# description: start, stop, restart God (bet you feel powerful)
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# Make this go with
|
10
|
+
# chmod +x /etc/init.d/god
|
11
|
+
# /usr/sbin/update-rc.d god defaults
|
12
|
+
|
13
|
+
RETVAL=0
|
14
|
+
|
15
|
+
case "$1" in
|
16
|
+
start)
|
17
|
+
/usr/bin/god -P /var/run/god.pid -l /var/log/god.log
|
18
|
+
/usr/bin/god load /etc/god.conf
|
19
|
+
RETVAL=$?
|
20
|
+
;;
|
21
|
+
stop)
|
22
|
+
kill `cat /var/run/god.pid`
|
23
|
+
RETVAL=$?
|
24
|
+
;;
|
25
|
+
restart)
|
26
|
+
kill `cat /var/run/god.pid`
|
27
|
+
/usr/bin/god -P /var/run/god.pid -l /var/log/god.log
|
28
|
+
/usr/bin/god load /etc/god.conf
|
29
|
+
RETVAL=$?
|
30
|
+
;;
|
31
|
+
status)
|
32
|
+
RETVAL=$?
|
33
|
+
;;
|
34
|
+
*)
|
35
|
+
echo "Usage: god {start|stop|restart|status}"
|
36
|
+
exit 1
|
37
|
+
;;
|
38
|
+
esac
|
39
|
+
|
40
|
+
exit $RETVAL
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module God
|
2
|
+
def self.setup_email config
|
3
|
+
God::Contacts::Email.message_settings = {
|
4
|
+
:from => config[:username], }
|
5
|
+
God.contact(:email) do |c|
|
6
|
+
c.name = config[:to_name]
|
7
|
+
c.email = config[:to]
|
8
|
+
c.group = config[:group] || 'default'
|
9
|
+
end
|
10
|
+
if config[:address] then delivery_by_smtp(config)
|
11
|
+
else delivery_by_gmail(config) 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 config
|
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 => config[:email_domain],
|
26
|
+
:user_name => config[:username],
|
27
|
+
:password => config[:password],
|
28
|
+
:authentication => :plain
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# SMTP email
|
34
|
+
#
|
35
|
+
def self.delivery_by_smtp config
|
36
|
+
God::Contacts::Email.server_settings = {
|
37
|
+
:address => config[:address],
|
38
|
+
:port => 25,
|
39
|
+
:domain => config[:email_domain],
|
40
|
+
:user_name => config[:username],
|
41
|
+
:password => config[:password],
|
42
|
+
:authentication => :plain,
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
class GodProcess
|
2
|
+
CONFIG_DEFAULTS = {
|
3
|
+
:monitor_group => nil,
|
4
|
+
:uid => nil,
|
5
|
+
:gid => nil,
|
6
|
+
:start_notify => nil,
|
7
|
+
:restart_notify => nil,
|
8
|
+
:flapping_notify => nil,
|
9
|
+
|
10
|
+
:process_log_dir => '/var/log/god',
|
11
|
+
|
12
|
+
:start_grace_time => 20.seconds,
|
13
|
+
:restart_grace_time => nil, # start_grace_time+2 if nil
|
14
|
+
:default_interval => 5.minutes,
|
15
|
+
:start_interval => 5.minutes,
|
16
|
+
:mem_usage_interval => 20.minutes,
|
17
|
+
:cpu_usage_interval => 20.minutes,
|
18
|
+
}
|
19
|
+
|
20
|
+
attr_accessor :config
|
21
|
+
def initialize config
|
22
|
+
self.config = CONFIG_DEFAULTS.compact.merge(config)
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup
|
26
|
+
LOG.info config.inspect
|
27
|
+
God.watch do |watcher|
|
28
|
+
config_watcher watcher
|
29
|
+
setup_start watcher
|
30
|
+
setup_restart watcher
|
31
|
+
setup_lifecycle watcher
|
32
|
+
end
|
33
|
+
end
|
34
|
+
def self.create config={}
|
35
|
+
proc = self.new config
|
36
|
+
proc.setup
|
37
|
+
proc
|
38
|
+
end
|
39
|
+
|
40
|
+
def handle
|
41
|
+
(config[:handle] || "#{self.class.kind}_#{config[:port]}").to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
def process_log_file
|
45
|
+
File.join(config[:process_log_dir], handle+".log")
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def mkdirs!
|
50
|
+
require 'fileutils'
|
51
|
+
FileUtils.mkdir_p File.dirname(process_log_file)
|
52
|
+
end
|
53
|
+
|
54
|
+
# command to start the daemon
|
55
|
+
def start_command
|
56
|
+
config[:start_command]
|
57
|
+
end
|
58
|
+
# command to stop the daemon
|
59
|
+
# return nil to have god daemonize the process
|
60
|
+
def stop_command
|
61
|
+
config[:stop_command]
|
62
|
+
end
|
63
|
+
# command to restart
|
64
|
+
# if stop_command is nil, it lets god daemonize the process
|
65
|
+
# otherwise, by default it runs stop_command, pauses for 1 second, then runs start_command
|
66
|
+
def restart_command
|
67
|
+
return unless stop_command
|
68
|
+
[stop_command, "sleep 1", start_command].join(" && ")
|
69
|
+
end
|
70
|
+
|
71
|
+
def config_watcher watcher
|
72
|
+
watcher.name = self.handle
|
73
|
+
watcher.start = start_command
|
74
|
+
watcher.stop = stop_command if stop_command
|
75
|
+
watcher.restart = restart_command if restart_command
|
76
|
+
watcher.group = config[:monitor_group] if config[:monitor_group]
|
77
|
+
watcher.uid = config[:uid] if config[:uid]
|
78
|
+
watcher.gid = config[:gid] if config[:gid]
|
79
|
+
watcher.interval = config[:default_interval]
|
80
|
+
watcher.start_grace = config[:start_grace_time]
|
81
|
+
watcher.restart_grace = config[:restart_grace_time] || (config[:start_grace_time] + 2.seconds)
|
82
|
+
watcher.behavior(:clean_pid_file)
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
def setup_start watcher
|
87
|
+
watcher.start_if do |start|
|
88
|
+
start.condition(:process_running) do |c|
|
89
|
+
c.interval = config[:start_interval]
|
90
|
+
c.running = false
|
91
|
+
c.notify = config[:start_notify] if config[:start_notify]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
def setup_restart watcher
|
98
|
+
watcher.restart_if do |restart|
|
99
|
+
restart.condition(:memory_usage) do |c|
|
100
|
+
c.interval = config[:mem_usage_interval] if config[:mem_usage_interval]
|
101
|
+
c.above = config[:max_mem_usage] || 150.megabytes
|
102
|
+
c.times = [3, 5] # 3 out of 5 intervals
|
103
|
+
c.notify = config[:restart_notify] if config[:restart_notify]
|
104
|
+
end
|
105
|
+
restart.condition(:cpu_usage) do |c|
|
106
|
+
c.interval = config[:cpu_usage_interval] if config[:cpu_usage_interval]
|
107
|
+
c.above = config[:max_cpu_usage] || 50.percent
|
108
|
+
c.times = 5
|
109
|
+
c.notify = config[:restart_notify] if config[:restart_notify]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Define lifecycle
|
115
|
+
def setup_lifecycle watcher
|
116
|
+
watcher.lifecycle do |on|
|
117
|
+
on.condition(:flapping) do |c|
|
118
|
+
c.to_state = [:start, :restart]
|
119
|
+
c.times = 10
|
120
|
+
c.within = 15.minute
|
121
|
+
c.transition = :unmonitored
|
122
|
+
c.retry_in = 60.minutes
|
123
|
+
c.retry_times = 5
|
124
|
+
c.retry_within = 12.hours
|
125
|
+
c.notify = config[:flapping_notify] if config[:flapping_notify]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class Hash
|
132
|
+
# remove all key-value pairs where the value is nil
|
133
|
+
def compact
|
134
|
+
reject{|key,val| val.nil? }
|
135
|
+
end
|
136
|
+
# Replace the hash with its compacted self
|
137
|
+
def compact!
|
138
|
+
replace(compact)
|
139
|
+
end
|
140
|
+
end
|