dosire-god 0.7.9
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/History.txt +261 -0
- data/Manifest.txt +107 -0
- data/README.txt +59 -0
- data/Rakefile +35 -0
- data/bin/god +127 -0
- data/examples/events.god +84 -0
- data/examples/gravatar.god +54 -0
- data/examples/single.god +66 -0
- data/ext/god/extconf.rb +55 -0
- data/ext/god/kqueue_handler.c +123 -0
- data/ext/god/netlink_handler.c +167 -0
- data/init/god +42 -0
- data/lib/god/behavior.rb +52 -0
- data/lib/god/behaviors/clean_pid_file.rb +21 -0
- data/lib/god/behaviors/clean_unix_socket.rb +21 -0
- data/lib/god/behaviors/notify_when_flapping.rb +51 -0
- data/lib/god/cli/command.rb +206 -0
- data/lib/god/cli/run.rb +177 -0
- data/lib/god/cli/version.rb +23 -0
- data/lib/god/condition.rb +96 -0
- data/lib/god/conditions/always.rb +23 -0
- data/lib/god/conditions/complex.rb +86 -0
- data/lib/god/conditions/cpu_usage.rb +80 -0
- data/lib/god/conditions/degrading_lambda.rb +52 -0
- data/lib/god/conditions/disk_usage.rb +27 -0
- data/lib/god/conditions/flapping.rb +128 -0
- data/lib/god/conditions/http_response_code.rb +168 -0
- data/lib/god/conditions/lambda.rb +25 -0
- data/lib/god/conditions/memory_usage.rb +82 -0
- data/lib/god/conditions/process_exits.rb +72 -0
- data/lib/god/conditions/process_running.rb +74 -0
- data/lib/god/conditions/tries.rb +44 -0
- data/lib/god/configurable.rb +57 -0
- data/lib/god/contact.rb +106 -0
- data/lib/god/contacts/email.rb +95 -0
- data/lib/god/dependency_graph.rb +41 -0
- data/lib/god/diagnostics.rb +37 -0
- data/lib/god/driver.rb +206 -0
- data/lib/god/errors.rb +24 -0
- data/lib/god/event_handler.rb +111 -0
- data/lib/god/event_handlers/dummy_handler.rb +13 -0
- data/lib/god/event_handlers/kqueue_handler.rb +17 -0
- data/lib/god/event_handlers/netlink_handler.rb +13 -0
- data/lib/god/logger.rb +120 -0
- data/lib/god/metric.rb +59 -0
- data/lib/god/process.rb +327 -0
- data/lib/god/registry.rb +32 -0
- data/lib/god/simple_logger.rb +53 -0
- data/lib/god/socket.rb +96 -0
- data/lib/god/sugar.rb +47 -0
- data/lib/god/system/portable_poller.rb +42 -0
- data/lib/god/system/process.rb +42 -0
- data/lib/god/system/slash_proc_poller.rb +82 -0
- data/lib/god/task.rb +487 -0
- data/lib/god/timeline.rb +25 -0
- data/lib/god/trigger.rb +43 -0
- data/lib/god/watch.rb +183 -0
- data/lib/god.rb +644 -0
- data/test/configs/child_events/child_events.god +44 -0
- data/test/configs/child_events/simple_server.rb +3 -0
- data/test/configs/child_polls/child_polls.god +37 -0
- data/test/configs/child_polls/simple_server.rb +12 -0
- data/test/configs/complex/complex.god +59 -0
- data/test/configs/complex/simple_server.rb +3 -0
- data/test/configs/contact/contact.god +74 -0
- data/test/configs/contact/simple_server.rb +3 -0
- data/test/configs/daemon_events/daemon_events.god +37 -0
- data/test/configs/daemon_events/simple_server.rb +8 -0
- data/test/configs/daemon_events/simple_server_stop.rb +11 -0
- data/test/configs/daemon_polls/daemon_polls.god +17 -0
- data/test/configs/daemon_polls/simple_server.rb +6 -0
- data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
- data/test/configs/degrading_lambda/tcp_server.rb +15 -0
- data/test/configs/matias/matias.god +50 -0
- data/test/configs/real.rb +59 -0
- data/test/configs/running_load/running_load.god +16 -0
- data/test/configs/stress/simple_server.rb +3 -0
- data/test/configs/stress/stress.god +15 -0
- data/test/configs/task/logs/.placeholder +0 -0
- data/test/configs/task/task.god +26 -0
- data/test/configs/test.rb +61 -0
- data/test/helper.rb +151 -0
- data/test/suite.rb +6 -0
- data/test/test_behavior.rb +21 -0
- data/test/test_condition.rb +50 -0
- data/test/test_conditions_disk_usage.rb +56 -0
- data/test/test_conditions_http_response_code.rb +109 -0
- data/test/test_conditions_process_running.rb +44 -0
- data/test/test_conditions_tries.rb +67 -0
- data/test/test_contact.rb +109 -0
- data/test/test_dependency_graph.rb +62 -0
- data/test/test_driver.rb +11 -0
- data/test/test_event_handler.rb +80 -0
- data/test/test_god.rb +598 -0
- data/test/test_handlers_kqueue_handler.rb +16 -0
- data/test/test_logger.rb +63 -0
- data/test/test_metric.rb +72 -0
- data/test/test_process.rb +246 -0
- data/test/test_registry.rb +15 -0
- data/test/test_socket.rb +42 -0
- data/test/test_sugar.rb +42 -0
- data/test/test_system_portable_poller.rb +17 -0
- data/test/test_system_process.rb +30 -0
- data/test/test_task.rb +262 -0
- data/test/test_timeline.rb +37 -0
- data/test/test_trigger.rb +59 -0
- data/test/test_watch.rb +279 -0
- metadata +186 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
# run with: god -c /path/to/gravatar.god
|
2
|
+
#
|
3
|
+
# This is the actual config file used to keep the mongrels of
|
4
|
+
# gravatar.com running.
|
5
|
+
|
6
|
+
RAILS_ROOT = "/Users/tom/dev/gravatar2"
|
7
|
+
|
8
|
+
%w{8200 8201 8202}.each do |port|
|
9
|
+
God.watch do |w|
|
10
|
+
w.name = "gravatar2-mongrel-#{port}"
|
11
|
+
w.interval = 30.seconds # default
|
12
|
+
w.start = "mongrel_rails start -c #{RAILS_ROOT} -p #{port} \
|
13
|
+
-P #{RAILS_ROOT}/log/mongrel.#{port}.pid -d"
|
14
|
+
w.stop = "mongrel_rails stop -P #{RAILS_ROOT}/log/mongrel.#{port}.pid"
|
15
|
+
w.restart = "mongrel_rails restart -P #{RAILS_ROOT}/log/mongrel.#{port}.pid"
|
16
|
+
w.start_grace = 10.seconds
|
17
|
+
w.restart_grace = 10.seconds
|
18
|
+
w.pid_file = File.join(RAILS_ROOT, "log/mongrel.#{port}.pid")
|
19
|
+
|
20
|
+
w.behavior(:clean_pid_file)
|
21
|
+
|
22
|
+
w.start_if do |start|
|
23
|
+
start.condition(:process_running) do |c|
|
24
|
+
c.interval = 5.seconds
|
25
|
+
c.running = false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
w.restart_if do |restart|
|
30
|
+
restart.condition(:memory_usage) do |c|
|
31
|
+
c.above = 150.megabytes
|
32
|
+
c.times = [3, 5] # 3 out of 5 intervals
|
33
|
+
end
|
34
|
+
|
35
|
+
restart.condition(:cpu_usage) do |c|
|
36
|
+
c.above = 50.percent
|
37
|
+
c.times = 5
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# lifecycle
|
42
|
+
w.lifecycle do |on|
|
43
|
+
on.condition(:flapping) do |c|
|
44
|
+
c.to_state = [:start, :restart]
|
45
|
+
c.times = 5
|
46
|
+
c.within = 5.minute
|
47
|
+
c.transition = :unmonitored
|
48
|
+
c.retry_in = 10.minutes
|
49
|
+
c.retry_times = 5
|
50
|
+
c.retry_within = 2.hours
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/examples/single.god
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
RAILS_ROOT = "/Users/tom/dev/gravatar2"
|
2
|
+
|
3
|
+
God.watch do |w|
|
4
|
+
w.name = "local-3000"
|
5
|
+
w.interval = 5.seconds # default
|
6
|
+
w.start = "mongrel_rails start -c #{RAILS_ROOT} -P #{RAILS_ROOT}/log/mongrel.pid -p 3000 -d"
|
7
|
+
w.stop = "mongrel_rails stop -P #{RAILS_ROOT}/log/mongrel.pid"
|
8
|
+
w.restart = "mongrel_rails restart -P #{RAILS_ROOT}/log/mongrel.pid"
|
9
|
+
w.pid_file = File.join(RAILS_ROOT, "log/mongrel.pid")
|
10
|
+
|
11
|
+
# clean pid files before start if necessary
|
12
|
+
w.behavior(:clean_pid_file)
|
13
|
+
|
14
|
+
# determine the state on startup
|
15
|
+
w.transition(:init, { true => :up, false => :start }) do |on|
|
16
|
+
on.condition(:process_running) do |c|
|
17
|
+
c.running = true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# determine when process has finished starting
|
22
|
+
w.transition([:start, :restart], :up) do |on|
|
23
|
+
on.condition(:process_running) do |c|
|
24
|
+
c.running = true
|
25
|
+
end
|
26
|
+
|
27
|
+
# failsafe
|
28
|
+
on.condition(:tries) do |c|
|
29
|
+
c.times = 5
|
30
|
+
c.transition = :start
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# start if process is not running
|
35
|
+
w.transition(:up, :start) do |on|
|
36
|
+
on.condition(:process_exits)
|
37
|
+
end
|
38
|
+
|
39
|
+
# restart if memory or cpu is too high
|
40
|
+
w.transition(:up, :restart) do |on|
|
41
|
+
on.condition(:memory_usage) do |c|
|
42
|
+
c.interval = 20
|
43
|
+
c.above = 50.megabytes
|
44
|
+
c.times = [3, 5]
|
45
|
+
end
|
46
|
+
|
47
|
+
on.condition(:cpu_usage) do |c|
|
48
|
+
c.interval = 10
|
49
|
+
c.above = 10.percent
|
50
|
+
c.times = [3, 5]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# lifecycle
|
55
|
+
w.lifecycle do |on|
|
56
|
+
on.condition(:flapping) do |c|
|
57
|
+
c.to_state = [:start, :restart]
|
58
|
+
c.times = 5
|
59
|
+
c.within = 5.minute
|
60
|
+
c.transition = :unmonitored
|
61
|
+
c.retry_in = 10.minutes
|
62
|
+
c.retry_times = 5
|
63
|
+
c.retry_within = 2.hours
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/ext/god/extconf.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
fail = false
|
4
|
+
|
5
|
+
def create_dummy_makefile
|
6
|
+
File.open("Makefile", 'w') do |f|
|
7
|
+
f.puts "all:"
|
8
|
+
f.puts "install:"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
case RUBY_PLATFORM
|
13
|
+
when /bsd/i, /darwin/i
|
14
|
+
unless have_header('sys/event.h')
|
15
|
+
puts
|
16
|
+
puts "Missing 'sys/event.h' header"
|
17
|
+
fail = true
|
18
|
+
end
|
19
|
+
|
20
|
+
if fail
|
21
|
+
puts
|
22
|
+
puts "Events handler could not be compiled (see above error). Your god installation will not support event conditions."
|
23
|
+
create_dummy_makefile
|
24
|
+
else
|
25
|
+
create_makefile 'kqueue_handler_ext'
|
26
|
+
end
|
27
|
+
when /linux/i
|
28
|
+
unless have_header('linux/netlink.h')
|
29
|
+
puts
|
30
|
+
puts "Missing 'linux/netlink.h' header(s)"
|
31
|
+
puts "You may need to install a header package for your system"
|
32
|
+
fail = true
|
33
|
+
end
|
34
|
+
|
35
|
+
unless have_header('linux/connector.h') && have_header('linux/cn_proc.h')
|
36
|
+
puts
|
37
|
+
puts "Missing 'linux/connector.h', or 'linux/cn_proc.h' header(s)"
|
38
|
+
puts "These are only available in Linux kernel 2.6.15 and later (run `uname -a` to find yours)"
|
39
|
+
puts "You may need to install a header package for your system"
|
40
|
+
fail = true
|
41
|
+
end
|
42
|
+
|
43
|
+
if fail
|
44
|
+
puts
|
45
|
+
puts "Events handler could not be compiled (see above error). Your god installation will not support event conditions."
|
46
|
+
create_dummy_makefile
|
47
|
+
else
|
48
|
+
create_makefile 'netlink_handler_ext'
|
49
|
+
end
|
50
|
+
else
|
51
|
+
puts
|
52
|
+
puts "Unsupported platform '#{RUBY_PLATFORM}'. Supported platforms are BSD, DARWIN, and LINUX."
|
53
|
+
puts "Your god installation will not support event conditions."
|
54
|
+
create_dummy_makefile
|
55
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
#if defined(__FreeBSD__) || defined(__APPLE__)
|
2
|
+
|
3
|
+
#include <ruby.h>
|
4
|
+
#include <sys/event.h>
|
5
|
+
#include <sys/time.h>
|
6
|
+
#include <errno.h>
|
7
|
+
|
8
|
+
static VALUE mGod;
|
9
|
+
static VALUE cKQueueHandler;
|
10
|
+
static VALUE cEventHandler;
|
11
|
+
|
12
|
+
static ID proc_exit;
|
13
|
+
static ID proc_fork;
|
14
|
+
static ID m_call;
|
15
|
+
static ID m_size;
|
16
|
+
static ID m_deregister;
|
17
|
+
|
18
|
+
static int kq;
|
19
|
+
int num_events;
|
20
|
+
|
21
|
+
#define NUM_EVENTS FIX2INT(rb_funcall(rb_cv_get(cEventHandler, "@@actions"), m_size, 0))
|
22
|
+
|
23
|
+
VALUE
|
24
|
+
kqh_event_mask(VALUE klass, VALUE sym)
|
25
|
+
{
|
26
|
+
ID id = SYM2ID(sym);
|
27
|
+
if (proc_exit == id) {
|
28
|
+
return UINT2NUM(NOTE_EXIT);
|
29
|
+
} else if (proc_fork == id) {
|
30
|
+
return UINT2NUM(NOTE_FORK);
|
31
|
+
} else {
|
32
|
+
rb_raise(rb_eNotImpError, "Event `%s` not implemented", rb_id2name(id));
|
33
|
+
}
|
34
|
+
|
35
|
+
return Qnil;
|
36
|
+
}
|
37
|
+
|
38
|
+
|
39
|
+
VALUE
|
40
|
+
kqh_monitor_process(VALUE klass, VALUE pid, VALUE mask)
|
41
|
+
{
|
42
|
+
struct kevent new_event;
|
43
|
+
ID event;
|
44
|
+
|
45
|
+
u_int fflags = NUM2UINT(mask);
|
46
|
+
|
47
|
+
EV_SET(&new_event, FIX2UINT(pid), EVFILT_PROC,
|
48
|
+
EV_ADD | EV_ENABLE, fflags, 0, 0);
|
49
|
+
|
50
|
+
if (-1 == kevent(kq, &new_event, 1, NULL, 0, NULL)) {
|
51
|
+
rb_raise(rb_eStandardError, strerror(errno));
|
52
|
+
}
|
53
|
+
|
54
|
+
num_events = NUM_EVENTS;
|
55
|
+
|
56
|
+
return Qnil;
|
57
|
+
}
|
58
|
+
|
59
|
+
VALUE
|
60
|
+
kqh_handle_events()
|
61
|
+
{
|
62
|
+
int nevents, i, num_to_fetch;
|
63
|
+
struct kevent *events;
|
64
|
+
fd_set read_set;
|
65
|
+
|
66
|
+
FD_ZERO(&read_set);
|
67
|
+
FD_SET(kq, &read_set);
|
68
|
+
|
69
|
+
// Don't actually run this method until we've got an event
|
70
|
+
rb_thread_select(kq + 1, &read_set, NULL, NULL, NULL);
|
71
|
+
|
72
|
+
// Grabbing num_events once for thread safety
|
73
|
+
num_to_fetch = num_events;
|
74
|
+
events = (struct kevent*)malloc(num_to_fetch * sizeof(struct kevent));
|
75
|
+
|
76
|
+
if (NULL == events) {
|
77
|
+
rb_raise(rb_eStandardError, strerror(errno));
|
78
|
+
}
|
79
|
+
|
80
|
+
nevents = kevent(kq, NULL, 0, events, num_to_fetch, NULL);
|
81
|
+
|
82
|
+
if (-1 == nevents) {
|
83
|
+
free(events);
|
84
|
+
rb_raise(rb_eStandardError, strerror(errno));
|
85
|
+
} else {
|
86
|
+
for (i = 0; i < nevents; i++) {
|
87
|
+
if (events[i].fflags & NOTE_EXIT) {
|
88
|
+
rb_funcall(cEventHandler, m_call, 2, INT2NUM(events[i].ident), ID2SYM(proc_exit));
|
89
|
+
} else if (events[i].fflags & NOTE_FORK) {
|
90
|
+
rb_funcall(cEventHandler, m_call, 2, INT2NUM(events[i].ident), ID2SYM(proc_fork));
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
free(events);
|
96
|
+
|
97
|
+
return INT2FIX(nevents);
|
98
|
+
}
|
99
|
+
|
100
|
+
void
|
101
|
+
Init_kqueue_handler_ext()
|
102
|
+
{
|
103
|
+
kq = kqueue();
|
104
|
+
|
105
|
+
if (kq == -1) {
|
106
|
+
rb_raise(rb_eStandardError, "kqueue initilization failed");
|
107
|
+
}
|
108
|
+
|
109
|
+
proc_exit = rb_intern("proc_exit");
|
110
|
+
proc_fork = rb_intern("proc_fork");
|
111
|
+
m_call = rb_intern("call");
|
112
|
+
m_size = rb_intern("size");
|
113
|
+
m_deregister = rb_intern("deregister");
|
114
|
+
|
115
|
+
mGod = rb_const_get(rb_cObject, rb_intern("God"));
|
116
|
+
cEventHandler = rb_const_get(mGod, rb_intern("EventHandler"));
|
117
|
+
cKQueueHandler = rb_define_class_under(mGod, "KQueueHandler", rb_cObject);
|
118
|
+
rb_define_singleton_method(cKQueueHandler, "monitor_process", kqh_monitor_process, 2);
|
119
|
+
rb_define_singleton_method(cKQueueHandler, "handle_events", kqh_handle_events, 0);
|
120
|
+
rb_define_singleton_method(cKQueueHandler, "event_mask", kqh_event_mask, 1);
|
121
|
+
}
|
122
|
+
|
123
|
+
#endif
|
@@ -0,0 +1,167 @@
|
|
1
|
+
#ifdef __linux__ /* only build on linux */
|
2
|
+
|
3
|
+
#include <ruby.h>
|
4
|
+
#include <sys/types.h>
|
5
|
+
#include <unistd.h>
|
6
|
+
#include <sys/socket.h>
|
7
|
+
#include <linux/netlink.h>
|
8
|
+
#include <linux/connector.h>
|
9
|
+
#include <linux/cn_proc.h>
|
10
|
+
#include <errno.h>
|
11
|
+
|
12
|
+
static VALUE mGod;
|
13
|
+
static VALUE cNetlinkHandler;
|
14
|
+
static VALUE cEventHandler;
|
15
|
+
|
16
|
+
static ID proc_exit;
|
17
|
+
static ID proc_fork;
|
18
|
+
static ID m_call;
|
19
|
+
static ID m_watching_pid;
|
20
|
+
|
21
|
+
static int nl_sock; /* socket for netlink connection */
|
22
|
+
|
23
|
+
|
24
|
+
VALUE
|
25
|
+
nlh_handle_events()
|
26
|
+
{
|
27
|
+
char buff[CONNECTOR_MAX_MSG_SIZE];
|
28
|
+
struct nlmsghdr *hdr;
|
29
|
+
struct proc_event *event;
|
30
|
+
|
31
|
+
VALUE extra_data;
|
32
|
+
|
33
|
+
fd_set fds;
|
34
|
+
|
35
|
+
FD_ZERO(&fds);
|
36
|
+
FD_SET(nl_sock, &fds);
|
37
|
+
|
38
|
+
if (0 > rb_thread_select(nl_sock + 1, &fds, NULL, NULL, NULL)) {
|
39
|
+
rb_raise(rb_eStandardError, strerror(errno));
|
40
|
+
}
|
41
|
+
|
42
|
+
/* If there were no events detected, return */
|
43
|
+
if (! FD_ISSET(nl_sock, &fds)) {
|
44
|
+
return INT2FIX(0);
|
45
|
+
}
|
46
|
+
|
47
|
+
/* if there are events, make calls */
|
48
|
+
if (-1 == recv(nl_sock, buff, sizeof(buff), 0)) {
|
49
|
+
rb_raise(rb_eStandardError, strerror(errno));
|
50
|
+
}
|
51
|
+
|
52
|
+
hdr = (struct nlmsghdr *)buff;
|
53
|
+
|
54
|
+
if (NLMSG_ERROR == hdr->nlmsg_type) {
|
55
|
+
rb_raise(rb_eStandardError, strerror(errno));
|
56
|
+
} else if (NLMSG_DONE == hdr->nlmsg_type) {
|
57
|
+
|
58
|
+
event = (struct proc_event *)((struct cn_msg *)NLMSG_DATA(hdr))->data;
|
59
|
+
|
60
|
+
switch(event->what) {
|
61
|
+
case PROC_EVENT_EXIT:
|
62
|
+
if (Qnil == rb_funcall(cEventHandler, m_watching_pid, 1, INT2FIX(event->event_data.exit.process_pid))) {
|
63
|
+
return INT2FIX(0);
|
64
|
+
}
|
65
|
+
|
66
|
+
extra_data = rb_hash_new();
|
67
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("pid")), INT2FIX(event->event_data.exit.process_pid));
|
68
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("exit_code")), INT2FIX(event->event_data.exit.exit_code));
|
69
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("exit_signal")), INT2FIX(event->event_data.exit.exit_signal));
|
70
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("thread_group_id")), INT2FIX(event->event_data.exit.process_tgid));
|
71
|
+
|
72
|
+
rb_funcall(cEventHandler, m_call, 3, INT2FIX(event->event_data.exit.process_pid), ID2SYM(proc_exit), extra_data);
|
73
|
+
return INT2FIX(1);
|
74
|
+
|
75
|
+
case PROC_EVENT_FORK:
|
76
|
+
if (Qnil == rb_funcall(cEventHandler, m_watching_pid, 1, INT2FIX(event->event_data.fork.parent_pid))) {
|
77
|
+
return INT2FIX(0);
|
78
|
+
}
|
79
|
+
|
80
|
+
extra_data = rb_hash_new();
|
81
|
+
rb_hash_aset(extra_data, rb_intern("parent_pid"), INT2FIX(event->event_data.fork.parent_pid));
|
82
|
+
rb_hash_aset(extra_data, rb_intern("parent_thread_group_id"), INT2FIX(event->event_data.fork.parent_tgid));
|
83
|
+
rb_hash_aset(extra_data, rb_intern("child_pid"), INT2FIX(event->event_data.fork.child_pid));
|
84
|
+
rb_hash_aset(extra_data, rb_intern("child_thread_group_id"), INT2FIX(event->event_data.fork.child_tgid));
|
85
|
+
|
86
|
+
rb_funcall(cEventHandler, m_call, 3, INT2FIX(event->event_data.fork.parent_pid), ID2SYM(proc_fork), extra_data);
|
87
|
+
return INT2FIX(1);
|
88
|
+
|
89
|
+
case PROC_EVENT_NONE:
|
90
|
+
case PROC_EVENT_EXEC:
|
91
|
+
case PROC_EVENT_UID:
|
92
|
+
case PROC_EVENT_GID:
|
93
|
+
break;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
return Qnil;
|
98
|
+
}
|
99
|
+
|
100
|
+
|
101
|
+
#define NL_MESSAGE_SIZE (sizeof(struct nlmsghdr) + sizeof(struct cn_msg) + \
|
102
|
+
sizeof(int))
|
103
|
+
|
104
|
+
void
|
105
|
+
connect_to_netlink()
|
106
|
+
{
|
107
|
+
struct sockaddr_nl sa_nl; /* netlink interface info */
|
108
|
+
char buff[NL_MESSAGE_SIZE];
|
109
|
+
struct nlmsghdr *hdr; /* for telling netlink what we want */
|
110
|
+
struct cn_msg *msg; /* the actual connector message */
|
111
|
+
|
112
|
+
/* connect to netlink socket */
|
113
|
+
nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
|
114
|
+
|
115
|
+
if (-1 == nl_sock) {
|
116
|
+
rb_raise(rb_eStandardError, strerror(errno));
|
117
|
+
}
|
118
|
+
|
119
|
+
bzero(&sa_nl, sizeof(sa_nl));
|
120
|
+
sa_nl.nl_family = AF_NETLINK;
|
121
|
+
sa_nl.nl_groups = CN_IDX_PROC;
|
122
|
+
sa_nl.nl_pid = getpid();
|
123
|
+
|
124
|
+
if (-1 == bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl))) {
|
125
|
+
rb_raise(rb_eStandardError, strerror(errno));
|
126
|
+
}
|
127
|
+
|
128
|
+
/* Fill header */
|
129
|
+
hdr = (struct nlmsghdr *)buff;
|
130
|
+
hdr->nlmsg_len = NL_MESSAGE_SIZE;
|
131
|
+
hdr->nlmsg_type = NLMSG_DONE;
|
132
|
+
hdr->nlmsg_flags = 0;
|
133
|
+
hdr->nlmsg_seq = 0;
|
134
|
+
hdr->nlmsg_pid = getpid();
|
135
|
+
|
136
|
+
/* Fill message */
|
137
|
+
msg = (struct cn_msg *)NLMSG_DATA(hdr);
|
138
|
+
msg->id.idx = CN_IDX_PROC; /* Connecting to process information */
|
139
|
+
msg->id.val = CN_VAL_PROC;
|
140
|
+
msg->seq = 0;
|
141
|
+
msg->ack = 0;
|
142
|
+
msg->flags = 0;
|
143
|
+
msg->len = sizeof(int);
|
144
|
+
*(int*)msg->data = PROC_CN_MCAST_LISTEN;
|
145
|
+
|
146
|
+
if (-1 == send(nl_sock, hdr, hdr->nlmsg_len, 0)) {
|
147
|
+
rb_raise(rb_eStandardError, strerror(errno));
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
void
|
152
|
+
Init_netlink_handler_ext()
|
153
|
+
{
|
154
|
+
proc_exit = rb_intern("proc_exit");
|
155
|
+
proc_fork = rb_intern("proc_fork");
|
156
|
+
m_call = rb_intern("call");
|
157
|
+
m_watching_pid = rb_intern("watching_pid?");
|
158
|
+
|
159
|
+
mGod = rb_const_get(rb_cObject, rb_intern("God"));
|
160
|
+
cEventHandler = rb_const_get(mGod, rb_intern("EventHandler"));
|
161
|
+
cNetlinkHandler = rb_define_class_under(mGod, "NetlinkHandler", rb_cObject);
|
162
|
+
rb_define_singleton_method(cNetlinkHandler, "handle_events", nlh_handle_events, 0);
|
163
|
+
|
164
|
+
connect_to_netlink();
|
165
|
+
}
|
166
|
+
|
167
|
+
#endif
|
data/init/god
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
#
|
3
|
+
# god Startup script for god (http://god.rubyforge.org)
|
4
|
+
#
|
5
|
+
# chkconfig: - 85 15
|
6
|
+
# description: God is an easy to configure, easy to extend monitoring \
|
7
|
+
# framework written in Ruby.
|
8
|
+
#
|
9
|
+
|
10
|
+
CONF_DIR=/etc/god
|
11
|
+
|
12
|
+
RETVAL=0
|
13
|
+
|
14
|
+
# Go no further if config directory is missing.
|
15
|
+
[ -d "$CONF_DIR" ] || exit 0
|
16
|
+
|
17
|
+
case "$1" in
|
18
|
+
start)
|
19
|
+
# Create pid directory
|
20
|
+
ruby /usr/bin/god -c $CONF_DIR/master.conf
|
21
|
+
RETVAL=$?
|
22
|
+
;;
|
23
|
+
stop)
|
24
|
+
ruby /usr/bin/god terminate
|
25
|
+
RETVAL=$?
|
26
|
+
;;
|
27
|
+
restart)
|
28
|
+
ruby /usr/bin/god terminate
|
29
|
+
ruby /usr/bin/god -c $CONF_DIR/master.conf
|
30
|
+
RETVAL=$?
|
31
|
+
;;
|
32
|
+
status)
|
33
|
+
ruby /usr/bin/god status
|
34
|
+
RETVAL=$?
|
35
|
+
;;
|
36
|
+
*)
|
37
|
+
echo "Usage: god {start|stop|restart|status}"
|
38
|
+
exit 1
|
39
|
+
;;
|
40
|
+
esac
|
41
|
+
|
42
|
+
exit $RETVAL
|
data/lib/god/behavior.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module God
|
2
|
+
|
3
|
+
class Behavior
|
4
|
+
include Configurable
|
5
|
+
|
6
|
+
attr_accessor :watch
|
7
|
+
|
8
|
+
# Generate a Behavior of the given kind. The proper class is found by camel casing the
|
9
|
+
# kind (which is given as an underscored symbol).
|
10
|
+
# +kind+ is the underscored symbol representing the class (e.g. foo_bar for God::Behaviors::FooBar)
|
11
|
+
def self.generate(kind, watch)
|
12
|
+
sym = kind.to_s.capitalize.gsub(/_(.)/){$1.upcase}.intern
|
13
|
+
b = God::Behaviors.const_get(sym).new
|
14
|
+
b.watch = watch
|
15
|
+
b
|
16
|
+
rescue NameError
|
17
|
+
raise NoSuchBehaviorError.new("No Behavior found with the class name God::Behaviors::#{sym}")
|
18
|
+
end
|
19
|
+
|
20
|
+
def valid?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
#######
|
25
|
+
|
26
|
+
def before_start
|
27
|
+
end
|
28
|
+
|
29
|
+
def after_start
|
30
|
+
end
|
31
|
+
|
32
|
+
def before_restart
|
33
|
+
end
|
34
|
+
|
35
|
+
def after_restart
|
36
|
+
end
|
37
|
+
|
38
|
+
def before_stop
|
39
|
+
end
|
40
|
+
|
41
|
+
def after_stop
|
42
|
+
end
|
43
|
+
|
44
|
+
# Construct the friendly name of this Behavior, looks like:
|
45
|
+
#
|
46
|
+
# Behavior FooBar on Watch 'baz'
|
47
|
+
def friendly_name
|
48
|
+
"Behavior " + super + " on Watch '#{self.watch.name}'"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module God
|
2
|
+
module Behaviors
|
3
|
+
|
4
|
+
class CleanPidFile < Behavior
|
5
|
+
def valid?
|
6
|
+
valid = true
|
7
|
+
valid &= complain("Attribute 'pid_file' must be specified", self) if self.watch.pid_file.nil?
|
8
|
+
valid
|
9
|
+
end
|
10
|
+
|
11
|
+
def before_start
|
12
|
+
File.delete(self.watch.pid_file)
|
13
|
+
|
14
|
+
"deleted pid file"
|
15
|
+
rescue
|
16
|
+
"no pid file to delete"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module God
|
2
|
+
module Behaviors
|
3
|
+
|
4
|
+
class CleanUnixSocket < Behavior
|
5
|
+
def valid?
|
6
|
+
valid = true
|
7
|
+
valid &= complain("Attribute 'unix_socket' must be specified", self) if self.watch.unix_socket.nil?
|
8
|
+
valid
|
9
|
+
end
|
10
|
+
|
11
|
+
def before_start
|
12
|
+
File.delete(self.watch.unix_socket)
|
13
|
+
|
14
|
+
"deleted unix socket"
|
15
|
+
rescue
|
16
|
+
"no unix socket to delete"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module God
|
2
|
+
module Behaviors
|
3
|
+
|
4
|
+
class NotifyWhenFlapping < Behavior
|
5
|
+
attr_accessor :failures # number of failures
|
6
|
+
attr_accessor :seconds # number of seconds
|
7
|
+
attr_accessor :notifier # class to notify with
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
super
|
11
|
+
@startup_times = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid?
|
15
|
+
valid = true
|
16
|
+
valid &= complain("Attribute 'failures' must be specified", self) unless self.failures
|
17
|
+
valid &= complain("Attribute 'seconds' must be specified", self) unless self.seconds
|
18
|
+
valid &= complain("Attribute 'notifier' must be specified", self) unless self.notifier
|
19
|
+
|
20
|
+
# Must take one arg or variable args
|
21
|
+
unless self.notifier.respond_to?(:notify) and [1,-1].include?(self.notifier.method(:notify).arity)
|
22
|
+
valid &= complain("The 'notifier' must have a method 'notify' which takes 1 or variable args", self)
|
23
|
+
end
|
24
|
+
|
25
|
+
valid
|
26
|
+
end
|
27
|
+
|
28
|
+
def before_start
|
29
|
+
now = Time.now.to_i
|
30
|
+
@startup_times << now
|
31
|
+
check_for_flapping(now)
|
32
|
+
end
|
33
|
+
|
34
|
+
def before_restart
|
35
|
+
now = Time.now.to_i
|
36
|
+
@startup_times << now
|
37
|
+
check_for_flapping(now)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def check_for_flapping(now)
|
43
|
+
@startup_times.select! {|time| time >= now - self.seconds }
|
44
|
+
if @startup_times.length >= self.failures
|
45
|
+
self.notifier.notify("#{self.watch.name} has called start/restart #{@startup_times.length} times in #{self.seconds} seconds")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|