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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2a4ce0513f92a734194ec12a2c10aa8cadcfaafa
4
- data.tar.gz: 43e96f1ccc671f83454d790d8e546785b3cb8c18
3
+ metadata.gz: a1b6bc63e403f70e9de31aeedabd8712ed291fbf
4
+ data.tar.gz: 9c0d9241079bd13bff558852a5cd71c404f1b50e
5
5
  SHA512:
6
- metadata.gz: 780ee7abb50e1a473614d33e132ccbea9854cdf4e7f5b5ca77ee003caebcddf2759f2d6ae489f7585a915b8630d3813f60924e2762c224bd2f65ad51defcaddb
7
- data.tar.gz: 26b46e22aeddce190a889072e88d41bb1d638db3147c6fe4cd79d6861ac5e11063de82deb0fa5e78ee34a8c106c2592c05db11748ff7569e6e0e2f7df2d95661
6
+ metadata.gz: c86cba4c4a63f6febe9bad36d7f4696f766a8695129eef747518b2edad3e48a48d2d13523ba65d92ea8b346446cf50976e7b2e45ee3c143e5b4a1ce617880605
7
+ data.tar.gz: 5f760e64c45443a4429a127736770a1f66a31d61b56347c69c61242fdf7f28b427f0d995af0b5dd054c327bf99422150b9ee9033dde04fcce06b73143062451d
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. An alternative to God and Bluepill. With Bluepill like config syntax. Requires MRI Ruby >= 1.9.3-p194. Uses Celluloid and Celluloid::IO.
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
- triggers :flapping, :times => 10, :within => 1.minute, :retry_in => 10.minutes
40
- checks :cpu, :below => 100, :times => 3 # global check for all processes
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
- checks :cpu, :below => 30, :times => [3, 5]
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
- checks :memory, :below => 300.megabytes, :times => 3
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 5.seconds
77
- stop_grace 5.seconds
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
- checks :memory, :below => 300.megabytes, :times => 3
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
- checks :socket, :addr => "tcp://127.0.0.1:33221", :every => 10.seconds, :times => 2,
94
- :timeout => 1.second, :send_data => "ping", :expect_data => /pong/
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
- checks :http, :url => "http://127.0.0.1:33233/hello", :pattern => /World/, :every => 5.seconds,
104
- :times => [2, 3], :timeout => 1.second
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 monitoring and load config:
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
- Load also uses for config synchronization and load new application into runned eye daemon. Light operation, so i recommend to use with every deploy (and than restart processes).
120
- (for processes with option `stop_on_delete`, `load` becomes a tool for full config synchronization, which stopps deleted from config processes).
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
- ### Config options:
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
- Waiting for pull requests here..., until that you can read `examples` and `spec/dsl` folders.
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! "eye monitoring not found, did you start it?"
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
@@ -23,7 +23,7 @@ def thin(proxy, port)
23
23
 
24
24
  stdall "thin.stdall.log"
25
25
 
26
- checks :http, :url => "http://127.0.0.1:#{port}/hello", :pattern => /World/,
27
- :every => 5.seconds, :times => [2, 3], :timeout => 1.second
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
@@ -1,34 +1,29 @@
1
- RUBY = '/usr/local/ruby/1.9.3-p392/bin/ruby'
1
+ BUNDLE = 'bundle'
2
2
  RAILS_ENV = 'production'
3
- ROOT = File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
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 "#{LOGS}/eye.log"
10
- logger_level Logger::ERROR
6
+ logger "#{ROOT}/eye.log"
11
7
  end
12
8
 
13
- Eye.application :super_app do
9
+ Eye.application :puma do
14
10
  env 'RAILS_ENV' => RAILS_ENV
15
11
  working_dir ROOT
16
- triggers :flapping, :times => 10, :within => 1.minute
12
+ trigger :flapping, :times => 10, :within => 1.minute
17
13
 
18
14
  process :puma do
19
15
  daemonize true
20
- pid_file "#{PIDS}/puma.pid"
21
- stdall "#{LOGS}/#{RAILS_ENV}.log"
16
+ pid_file "puma.pid"
17
+ stdall "puma.log"
22
18
 
