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.
Files changed (108) hide show
  1. data/History.txt +261 -0
  2. data/Manifest.txt +107 -0
  3. data/README.txt +59 -0
  4. data/Rakefile +35 -0
  5. data/bin/god +127 -0
  6. data/examples/events.god +84 -0
  7. data/examples/gravatar.god +54 -0
  8. data/examples/single.god +66 -0
  9. data/ext/god/extconf.rb +55 -0
  10. data/ext/god/kqueue_handler.c +123 -0
  11. data/ext/god/netlink_handler.c +167 -0
  12. data/init/god +42 -0
  13. data/lib/god/behavior.rb +52 -0
  14. data/lib/god/behaviors/clean_pid_file.rb +21 -0
  15. data/lib/god/behaviors/clean_unix_socket.rb +21 -0
  16. data/lib/god/behaviors/notify_when_flapping.rb +51 -0
  17. data/lib/god/cli/command.rb +206 -0
  18. data/lib/god/cli/run.rb +177 -0
  19. data/lib/god/cli/version.rb +23 -0
  20. data/lib/god/condition.rb +96 -0
  21. data/lib/god/conditions/always.rb +23 -0
  22. data/lib/god/conditions/complex.rb +86 -0
  23. data/lib/god/conditions/cpu_usage.rb +80 -0
  24. data/lib/god/conditions/degrading_lambda.rb +52 -0
  25. data/lib/god/conditions/disk_usage.rb +27 -0
  26. data/lib/god/conditions/flapping.rb +128 -0
  27. data/lib/god/conditions/http_response_code.rb +168 -0
  28. data/lib/god/conditions/lambda.rb +25 -0
  29. data/lib/god/conditions/memory_usage.rb +82 -0
  30. data/lib/god/conditions/process_exits.rb +72 -0
  31. data/lib/god/conditions/process_running.rb +74 -0
  32. data/lib/god/conditions/tries.rb +44 -0
  33. data/lib/god/configurable.rb +57 -0
  34. data/lib/god/contact.rb +106 -0
  35. data/lib/god/contacts/email.rb +95 -0
  36. data/lib/god/dependency_graph.rb +41 -0
  37. data/lib/god/diagnostics.rb +37 -0
  38. data/lib/god/driver.rb +206 -0
  39. data/lib/god/errors.rb +24 -0
  40. data/lib/god/event_handler.rb +111 -0
  41. data/lib/god/event_handlers/dummy_handler.rb +13 -0
  42. data/lib/god/event_handlers/kqueue_handler.rb +17 -0
  43. data/lib/god/event_handlers/netlink_handler.rb +13 -0
  44. data/lib/god/logger.rb +120 -0
  45. data/lib/god/metric.rb +59 -0
  46. data/lib/god/process.rb +327 -0
  47. data/lib/god/registry.rb +32 -0
  48. data/lib/god/simple_logger.rb +53 -0
  49. data/lib/god/socket.rb +96 -0
  50. data/lib/god/sugar.rb +47 -0
  51. data/lib/god/system/portable_poller.rb +42 -0
  52. data/lib/god/system/process.rb +42 -0
  53. data/lib/god/system/slash_proc_poller.rb +82 -0
  54. data/lib/god/task.rb +487 -0
  55. data/lib/god/timeline.rb +25 -0
  56. data/lib/god/trigger.rb +43 -0
  57. data/lib/god/watch.rb +183 -0
  58. data/lib/god.rb +644 -0
  59. data/test/configs/child_events/child_events.god +44 -0
  60. data/test/configs/child_events/simple_server.rb +3 -0
  61. data/test/configs/child_polls/child_polls.god +37 -0
  62. data/test/configs/child_polls/simple_server.rb +12 -0
  63. data/test/configs/complex/complex.god +59 -0
  64. data/test/configs/complex/simple_server.rb +3 -0
  65. data/test/configs/contact/contact.god +74 -0
  66. data/test/configs/contact/simple_server.rb +3 -0
  67. data/test/configs/daemon_events/daemon_events.god +37 -0
  68. data/test/configs/daemon_events/simple_server.rb +8 -0
  69. data/test/configs/daemon_events/simple_server_stop.rb +11 -0
  70. data/test/configs/daemon_polls/daemon_polls.god +17 -0
  71. data/test/configs/daemon_polls/simple_server.rb +6 -0
  72. data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
  73. data/test/configs/degrading_lambda/tcp_server.rb +15 -0
  74. data/test/configs/matias/matias.god +50 -0
  75. data/test/configs/real.rb +59 -0
  76. data/test/configs/running_load/running_load.god +16 -0
  77. data/test/configs/stress/simple_server.rb +3 -0
  78. data/test/configs/stress/stress.god +15 -0
  79. data/test/configs/task/logs/.placeholder +0 -0
  80. data/test/configs/task/task.god +26 -0
  81. data/test/configs/test.rb +61 -0
  82. data/test/helper.rb +151 -0
  83. data/test/suite.rb +6 -0
  84. data/test/test_behavior.rb +21 -0
  85. data/test/test_condition.rb +50 -0
  86. data/test/test_conditions_disk_usage.rb +56 -0
  87. data/test/test_conditions_http_response_code.rb +109 -0
  88. data/test/test_conditions_process_running.rb +44 -0
  89. data/test/test_conditions_tries.rb +67 -0
  90. data/test/test_contact.rb +109 -0
  91. data/test/test_dependency_graph.rb +62 -0
  92. data/test/test_driver.rb +11 -0
  93. data/test/test_event_handler.rb +80 -0
  94. data/test/test_god.rb +598 -0
  95. data/test/test_handlers_kqueue_handler.rb +16 -0
  96. data/test/test_logger.rb +63 -0
  97. data/test/test_metric.rb +72 -0
  98. data/test/test_process.rb +246 -0
  99. data/test/test_registry.rb +15 -0
  100. data/test/test_socket.rb +42 -0
  101. data/test/test_sugar.rb +42 -0
  102. data/test/test_system_portable_poller.rb +17 -0
  103. data/test/test_system_process.rb +30 -0
  104. data/test/test_task.rb +262 -0
  105. data/test/test_timeline.rb +37 -0
  106. data/test/test_trigger.rb +59 -0
  107. data/test/test_watch.rb +279 -0
  108. 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
@@ -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
@@ -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
@@ -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