eye 0.4 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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: []