23
- start_command "#{RUBY} bin/puma --port 80 --pidfile #{PIDS}/puma.pid --environment #{RAILS_ENV} config.ru"
24
- stop_command "kill -TERM {{PID}}"
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
- start_timeout 15.seconds
28
- stop_grace 10.seconds
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
- checks :cpu, :every => 30, :below => 80, :times => 3
32
- checks :memory, :every => 30, :below => 70.megabytes, :times => [3,5]
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
@@ -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
- checks :cpu, :every => 30, :below => 100, :times => 5
14
- checks :memory, :every => 30, :below => 300.megabytes, :times => 5
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
 
@@ -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
- triggers :flapping, :times => 10, :within => 1.minute, :retry_in => 10.minutes
18
- checks :cpu, :below => 100, :times => 3 # global check for all processes
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
- checks :cpu, :below => 30, :times => [3, 5]
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
- checks :memory, :below => 300.megabytes, :times => 3
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 5.seconds
55
- stop_grace 5.seconds
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
- checks :memory, :below => 300.megabytes, :times => 3
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
- checks :socket, :addr => "tcp://127.0.0.1:33221", :every => 10.seconds, :times => 2,
72
- :timeout => 1.second, :send_data => "ping", :expect_data => /pong/
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
- checks :http, :url => "http://127.0.0.1:33233/hello", :pattern => /World/, :every => 5.seconds,
82
- :times => [2, 3], :timeout => 1.second
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
@@ -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
- triggers :flapping, :times => 10, :within => 1.minute
18
- checks :memory, :below => 60.megabytes, :every => 30.seconds, :times => 5
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
@@ -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, "PATH" => "#{File.dirname(RUBY)}:#{ENV['PATH']}"
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
- checks :cpu, :every => 30, :below => 80, :times => 3
18
- checks :memory, :every => 30, :below => 150.megabytes, :times => [3,5]
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 {{PID}}"
26
- checks :cpu, :every => 30, :below => 80, :times => 3
27
- checks :memory, :every => 30, :below => 150.megabytes, :times => [3,5]
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
 
@@ -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. An alternative to God and Bluepill. With Bluepill like config syntax. Requires MRI Ruby >= 1.9.2. Uses Celluloid and Celluloid::IO.}
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
@@ -1,5 +1,5 @@
1
1
  module Eye
2
- VERSION = "0.4"
2
+ VERSION = "0.4.1"
3
3
  ABOUT = "Eye v#{VERSION} (c) 2012-2013 @kostya"
4
4
  PROCLINE = "eye monitoring v#{VERSION}"
5
5
 
@@ -17,10 +17,6 @@ class Eye::Application
17
17
  @name
18
18
  end
19
19
 
20
- def update_config(cfg)
21
- @config = cfg
22
- end
23
-
24
20
  def add_group(group)
25
21
  @groups << group
26
22
  end
@@ -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 :fire, Symbol, nil, nil, [:stop, :restart, :unmonitor, :nothing]
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='#{@process.full_name}' @options=#{@options} @pid=#{@pid}>"
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 Custom < Eye::Checker
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)
@@ -0,0 +1,6 @@
1
+ class Eye::Checker::Nop < Eye::Checker
2
+
3
+ # check :nop, :every => 10.hours # means restart every 10 hours
4
+
5
+ def get_value; end
6
+ end
@@ -35,6 +35,8 @@ class Eye::Controller
35
35
 
36
36
  attr_reader :applications, :current_config
37
37
 
38
+ exclusive :load # load is hard command, so better to run it safely blocked
39
+
38
40
  def initialize
39
41
  @applications = []
40
42
  @current_config = Eye::Config.new
@@ -8,16 +8,14 @@ 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
- exclusive{ load(*args) }
18
+ load(*args)
21
19
  when :info
22
20
  info_string(*args)
23
21
  when :xinfo
@@ -10,7 +10,7 @@ module Eye::Controller::Helpers
10
10
  def save_cache
11
11
  File.open(Eye::Settings.cache_path, 'w') { |f| f.write(cache_str) }
