reel-eye 0.4 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +10 -1
- data/README.md +36 -17
- data/bin/eye +1 -1
- data/examples/process_thin.rb +2 -2
- data/examples/puma.eye +13 -18
- data/examples/sidekiq.eye +2 -2
- data/examples/test.eye +11 -11
- data/examples/thin-farm.eye +2 -2
- data/examples/unicorn.eye +17 -9
- data/eye.gemspec +1 -1
- data/lib/eye.rb +1 -1
- data/lib/eye/application.rb +0 -4
- data/lib/eye/checker.rb +21 -4
- data/lib/eye/checker/nop.rb +6 -0
- data/lib/eye/controller/commands.rb +1 -3
- data/lib/eye/controller/helpers.rb +1 -1
- data/lib/eye/controller/load.rb +0 -1
- data/lib/eye/controller/send_command.rb +27 -22
- data/lib/eye/controller/status.rb +0 -1
- data/lib/eye/dsl/child_process_opts.rb +4 -0
- data/lib/eye/dsl/config_opts.rb +6 -5
- data/lib/eye/dsl/main.rb +1 -0
- data/lib/eye/dsl/opts.rb +1 -0
- data/lib/eye/dsl/validation.rb +8 -2
- data/lib/eye/logger.rb +5 -0
- data/lib/eye/notify.rb +18 -0
- data/lib/eye/process/commands.rb +13 -5
- data/lib/eye/process/system.rb +1 -1
- data/lib/eye/process/trigger.rb +1 -1
- data/lib/eye/process/watchers.rb +7 -4
- data/lib/eye/system.rb +6 -6
- data/lib/eye/trigger.rb +40 -7
- data/lib/eye/trigger/flapping.rb +1 -3
- data/lib/eye/trigger/state.rb +1 -18
- data/lib/eye/trigger/stop_childs.rb +10 -0
- data/lib/eye/utils/alive_array.rb +1 -1
- data/lib/eye/utils/celluloid_chain.rb +2 -1
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed60352af3eb561a96fd4efabdceecaad8c2273c
|
4
|
+
data.tar.gz: 0efd153d8661f1c26f13561851ad4331f7302a9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26f5b48af35f9301fad4493b86429257841482c0e557611d452716a0a5ee3e8292d840ccdc3c65217e70be8680a7783afc14e7ed15e76c519a1f189caddaab0d
|
7
|
+
data.tar.gz: 88a5486efec951fab7ee6b1e34ed95ab6a643291f59d594d3f443b3d711bab40f127e535da67f6e05dc20ad1aab74d103a446872a648387c892ae2866869f6fe
|
data/CHANGES.md
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
0.4.dev
|
1
|
+
0.4.1.dev
|
2
|
+
---------
|
3
|
+
* add nop checker for periodic restart
|
4
|
+
* catch errors in custom checkers, triggers
|
5
|
+
* add custom notify
|
6
|
+
* checker can fires array of commands
|
7
|
+
* fix targets matching
|
8
|
+
* remove autoset PWD env
|
9
|
+
|
10
|
+
0.4
|
2
11
|
---------
|
3
12
|
* pass tests on 1.9.2
|
4
13
|
* relax activesupport dependency
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Eye
|
|
4
4
|
[![Build Status](https://secure.travis-ci.org/kostya/eye.png?branch=master)](http://travis-ci.org/kostya/eye)
|
5
5
|
[![Coverage Status](https://coveralls.io/repos/kostya/eye/badge.png?branch=master)](https://coveralls.io/r/kostya/eye?branch=master)
|
6
6
|
|
7
|
-
Process monitoring tool.
|
7
|
+
Process monitoring tool. Inspired from Bluepill and God. Requires Ruby(MRI) >= 1.9.3-p194. Uses Celluloid and Celluloid::IO.
|
8
8
|
|
9
9
|
Little demo, shows general commands and how chain works:
|
10
10
|
|
@@ -36,8 +36,8 @@ Eye.application "test" do
|
|
36
36
|
working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
37
37
|
stdall "trash.log" # stdout,err logs for processes by default
|
38
38
|
env "APP_ENV" => "production" # global env for each processes
|
39
|
-
|
40
|
-
|
39
|
+
trigger :flapping, :times => 10, :within => 1.minute, :retry_in => 10.minutes
|
40
|
+
check :cpu, :below => 100, :times => 3 # global check for all processes
|
41
41
|
|
42
42
|
group "samples" do
|
43
43
|
chain :grace => 5.seconds # chained start-restart with 5s interval, one by one.
|
@@ -53,7 +53,7 @@ Eye.application "test" do
|
|
53
53
|
daemonize true
|
54
54
|
stdall "sample1.log"
|
55
55
|
|
56
|
-
|
56
|
+
check :cpu, :below => 30, :times => [3, 5]
|
57
57
|
end
|
58
58
|
|
59
59
|
# self daemonized process
|
@@ -62,7 +62,7 @@ Eye.application "test" do
|
|
62
62
|
start_command "ruby ./sample.rb -d --pid 2.pid --log sample2.log"
|
63
63
|
stop_command "kill -9 {PID}"
|
64
64
|
|
65
|
-
|
65
|
+
check :memory, :below => 300.megabytes, :times => 3
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
@@ -73,12 +73,12 @@ Eye.application "test" do
|
|
73
73
|
stop_command "ruby forking.rb stop"
|
74
74
|
stdall "forking.log"
|
75
75
|
|
76
|
-
start_timeout
|
77
|
-
|
76
|
+
start_timeout 10.seconds
|
77
|
+
stop_timeout 5.seconds
|
78
78
|
|
79
79
|
monitor_children do
|
80
80
|
restart_command "kill -2 {PID}" # for this child process
|
81
|
-
|
81
|
+
check :memory, :below => 300.megabytes, :times => 3
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
@@ -90,8 +90,8 @@ Eye.application "test" do
|
|
90
90
|
daemonize true
|
91
91
|
stop_signals [:QUIT, 2.seconds, :KILL]
|
92
92
|
|
93
|
-
|
94
|
-
|
93
|
+
check :socket, :addr => "tcp://127.0.0.1:33221", :every => 10.seconds, :times => 2,
|
94
|
+
:timeout => 1.second, :send_data => "ping", :expect_data => /pong/
|
95
95
|
end
|
96
96
|
|
97
97
|
# thin process, self daemonized
|
@@ -100,14 +100,14 @@ Eye.application "test" do
|
|
100
100
|
start_command "bundle exec thin start -R thin.ru -p 33233 -d -l thin.log -P thin.pid"
|
101
101
|
stop_signals [:QUIT, 2.seconds, :TERM, 1.seconds, :KILL]
|
102
102
|
|
103
|
-
|
104
|
-
|
103
|
+
check :http, :url => "http://127.0.0.1:33233/hello", :pattern => /World/, :every => 5.seconds,
|
104
|
+
:times => [2, 3], :timeout => 1.second
|
105
105
|
end
|
106
106
|
|
107
107
|
end
|
108
108
|
```
|
109
109
|
|
110
|
-
### Start
|
110
|
+
### Start eye daemon and/or load config:
|
111
111
|
|
112
112
|
$ eye l(oad) examples/test.eye
|
113
113
|
|
@@ -116,8 +116,11 @@ load folder with configs:
|
|
116
116
|
$ eye l examples/
|
117
117
|
$ eye l examples/*.rb
|
118
118
|
|
119
|
-
|
120
|
-
|
119
|
+
foregraund load:
|
120
|
+
|
121
|
+
$ eye l CONF -f
|
122
|
+
|
123
|
+
If eye daemon already started and you call `load` command, config will be updated (into eye daemon). New objects(applications, groups, processes) will be added and monitored. Removed from config processes will be removed (and stopped if process has `stop_on_delete true`). Other objects update their configs.
|
121
124
|
|
122
125
|
|
123
126
|
Process statuses:
|
@@ -171,6 +174,22 @@ Quit monitoring:
|
|
171
174
|
|
172
175
|
$ eye q(uit)
|
173
176
|
|
174
|
-
|
177
|
+
Interactive info:
|
178
|
+
|
179
|
+
$ eye w(atch)
|
180
|
+
|
181
|
+
Process statuses history:
|
182
|
+
|
183
|
+
$ eye hi(story)
|
184
|
+
|
185
|
+
Eye daemon info:
|
186
|
+
|
187
|
+
$ eye x(info)
|
188
|
+
$ eye x -c # for show current config
|
189
|
+
|
190
|
+
Process states and events:
|
191
|
+
|
192
|
+
[![Eye](https://raw.github.com/kostya/stuff/master/eye/mprocess.png)](https://raw.github.com/kostya/stuff/master/eye/process.png)
|
193
|
+
|
175
194
|
|
176
|
-
|
195
|
+
Thanks `Bluepill` for the nice config ideas.
|
data/bin/eye
CHANGED
@@ -160,7 +160,7 @@ private
|
|
160
160
|
res = _cmd(cmd, *args)
|
161
161
|
|
162
162
|
if res == :not_started
|
163
|
-
error! "
|
163
|
+
error! "socket(#{Eye::Settings.socket_path}) not found, did you `eye load`?"
|
164
164
|
elsif res == :timeouted
|
165
165
|
error! "eye does not answer, timeouted..."
|
166
166
|
end
|
data/examples/process_thin.rb
CHANGED
@@ -23,7 +23,7 @@ def thin(proxy, port)
|
|
23
23
|
|
24
24
|
stdall "thin.stdall.log"
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
check :http, :url => "http://127.0.0.1:#{port}/hello", :pattern => /World/,
|
27
|
+
:every => 5.seconds, :times => [2, 3], :timeout => 1.second
|
28
28
|
end
|
29
29
|
end
|
data/examples/puma.eye
CHANGED
@@ -1,34 +1,29 @@
|
|
1
|
-
|
1
|
+
BUNDLE = 'bundle'
|
2
2
|
RAILS_ENV = 'production'
|
3
|
-
ROOT
|
4
|
-
CURRENT = File.expand_path(File.join(ROOT, %w{current}))
|
5
|
-
LOGS = File.expand_path(File.join(ROOT, %w{shared log}))
|
6
|
-
PIDS = File.expand_path(File.join(ROOT, %w{shared pids}))
|
3
|
+
ROOT = File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
7
4
|
|
8
5
|
Eye.config do
|
9
|
-
logger "#{
|
10
|
-
logger_level Logger::ERROR
|
6
|
+
logger "#{ROOT}/eye.log"
|
11
7
|
end
|
12
8
|
|
13
|
-
Eye.application :
|
9
|
+
Eye.application :puma do
|
14
10
|
env 'RAILS_ENV' => RAILS_ENV
|
15
11
|
working_dir ROOT
|
16
|
-
|
12
|
+
trigger :flapping, :times => 10, :within => 1.minute
|
17
13
|
|
18
14
|
process :puma do
|
19
15
|
daemonize true
|
20
|
-
pid_file "
|
21
|
-
stdall "
|
16
|
+
pid_file "puma.pid"
|
17
|
+
stdall "puma.log"
|
22
18
|
|
23
|
-
start_command "#{
|
24
|
-
|
19
|
+
start_command "#{BUNDLE} exec puma --port 33280 --environment #{RAILS_ENV} thin.ru"
|
20
|
+
stop_signals [:TERM, 5.seconds, :KILL]
|
25
21
|
restart_command "kill -USR2 {{PID}}"
|
26
22
|
|
27
|
-
|
28
|
-
|
29
|
-
restart_grace 10.seconds
|
23
|
+
restart_grace 10.seconds # just sleep this until process get up status
|
24
|
+
# (maybe enought to puma soft restart)
|
30
25
|
|
31
|
-
|
32
|
-
|
26
|
+
check :cpu, :every => 30, :below => 80, :times => 3
|
27
|
+
check :memory, :every => 30, :below => 70.megabytes, :times => [3,5]
|
33
28
|
end
|
34
29
|
end
|
data/examples/sidekiq.eye
CHANGED
@@ -10,8 +10,8 @@ def sidekiq_process(proxy, name)
|
|
10
10
|
daemonize true
|
11
11
|
stop_signals [:QUIT, 5.seconds, :TERM, 5.seconds, :KILL]
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
check :cpu, :every => 30, :below => 100, :times => 5
|
14
|
+
check :memory, :every => 30, :below => 300.megabytes, :times => 5
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
data/examples/test.eye
CHANGED
@@ -14,8 +14,8 @@ Eye.application "test" do
|
|
14
14
|
working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
15
15
|
stdall "trash.log" # stdout,err logs for processes by default
|
16
16
|
env "APP_ENV" => "production" # global env for each processes
|
17
|
-
|
18
|
-
|
17
|
+
trigger :flapping, :times => 10, :within => 1.minute, :retry_in => 10.minutes
|
18
|
+
check :cpu, :below => 100, :times => 3 # global check for all processes
|
19
19
|
|
20
20
|
group "samples" do
|
21
21
|
chain :grace => 5.seconds # chained start-restart with 5s interval, one by one.
|
@@ -31,7 +31,7 @@ Eye.application "test" do
|
|
31
31
|
daemonize true
|
32
32
|
stdall "sample1.log"
|
33
33
|
|
34
|
-
|
34
|
+
check :cpu, :below => 30, :times => [3, 5]
|
35
35
|
end
|
36
36
|
|
37
37
|
# self daemonized process
|
@@ -40,7 +40,7 @@ Eye.application "test" do
|
|
40
40
|
start_command "ruby ./sample.rb -d --pid 2.pid --log sample2.log"
|
41
41
|
stop_command "kill -9 {PID}"
|
42
42
|
|
43
|
-
|
43
|
+
check :memory, :below => 300.megabytes, :times => 3
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
@@ -51,12 +51,12 @@ Eye.application "test" do
|
|
51
51
|
stop_command "ruby forking.rb stop"
|
52
52
|
stdall "forking.log"
|
53
53
|
|
54
|
-
start_timeout
|
55
|
-
|
54
|
+
start_timeout 10.seconds
|
55
|
+
stop_timeout 5.seconds
|
56
56
|
|
57
57
|
monitor_children do
|
58
58
|
restart_command "kill -2 {PID}" # for this child process
|
59
|
-
|
59
|
+
check :memory, :below => 300.megabytes, :times => 3
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -68,8 +68,8 @@ Eye.application "test" do
|
|
68
68
|
daemonize true
|
69
69
|
stop_signals [:QUIT, 2.seconds, :KILL]
|
70
70
|
|
71
|
-
|
72
|
-
|
71
|
+
check :socket, :addr => "tcp://127.0.0.1:33221", :every => 10.seconds, :times => 2,
|
72
|
+
:timeout => 1.second, :send_data => "ping", :expect_data => /pong/
|
73
73
|
end
|
74
74
|
|
75
75
|
# thin process, self daemonized
|
@@ -78,8 +78,8 @@ Eye.application "test" do
|
|
78
78
|
start_command "bundle exec thin start -R thin.ru -p 33233 -d -l thin.log -P thin.pid"
|
79
79
|
stop_signals [:QUIT, 2.seconds, :TERM, 1.seconds, :KILL]
|
80
80
|
|
81
|
-
|
82
|
-
|
81
|
+
check :http, :url => "http://127.0.0.1:33233/hello", :pattern => /World/, :every => 5.seconds,
|
82
|
+
:times => [2, 3], :timeout => 1.second
|
83
83
|
end
|
84
84
|
|
85
85
|
end
|
data/examples/thin-farm.eye
CHANGED
@@ -14,8 +14,8 @@ Eye.app 'thin-farm' do
|
|
14
14
|
stop_on_delete true # this option means, when we change pids and load config,
|
15
15
|
# deleted processes will be stops
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
trigger :flapping, :times => 10, :within => 1.minute
|
18
|
+
check :memory, :below => 60.megabytes, :every => 30.seconds, :times => 5
|
19
19
|
start_timeout 30.seconds
|
20
20
|
|
21
21
|
group :web do
|
data/examples/unicorn.eye
CHANGED
@@ -4,27 +4,35 @@ RUBY = '/usr/local/ruby/1.9.3/bin/ruby' # ruby on the server
|
|
4
4
|
RAILS_ENV = 'production'
|
5
5
|
|
6
6
|
Eye.application "rails_unicorn" do
|
7
|
-
env "RAILS_ENV" => RAILS_ENV
|
7
|
+
env "RAILS_ENV" => RAILS_ENV
|
8
|
+
|
9
|
+
# unicorn requires to be `ruby` in path (for soft restart)
|
10
|
+
env "PATH" => "#{File.dirname(RUBY)}:#{ENV['PATH']}"
|
11
|
+
|
8
12
|
working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
|
9
13
|
|
10
14
|
process("unicorn") do
|
11
15
|
pid_file "tmp/pids/unicorn.pid"
|
12
16
|
start_command "#{RUBY} ./bin/unicorn -Dc ./config/unicorn.rb -E #{RAILS_ENV}"
|
13
|
-
stop_command "kill -QUIT {{PID}}"
|
14
|
-
restart_command "kill -USR2 {{PID}}"
|
15
17
|
stdall "log/unicorn.log"
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
+
# stop signals:
|
20
|
+
# http://unicorn.bogomips.org/SIGNALS.html
|
21
|
+
stop_signals [:TERM, 10.seconds]
|
22
|
+
|
23
|
+
# soft restart
|
24
|
+
restart_command "kill -USR2 {PID}"
|
25
|
+
|
26
|
+
check :cpu, :every => 30, :below => 80, :times => 3
|
27
|
+
check :memory, :every => 30, :below => 150.megabytes, :times => [3,5]
|
19
28
|
|
20
29
|
start_timeout 30.seconds
|
21
|
-
stop_grace 5.seconds
|
22
30
|
restart_grace 30.seconds
|
23
31
|
|
24
32
|
monitor_children do
|
25
|
-
stop_command "kill -QUIT {
|
26
|
-
|
27
|
-
|
33
|
+
stop_command "kill -QUIT {PID}"
|
34
|
+
check :cpu, :every => 30, :below => 80, :times => 3
|
35
|
+
check :memory, :every => 30, :below => 150.megabytes, :times => [3,5]
|
28
36
|
end
|
29
37
|
end
|
30
38
|
|
data/eye.gemspec
CHANGED
@@ -5,7 +5,7 @@ Gem::Specification.new do |gem|
|
|
5
5
|
gem.email = "kostya27@gmail.com"
|
6
6
|
|
7
7
|
gem.description = gem.summary = \
|
8
|
-
%q{Process monitoring tool.
|
8
|
+
%q{Process monitoring tool. Inspired from Bluepill and God. Requires Ruby(MRI) >= 1.9.3-p194. Uses Celluloid and Celluloid::IO.}
|
9
9
|
gem.homepage = "http://github.com/kostya/eye"
|
10
10
|
|
11
11
|
gem.files = `git ls-files`.split($\).reject{|n| n =~ %r[png|gif\z]}.reject{|n| n =~ %r[^(test|spec|features)/]}
|
data/lib/eye.rb
CHANGED
data/lib/eye/application.rb
CHANGED
data/lib/eye/checker.rb
CHANGED
@@ -6,16 +6,18 @@ class Eye::Checker
|
|
6
6
|
autoload :FileCTime, 'eye/checker/file_ctime'
|
7
7
|
autoload :FileSize, 'eye/checker/file_size'
|
8
8
|
autoload :Socket, 'eye/checker/socket'
|
9
|
+
autoload :Nop, 'eye/checker/nop'
|
9
10
|
|
10
11
|
TYPES = {:memory => "Memory", :cpu => "Cpu", :http => "Http",
|
11
|
-
:ctime => "FileCTime", :fsize => "FileSize", :socket => "Socket"
|
12
|
+
:ctime => "FileCTime", :fsize => "FileSize", :socket => "Socket",
|
13
|
+
:nop => "Nop" }
|
12
14
|
|
13
15
|
attr_accessor :value, :values, :options, :pid, :type, :check_count, :process
|
14
16
|
|
15
17
|
extend Eye::Dsl::Validation
|
16
18
|
param :every, [Fixnum, Float], false, 5
|
17
19
|
param :times, [Fixnum, Array], nil, 1
|
18
|
-
param :
|
20
|
+
param :fires, [Symbol, Array], nil, nil, [:stop, :restart, :unmonitor, :nothing, :start, :delete]
|
19
21
|
|
20
22
|
def self.name_and_class(type)
|
21
23
|
type = type.to_sym
|
@@ -35,6 +37,10 @@ class Eye::Checker
|
|
35
37
|
|
36
38
|
def self.create(pid, options = {}, process = nil)
|
37
39
|
get_class(options[:type]).new(pid, options, process)
|
40
|
+
|
41
|
+
rescue Exception, Timeout::Error => ex
|
42
|
+
log_ex(ex)
|
43
|
+
nil
|
38
44
|
end
|
39
45
|
|
40
46
|
def self.validate!(options)
|
@@ -46,6 +52,7 @@ class Eye::Checker
|
|
46
52
|
@pid = pid
|
47
53
|
@options = options
|
48
54
|
@type = options[:type]
|
55
|
+
@full_name = @process.full_name if @process
|
49
56
|
|
50
57
|
debug "create checker, with #{options}"
|
51
58
|
|
@@ -55,7 +62,7 @@ class Eye::Checker
|
|
55
62
|
end
|
56
63
|
|
57
64
|
def inspect
|
58
|
-
"<#{self.class} @process='#{@
|
65
|
+
"<#{self.class} @process='#{@full_name}' @options=#{@options} @pid=#{@pid}>"
|
59
66
|
end
|
60
67
|
|
61
68
|
def logger_tag
|
@@ -89,6 +96,9 @@ class Eye::Checker
|
|
89
96
|
|
90
97
|
info "#{last_human_values} => #{result ? 'OK' : 'Fail'}"
|
91
98
|
result
|
99
|
+
|
100
|
+
rescue Exception, Timeout::Error => ex
|
101
|
+
log_ex(ex)
|
92
102
|
end
|
93
103
|
|
94
104
|
def get_value_safe
|
@@ -162,7 +172,14 @@ class Eye::Checker
|
|
162
172
|
Eye::Checker.const_set(name, base)
|
163
173
|
end
|
164
174
|
|
165
|
-
class
|
175
|
+
class CustomCell < Eye::Checker
|
176
|
+
def self.inherited(base)
|
177
|
+
super
|
178
|
+
register(base)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class Custom < Defer
|
166
183
|
def self.inherited(base)
|
167
184
|
super
|
168
185
|
register(base)
|
@@ -8,14 +8,12 @@ module Eye::Controller::Commands
|
|
8
8
|
cmd = cmd.to_sym
|
9
9
|
|
10
10
|
res = case cmd
|
11
|
-
when :start, :stop, :restart, :unmonitor, :monitor
|
11
|
+
when :start, :stop, :restart, :unmonitor, :monitor, :break_chain
|
12
12
|
send_command(cmd, *args)
|
13
13
|
when :delete
|
14
14
|
exclusive{ send_command(cmd, *args) }
|
15
15
|
when :signal
|
16
16
|
signal(*args)
|
17
|
-
when :break_chain
|
18
|
-
break_chain(*args)
|
19
17
|
when :load
|
20
18
|
load(*args)
|
21
19
|
when :info
|
data/lib/eye/controller/load.rb
CHANGED
@@ -23,27 +23,18 @@ module Eye::Controller::SendCommand
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
def break_chain(*args)
|
27
|
-
matched_objects(*args) do |obj|
|
28
|
-
obj.send_command(:break_chain)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
26
|
private
|
33
27
|
|
34
28
|
class Error < Exception; end
|
35
29
|
|
36
30
|
def matched_objects(*args, &block)
|
37
|
-
|
38
|
-
obj_strs = args
|
39
|
-
|
40
|
-
objs = find_objects(*obj_strs)
|
31
|
+
objs = find_objects(*args)
|
41
32
|
res = objs.map(&:full_name)
|
42
33
|
objs.each{|obj| block[obj] } if block
|
43
34
|
{:result => res}
|
44
35
|
|
45
36
|
rescue Error => ex
|
46
|
-
|
37
|
+
log_ex(ex)
|
47
38
|
|
48
39
|
{:error => ex.message}
|
49
40
|
end
|
@@ -69,13 +60,25 @@ private
|
|
69
60
|
|
70
61
|
# find object to action, restart ... (app, group or process)
|
71
62
|
# nil if not found
|
72
|
-
def find_objects(*
|
63
|
+
def find_objects(*args)
|
64
|
+
h = args.extract_options!
|
65
|
+
obj_strs = args
|
66
|
+
|
73
67
|
return [] if obj_strs.blank?
|
74
|
-
|
68
|
+
|
69
|
+
if obj_strs.size == 1 && (obj_strs[0].to_s.strip == 'all' || obj_strs[0].to_s.strip == '*')
|
70
|
+
if h[:application]
|
71
|
+
return @applications.select { |app| app.name == h[:application]}
|
72
|
+
else
|
73
|
+
return @applications.dup
|
74
|
+
end
|
75
|
+
end
|
75
76
|
|
76
77
|
res = Eye::Utils::AliveArray.new
|
77
78
|
obj_strs.map{|c| c.to_s.split(",")}.flatten.each do |mask|
|
78
|
-
|
79
|
+
objs = find_objects_by_mask(mask.to_s.strip)
|
80
|
+
objs.select! { |obj| obj.app_name == h[:application] } if h[:application]
|
81
|
+
res += objs
|
79
82
|
end
|
80
83
|
res
|
81
84
|
end
|
@@ -88,9 +91,9 @@ private
|
|
88
91
|
|
89
92
|
# try to find exactly matched
|
90
93
|
if mask[-1] != '*'
|
91
|
-
r =
|
94
|
+
r = exact_regexp(mask)
|
92
95
|
res.each do |obj|
|
93
|
-
final << obj if obj.full_name =~ r
|
96
|
+
final << obj if obj.name =~ r || obj.full_name =~ r
|
94
97
|
end
|
95
98
|
end
|
96
99
|
|
@@ -106,16 +109,18 @@ private
|
|
106
109
|
res = final
|
107
110
|
|
108
111
|
# try to remove objects with different applications
|
109
|
-
|
112
|
+
apps, objs = Eye::Utils::AliveArray.new, Eye::Utils::AliveArray.new
|
110
113
|
res.each do |obj|
|
111
114
|
if obj.is_a?(Eye::Application)
|
112
|
-
|
115
|
+
apps << obj
|
113
116
|
else
|
114
|
-
|
117
|
+
objs << obj
|
115
118
|
end
|
116
119
|
end
|
117
120
|
|
118
|
-
|
121
|
+
return apps if apps.size > 0
|
122
|
+
|
123
|
+
if objs.map(&:app_name).uniq.size > 1
|
119
124
|
raise Error, "cant match targets from different applications: #{res.map(&:full_name)}"
|
120
125
|
end
|
121
126
|
end
|
@@ -163,8 +168,8 @@ private
|
|
163
168
|
%r|\A#{str}|
|
164
169
|
end
|
165
170
|
|
166
|
-
def
|
171
|
+
def exact_regexp(mask)
|
167
172
|
str = Regexp.escape(mask).gsub('\*', '.*?')
|
168
|
-
%r
|
173
|
+
%r|\A#{str}\z|
|
169
174
|
end
|
170
175
|
end
|
@@ -17,7 +17,6 @@ About: #{Eye::ABOUT}
|
|
17
17
|
Info: #{resources_str(Eye::SystemResources.resources($$))}
|
18
18
|
Ruby: #{RUBY_DESCRIPTION}
|
19
19
|
Gems: #{%w|Celluloid Celluloid::IO ActiveSupport StateMachine NIO|.map{|c| gem_version(c) }}
|
20
|
-
Checks: #{Eye::Checker::TYPES}, #{Eye::Trigger::TYPES}
|
21
20
|
Logger: #{Eye::Logger.dev}
|
22
21
|
Http: #{@http ? "#{@http.host}:#{@http.port}" : '-'}
|
23
22
|
Socket: #{Eye::Settings::socket_path}
|
data/lib/eye/dsl/config_opts.rb
CHANGED
@@ -9,17 +9,18 @@ class Eye::Dsl::ConfigOpts < Eye::Dsl::PureOpts
|
|
9
9
|
end
|
10
10
|
|
11
11
|
# ==== contact options ==============================
|
12
|
+
def self.add_notify(type)
|
13
|
+
create_options_methods([type], Hash)
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
define_method("set_#{not_system}") do |value|
|
17
|
-
value = value.merge(:type => not_system)
|
15
|
+
define_method("set_#{type}") do |value|
|
16
|
+
value = value.merge(:type => type)
|
18
17
|
super(value)
|
19
18
|
Eye::Notify.validate!(value)
|
20
19
|
end
|
21
20
|
end
|
22
21
|
|
22
|
+
Eye::Notify::TYPES.keys.each { |name| add_notify(name) }
|
23
|
+
|
23
24
|
def contact(contact_name, contact_type, contact, contact_opts = {})
|
24
25
|
raise Eye::Dsl::Error, "unknown contact_type #{contact_type}" unless Eye::Notify::TYPES[contact_type]
|
25
26
|
raise Eye::Dsl::Error, "contact should be a String" unless contact.is_a?(String)
|
data/lib/eye/dsl/main.rb
CHANGED
data/lib/eye/dsl/opts.rb
CHANGED
data/lib/eye/dsl/validation.rb
CHANGED
@@ -41,8 +41,14 @@ module Eye::Dsl::Validation
|
|
41
41
|
end
|
42
42
|
|
43
43
|
if self.variants[param]
|
44
|
-
if value && !value.is_a?(Proc)
|
45
|
-
|
44
|
+
if value && !value.is_a?(Proc)
|
45
|
+
if value.is_a?(Array)
|
46
|
+
if (value - self.variants[param]).present?
|
47
|
+
raise Error, "#{value.inspect} should within #{self.variants[param].inspect}"
|
48
|
+
end
|
49
|
+
elsif !self.variants[param].include?(value)
|
50
|
+
raise Error, "#{value.inspect} should within #{self.variants[param].inspect}"
|
51
|
+
end
|
46
52
|
end
|
47
53
|
end
|
48
54
|
|
data/lib/eye/logger.rb
CHANGED
data/lib/eye/notify.rb
CHANGED
@@ -40,6 +40,9 @@ class Eye::Notify
|
|
40
40
|
else
|
41
41
|
create_proc[needed_hash]
|
42
42
|
end
|
43
|
+
|
44
|
+
rescue Exception, Timeout::Error => ex
|
45
|
+
log_ex(ex)
|
43
46
|
end
|
44
47
|
|
45
48
|
TIMEOUT = 1.minute
|
@@ -81,6 +84,21 @@ class Eye::Notify
|
|
81
84
|
"#{message_subject} at #{msg_at.to_s(:short)}"
|
82
85
|
end
|
83
86
|
|
87
|
+
def self.register(base)
|
88
|
+
name = base.to_s.gsub("Eye::Notify::", '')
|
89
|
+
type = name.underscore.to_sym
|
90
|
+
Eye::Notify::TYPES[type] = name
|
91
|
+
Eye::Notify.const_set(name, base)
|
92
|
+
Eye::Dsl::ConfigOpts.add_notify(type)
|
93
|
+
end
|
94
|
+
|
95
|
+
class Custom < Eye::Notify
|
96
|
+
def self.inherited(base)
|
97
|
+
super
|
98
|
+
register(base)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
84
102
|
private
|
85
103
|
|
86
104
|
%w{at host message name full_name pid level}.each do |name|
|
data/lib/eye/process/commands.rb
CHANGED
@@ -87,7 +87,10 @@ module Eye::Process::Commands
|
|
87
87
|
private
|
88
88
|
|
89
89
|
def kill_process
|
90
|
-
|
90
|
+
unless self.pid
|
91
|
+
error "try to kill process without pid"
|
92
|
+
return
|
93
|
+
end
|
91
94
|
|
92
95
|
if self[:stop_command]
|
93
96
|
cmd = prepare_command(self[:stop_command])
|
@@ -120,7 +123,7 @@ private
|
|
120
123
|
break
|
121
124
|
end
|
122
125
|
|
123
|
-
send_signal(signal)
|
126
|
+
send_signal(signal) if signal
|
124
127
|
end
|
125
128
|
|
126
129
|
sleep_grace(:stop_grace)
|
@@ -141,6 +144,11 @@ private
|
|
141
144
|
end
|
142
145
|
|
143
146
|
def execute_restart_command
|
147
|
+
unless self.pid
|
148
|
+
error "try to execute restart_command without pid"
|
149
|
+
return
|
150
|
+
end
|
151
|
+
|
144
152
|
cmd = prepare_command(self[:restart_command])
|
145
153
|
info "executing: `#{cmd}` with restart_timeout: #{self[:restart_timeout].to_f}s and restart_grace: #{self[:restart_grace].to_f}s"
|
146
154
|
|
@@ -219,17 +227,17 @@ private
|
|
219
227
|
sleep_grace(:start_grace)
|
220
228
|
|
221
229
|
unless set_pid_from_file
|
222
|
-
error "pid_file(#{self[:pid_file_ex]}) does not appears after start_grace #{self[:start_grace].to_f}, check start_command, or tune start_grace (eye dont know what to monitor without pid)"
|
230
|
+
error "exit status #{res[:exitstatus]}, pid_file(#{self[:pid_file_ex]}) does not appears after start_grace #{self[:start_grace].to_f}, check start_command, or tune start_grace (eye dont know what to monitor without pid)"
|
223
231
|
return {:error => :pid_not_found}
|
224
232
|
end
|
225
233
|
|
226
234
|
unless process_realy_running?
|
227
|
-
error "process in pid_file(#{self[:pid_file_ex]})(#{self.pid}) not found, maybe process do not write there actual pid, or just crashed (#{check_logs_str})"
|
235
|
+
error "exit status #{res[:exitstatus]}, process in pid_file(#{self[:pid_file_ex]})(#{self.pid}) not found, maybe process do not write there actual pid, or just crashed (#{check_logs_str})"
|
228
236
|
return {:error => :not_realy_running}
|
229
237
|
end
|
230
238
|
|
231
239
|
res[:pid] = self.pid
|
232
|
-
info "process get pid:#{res[:pid]}, pid_file #{self[:pid_file_ex]}"
|
240
|
+
info "process get pid:#{res[:pid]}, pid_file #{self[:pid_file_ex]}, exit status #{res[:exitstatus]}"
|
233
241
|
res
|
234
242
|
end
|
235
243
|
|
data/lib/eye/process/system.rb
CHANGED
data/lib/eye/process/trigger.rb
CHANGED
data/lib/eye/process/watchers.rb
CHANGED
@@ -53,16 +53,19 @@ private
|
|
53
53
|
subject = Eye::Checker.create(pid, cfg, current_actor)
|
54
54
|
|
55
55
|
# ex: {:type => :memory, :every => 5.seconds, :below => 100.megabytes, :times => [3,5]}
|
56
|
-
add_watcher("check_#{name}".to_sym, subject.every, subject, &method(:watcher_tick).to_proc)
|
56
|
+
add_watcher("check_#{name}".to_sym, subject.every, subject, &method(:watcher_tick).to_proc) if subject
|
57
57
|
end
|
58
58
|
|
59
59
|
def watcher_tick(subject)
|
60
60
|
unless subject.check
|
61
61
|
return unless up?
|
62
62
|
|
63
|
-
|
64
|
-
notify :warn, "Bounded #{subject.check_name}: #{subject.last_human_values} send to
|
65
|
-
|
63
|
+
actions = subject.fires ? Array(subject.fires) : [:restart]
|
64
|
+
notify :warn, "Bounded #{subject.check_name}: #{subject.last_human_values} send to #{actions}"
|
65
|
+
|
66
|
+
actions.each do |action|
|
67
|
+
schedule action, Eye::Reason.new("bounded #{subject.check_name}")
|
68
|
+
end
|
66
69
|
end
|
67
70
|
end
|
68
71
|
|
data/lib/eye/system.rb
CHANGED
@@ -58,7 +58,7 @@ module Eye::System
|
|
58
58
|
opts = spawn_options(cfg)
|
59
59
|
pid = Process::spawn(prepare_env(cfg), *Shellwords.shellwords(cmd), opts)
|
60
60
|
Process.detach(pid)
|
61
|
-
{:pid => pid}
|
61
|
+
{:pid => pid, :exitstatus => 0}
|
62
62
|
|
63
63
|
rescue Errno::ENOENT, Errno::EACCES => ex
|
64
64
|
{:error => ex}
|
@@ -74,11 +74,14 @@ module Eye::System
|
|
74
74
|
pid = Process::spawn(prepare_env(cfg), *Shellwords.shellwords(cmd), opts)
|
75
75
|
|
76
76
|
timeout = cfg[:timeout] || 1.second
|
77
|
+
status = 0
|
78
|
+
|
77
79
|
Timeout.timeout(timeout) do
|
78
|
-
Process.
|
80
|
+
_, st = Process.waitpid2(pid)
|
81
|
+
status = st.exitstatus || st.termsig
|
79
82
|
end
|
80
83
|
|
81
|
-
{:pid => pid}
|
84
|
+
{:pid => pid, :exitstatus => status}
|
82
85
|
|
83
86
|
rescue Timeout::Error => ex
|
84
87
|
if pid
|
@@ -156,9 +159,6 @@ module Eye::System
|
|
156
159
|
env[k.to_s] = v.to_s if v
|
157
160
|
end
|
158
161
|
|
159
|
-
# set PWD for unicorn respawn
|
160
|
-
env['PWD'] = config[:working_dir] if config[:working_dir]
|
161
|
-
|
162
162
|
env
|
163
163
|
end
|
164
164
|
end
|
data/lib/eye/trigger.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
class Eye::Trigger
|
2
2
|
autoload :Flapping, 'eye/trigger/flapping'
|
3
3
|
autoload :State, 'eye/trigger/state'
|
4
|
+
autoload :StopChilds, 'eye/trigger/stop_childs'
|
4
5
|
|
5
6
|
# ex: { :type => :flapping, :times => 2, :within => 30.seconds}
|
6
7
|
|
7
|
-
TYPES = {:flapping => "Flapping", :state => "State"}
|
8
|
+
TYPES = {:flapping => "Flapping", :state => "State", :stop_childs => "StopChilds"}
|
8
9
|
|
9
10
|
attr_reader :message, :options, :process
|
10
11
|
|
@@ -28,6 +29,10 @@ class Eye::Trigger
|
|
28
29
|
|
29
30
|
def self.create(process, options = {})
|
30
31
|
get_class(options[:type]).new(process, options)
|
32
|
+
|
33
|
+
rescue Exception, Timeout::Error => ex
|
34
|
+
log_ex(ex)
|
35
|
+
nil
|
31
36
|
end
|
32
37
|
|
33
38
|
def self.validate!(options = {})
|
@@ -37,12 +42,13 @@ class Eye::Trigger
|
|
37
42
|
def initialize(process, options = {})
|
38
43
|
@options = options
|
39
44
|
@process = process
|
45
|
+
@full_name = @process.full_name if @process
|
40
46
|
|
41
47
|
debug "add #{options}"
|
42
48
|
end
|
43
49
|
|
44
50
|
def inspect
|
45
|
-
"<#{self.class} @process='#{@
|
51
|
+
"<#{self.class} @process='#{@full_name}' @options=#{@options}>"
|
46
52
|
end
|
47
53
|
|
48
54
|
def logger_tag
|
@@ -54,11 +60,25 @@ class Eye::Trigger
|
|
54
60
|
end
|
55
61
|
|
56
62
|
def notify(transition)
|
57
|
-
debug "check"
|
63
|
+
debug "check (:#{transition.event}) :#{transition.from} => :#{transition.to}"
|
58
64
|
@transition = transition
|
59
|
-
|
60
|
-
|
61
|
-
|
65
|
+
|
66
|
+
check(transition) if filter_transition(transition)
|
67
|
+
|
68
|
+
rescue Exception, Timeout::Error => ex
|
69
|
+
log_ex(ex)
|
70
|
+
end
|
71
|
+
|
72
|
+
param :to, [Symbol, Array]
|
73
|
+
param :from, [Symbol, Array]
|
74
|
+
param :event, [Symbol, Array]
|
75
|
+
|
76
|
+
def filter_transition(trans)
|
77
|
+
return true unless to || from || event
|
78
|
+
|
79
|
+
compare_state(trans.to_name, to) &&
|
80
|
+
compare_state(trans.from_name, from) &&
|
81
|
+
compare_state(trans.event, event)
|
62
82
|
end
|
63
83
|
|
64
84
|
def check(transition)
|
@@ -75,7 +95,6 @@ class Eye::Trigger
|
|
75
95
|
|
76
96
|
def self.register(base)
|
77
97
|
name = base.to_s.gsub("Eye::Trigger::", '')
|
78
|
-
name = base.to_s
|
79
98
|
type = name.underscore.to_sym
|
80
99
|
Eye::Trigger::TYPES[type] = name
|
81
100
|
Eye::Trigger.const_set(name, base)
|
@@ -87,4 +106,18 @@ class Eye::Trigger
|
|
87
106
|
register(base)
|
88
107
|
end
|
89
108
|
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def compare_state(state_name, condition)
|
113
|
+
case condition
|
114
|
+
when Symbol
|
115
|
+
state_name == condition
|
116
|
+
when Array
|
117
|
+
condition.include?(state_name)
|
118
|
+
else
|
119
|
+
true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
90
123
|
end
|
data/lib/eye/trigger/flapping.rb
CHANGED
data/lib/eye/trigger/state.rb
CHANGED
@@ -2,27 +2,10 @@ class Eye::Trigger::State < Eye::Trigger
|
|
2
2
|
|
3
3
|
# triggers :state, :to => :up, :from => :starting, :do => ->{ ... }
|
4
4
|
|
5
|
-
param :to, [Symbol, Array]
|
6
|
-
param :from, [Symbol, Array]
|
7
|
-
param :event, [Symbol, Array]
|
8
5
|
param :do, [Proc]
|
9
6
|
|
10
7
|
def check(trans)
|
11
|
-
|
12
|
-
debug "trans ok #{trans}, executing proc!"
|
13
|
-
run_in_process_context(@options[:do]) if @options[:do]
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def compare(state_name, condition)
|
20
|
-
case condition
|
21
|
-
when Symbol
|
22
|
-
state_name == condition
|
23
|
-
when Array
|
24
|
-
condition.include?(state_name)
|
25
|
-
end
|
8
|
+
instance_exec(&@options[:do]) if @options[:do]
|
26
9
|
end
|
27
10
|
|
28
11
|
end
|
@@ -7,6 +7,7 @@ class Eye::Utils::CelluloidChain
|
|
7
7
|
@target = target
|
8
8
|
@calls = []
|
9
9
|
@running = false
|
10
|
+
@target_class = @target.class
|
10
11
|
end
|
11
12
|
|
12
13
|
def add(method_name, *args, &block)
|
@@ -46,7 +47,7 @@ class Eye::Utils::CelluloidChain
|
|
46
47
|
|
47
48
|
# need, because of https://github.com/celluloid/celluloid/issues/22
|
48
49
|
def inspect
|
49
|
-
"Celluloid::Chain(#{@
|
50
|
+
"Celluloid::Chain(#{@target_class}: #{@calls.size})"
|
50
51
|
end
|
51
52
|
|
52
53
|
attr_reader :running
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reel-eye
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Konstantin Makarchev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-08-
|
11
|
+
date: 2013-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: celluloid
|
@@ -276,8 +276,8 @@ dependencies:
|
|
276
276
|
- - '>='
|
277
277
|
- !ruby/object:Gem::Version
|
278
278
|
version: '0'
|
279
|
-
description: Process monitoring tool.
|
280
|
-
|
279
|
+
description: Process monitoring tool. Inspired from Bluepill and God. Requires Ruby(MRI)
|
280
|
+
>= 1.9.3-p194. Uses Celluloid and Celluloid::IO.
|
281
281
|
email: kostya27@gmail.com
|
282
282
|
executables:
|
283
283
|
- eye
|
@@ -316,6 +316,7 @@ files:
|
|
316
316
|
- lib/eye/checker/file_size.rb
|
317
317
|
- lib/eye/checker/http.rb
|
318
318
|
- lib/eye/checker/memory.rb
|
319
|
+
- lib/eye/checker/nop.rb
|
319
320
|
- lib/eye/checker/socket.rb
|
320
321
|
- lib/eye/child_process.rb
|
321
322
|
- lib/eye/client.rb
|
@@ -373,6 +374,7 @@ files:
|
|
373
374
|
- lib/eye/trigger.rb
|
374
375
|
- lib/eye/trigger/flapping.rb
|
375
376
|
- lib/eye/trigger/state.rb
|
377
|
+
- lib/eye/trigger/stop_childs.rb
|
376
378
|
- lib/eye/utils.rb
|
377
379
|
- lib/eye/utils/alive_array.rb
|
378
380
|
- lib/eye/utils/celluloid_chain.rb
|
@@ -402,6 +404,6 @@ rubyforge_project:
|
|
402
404
|
rubygems_version: 2.0.2
|
403
405
|
signing_key:
|
404
406
|
specification_version: 4
|
405
|
-
summary: Process monitoring tool.
|
406
|
-
|
407
|
+
summary: Process monitoring tool. Inspired from Bluepill and God. Requires Ruby(MRI)
|
408
|
+
>= 1.9.3-p194. Uses Celluloid and Celluloid::IO.
|
407
409
|
test_files: []
|