god 0.11.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Announce.txt +6 -6
- data/Gemfile +2 -0
- data/History.txt +19 -2
- data/{README.txt → LICENSE} +0 -37
- data/README.md +31 -0
- data/Rakefile +80 -38
- data/bin/god +21 -21
- data/doc/god.asciidoc +1487 -0
- data/doc/intro.asciidoc +20 -0
- data/ext/god/extconf.rb +3 -3
- data/ext/god/kqueue_handler.c +18 -18
- data/ext/god/netlink_handler.c +31 -31
- data/god.gemspec +24 -16
- data/lib/god.rb +261 -204
- data/lib/god/behavior.rb +14 -14
- data/lib/god/behaviors/clean_pid_file.rb +5 -5
- data/lib/god/behaviors/clean_unix_socket.rb +10 -10
- data/lib/god/behaviors/notify_when_flapping.rb +12 -12
- data/lib/god/cli/command.rb +59 -46
- data/lib/god/cli/run.rb +33 -37
- data/lib/god/cli/version.rb +6 -6
- data/lib/god/compat19.rb +1 -4
- data/lib/god/condition.rb +21 -21
- data/lib/god/conditions/always.rb +19 -6
- data/lib/god/conditions/complex.rb +18 -18
- data/lib/god/conditions/cpu_usage.rb +14 -14
- data/lib/god/conditions/degrading_lambda.rb +8 -8
- data/lib/god/conditions/disk_usage.rb +5 -5
- data/lib/god/conditions/flapping.rb +23 -23
- data/lib/god/conditions/http_response_code.rb +35 -19
- data/lib/god/conditions/lambda.rb +2 -2
- data/lib/god/conditions/memory_usage.rb +13 -13
- data/lib/god/conditions/process_exits.rb +14 -20
- data/lib/god/conditions/process_running.rb +16 -25
- data/lib/god/conditions/socket_responding.rb +132 -0
- data/lib/god/conditions/tries.rb +10 -10
- data/lib/god/configurable.rb +10 -10
- data/lib/god/contact.rb +20 -20
- data/lib/god/contacts/email.rb +7 -4
- data/lib/god/contacts/jabber.rb +1 -1
- data/lib/god/driver.rb +96 -64
- data/lib/god/errors.rb +9 -9
- data/lib/god/event_handler.rb +19 -19
- data/lib/god/event_handlers/dummy_handler.rb +4 -4
- data/lib/god/event_handlers/kqueue_handler.rb +3 -3
- data/lib/god/event_handlers/netlink_handler.rb +2 -2
- data/lib/god/logger.rb +13 -13
- data/lib/god/metric.rb +50 -22
- data/lib/god/process.rb +53 -52
- data/lib/god/registry.rb +7 -7
- data/lib/god/simple_logger.rb +14 -14
- data/lib/god/socket.rb +11 -11
- data/lib/god/sugar.rb +30 -15
- data/lib/god/sys_logger.rb +2 -2
- data/lib/god/system/portable_poller.rb +8 -8
- data/lib/god/system/process.rb +8 -8
- data/lib/god/system/slash_proc_poller.rb +13 -13
- data/lib/god/task.rb +237 -188
- data/lib/god/timeline.rb +5 -5
- data/lib/god/trigger.rb +11 -11
- data/lib/god/watch.rb +205 -53
- data/test/configs/child_events/child_events.god +5 -5
- data/test/configs/child_events/simple_server.rb +1 -1
- data/test/configs/child_polls/child_polls.god +4 -4
- data/test/configs/child_polls/simple_server.rb +4 -4
- data/test/configs/complex/complex.god +7 -7
- data/test/configs/complex/simple_server.rb +1 -1
- data/test/configs/contact/contact.god +1 -1
- data/test/configs/contact/simple_server.rb +1 -1
- data/test/configs/daemon_events/daemon_events.god +5 -5
- data/test/configs/daemon_events/simple_server.rb +1 -1
- data/test/configs/daemon_events/simple_server_stop.rb +1 -1
- data/test/configs/daemon_polls/daemon_polls.god +3 -3
- data/test/configs/daemon_polls/simple_server.rb +1 -1
- data/test/configs/degrading_lambda/degrading_lambda.god +3 -3
- data/test/configs/keepalive/keepalive.god +9 -0
- data/test/configs/keepalive/keepalive.rb +12 -0
- data/test/configs/lifecycle/lifecycle.god +2 -2
- data/test/configs/matias/matias.god +6 -6
- data/test/configs/real.rb +7 -7
- data/test/configs/running_load/running_load.god +2 -2
- data/test/configs/stop_options/simple_server.rb +1 -1
- data/test/configs/stress/simple_server.rb +1 -1
- data/test/configs/stress/stress.god +2 -2
- data/test/configs/task/task.god +5 -5
- data/test/configs/test.rb +7 -7
- data/test/helper.rb +8 -8
- data/test/test_behavior.rb +3 -3
- data/test/test_campfire.rb +1 -2
- data/test/test_condition.rb +10 -10
- data/test/test_conditions_disk_usage.rb +12 -12
- data/test/test_conditions_http_response_code.rb +24 -24
- data/test/test_conditions_process_running.rb +7 -7
- data/test/test_conditions_socket_responding.rb +122 -0
- data/test/test_conditions_tries.rb +12 -12
- data/test/test_contact.rb +19 -19
- data/test/test_driver.rb +17 -3
- data/test/test_event_handler.rb +12 -12
- data/test/test_god.rb +195 -117
- data/test/test_handlers_kqueue_handler.rb +4 -4
- data/test/test_jabber.rb +1 -1
- data/test/test_logger.rb +17 -17
- data/test/test_metric.rb +16 -16
- data/test/test_process.rb +47 -41
- data/test/test_prowl.rb +1 -1
- data/test/test_registry.rb +2 -2
- data/test/test_socket.rb +3 -3
- data/test/test_sugar.rb +7 -7
- data/test/test_system_portable_poller.rb +1 -1
- data/test/test_system_process.rb +5 -5
- data/test/test_task.rb +57 -57
- data/test/test_timeline.rb +8 -8
- data/test/test_trigger.rb +16 -16
- data/test/test_watch.rb +69 -62
- metadata +182 -69
- data/lib/god/dependency_graph.rb +0 -41
- data/lib/god/diagnostics.rb +0 -37
- data/test/test_dependency_graph.rb +0 -62
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/extconf.rb
CHANGED
@@ -16,7 +16,7 @@ when /bsd/i, /darwin/i
|
|
16
16
|
puts "Missing 'sys/event.h' header"
|
17
17
|
fail = true
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
if fail
|
21
21
|
puts
|
22
22
|
puts "Events handler could not be compiled (see above error). Your god installation will not support event conditions."
|
@@ -31,7 +31,7 @@ when /linux/i
|
|
31
31
|
puts "You may need to install a header package for your system"
|
32
32
|
fail = true
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
unless have_header('linux/connector.h') && have_header('linux/cn_proc.h')
|
36
36
|
puts
|
37
37
|
puts "Missing 'linux/connector.h', or 'linux/cn_proc.h' header(s)"
|
@@ -39,7 +39,7 @@ when /linux/i
|
|
39
39
|
puts "You may need to install a header package for your system"
|
40
40
|
fail = true
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
if fail
|
44
44
|
puts
|
45
45
|
puts "Events handler could not be compiled (see above error). Your god installation will not support event conditions."
|
data/ext/god/kqueue_handler.c
CHANGED
@@ -31,11 +31,11 @@ kqh_event_mask(VALUE klass, VALUE sym)
|
|
31
31
|
} else {
|
32
32
|
rb_raise(rb_eNotImpError, "Event `%s` not implemented", rb_id2name(id));
|
33
33
|
}
|
34
|
-
|
34
|
+
|
35
35
|
return Qnil;
|
36
36
|
}
|
37
37
|
|
38
|
-
|
38
|
+
|
39
39
|
VALUE
|
40
40
|
kqh_monitor_process(VALUE klass, VALUE pid, VALUE mask)
|
41
41
|
{
|
@@ -43,18 +43,18 @@ kqh_monitor_process(VALUE klass, VALUE pid, VALUE mask)
|
|
43
43
|
ID event;
|
44
44
|
|
45
45
|
(void)event; //!< Silence warning about unused var, should be removed?
|
46
|
-
|
46
|
+
|
47
47
|
u_int fflags = NUM2UINT(mask);
|
48
|
-
|
48
|
+
|
49
49
|
EV_SET(&new_event, FIX2UINT(pid), EVFILT_PROC,
|
50
50
|
EV_ADD | EV_ENABLE, fflags, 0, 0);
|
51
|
-
|
51
|
+
|
52
52
|
if (-1 == kevent(kq, &new_event, 1, NULL, 0, NULL)) {
|
53
53
|
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
54
54
|
}
|
55
|
-
|
55
|
+
|
56
56
|
num_events = NUM_EVENTS;
|
57
|
-
|
57
|
+
|
58
58
|
return Qnil;
|
59
59
|
}
|
60
60
|
|
@@ -64,23 +64,23 @@ kqh_handle_events()
|
|
64
64
|
int nevents, i, num_to_fetch;
|
65
65
|
struct kevent *events;
|
66
66
|
fd_set read_set;
|
67
|
-
|
67
|
+
|
68
68
|
FD_ZERO(&read_set);
|
69
69
|
FD_SET(kq, &read_set);
|
70
|
-
|
70
|
+
|
71
71
|
// Don't actually run this method until we've got an event
|
72
|
-
rb_thread_select(kq + 1, &read_set, NULL, NULL, NULL);
|
73
|
-
|
72
|
+
rb_thread_select(kq + 1, &read_set, NULL, NULL, NULL);
|
73
|
+
|
74
74
|
// Grabbing num_events once for thread safety
|
75
75
|
num_to_fetch = num_events;
|
76
76
|
events = (struct kevent*)malloc(num_to_fetch * sizeof(struct kevent));
|
77
|
-
|
77
|
+
|
78
78
|
if (NULL == events) {
|
79
79
|
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
80
80
|
}
|
81
|
-
|
81
|
+
|
82
82
|
nevents = kevent(kq, NULL, 0, events, num_to_fetch, NULL);
|
83
|
-
|
83
|
+
|
84
84
|
if (-1 == nevents) {
|
85
85
|
free(events);
|
86
86
|
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
@@ -93,7 +93,7 @@ kqh_handle_events()
|
|
93
93
|
}
|
94
94
|
}
|
95
95
|
}
|
96
|
-
|
96
|
+
|
97
97
|
free(events);
|
98
98
|
|
99
99
|
return INT2FIX(nevents);
|
@@ -103,17 +103,17 @@ void
|
|
103
103
|
Init_kqueue_handler_ext()
|
104
104
|
{
|
105
105
|
kq = kqueue();
|
106
|
-
|
106
|
+
|
107
107
|
if (kq == -1) {
|
108
108
|
rb_raise(rb_eStandardError, "kqueue initilization failed");
|
109
109
|
}
|
110
|
-
|
110
|
+
|
111
111
|
proc_exit = rb_intern("proc_exit");
|
112
112
|
proc_fork = rb_intern("proc_fork");
|
113
113
|
m_call = rb_intern("call");
|
114
114
|
m_size = rb_intern("size");
|
115
115
|
m_deregister = rb_intern("deregister");
|
116
|
-
|
116
|
+
|
117
117
|
mGod = rb_const_get(rb_cObject, rb_intern("God"));
|
118
118
|
cEventHandler = rb_const_get(mGod, rb_intern("EventHandler"));
|
119
119
|
cKQueueHandler = rb_define_class_under(mGod, "KQueueHandler", rb_cObject);
|
data/ext/god/netlink_handler.c
CHANGED
@@ -28,42 +28,42 @@ nlh_handle_events()
|
|
28
28
|
char buff[CONNECTOR_MAX_MSG_SIZE];
|
29
29
|
struct nlmsghdr *hdr;
|
30
30
|
struct proc_event *event;
|
31
|
-
|
31
|
+
|
32
32
|
VALUE extra_data;
|
33
|
-
|
33
|
+
|
34
34
|
fd_set fds;
|
35
|
-
|
35
|
+
|
36
36
|
FD_ZERO(&fds);
|
37
37
|
FD_SET(nl_sock, &fds);
|
38
|
-
|
38
|
+
|
39
39
|
if (0 > rb_thread_select(nl_sock + 1, &fds, NULL, NULL, NULL)) {
|
40
40
|
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
41
41
|
}
|
42
|
-
|
42
|
+
|
43
43
|
/* If there were no events detected, return */
|
44
44
|
if (! FD_ISSET(nl_sock, &fds)) {
|
45
45
|
return INT2FIX(0);
|
46
46
|
}
|
47
|
-
|
48
|
-
/* if there are events, make calls */
|
47
|
+
|
48
|
+
/* if there are events, make calls */
|
49
49
|
if (-1 == recv(nl_sock, buff, sizeof(buff), 0)) {
|
50
50
|
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
51
51
|
}
|
52
|
-
|
52
|
+
|
53
53
|
hdr = (struct nlmsghdr *)buff;
|
54
|
-
|
54
|
+
|
55
55
|
if (NLMSG_ERROR == hdr->nlmsg_type) {
|
56
56
|
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
57
|
-
} else if (NLMSG_DONE == hdr->nlmsg_type) {
|
58
|
-
|
57
|
+
} else if (NLMSG_DONE == hdr->nlmsg_type) {
|
58
|
+
|
59
59
|
event = (struct proc_event *)((struct cn_msg *)NLMSG_DATA(hdr))->data;
|
60
|
-
|
60
|
+
|
61
61
|
switch(event->what) {
|
62
62
|
case PROC_EVENT_EXIT:
|
63
63
|
if (Qnil == rb_funcall(cEventHandler, m_watching_pid, 1, INT2FIX(event->event_data.exit.process_pid))) {
|
64
64
|
return INT2FIX(0);
|
65
65
|
}
|
66
|
-
|
66
|
+
|
67
67
|
extra_data = rb_hash_new();
|
68
68
|
rb_hash_aset(extra_data, ID2SYM(rb_intern("pid")), INT2FIX(event->event_data.exit.process_pid));
|
69
69
|
rb_hash_aset(extra_data, ID2SYM(rb_intern("exit_code")), INT2FIX(event->event_data.exit.exit_code));
|
@@ -72,18 +72,18 @@ nlh_handle_events()
|
|
72
72
|
|
73
73
|
rb_funcall(cEventHandler, m_call, 3, INT2FIX(event->event_data.exit.process_pid), ID2SYM(proc_exit), extra_data);
|
74
74
|
return INT2FIX(1);
|
75
|
-
|
75
|
+
|
76
76
|
case PROC_EVENT_FORK:
|
77
77
|
if (Qnil == rb_funcall(cEventHandler, m_watching_pid, 1, INT2FIX(event->event_data.fork.parent_pid))) {
|
78
78
|
return INT2FIX(0);
|
79
79
|
}
|
80
|
-
|
80
|
+
|
81
81
|
extra_data = rb_hash_new();
|
82
|
-
rb_hash_aset(extra_data, rb_intern("parent_pid"), INT2FIX(event->event_data.fork.parent_pid));
|
83
|
-
rb_hash_aset(extra_data, rb_intern("parent_thread_group_id"), INT2FIX(event->event_data.fork.parent_tgid));
|
84
|
-
rb_hash_aset(extra_data, rb_intern("child_pid"), INT2FIX(event->event_data.fork.child_pid));
|
85
|
-
rb_hash_aset(extra_data, rb_intern("child_thread_group_id"), INT2FIX(event->event_data.fork.child_tgid));
|
86
|
-
|
82
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("parent_pid")), INT2FIX(event->event_data.fork.parent_pid));
|
83
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("parent_thread_group_id")), INT2FIX(event->event_data.fork.parent_tgid));
|
84
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("child_pid")), INT2FIX(event->event_data.fork.child_pid));
|
85
|
+
rb_hash_aset(extra_data, ID2SYM(rb_intern("child_thread_group_id")), INT2FIX(event->event_data.fork.child_tgid));
|
86
|
+
|
87
87
|
rb_funcall(cEventHandler, m_call, 3, INT2FIX(event->event_data.fork.parent_pid), ID2SYM(proc_fork), extra_data);
|
88
88
|
return INT2FIX(1);
|
89
89
|
|
@@ -94,7 +94,7 @@ nlh_handle_events()
|
|
94
94
|
break;
|
95
95
|
}
|
96
96
|
}
|
97
|
-
|
97
|
+
|
98
98
|
return Qnil;
|
99
99
|
}
|
100
100
|
|
@@ -109,23 +109,23 @@ connect_to_netlink()
|
|
109
109
|
char buff[NL_MESSAGE_SIZE];
|
110
110
|
struct nlmsghdr *hdr; /* for telling netlink what we want */
|
111
111
|
struct cn_msg *msg; /* the actual connector message */
|
112
|
-
|
112
|
+
|
113
113
|
/* connect to netlink socket */
|
114
114
|
nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
|
115
|
-
|
115
|
+
|
116
116
|
if (-1 == nl_sock) {
|
117
117
|
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
118
118
|
}
|
119
|
-
|
119
|
+
|
120
120
|
bzero(&sa_nl, sizeof(sa_nl));
|
121
121
|
sa_nl.nl_family = AF_NETLINK;
|
122
122
|
sa_nl.nl_groups = CN_IDX_PROC;
|
123
123
|
sa_nl.nl_pid = getpid();
|
124
|
-
|
124
|
+
|
125
125
|
if (-1 == bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl))) {
|
126
126
|
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
127
127
|
}
|
128
|
-
|
128
|
+
|
129
129
|
/* Fill header */
|
130
130
|
hdr = (struct nlmsghdr *)buff;
|
131
131
|
hdr->nlmsg_len = NL_MESSAGE_SIZE;
|
@@ -133,7 +133,7 @@ connect_to_netlink()
|
|
133
133
|
hdr->nlmsg_flags = 0;
|
134
134
|
hdr->nlmsg_seq = 0;
|
135
135
|
hdr->nlmsg_pid = getpid();
|
136
|
-
|
136
|
+
|
137
137
|
/* Fill message */
|
138
138
|
msg = (struct cn_msg *)NLMSG_DATA(hdr);
|
139
139
|
msg->id.idx = CN_IDX_PROC; /* Connecting to process information */
|
@@ -143,7 +143,7 @@ connect_to_netlink()
|
|
143
143
|
msg->flags = 0;
|
144
144
|
msg->len = sizeof(int);
|
145
145
|
*(int*)msg->data = PROC_CN_MCAST_LISTEN;
|
146
|
-
|
146
|
+
|
147
147
|
if (-1 == send(nl_sock, hdr, hdr->nlmsg_len, 0)) {
|
148
148
|
rb_raise(rb_eStandardError, "%s", strerror(errno));
|
149
149
|
}
|
@@ -151,17 +151,17 @@ connect_to_netlink()
|
|
151
151
|
|
152
152
|
void
|
153
153
|
Init_netlink_handler_ext()
|
154
|
-
{
|
154
|
+
{
|
155
155
|
proc_exit = rb_intern("proc_exit");
|
156
156
|
proc_fork = rb_intern("proc_fork");
|
157
157
|
m_call = rb_intern("call");
|
158
158
|
m_watching_pid = rb_intern("watching_pid?");
|
159
|
-
|
159
|
+
|
160
160
|
mGod = rb_const_get(rb_cObject, rb_intern("God"));
|
161
161
|
cEventHandler = rb_const_get(mGod, rb_intern("EventHandler"));
|
162
162
|
cNetlinkHandler = rb_define_class_under(mGod, "NetlinkHandler", rb_cObject);
|
163
163
|
rb_define_singleton_method(cNetlinkHandler, "handle_events", nlh_handle_events, 0);
|
164
|
-
|
164
|
+
|
165
165
|
connect_to_netlink();
|
166
166
|
}
|
167
167
|
|
data/god.gemspec
CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
|
|
3
3
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
4
4
|
|
5
5
|
s.name = 'god'
|
6
|
-
s.version = '0.
|
7
|
-
s.date = '
|
6
|
+
s.version = '0.12.0'
|
7
|
+
s.date = '2012-01-13'
|
8
8
|
|
9
9
|
s.summary = "Process monitoring framework."
|
10
10
|
s.description = "An easy to configure, easy to extend monitoring framework written in Ruby."
|
@@ -18,28 +18,35 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.require_paths = %w[lib ext]
|
19
19
|
|
20
20
|
s.executables = ["god"]
|
21
|
-
s.default_executable = 'god'
|
22
21
|
s.extensions = %w[ext/god/extconf.rb]
|
23
22
|
|
24
23
|
s.rdoc_options = ["--charset=UTF-8"]
|
25
|
-
s.extra_rdoc_files = %w[README.
|
24
|
+
s.extra_rdoc_files = %w[README.md]
|
26
25
|
|
27
|
-
s.add_development_dependency('
|
28
|
-
s.add_development_dependency('
|
29
|
-
s.add_development_dependency('
|
30
|
-
s.add_development_dependency('
|
31
|
-
s.add_development_dependency('
|
32
|
-
s.add_development_dependency('
|
33
|
-
s.add_development_dependency('
|
34
|
-
s.add_development_dependency('
|
26
|
+
s.add_development_dependency('json', '~> 1.6')
|
27
|
+
s.add_development_dependency('rake', '~> 0.9')
|
28
|
+
s.add_development_dependency('rdoc', '~> 3.10')
|
29
|
+
s.add_development_dependency('twitter', '~> 2.0')
|
30
|
+
s.add_development_dependency('prowly', '~> 0.3')
|
31
|
+
s.add_development_dependency('xmpp4r', '~> 0.5')
|
32
|
+
s.add_development_dependency('dike', '~> 0.0.3')
|
33
|
+
s.add_development_dependency('snapshot', '~> 1.0')
|
34
|
+
s.add_development_dependency('rcov', '~> 0.9')
|
35
|
+
s.add_development_dependency('daemons', '~> 1.1')
|
36
|
+
s.add_development_dependency('mocha', '~> 0.10')
|
37
|
+
s.add_development_dependency('gollum', '~> 1.3.1')
|
35
38
|
|
36
39
|
# = MANIFEST =
|
37
40
|
s.files = %w[
|
38
41
|
Announce.txt
|
42
|
+
Gemfile
|
39
43
|
History.txt
|
40
|
-
|
44
|
+
LICENSE
|
45
|
+
README.md
|
41
46
|
Rakefile
|
42
47
|
bin/god
|
48
|
+
doc/god.asciidoc
|
49
|
+
doc/intro.asciidoc
|
43
50
|
ext/god/.gitignore
|
44
51
|
ext/god/extconf.rb
|
45
52
|
ext/god/kqueue_handler.c
|
@@ -67,6 +74,7 @@ Gem::Specification.new do |s|
|
|
67
74
|
lib/god/conditions/memory_usage.rb
|
68
75
|
lib/god/conditions/process_exits.rb
|
69
76
|
lib/god/conditions/process_running.rb
|
77
|
+
lib/god/conditions/socket_responding.rb
|
70
78
|
lib/god/conditions/tries.rb
|
71
79
|
lib/god/configurable.rb
|
72
80
|
lib/god/contact.rb
|
@@ -77,8 +85,6 @@ Gem::Specification.new do |s|
|
|
77
85
|
lib/god/contacts/scout.rb
|
78
86
|
lib/god/contacts/twitter.rb
|
79
87
|
lib/god/contacts/webhook.rb
|
80
|
-
lib/god/dependency_graph.rb
|
81
|
-
lib/god/diagnostics.rb
|
82
88
|
lib/god/driver.rb
|
83
89
|
lib/god/errors.rb
|
84
90
|
lib/god/event_handler.rb
|
@@ -115,6 +121,8 @@ Gem::Specification.new do |s|
|
|
115
121
|
test/configs/daemon_polls/simple_server.rb
|
116
122
|
test/configs/degrading_lambda/degrading_lambda.god
|
117
123
|
test/configs/degrading_lambda/tcp_server.rb
|
124
|
+
test/configs/keepalive/keepalive.god
|
125
|
+
test/configs/keepalive/keepalive.rb
|
118
126
|
test/configs/lifecycle/lifecycle.god
|
119
127
|
test/configs/matias/matias.god
|
120
128
|
test/configs/real.rb
|
@@ -134,9 +142,9 @@ Gem::Specification.new do |s|
|
|
134
142
|
test/test_conditions_disk_usage.rb
|
135
143
|
test/test_conditions_http_response_code.rb
|
136
144
|
test/test_conditions_process_running.rb
|
145
|
+
test/test_conditions_socket_responding.rb
|
137
146
|
test/test_conditions_tries.rb
|
138
147
|
test/test_contact.rb
|
139
|
-
test/test_dependency_graph.rb
|
140
148
|
test/test_driver.rb
|
141
149
|
test/test_email.rb
|
142
150
|
test/test_event_handler.rb
|
data/lib/god.rb
CHANGED
@@ -17,12 +17,12 @@ end
|
|
17
17
|
require 'god/errors'
|
18
18
|
require 'god/simple_logger'
|
19
19
|
require 'god/logger'
|
20
|
+
require 'god/sugar'
|
20
21
|
|
21
22
|
require 'god/system/process'
|
22
23
|
require 'god/system/portable_poller'
|
23
24
|
require 'god/system/slash_proc_poller'
|
24
25
|
|
25
|
-
require 'god/dependency_graph'
|
26
26
|
require 'god/timeline'
|
27
27
|
require 'god/configurable'
|
28
28
|
|
@@ -47,6 +47,7 @@ require 'god/conditions/http_response_code'
|
|
47
47
|
require 'god/conditions/disk_usage'
|
48
48
|
require 'god/conditions/complex'
|
49
49
|
require 'god/conditions/file_mtime'
|
50
|
+
require 'god/conditions/socket_responding'
|
50
51
|
|
51
52
|
require 'god/socket'
|
52
53
|
require 'god/driver'
|
@@ -59,12 +60,13 @@ require 'god/event_handler'
|
|
59
60
|
require 'god/registry'
|
60
61
|
require 'god/process'
|
61
62
|
|
62
|
-
require 'god/sugar'
|
63
|
-
|
64
63
|
require 'god/cli/version'
|
65
64
|
require 'god/cli/command'
|
66
65
|
|
67
|
-
|
66
|
+
# ruby 1.8 specific configuration
|
67
|
+
if RUBY_VERSION < '1.9'
|
68
|
+
$KCODE = 'u'
|
69
|
+
end
|
68
70
|
|
69
71
|
CONTACT_DEPS = { }
|
70
72
|
CONTACT_LOAD_SUCCESS = { }
|
@@ -109,15 +111,15 @@ end
|
|
109
111
|
|
110
112
|
module Kernel
|
111
113
|
alias_method :abort_orig, :abort
|
112
|
-
|
114
|
+
|
113
115
|
def abort(text = nil)
|
114
116
|
$run = false
|
115
117
|
applog(nil, :error, text) if text
|
116
118
|
exit(1)
|
117
119
|
end
|
118
|
-
|
120
|
+
|
119
121
|
alias_method :exit_orig, :exit
|
120
|
-
|
122
|
+
|
121
123
|
def exit(code = 0)
|
122
124
|
$run = false
|
123
125
|
exit_orig(code)
|
@@ -131,15 +133,15 @@ class Module
|
|
131
133
|
if !self.running && self.inited
|
132
134
|
abort "God.#{arg} must be set before any Tasks are defined"
|
133
135
|
end
|
134
|
-
|
136
|
+
|
135
137
|
if self.running && self.inited
|
136
138
|
applog(nil, :warn, "God.#{arg} can't be set while god is running")
|
137
139
|
return
|
138
140
|
end
|
139
|
-
|
141
|
+
|
140
142
|
instance_variable_set(('@' + arg.to_s).intern, other)
|
141
143
|
end
|
142
|
-
|
144
|
+
|
143
145
|
define_method(arg) do
|
144
146
|
instance_variable_get(('@' + arg.to_s).intern)
|
145
147
|
end
|
@@ -148,16 +150,36 @@ class Module
|
|
148
150
|
end
|
149
151
|
|
150
152
|
module God
|
151
|
-
|
153
|
+
# The String version number for this package.
|
154
|
+
VERSION = '0.12.0'
|
155
|
+
|
156
|
+
# The Integer number of lines of backlog to keep for the logger.
|
152
157
|
LOG_BUFFER_SIZE_DEFAULT = 100
|
158
|
+
|
159
|
+
# An Array of directory paths to be used as the default PID file directory.
|
160
|
+
# This list will be searched in order and the first one that has write
|
161
|
+
# permissions will be used.
|
153
162
|
PID_FILE_DIRECTORY_DEFAULTS = ['/var/run/god', '~/.god/pids']
|
163
|
+
|
164
|
+
# The default Integer port number for the DRb communcations channel.
|
154
165
|
DRB_PORT_DEFAULT = 17165
|
166
|
+
|
167
|
+
# The default Array of String IPs that will allow DRb communication access.
|
155
168
|
DRB_ALLOW_DEFAULT = ['127.0.0.1']
|
169
|
+
|
170
|
+
# The default Symbol log level.
|
156
171
|
LOG_LEVEL_DEFAULT = :info
|
172
|
+
|
173
|
+
# The default Integer number of seconds to wait for god to terminate when
|
174
|
+
# issued the quit command.
|
157
175
|
TERMINATE_TIMEOUT_DEFAULT = 10
|
176
|
+
|
177
|
+
# The default Integer number of seconds to wait for a process to terminate.
|
158
178
|
STOP_TIMEOUT_DEFAULT = 10
|
179
|
+
|
180
|
+
# The default String signal to send for the stop command.
|
159
181
|
STOP_SIGNAL_DEFAULT = 'TERM'
|
160
|
-
|
182
|
+
|
161
183
|
class << self
|
162
184
|
# user configurable
|
163
185
|
safe_attr_accessor :pid,
|
@@ -173,7 +195,7 @@ module God
|
|
173
195
|
:socket_user,
|
174
196
|
:socket_group,
|
175
197
|
:socket_perms
|
176
|
-
|
198
|
+
|
177
199
|
# internal
|
178
200
|
attr_accessor :inited,
|
179
201
|
:running,
|
@@ -186,8 +208,8 @@ module God
|
|
186
208
|
:contact_groups,
|
187
209
|
:main
|
188
210
|
end
|
189
|
-
|
190
|
-
#
|
211
|
+
|
212
|
+
# Initialize class instance variables.
|
191
213
|
self.pid = nil
|
192
214
|
self.host = nil
|
193
215
|
self.port = nil
|
@@ -199,158 +221,150 @@ module God
|
|
199
221
|
self.socket_user = nil
|
200
222
|
self.socket_group = nil
|
201
223
|
self.socket_perms = 0755
|
202
|
-
|
224
|
+
|
203
225
|
# Initialize internal data.
|
204
226
|
#
|
205
|
-
# Returns nothing
|
227
|
+
# Returns nothing.
|
206
228
|
def self.internal_init
|
207
|
-
#
|
229
|
+
# Only do this once.
|
208
230
|
return if self.inited
|
209
|
-
|
210
|
-
#
|
231
|
+
|
232
|
+
# Variable init.
|
211
233
|
self.watches = {}
|
212
234
|
self.groups = {}
|
213
235
|
self.pending_watches = []
|
214
236
|
self.pending_watch_states = {}
|
215
237
|
self.contacts = {}
|
216
238
|
self.contact_groups = {}
|
217
|
-
|
218
|
-
#
|
239
|
+
|
240
|
+
# Set defaults.
|
219
241
|
self.log_buffer_size ||= LOG_BUFFER_SIZE_DEFAULT
|
220
242
|
self.port ||= DRB_PORT_DEFAULT
|
221
243
|
self.allow ||= DRB_ALLOW_DEFAULT
|
222
244
|
self.log_level ||= LOG_LEVEL_DEFAULT
|
223
245
|
self.terminate_timeout ||= TERMINATE_TIMEOUT_DEFAULT
|
224
|
-
|
225
|
-
#
|
246
|
+
|
247
|
+
# Additional setup.
|
226
248
|
self.setup
|
227
|
-
|
228
|
-
#
|
249
|
+
|
250
|
+
# Log level.
|
229
251
|
log_level_map = {:debug => Logger::DEBUG,
|
230
252
|
:info => Logger::INFO,
|
231
253
|
:warn => Logger::WARN,
|
232
254
|
:error => Logger::ERROR,
|
233
255
|
:fatal => Logger::FATAL}
|
234
256
|
LOG.level = log_level_map[self.log_level]
|
235
|
-
|
236
|
-
#
|
257
|
+
|
258
|
+
# Init has been executed.
|
237
259
|
self.inited = true
|
238
|
-
|
239
|
-
#
|
260
|
+
|
261
|
+
# Not yet running.
|
240
262
|
self.running = false
|
241
263
|
end
|
242
|
-
|
243
|
-
# Instantiate a new, empty Watch object and pass it to the mandatory
|
244
|
-
#
|
245
|
-
#
|
246
|
-
#
|
247
|
-
# Aborts on duplicate watch name
|
248
|
-
# invalid watch
|
249
|
-
# conflicting group name
|
264
|
+
|
265
|
+
# Instantiate a new, empty Watch object and pass it to the mandatory block.
|
266
|
+
# The attributes of the watch will be set by the configuration file. Aborts
|
267
|
+
# on duplicate watch name, invalid watch, or conflicting group name.
|
250
268
|
#
|
251
|
-
# Returns nothing
|
269
|
+
# Returns nothing.
|
252
270
|
def self.watch(&block)
|
253
271
|
self.task(Watch, &block)
|
254
272
|
end
|
255
|
-
|
256
|
-
# Instantiate a new, empty Task object and yield it to the mandatory
|
257
|
-
#
|
258
|
-
#
|
259
|
-
#
|
260
|
-
# Aborts on duplicate task name
|
261
|
-
# invalid task
|
262
|
-
# conflicting group name
|
273
|
+
|
274
|
+
# Instantiate a new, empty Task object and yield it to the mandatory block.
|
275
|
+
# The attributes of the task will be set by the configuration file. Aborts
|
276
|
+
# on duplicate task name, invalid task, or conflicting group name.
|
263
277
|
#
|
264
|
-
# Returns nothing
|
278
|
+
# Returns nothing.
|
265
279
|
def self.task(klass = Task)
|
280
|
+
# Ensure internal init has run.
|
266
281
|
self.internal_init
|
267
|
-
|
282
|
+
|
268
283
|
t = klass.new
|
269
284
|
yield(t)
|
270
|
-
|
271
|
-
#
|
285
|
+
|
286
|
+
# Do the post-configuration.
|
272
287
|
t.prepare
|
273
|
-
|
274
|
-
#
|
275
|
-
#
|
288
|
+
|
289
|
+
# If running, completely remove the watch (if necessary) to prepare for
|
290
|
+
# the reload
|
276
291
|
existing_watch = self.watches[t.name]
|
277
292
|
if self.running && existing_watch
|
278
293
|
self.pending_watch_states[existing_watch.name] = existing_watch.state
|
279
294
|
self.unwatch(existing_watch)
|
280
295
|
end
|
281
|
-
|
282
|
-
#
|
296
|
+
|
297
|
+
# Ensure the new watch has a unique name.
|
283
298
|
if self.watches[t.name] || self.groups[t.name]
|
284
299
|
abort "Task name '#{t.name}' already used for a Task or Group"
|
285
300
|
end
|
286
|
-
|
287
|
-
#
|
301
|
+
|
302
|
+
# Ensure watch is internally valid.
|
288
303
|
t.valid? || abort("Task '#{t.name}' is not valid (see above)")
|
289
|
-
|
290
|
-
#
|
304
|
+
|
305
|
+
# Add to list of watches.
|
291
306
|
self.watches[t.name] = t
|
292
|
-
|
293
|
-
#
|
307
|
+
|
308
|
+
# Add to pending watches.
|
294
309
|
self.pending_watches << t
|
295
|
-
|
296
|
-
#
|
310
|
+
|
311
|
+
# Add to group if specified.
|
297
312
|
if t.group
|
298
|
-
#
|
313
|
+
# Ensure group name hasn't been used for a watch already.
|
299
314
|
if self.watches[t.group]
|
300
315
|
abort "Group name '#{t.group}' already used for a Task"
|
301
316
|
end
|
302
|
-
|
317
|
+
|
303
318
|
self.groups[t.group] ||= []
|
304
319
|
self.groups[t.group] << t
|
305
320
|
end
|
306
|
-
|
307
|
-
#
|
321
|
+
|
322
|
+
# Register watch.
|
308
323
|
t.register!
|
309
|
-
|
310
|
-
#
|
324
|
+
|
325
|
+
# Log.
|
311
326
|
if self.running && existing_watch
|
312
327
|
applog(t, :info, "#{t.name} Reloaded config")
|
313
328
|
elsif self.running
|
314
329
|
applog(t, :info, "#{t.name} Loaded config")
|
315
330
|
end
|
316
331
|
end
|
317
|
-
|
332
|
+
|
318
333
|
# Unmonitor and remove the given watch from god.
|
319
|
-
# +watch+ is the Watch to remove
|
320
334
|
#
|
321
|
-
#
|
335
|
+
# watch - The Watch to remove.
|
336
|
+
#
|
337
|
+
# Returns nothing.
|
322
338
|
def self.unwatch(watch)
|
323
|
-
#
|
339
|
+
# Unmonitor.
|
324
340
|
watch.unmonitor unless watch.state == :unmonitored
|
325
|
-
|
326
|
-
#
|
341
|
+
|
342
|
+
# Unregister.
|
327
343
|
watch.unregister!
|
328
|
-
|
329
|
-
#
|
344
|
+
|
345
|
+
# Remove from watches.
|
330
346
|
self.watches.delete(watch.name)
|
331
|
-
|
332
|
-
#
|
347
|
+
|
348
|
+
# Remove from groups.
|
333
349
|
if watch.group
|
334
350
|
self.groups[watch.group].delete(watch)
|
335
351
|
end
|
336
|
-
|
352
|
+
|
337
353
|
applog(watch, :info, "#{watch.name} unwatched")
|
338
354
|
end
|
339
|
-
|
355
|
+
|
340
356
|
# Instantiate a new Contact of the given kind and send it to the block.
|
341
|
-
# Then prepare, validate, and record the Contact.
|
342
|
-
#
|
357
|
+
# Then prepare, validate, and record the Contact. Aborts on invalid kind,
|
358
|
+
# duplicate contact name, invalid contact, or conflicting group name.
|
343
359
|
#
|
344
|
-
#
|
345
|
-
# duplicate contact name
|
346
|
-
# invalid contact
|
347
|
-
# conflicting group name
|
360
|
+
# kind - The Symbol contact class specifier.
|
348
361
|
#
|
349
|
-
# Returns nothing
|
362
|
+
# Returns nothing.
|
350
363
|
def self.contact(kind)
|
364
|
+
# Ensure internal init has run.
|
351
365
|
self.internal_init
|
352
|
-
|
353
|
-
#
|
366
|
+
|
367
|
+
# Verify contact has been loaded.
|
354
368
|
if CONTACT_LOAD_SUCCESS[kind] == false
|
355
369
|
applog(nil, :error, "A required dependency for the #{kind} contact is unavailable.")
|
356
370
|
applog(nil, :error, "Run the following commands to install the dependencies:")
|
@@ -359,82 +373,79 @@ module God
|
|
359
373
|
end
|
360
374
|
abort
|
361
375
|
end
|
362
|
-
|
363
|
-
#
|
376
|
+
|
377
|
+
# Create the contact.
|
364
378
|
begin
|
365
379
|
c = Contact.generate(kind)
|
366
380
|
rescue NoSuchContactError => e
|
367
381
|
abort e.message
|
368
382
|
end
|
369
|
-
|
370
|
-
#
|
383
|
+
|
384
|
+
# Send to block so config can set attributes.
|
371
385
|
yield(c) if block_given?
|
372
|
-
|
373
|
-
#
|
386
|
+
|
387
|
+
# Call prepare on the contact.
|
374
388
|
c.prepare
|
375
|
-
|
376
|
-
#
|
389
|
+
|
390
|
+
# Remove existing contacts of same name.
|
377
391
|
existing_contact = self.contacts[c.name]
|
378
392
|
if self.running && existing_contact
|
379
393
|
self.uncontact(existing_contact)
|
380
394
|
end
|
381
|
-
|
382
|
-
#
|
395
|
+
|
396
|
+
# Warn and noop if the contact has been defined before.
|
383
397
|
if self.contacts[c.name] || self.contact_groups[c.name]
|
384
398
|
applog(nil, :warn, "Contact name '#{c.name}' already used for a Contact or Contact Group")
|
385
399
|
return
|
386
400
|
end
|
387
|
-
|
388
|
-
#
|
389
|
-
#
|
401
|
+
|
402
|
+
# Abort if the Contact is invalid, the Contact will have printed out its
|
403
|
+
# own error messages by now.
|
390
404
|
unless Contact.valid?(c) && c.valid?
|
391
405
|
abort "Exiting on invalid contact"
|
392
406
|
end
|
393
|
-
|
394
|
-
#
|
407
|
+
|
408
|
+
# Add to list of contacts.
|
395
409
|
self.contacts[c.name] = c
|
396
|
-
|
397
|
-
#
|
410
|
+
|
411
|
+
# Add to contact group if specified.
|
398
412
|
if c.group
|
399
|
-
#
|
413
|
+
# Ensure group name hasn't been used for a contact already.
|
400
414
|
if self.contacts[c.group]
|
401
415
|
abort "Contact Group name '#{c.group}' already used for a Contact"
|
402
416
|
end
|
403
|
-
|
417
|
+
|
404
418
|
self.contact_groups[c.group] ||= []
|
405
419
|
self.contact_groups[c.group] << c
|
406
420
|
end
|
407
421
|
end
|
408
|
-
|
422
|
+
|
409
423
|
# Remove the given contact from god.
|
410
|
-
# +contact+ is the Contact to remove
|
411
424
|
#
|
412
|
-
#
|
425
|
+
# contact - The Contact to remove.
|
426
|
+
#
|
427
|
+
# Returns nothing.
|
413
428
|
def self.uncontact(contact)
|
414
429
|
self.contacts.delete(contact.name)
|
415
430
|
if contact.group
|
416
431
|
self.contact_groups[contact.group].delete(contact)
|
417
432
|
end
|
418
433
|
end
|
419
|
-
|
434
|
+
|
420
435
|
# Control the lifecycle of the given task(s).
|
421
|
-
#
|
422
|
-
#
|
423
|
-
#
|
424
|
-
#
|
425
|
-
#
|
426
|
-
#
|
427
|
-
# "unmonitor"
|
428
|
-
# "remove"
|
429
|
-
#
|
430
|
-
# Returns String[]:task_names
|
436
|
+
#
|
437
|
+
# name - The String name of a task/group.
|
438
|
+
# command - The String command to run. Valid commands are:
|
439
|
+
# "start", "monitor", "restart", "stop", "unmonitor", "remove".
|
440
|
+
#
|
441
|
+
# Returns an Array of String task names affected by the command.
|
431
442
|
def self.control(name, command)
|
432
|
-
#
|
443
|
+
# Get the list of items.
|
433
444
|
items = Array(self.watches[name] || self.groups[name]).dup
|
434
|
-
|
445
|
+
|
435
446
|
jobs = []
|
436
|
-
|
437
|
-
#
|
447
|
+
|
448
|
+
# Do the command.
|
438
449
|
case command
|
439
450
|
when "start", "monitor"
|
440
451
|
items.each { |w| jobs << Thread.new { w.monitor if w.state != :up } }
|
@@ -449,16 +460,16 @@ module God
|
|
449
460
|
else
|
450
461
|
raise InvalidCommandError.new
|
451
462
|
end
|
452
|
-
|
463
|
+
|
453
464
|
jobs.each { |j| j.join }
|
454
|
-
|
465
|
+
|
455
466
|
items.map { |x| x.name }
|
456
467
|
end
|
457
|
-
|
468
|
+
|
458
469
|
# Unmonitor and stop all tasks.
|
459
470
|
#
|
460
|
-
# Returns true on success
|
461
|
-
#
|
471
|
+
# Returns true on success, false if all tasks could not be stopped within 10
|
472
|
+
# seconds
|
462
473
|
def self.stop_all
|
463
474
|
self.watches.sort.each do |name, w|
|
464
475
|
Thread.new do
|
@@ -466,19 +477,19 @@ module God
|
|
466
477
|
w.action(:stop) if w.alive?
|
467
478
|
end
|
468
479
|
end
|
469
|
-
|
480
|
+
|
470
481
|
terminate_timeout.times do
|
471
482
|
return true unless self.watches.map { |name, w| w.alive? }.any?
|
472
483
|
sleep 1
|
473
484
|
end
|
474
|
-
|
485
|
+
|
475
486
|
return false
|
476
487
|
end
|
477
|
-
|
488
|
+
|
478
489
|
# Force the termination of god.
|
479
|
-
#
|
480
|
-
#
|
481
|
-
#
|
490
|
+
# * Clean up pid file if one exists
|
491
|
+
# * Stop DRb service
|
492
|
+
# * Hard exit using exit!
|
482
493
|
#
|
483
494
|
# Never returns because the process will no longer exist!
|
484
495
|
def self.terminate
|
@@ -486,14 +497,16 @@ module God
|
|
486
497
|
self.server.stop if self.server
|
487
498
|
exit!(0)
|
488
499
|
end
|
489
|
-
|
500
|
+
|
490
501
|
# Gather the status of each task.
|
491
502
|
#
|
492
503
|
# Examples
|
504
|
+
#
|
493
505
|
# God.status
|
494
506
|
# # => { 'mongrel' => :up, 'nginx' => :up }
|
495
507
|
#
|
496
|
-
# Returns
|
508
|
+
# Returns a Hash where the key is the String task name and the value is the
|
509
|
+
# Symbol status.
|
497
510
|
def self.status
|
498
511
|
info = {}
|
499
512
|
self.watches.map do |name, w|
|
@@ -501,12 +514,13 @@ module God
|
|
501
514
|
end
|
502
515
|
info
|
503
516
|
end
|
504
|
-
|
517
|
+
|
505
518
|
# Send a signal to each task.
|
506
|
-
# +name+ is the String name of the task or group
|
507
|
-
# +signal+ is the signal to send. e.g. HUP, 9
|
508
519
|
#
|
509
|
-
#
|
520
|
+
# name - The String name of the task or group.
|
521
|
+
# signal - The String or integer signal to send. e.g. 'HUP', 9.
|
522
|
+
#
|
523
|
+
# Returns an Array of String names of the tasks affected.
|
510
524
|
def self.signal(name, signal)
|
511
525
|
items = Array(self.watches[name] || self.groups[name]).dup
|
512
526
|
jobs = []
|
@@ -514,37 +528,50 @@ module God
|
|
514
528
|
jobs.each { |j| j.join }
|
515
529
|
items.map { |x| x.name }
|
516
530
|
end
|
517
|
-
|
531
|
+
|
518
532
|
# Log lines for the given task since the specified time.
|
519
|
-
# +watch_name+ is the name of the task (may be abbreviated)
|
520
|
-
# +since+ is the Time since which to report log lines
|
521
533
|
#
|
522
|
-
#
|
534
|
+
# watch_name - The String name of the task (may be abbreviated).
|
535
|
+
# since - The Time since which to report log lines.
|
523
536
|
#
|
524
|
-
#
|
537
|
+
# Raises God::NoSuchWatchError if no tasks matched.
|
538
|
+
# Returns the String of newline separated log lines.
|
525
539
|
def self.running_log(watch_name, since)
|
526
540
|
matches = pattern_match(watch_name, self.watches.keys)
|
527
|
-
|
541
|
+
|
528
542
|
unless matches.first
|
529
543
|
raise NoSuchWatchError.new
|
530
544
|
end
|
531
|
-
|
545
|
+
|
532
546
|
LOG.watch_log_since(matches.first, since)
|
533
547
|
end
|
534
|
-
|
548
|
+
|
535
549
|
# Load a config file into a running god instance. Rescues any exceptions
|
536
550
|
# that the config may raise and reports these back to the caller.
|
537
|
-
# +code+ is a String containing the config file
|
538
|
-
# +filename+ is the filename of the config file
|
539
551
|
#
|
540
|
-
#
|
541
|
-
|
552
|
+
# code - The String config file contents.
|
553
|
+
# filename - The filename of the config file.
|
554
|
+
# action - The optional String command specifying how to deal with
|
555
|
+
# existing watches. Valid options are: 'stop', 'remove' or
|
556
|
+
# 'leave' (default).
|
557
|
+
#
|
558
|
+
# Returns a three-tuple Array [loaded_names, errors, unloaded_names] where:
|
559
|
+
# loaded_names - The Array of String task names that were loaded.
|
560
|
+
# errors - The String of error messages produced during the
|
561
|
+
# load phase. Will be a blank String if no errors
|
562
|
+
# were encountered.
|
563
|
+
# unloaded_names - The Array of String task names that were unloaded
|
564
|
+
# from the system (if 'remove' or 'stop' was
|
565
|
+
# specified as the action).
|
566
|
+
def self.running_load(code, filename, action = nil)
|
542
567
|
errors = ""
|
543
|
-
|
544
|
-
|
568
|
+
loaded_watches = []
|
569
|
+
unloaded_watches = []
|
570
|
+
jobs = []
|
571
|
+
|
545
572
|
begin
|
546
573
|
LOG.start_capture
|
547
|
-
|
574
|
+
|
548
575
|
Gem.clear_paths
|
549
576
|
eval(code, root_binding, filename)
|
550
577
|
self.pending_watches.each do |w|
|
@@ -554,39 +581,61 @@ module God
|
|
554
581
|
w.monitor if w.autostart?
|
555
582
|
end
|
556
583
|
end
|
557
|
-
|
584
|
+
loaded_watches = self.pending_watches.map { |w| w.name }
|
558
585
|
self.pending_watches.clear
|
559
586
|
self.pending_watch_states.clear
|
560
587
|
|
561
|
-
|
588
|
+
self.watches.each do |name, watch|
|
589
|
+
next if loaded_watches.include?(name)
|
590
|
+
|
591
|
+
case action
|
592
|
+
when 'stop'
|
593
|
+
jobs << Thread.new(watch) { |w| w.action(:stop); self.unwatch(w) }
|
594
|
+
unloaded_watches << name
|
595
|
+
when 'remove'
|
596
|
+
jobs << Thread.new(watch) { |w| self.unwatch(w) }
|
597
|
+
unloaded_watches << name
|
598
|
+
when 'leave', '', nil
|
599
|
+
# Do nothing
|
600
|
+
else
|
601
|
+
raise InvalidCommandError, "Unknown action: #{action}"
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
# Make sure we quit capturing when we're done.
|
562
606
|
LOG.finish_capture
|
563
607
|
rescue Exception => e
|
564
|
-
#
|
608
|
+
# Don't ever let running_load take down god.
|
565
609
|
errors << LOG.finish_capture
|
566
|
-
|
610
|
+
|
567
611
|
unless e.instance_of?(SystemExit)
|
568
612
|
errors << e.message << "\n"
|
569
613
|
errors << e.backtrace.join("\n")
|
570
614
|
end
|
571
615
|
end
|
572
|
-
|
573
|
-
|
574
|
-
|
616
|
+
|
617
|
+
jobs.each { |t| t.join }
|
618
|
+
|
619
|
+
[loaded_watches, errors, unloaded_watches]
|
575
620
|
end
|
576
|
-
|
621
|
+
|
577
622
|
# Load the given file(s) according to the given glob.
|
578
|
-
# +glob+ is the glob-enabled path to load
|
579
623
|
#
|
580
|
-
#
|
624
|
+
# glob - The glob-enabled String path to load.
|
625
|
+
#
|
626
|
+
# Returns nothing.
|
581
627
|
def self.load(glob)
|
582
628
|
Dir[glob].each do |f|
|
583
629
|
Kernel.load f
|
584
630
|
end
|
585
631
|
end
|
586
|
-
|
632
|
+
|
633
|
+
# Setup pid file directory and log system.
|
634
|
+
#
|
635
|
+
# Returns nothing.
|
587
636
|
def self.setup
|
588
637
|
if self.pid_file_directory
|
589
|
-
#
|
638
|
+
# Pid file dir was specified, ensure it is created and writable.
|
590
639
|
unless File.exist?(self.pid_file_directory)
|
591
640
|
begin
|
592
641
|
FileUtils.mkdir_p(self.pid_file_directory)
|
@@ -594,12 +643,12 @@ module God
|
|
594
643
|
abort "Failed to create pid file directory: #{e.message}"
|
595
644
|
end
|
596
645
|
end
|
597
|
-
|
646
|
+
|
598
647
|
unless File.writable?(self.pid_file_directory)
|
599
648
|
abort "The pid file directory (#{self.pid_file_directory}) is not writable by #{Etc.getlogin}"
|
600
649
|
end
|
601
650
|
else
|
602
|
-
#
|
651
|
+
# No pid file dir specified, try defaults.
|
603
652
|
PID_FILE_DIRECTORY_DEFAULTS.each do |idir|
|
604
653
|
dir = File.expand_path(idir)
|
605
654
|
begin
|
@@ -611,69 +660,77 @@ module God
|
|
611
660
|
rescue Errno::EACCES => e
|
612
661
|
end
|
613
662
|
end
|
614
|
-
|
663
|
+
|
615
664
|
unless self.pid_file_directory
|
616
665
|
dirs = PID_FILE_DIRECTORY_DEFAULTS.map { |x| File.expand_path(x) }
|
617
666
|
abort "No pid file directory exists, could be created, or is writable at any of #{dirs.join(', ')}"
|
618
667
|
end
|
619
668
|
end
|
620
|
-
|
669
|
+
|
621
670
|
if God::Logger.syslog
|
622
671
|
LOG.info("Syslog enabled.")
|
623
672
|
else
|
624
673
|
LOG.info("Syslog disabled.")
|
625
674
|
end
|
626
|
-
|
675
|
+
|
627
676
|
applog(nil, :info, "Using pid file directory: #{self.pid_file_directory}")
|
628
677
|
end
|
629
|
-
|
678
|
+
|
630
679
|
# Initialize and startup the machinery that makes god work.
|
631
680
|
#
|
632
|
-
# Returns nothing
|
681
|
+
# Returns nothing.
|
633
682
|
def self.start
|
634
683
|
self.internal_init
|
635
|
-
|
636
|
-
#
|
684
|
+
|
685
|
+
# Instantiate server.
|
637
686
|
self.server = Socket.new(self.port, self.socket_user, self.socket_group, self.socket_perms)
|
638
|
-
|
639
|
-
#
|
687
|
+
|
688
|
+
# Start monitoring any watches set to autostart.
|
640
689
|
self.watches.values.each { |w| w.monitor if w.autostart? }
|
641
|
-
|
642
|
-
#
|
690
|
+
|
691
|
+
# Clear pending watches.
|
643
692
|
self.pending_watches.clear
|
644
|
-
|
645
|
-
#
|
693
|
+
|
694
|
+
# Mark as running.
|
646
695
|
self.running = true
|
647
|
-
|
648
|
-
#
|
649
|
-
self.main =
|
696
|
+
|
697
|
+
# Don't exit.
|
698
|
+
self.main =
|
650
699
|
Thread.new do
|
651
700
|
loop do
|
652
701
|
sleep 60
|
653
702
|
end
|
654
703
|
end
|
655
|
-
|
656
|
-
self.main.join
|
657
704
|
end
|
658
|
-
|
705
|
+
|
706
|
+
# Prevent god from exiting.
|
707
|
+
#
|
708
|
+
# Returns nothing.
|
709
|
+
def self.join
|
710
|
+
self.main.join if self.main
|
711
|
+
end
|
712
|
+
|
713
|
+
# Returns the version String.
|
659
714
|
def self.version
|
660
715
|
God::VERSION
|
661
716
|
end
|
662
|
-
|
663
|
-
# To be called on program exit to start god
|
717
|
+
|
718
|
+
# To be called on program exit to start god.
|
664
719
|
#
|
665
|
-
# Returns nothing
|
720
|
+
# Returns nothing.
|
666
721
|
def self.at_exit
|
667
722
|
self.start
|
723
|
+
self.join
|
668
724
|
end
|
669
|
-
|
725
|
+
|
670
726
|
# private
|
671
|
-
|
727
|
+
|
672
728
|
# Match a shortened pattern against a list of String candidates.
|
673
|
-
# The pattern is expanded into a regular expression by
|
729
|
+
# The pattern is expanded into a regular expression by
|
674
730
|
# inserting .* between each character.
|
675
|
-
#
|
676
|
-
#
|
731
|
+
#
|
732
|
+
# pattern - The String containing the abbreviation.
|
733
|
+
# list - The Array of Strings to match against.
|
677
734
|
#
|
678
735
|
# Examples
|
679
736
|
#
|
@@ -682,10 +739,10 @@ module God
|
|
682
739
|
# God.pattern_match(list, pattern)
|
683
740
|
# # => ['bar', 'bars']
|
684
741
|
#
|
685
|
-
# Returns
|
742
|
+
# Returns the Array of matching name Strings.
|
686
743
|
def self.pattern_match(pattern, list)
|
687
744
|
regex = pattern.split('').join('.*')
|
688
|
-
|
745
|
+
|
689
746
|
list.select do |item|
|
690
747
|
item =~ Regexp.new(regex)
|
691
748
|
end.sort_by { |x| x.size }
|
@@ -695,7 +752,7 @@ end
|
|
695
752
|
# Runs immediately before the program exits. If $run is true,
|
696
753
|
# start god, if $run is false, exit normally.
|
697
754
|
#
|
698
|
-
# Returns nothing
|
755
|
+
# Returns nothing.
|
699
756
|
at_exit do
|
700
757
|
God.at_exit if $run
|
701
758
|
end
|