12
12
  rescue => ex
13
- warn "save cache crashed with #{ex.message}"
13
+ log_ex(ex)
14
14
  end
15
15
 
16
16
  def cache_str
@@ -81,7 +81,6 @@ private
81
81
 
82
82
  # return: result, config
83
83
  def parse_config(filename)
84
- raise Eye::Dsl::Error, "config file '#{filename}' not found!" unless File.exists?(filename)
85
84
  debug "parse #{filename}"
86
85
 
87
86
  cfg = Eye::Dsl.parse(nil, filename)
@@ -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
- h = args.extract_options!
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
- error ex.message
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(*obj_strs)
63
+ def find_objects(*args)
64
+ h = args.extract_options!
65
+ obj_strs = args
66
+
73
67
  return [] if obj_strs.blank?
74
- return @applications.dup if obj_strs.size == 1 && (obj_strs[0].to_s.strip == 'all' || obj_strs[0].to_s.strip == '*')
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
- res += find_objects_by_mask(mask.to_s.strip)
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 = right_regexp(mask)
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
- fapps, apps = [], []
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
- fapps << obj.name
115
+ apps << obj
113
116
  else
114
- apps << obj.app_name
117
+ objs << obj
115
118
  end
116
119
  end
117
120
 
118
- if (apps).uniq.size > 1 || (fapps.size > 0 && (apps | fapps).size > fapps.size)
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 right_regexp(mask)
171
+ def exact_regexp(mask)
167
172
  str = Regexp.escape(mask).gsub('\*', '.*?')
168
- %r|#{str}\z|
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
  Socket: #{Eye::Settings::socket_path}
23
22
  Pid: #{Eye::Settings::pid_path}
@@ -5,4 +5,8 @@ class Eye::Dsl::ChildProcessOpts < Eye::Dsl::Opts
5
5
  :stop_signals, :stop_grace, :stop_timeout, :restart_timeout]
6
6
  end
7
7
 
8
+ def triggers(*args)
9
+ raise Eye::Dsl::Error, "triggers does not allowed in monitor_children"
10
+ end
11
+
8
12
  end
@@ -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
- Eye::Notify::TYPES.keys.each do |not_system|
14
- create_options_methods([not_system], Hash)
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)
@@ -11,6 +11,7 @@ module Eye::Dsl::Main
11
11
  Eye::Dsl.debug "<= app: #{name}"
12
12
  end
13
13
 
14
+ alias project application
14
15
  alias app application
15
16
 
16
17
  def load(glob = '')
@@ -87,6 +87,7 @@ class Eye::Dsl::Opts < Eye::Dsl::PureOpts
87
87
  @config[:environment].merge!(value)
88
88
  end
89
89
 
90
+ alias dir working_dir
90
91
  alias env environment
91
92
 
92
93
  def set_stdall(value)
@@ -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) && !self.variants[param].include?(value)
45
- raise Error, "#{value.inspect} should within #{self.variants[param].inspect}"
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
 
@@ -33,6 +33,11 @@ class Eye::Logger
33
33
  logger.send(method_name, msg)
34
34
  end
35
35
  end
36
+
37
+ def log_ex(ex)
38
+ error "#{ex.message} #{ex.backtrace}"
39
+ # notify here?
40
+ end
36
41
  end
37
42
 
38
43
  Logger::Severity.constants.each do |level|
@@ -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|
@@ -87,7 +87,10 @@ module Eye::Process::Commands
87
87
  private
88
88
 
89
89
  def kill_process
90
- return unless self.pid
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
 
@@ -86,7 +86,7 @@ module Eye::Process::System
86
86
  save_pid_to_file
87
87
  true
88
88
  rescue => ex
89
- error "failsafe_save_pid: #{ex.message}"
89
+ log_ex(ex)
90
90
  false
91
91
  end
92
92
 
@@ -29,7 +29,7 @@ private
29
29
 
30
30
  def add_trigger(cfg = {})
31
31
  trigger = Eye::Trigger.create(current_actor, cfg)
