resurrected_god 0.14.0
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.
- checksums.yaml +7 -0
- data/Announce.txt +135 -0
- data/Gemfile +5 -0
- data/LICENSE +22 -0
- data/README.md +33 -0
- data/Rakefile +129 -0
- data/bin/god +134 -0
- data/doc/god.asciidoc +1592 -0
- data/doc/intro.asciidoc +20 -0
- data/ext/god/.gitignore +5 -0
- data/ext/god/extconf.rb +56 -0
- data/ext/god/kqueue_handler.c +133 -0
- data/ext/god/netlink_handler.c +182 -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 +268 -0
- data/lib/god/cli/run.rb +170 -0
- data/lib/god/cli/version.rb +23 -0
- data/lib/god/compat19.rb +33 -0
- data/lib/god/condition.rb +96 -0
- data/lib/god/conditions/always.rb +36 -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 +32 -0
- data/lib/god/conditions/file_mtime.rb +28 -0
- data/lib/god/conditions/file_touched.rb +44 -0
- data/lib/god/conditions/flapping.rb +128 -0
- data/lib/god/conditions/http_response_code.rb +184 -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 +66 -0
- data/lib/god/conditions/process_running.rb +63 -0
- data/lib/god/conditions/socket_responding.rb +142 -0
- data/lib/god/conditions/tries.rb +44 -0
- data/lib/god/configurable.rb +57 -0
- data/lib/god/contact.rb +114 -0
- data/lib/god/contacts/airbrake.rb +44 -0
- data/lib/god/contacts/campfire.rb +121 -0
- data/lib/god/contacts/email.rb +130 -0
- data/lib/god/contacts/hipchat.rb +117 -0
- data/lib/god/contacts/jabber.rb +75 -0
- data/lib/god/contacts/prowl.rb +57 -0
- data/lib/god/contacts/scout.rb +55 -0
- data/lib/god/contacts/sensu.rb +59 -0
- data/lib/god/contacts/slack.rb +98 -0
- data/lib/god/contacts/statsd.rb +46 -0
- data/lib/god/contacts/twitter.rb +51 -0
- data/lib/god/contacts/webhook.rb +74 -0
- data/lib/god/driver.rb +238 -0
- data/lib/god/errors.rb +24 -0
- data/lib/god/event_handler.rb +112 -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 +109 -0
- data/lib/god/metric.rb +87 -0
- data/lib/god/process.rb +381 -0
- data/lib/god/registry.rb +32 -0
- data/lib/god/simple_logger.rb +59 -0
- data/lib/god/socket.rb +113 -0
- data/lib/god/sugar.rb +62 -0
- data/lib/god/sys_logger.rb +45 -0
- data/lib/god/system/portable_poller.rb +42 -0
- data/lib/god/system/process.rb +50 -0
- data/lib/god/system/slash_proc_poller.rb +92 -0
- data/lib/god/task.rb +552 -0
- data/lib/god/timeline.rb +25 -0
- data/lib/god/trigger.rb +43 -0
- data/lib/god/version.rb +4 -0
- data/lib/god/watch.rb +340 -0
- data/lib/god.rb +777 -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 +118 -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/keepalive/keepalive.god +9 -0
- data/test/configs/keepalive/keepalive.rb +12 -0
- data/test/configs/lifecycle/lifecycle.god +25 -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/stop_options/simple_server.rb +12 -0
- data/test/configs/stop_options/stop_options.god +39 -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/configs/usr1_trapper.rb +10 -0
- data/test/helper.rb +172 -0
- data/test/suite.rb +6 -0
- data/test/test_airbrake.rb +14 -0
- data/test/test_behavior.rb +18 -0
- data/test/test_campfire.rb +22 -0
- data/test/test_condition.rb +52 -0
- data/test/test_conditions_disk_usage.rb +50 -0
- data/test/test_conditions_http_response_code.rb +109 -0
- data/test/test_conditions_process_running.rb +40 -0
- data/test/test_conditions_socket_responding.rb +176 -0
- data/test/test_conditions_tries.rb +67 -0
- data/test/test_contact.rb +109 -0
- data/test/test_driver.rb +26 -0
- data/test/test_email.rb +34 -0
- data/test/test_event_handler.rb +82 -0
- data/test/test_god.rb +710 -0
- data/test/test_god_system.rb +201 -0
- data/test/test_handlers_kqueue_handler.rb +16 -0
- data/test/test_hipchat.rb +23 -0
- data/test/test_jabber.rb +29 -0
- data/test/test_logger.rb +55 -0
- data/test/test_metric.rb +74 -0
- data/test/test_process.rb +263 -0
- data/test/test_prowl.rb +15 -0
- data/test/test_registry.rb +15 -0
- data/test/test_sensu.rb +11 -0
- data/test/test_slack.rb +57 -0
- data/test/test_socket.rb +34 -0
- data/test/test_statsd.rb +22 -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 +246 -0
- data/test/test_timeline.rb +37 -0
- data/test/test_trigger.rb +63 -0
- data/test/test_watch.rb +286 -0
- data/test/test_webhook.rb +22 -0
- metadata +476 -0
data/doc/intro.asciidoc
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
God: The Ruby Framework for Process Management
|
|
2
|
+
==============================================
|
|
3
|
+
Tom Preston-Werner <tom@mojombo.com>
|
|
4
|
+
|
|
5
|
+
God is an easy to configure, easy to extend monitoring framework written in
|
|
6
|
+
Ruby.
|
|
7
|
+
|
|
8
|
+
Keeping your server processes and tasks running should be a simple part of
|
|
9
|
+
your deployment process. God aims to be the simplest, most powerful monitoring
|
|
10
|
+
application available.
|
|
11
|
+
|
|
12
|
+
Features
|
|
13
|
+
--------
|
|
14
|
+
|
|
15
|
+
* Config file is written in Ruby
|
|
16
|
+
* Easily write your own custom conditions in Ruby
|
|
17
|
+
* Supports both poll and event based conditions
|
|
18
|
+
* Different poll conditions can have different intervals
|
|
19
|
+
* Integrated notification system (write your own too!)
|
|
20
|
+
* Easily control non-daemonizing scripts
|
data/ext/god/.gitignore
ADDED
data/ext/god/extconf.rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
have_func('rb_wait_for_single_fd')
|
|
13
|
+
case RUBY_PLATFORM
|
|
14
|
+
when /bsd/i, /darwin/i
|
|
15
|
+
unless have_header('sys/event.h')
|
|
16
|
+
puts
|
|
17
|
+
puts "Missing 'sys/event.h' header"
|
|
18
|
+
fail = true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if fail
|
|
22
|
+
puts
|
|
23
|
+
puts "Events handler could not be compiled (see above error). Your god installation will not support event conditions."
|
|
24
|
+
create_dummy_makefile
|
|
25
|
+
else
|
|
26
|
+
create_makefile 'kqueue_handler_ext'
|
|
27
|
+
end
|
|
28
|
+
when /linux/i
|
|
29
|
+
unless have_header('linux/netlink.h')
|
|
30
|
+
puts
|
|
31
|
+
puts "Missing 'linux/netlink.h' header(s)"
|
|
32
|
+
puts "You may need to install a header package for your system"
|
|
33
|
+
fail = true
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
unless have_header('linux/connector.h') && have_header('linux/cn_proc.h')
|
|
37
|
+
puts
|
|
38
|
+
puts "Missing 'linux/connector.h', or 'linux/cn_proc.h' header(s)"
|
|
39
|
+
puts "These are only available in Linux kernel 2.6.15 and later (run `uname -a` to find yours)"
|
|
40
|
+
puts "You may need to install a header package for your system"
|
|
41
|
+
fail = true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if fail
|
|
45
|
+
puts
|
|
46
|
+
puts "Events handler could not be compiled (see above error). Your god installation will not support event conditions."
|
|
47
|
+
create_dummy_makefile
|
|
48
|
+
else
|
|
49
|
+
create_makefile 'netlink_handler_ext'
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
puts
|
|
53
|
+
puts "Unsupported platform '#{RUBY_PLATFORM}'. Supported platforms are BSD, DARWIN, and LINUX."
|
|
54
|
+
puts "Your god installation will not support event conditions."
|
|
55
|
+
create_dummy_makefile
|
|
56
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
|
2
|
+
|
|
3
|
+
#include <ruby.h>
|
|
4
|
+
#include <sys/event.h>
|
|
5
|
+
#include <sys/time.h>
|
|
6
|
+
#include <errno.h>
|
|
7
|
+
|
|
8
|
+
#ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
|
|
9
|
+
#include <ruby/io.h>
|
|
10
|
+
#endif
|
|
11
|
+
|
|
12
|
+
static VALUE mGod;
|
|
13
|
+
static VALUE cKQueueHandler;
|
|
14
|
+
static VALUE cEventHandler;
|
|
15
|
+
|
|
16
|
+
static ID proc_exit;
|
|
17
|
+
static ID proc_fork;
|
|
18
|
+
static ID m_call;
|
|
19
|
+
static ID m_size;
|
|
20
|
+
static ID m_deregister;
|
|
21
|
+
|
|
22
|
+
static int kq;
|
|
23
|
+
int num_events;
|
|
24
|
+
|
|
25
|
+
#define NUM_EVENTS FIX2INT(rb_funcall(rb_cv_get(cEventHandler, "@@actions"), m_size, 0))
|
|
26
|
+
|
|
27
|
+
VALUE
|
|
28
|
+
kqh_event_mask(VALUE klass, VALUE sym)
|
|
29
|
+
{
|
|
30
|
+
ID id = SYM2ID(sym);
|
|
31
|
+
if (proc_exit == id) {
|
|
32
|
+
return UINT2NUM(NOTE_EXIT);
|
|
33
|
+
} else if (proc_fork == id) {
|
|
34
|
+
return UINT2NUM(NOTE_FORK);
|
|
35
|
+
} else {
|
|
36
|
+
rb_raise(rb_eNotImpError, "Event `%s` not implemented", rb_id2name(id));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return Qnil;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
VALUE
|
|
44
|
+
kqh_monitor_process(VALUE klass, VALUE pid, VALUE mask)
|
|
45
|
+
{
|
|
46
|
+
struct kevent new_event;
|
|
47
|
+
ID event;
|
|
48
|
+
|
|
49
|
+
(void)event; //!< Silence warning about unused var, should be removed?
|
|
50
|
+
|
|
51
|
+
u_int fflags = NUM2UINT(mask);
|
|
52
|
+
|
|
53
|
+
EV_SET(&new_event, FIX2UINT(pid), EVFILT_PROC,
|
|
54
|
+
EV_ADD | EV_ENABLE, fflags, 0, 0);
|
|
55
|
+
|
|
56
|
+
if (-1 == kevent(kq, &new_event, 1, NULL, 0, NULL)) {
|
|
57
|
+
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
num_events = NUM_EVENTS;
|
|
61
|
+
|
|
62
|
+
return Qnil;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
VALUE
|
|
66
|
+
kqh_handle_events()
|
|
67
|
+
{
|
|
68
|
+
int nevents, i, num_to_fetch;
|
|
69
|
+
struct kevent *events;
|
|
70
|
+
|
|
71
|
+
#ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
|
|
72
|
+
rb_wait_for_single_fd(kq, RB_WAITFD_IN, NULL);
|
|
73
|
+
#else
|
|
74
|
+
fd_set read_set;
|
|
75
|
+
|
|
76
|
+
FD_ZERO(&read_set);
|
|
77
|
+
FD_SET(kq, &read_set);
|
|
78
|
+
|
|
79
|
+
// Don't actually run this method until we've got an event
|
|
80
|
+
rb_thread_select(kq + 1, &read_set, NULL, NULL, NULL);
|
|
81
|
+
#endif
|
|
82
|
+
// Grabbing num_events once for thread safety
|
|
83
|
+
num_to_fetch = num_events;
|
|
84
|
+
events = (struct kevent*)malloc(num_to_fetch * sizeof(struct kevent));
|
|
85
|
+
|
|
86
|
+
if (NULL == events) {
|
|
87
|
+
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
nevents = kevent(kq, NULL, 0, events, num_to_fetch, NULL);
|
|
91
|
+
|
|
92
|
+
if (-1 == nevents) {
|
|
93
|
+
free(events);
|
|
94
|
+
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
|
95
|
+
} else {
|
|
96
|
+
for (i = 0; i < nevents; i++) {
|
|
97
|
+
if (events[i].fflags & NOTE_EXIT) {
|
|
98
|
+
rb_funcall(cEventHandler, m_call, 2, INT2NUM(events[i].ident), ID2SYM(proc_exit));
|
|
99
|
+
} else if (events[i].fflags & NOTE_FORK) {
|
|
100
|
+
rb_funcall(cEventHandler, m_call, 2, INT2NUM(events[i].ident), ID2SYM(proc_fork));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
free(events);
|
|
106
|
+
|
|
107
|
+
return INT2FIX(nevents);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
void
|
|
111
|
+
Init_kqueue_handler_ext()
|
|
112
|
+
{
|
|
113
|
+
kq = kqueue();
|
|
114
|
+
|
|
115
|
+
if (kq == -1) {
|
|
116
|
+
rb_raise(rb_eStandardError, "kqueue initialization failed");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
proc_exit = rb_intern("proc_exit");
|
|
120
|
+
proc_fork = rb_intern("proc_fork");
|
|
121
|
+
m_call = rb_intern("call");
|
|
122
|
+
m_size = rb_intern("size");
|
|
123
|
+
m_deregister = rb_intern("deregister");
|
|
124
|
+
|
|
125
|
+
mGod = rb_const_get(rb_cObject, rb_intern("God"));
|
|
126
|
+
cEventHandler = rb_const_get(mGod, rb_intern("EventHandler"));
|
|
127
|
+
cKQueueHandler = rb_define_class_under(mGod, "KQueueHandler", rb_cObject);
|
|
128
|
+
rb_define_singleton_method(cKQueueHandler, "monitor_process", kqh_monitor_process, 2);
|
|
129
|
+
rb_define_singleton_method(cKQueueHandler, "handle_events", kqh_handle_events, 0);
|
|
130
|
+
rb_define_singleton_method(cKQueueHandler, "event_mask", kqh_event_mask, 1);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#endif
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#ifdef __linux__ /* only build on linux */
|
|
2
|
+
|
|
3
|
+
#include <ruby.h>
|
|
4
|
+
|
|
5
|
+
#ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
|
|
6
|
+
#include <ruby/io.h>
|
|
7
|
+
#endif
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
#include <sys/types.h>
|
|
11
|
+
#include <unistd.h>
|
|
12
|
+
#include <sys/socket.h>
|
|
13
|
+
#include <linux/netlink.h>
|
|
14
|
+
#include <linux/connector.h>
|
|
15
|
+
#define _LINUX_TIME_H
|
|
16
|
+
#include <linux/cn_proc.h>
|
|
17
|
+
#include <errno.h>
|
|
18
|
+
|
|
19
|
+
static VALUE mGod;
|
|
20
|
+
static VALUE cNetlinkHandler;
|
|
21
|
+
static VALUE cEventHandler;
|
|
22
|
+
|
|
23
|
+
static ID proc_exit;
|
|
24
|
+
static ID proc_fork;
|
|
25
|
+
static ID m_call;
|
|
26
|
+
static ID m_watching_pid;
|
|
27
|
+
|
|
28
|
+
static int nl_sock; /* socket for netlink connection */
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
VALUE
|
|
32
|
+
nlh_handle_events()
|
|
33
|
+
{
|
|
34
|
+
char buff[CONNECTOR_MAX_MSG_SIZE];
|
|
35
|
+
struct nlmsghdr *hdr;
|
|
36
|
+
struct proc_event *event;
|
|
37
|
+
|
|
38
|
+
VALUE extra_data;
|
|
39
|
+
|
|
40
|
+
#ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
|
|
41
|
+
int ret;
|
|
42
|
+
if((ret = rb_wait_for_single_fd(nl_sock, RB_WAITFD_IN, NULL)) < 0){
|
|
43
|
+
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
|
44
|
+
}
|
|
45
|
+
/* If there were no events detected, return */
|
|
46
|
+
if(!(ret & RB_WAITFD_IN)){
|
|
47
|
+
return INT2FIX(0);
|
|
48
|
+
}
|
|
49
|
+
#else
|
|
50
|
+
fd_set fds;
|
|
51
|
+
|
|
52
|
+
FD_ZERO(&fds);
|
|
53
|
+
FD_SET(nl_sock, &fds);
|
|
54
|
+
|
|
55
|
+
if (0 > rb_thread_select(nl_sock + 1, &fds, NULL, NULL, NULL)) {
|
|
56
|
+
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* If there were no events detected, return */
|
|
60
|
+
if (! FD_ISSET(nl_sock, &fds)) {
|
|
61
|
+
return INT2FIX(0);
|
|
62
|
+
}
|
|
63
|
+
#endif
|
|
64
|
+
|
|
65
|
+
/* if there are events, make calls */
|
|
66
|
+
if (-1 == recv(nl_sock, buff, sizeof(buff), 0)) {
|
|
67
|
+
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
hdr = (struct nlmsghdr *)buff;
|
|
71
|
+
|
|
72
|
+
if (NLMSG_ERROR == hdr->nlmsg_type) {
|
|
73
|
+
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
|
74
|
+
} else if (NLMSG_DONE == hdr->nlmsg_type) {
|
|
75
|
+
|
|
76
|
+
event = (struct proc_event *)((struct cn_msg *)NLMSG_DATA(hdr))->data;
|
|
77
|
+
|
|
78
|
+
switch(event->what) {
|
|
79
|
+
case PROC_EVENT_EXIT:
|
|
80
|
+
if (Qnil == rb_funcall(cEventHandler, m_watching_pid, 1, INT2FIX(event->event_data.exit.process_pid))) {
|
|
81
|
+
return INT2FIX(0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
extra_data = rb_hash_new();
|
|
85
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("pid")), INT2FIX(event->event_data.exit.process_pid));
|
|
86
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("exit_code")), INT2FIX(event->event_data.exit.exit_code));
|
|
87
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("exit_signal")), INT2FIX(event->event_data.exit.exit_signal));
|
|
88
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("thread_group_id")), INT2FIX(event->event_data.exit.process_tgid));
|
|
89
|
+
|
|
90
|
+
rb_funcall(cEventHandler, m_call, 3, INT2FIX(event->event_data.exit.process_pid), ID2SYM(proc_exit), extra_data);
|
|
91
|
+
return INT2FIX(1);
|
|
92
|
+
|
|
93
|
+
case PROC_EVENT_FORK:
|
|
94
|
+
if (Qnil == rb_funcall(cEventHandler, m_watching_pid, 1, INT2FIX(event->event_data.fork.parent_pid))) {
|
|
95
|
+
return INT2FIX(0);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
extra_data = rb_hash_new();
|
|
99
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("parent_pid")), INT2FIX(event->event_data.fork.parent_pid));
|
|
100
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("parent_thread_group_id")), INT2FIX(event->event_data.fork.parent_tgid));
|
|
101
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("child_pid")), INT2FIX(event->event_data.fork.child_pid));
|
|
102
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("child_thread_group_id")), INT2FIX(event->event_data.fork.child_tgid));
|
|
103
|
+
|
|
104
|
+
rb_funcall(cEventHandler, m_call, 3, INT2FIX(event->event_data.fork.parent_pid), ID2SYM(proc_fork), extra_data);
|
|
105
|
+
return INT2FIX(1);
|
|
106
|
+
|
|
107
|
+
default:
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return Qnil;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
#define NL_MESSAGE_SIZE (sizeof(struct nlmsghdr) + sizeof(struct cn_msg) + \
|
|
117
|
+
sizeof(int))
|
|
118
|
+
|
|
119
|
+
void
|
|
120
|
+
connect_to_netlink()
|
|
121
|
+
{
|
|
122
|
+
struct sockaddr_nl sa_nl; /* netlink interface info */
|
|
123
|
+
char buff[NL_MESSAGE_SIZE];
|
|
124
|
+
struct nlmsghdr *hdr; /* for telling netlink what we want */
|
|
125
|
+
struct cn_msg *msg; /* the actual connector message */
|
|
126
|
+
|
|
127
|
+
/* connect to netlink socket */
|
|
128
|
+
nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
|
|
129
|
+
|
|
130
|
+
if (-1 == nl_sock) {
|
|
131
|
+
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
bzero(&sa_nl, sizeof(sa_nl));
|
|
135
|
+
sa_nl.nl_family = AF_NETLINK;
|
|
136
|
+
sa_nl.nl_groups = CN_IDX_PROC;
|
|
137
|
+
sa_nl.nl_pid = getpid();
|
|
138
|
+
|
|
139
|
+
if (-1 == bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl))) {
|
|
140
|
+
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* Fill header */
|
|
144
|
+
hdr = (struct nlmsghdr *)buff;
|
|
145
|
+
hdr->nlmsg_len = NL_MESSAGE_SIZE;
|
|
146
|
+
hdr->nlmsg_type = NLMSG_DONE;
|
|
147
|
+
hdr->nlmsg_flags = 0;
|
|
148
|
+
hdr->nlmsg_seq = 0;
|
|
149
|
+
hdr->nlmsg_pid = getpid();
|
|
150
|
+
|
|
151
|
+
/* Fill message */
|
|
152
|
+
msg = (struct cn_msg *)NLMSG_DATA(hdr);
|
|
153
|
+
msg->id.idx = CN_IDX_PROC; /* Connecting to process information */
|
|
154
|
+
msg->id.val = CN_VAL_PROC;
|
|
155
|
+
msg->seq = 0;
|
|
156
|
+
msg->ack = 0;
|
|
157
|
+
msg->flags = 0;
|
|
158
|
+
msg->len = sizeof(int);
|
|
159
|
+
*(int*)msg->data = PROC_CN_MCAST_LISTEN;
|
|
160
|
+
|
|
161
|
+
if (-1 == send(nl_sock, hdr, hdr->nlmsg_len, 0)) {
|
|
162
|
+
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
void
|
|
167
|
+
Init_netlink_handler_ext()
|
|
168
|
+
{
|
|
169
|
+
proc_exit = rb_intern("proc_exit");
|
|
170
|
+
proc_fork = rb_intern("proc_fork");
|
|
171
|
+
m_call = rb_intern("call");
|
|
172
|
+
m_watching_pid = rb_intern("watching_pid?");
|
|
173
|
+
|
|
174
|
+
mGod = rb_const_get(rb_cObject, rb_intern("God"));
|
|
175
|
+
cEventHandler = rb_const_get(mGod, rb_intern("EventHandler"));
|
|
176
|
+
cNetlinkHandler = rb_define_class_under(mGod, "NetlinkHandler", rb_cObject);
|
|
177
|
+
rb_define_singleton_method(cNetlinkHandler, "handle_events", nlh_handle_events, 0);
|
|
178
|
+
|
|
179
|
+
connect_to_netlink();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
#endif
|
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
|