32
- self.triggers << trigger
32
+ self.triggers << trigger if trigger
33
33
  end
34
34
 
35
35
  end
@@ -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
- action = subject.fire || :restart
64
- notify :warn, "Bounded #{subject.check_name}: #{subject.last_human_values} send to :#{action}"
65
- schedule action, Eye::Reason.new("bounded #{subject.check_name}")
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
 
@@ -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.waitpid(pid)
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
@@ -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='#{@process.full_name}' @options=#{@options}>"
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
- check(transition)
60
- rescue => ex
61
- warn "failed #{ex.message} #{ex.backtrace}"
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
@@ -14,9 +14,7 @@ class Eye::Trigger::Flapping < Eye::Trigger
14
14
  end
15
15
 
16
16
  def check(transition)
17
- unless good?
18
- on_flapping
19
- end
17
+ on_flapping if transition.event == :crashed && !good?
20
18
  end
21
19
 
22
20
  private
@@ -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
- if compare(trans.to_name, to) || compare(trans.from_name, from) || compare(trans.event, event)
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
@@ -0,0 +1,10 @@
1
+ class Eye::Trigger::StopChilds < Eye::Trigger
2
+
3
+ param :timeout, [Fixnum, Float], nil, 60
4
+
5
+ def check(trans)
6
+ debug "stop childs"
7
+ process.childs.pmap { |pid, c| c.stop }
8
+ end
9
+
10
+ end
@@ -3,7 +3,7 @@ class Eye::Utils::AliveArray
3
3
  include Enumerable
4
4
 
5
5
  def_delegators :@arr, :[], :<<, :clear, :delete, :size, :empty?, :push,
6
- :flatten, :present?, :uniq!
6
+ :flatten, :present?, :uniq!, :select!
7
7
 
8
8
  def initialize(arr = [])
9
9
  @arr = arr
@@ -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(#{@target.class}: #{@calls.inspect})"
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: eye
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.4'
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 00:00:00.000000000 Z
11
+ date: 2013-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: celluloid
@@ -248,8 +248,8 @@ dependencies:
248
248
  - - '>='
249
249
  - !ruby/object:Gem::Version
250
250
  version: '0'
251
- description: Process monitoring tool. An alternative to God and Bluepill. With Bluepill
252
- like config syntax. Requires MRI Ruby >= 1.9.2. Uses Celluloid and Celluloid::IO.
251
+ description: Process monitoring tool. Inspired from Bluepill and God. Requires Ruby(MRI)
252
+ >= 1.9.3-p194. Uses Celluloid and Celluloid::IO.
253
253
  email: kostya27@gmail.com
254
254
  executables:
255
255
  - eye
@@ -288,6 +288,7 @@ files:
288
288
  - lib/eye/checker/file_size.rb
289
289
  - lib/eye/checker/http.rb
290
290
  - lib/eye/checker/memory.rb
291
+ - lib/eye/checker/nop.rb
291
292
  - lib/eye/checker/socket.rb
292
293
  - lib/eye/child_process.rb
293
294
  - lib/eye/client.rb
@@ -343,6 +344,7 @@ files:
343
344
  - lib/eye/trigger.rb
344
345
  - lib/eye/trigger/flapping.rb
345
346
  - lib/eye/trigger/state.rb
347
+ - lib/eye/trigger/stop_childs.rb
346
348
  - lib/eye/utils.rb
347
349
  - lib/eye/utils/alive_array.rb
348
350
  - lib/eye/utils/celluloid_chain.rb
@@ -372,6 +374,6 @@ rubyforge_project:
372
374
  rubygems_version: 2.0.2
373
375
  signing_key:
374
376
  specification_version: 4
375
- summary: Process monitoring tool. An alternative to God and Bluepill. With Bluepill
376
- like config syntax. Requires MRI Ruby >= 1.9.2. Uses Celluloid and Celluloid::IO.
377
+ summary: Process monitoring tool. Inspired from Bluepill and God. Requires Ruby(MRI)
378
+ >= 1.9.3-p194. Uses Celluloid and Celluloid::IO.
377
379
  test_files: []