reel-eye 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -29,4 +29,5 @@ experiments
29
29
  *sublime*
30
30
  examples/work*.eye
31
31
  script
32
- [0-9].rb
32
+ [0-9].rb
33
+ *.cache
data/.travis.yml CHANGED
@@ -1,3 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - "1.9.3"
4
+ script: COVA=1 bundle exec rake
data/CHANGES.md ADDED
@@ -0,0 +1,19 @@
1
+ 0.3.3.dev
2
+ ---------
3
+
4
+ 0.3.2
5
+ ---------
6
+ * improve matching targers
7
+ * possibility to add many checkers with the same type per process (ex: checks :http_2, ...)
8
+ * add uid,gid options (only for ruby 2.0)
9
+
10
+ 0.3.1
11
+ -----
12
+ * load multiple configs (folder,...) now not breaks on first error (each config loads separately)
13
+ * load ~/.eyeconfig with first eye load
14
+ * some concurrency fixes
15
+ * custom checker
16
+
17
+ 0.3
18
+ ---
19
+ * stable version
data/Gemfile CHANGED
@@ -2,3 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in eye.gemspec
4
4
  gemspec
5
+
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
- Eye [![Build Status](https://secure.travis-ci.org/kostya/eye.png?branch=master)](http://travis-ci.org/kostya/eye)
1
+ Eye
2
2
  ===
3
+ [![Gem Version](https://badge.fury.io/rb/eye.png)](http://rubygems.org/gems/eye)
4
+ [![Build Status](https://secure.travis-ci.org/kostya/eye.png?branch=master)](http://travis-ci.org/kostya/eye)
5
+ [![Coverage Status](https://coveralls.io/repos/kostya/eye/badge.png?branch=master)](https://coveralls.io/r/kostya/eye?branch=master)
3
6
 
4
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.
5
8
 
@@ -150,6 +153,10 @@ Check config syntax:
150
153
 
151
154
  $ eye c(heck) examples/test.eye
152
155
 
156
+ Config explain (for debug):
157
+
158
+ $ eye e(xplain) examples/test.eye
159
+
153
160
  Log tracing:
154
161
 
155
162
  $ eye trace
@@ -160,11 +167,6 @@ Quit monitoring:
160
167
 
161
168
  $ eye q(uit)
162
169
 
163
- Config explain (for debug):
164
-
165
- $ eye explain examples/test.eye
166
-
167
-
168
- ### Config api:
170
+ ### Config options:
169
171
 
170
- Waiting for pull requests ..., until that you can read `examples` and `spec/dsl` folders.
172
+ Waiting for pull requests here..., until that you can read `examples` and `spec/dsl` folders.
data/bin/eye CHANGED
@@ -10,9 +10,7 @@ class Cli < Thor
10
10
 
11
11
  desc "info [MASK]", "show process statuses"
12
12
  def info(mask = nil)
13
- res = cmd(:info, mask)
14
- puts res if res && !res.empty?
15
- puts
13
+ print cmd(:info, mask)
16
14
  end
17
15
 
18
16
  desc "status ", "show process statuses"
@@ -25,31 +23,22 @@ class Cli < Thor
25
23
  method_option :config, :type => :boolean, :aliases => "-c"
26
24
  method_option :show_processes, :type => :boolean, :aliases => "-p"
27
25
  def xinfo
28
- res = cmd(:xinfo, options[:config], options[:show_processes])
29
- puts res if res && !res.empty?
30
- puts
26
+ print cmd(:xinfo, options[:config], options[:show_processes])
31
27
  end
32
28
 
33
29
  desc "oinfo", "onelined info"
34
30
  def oinfo
35
- res = cmd(:oinfo)
36
- puts res if res && !res.empty?
37
- puts
31
+ print cmd(:oinfo)
38
32
  end
39
33
 
40
34
  desc "load [CONF, ...]", "load config (and start server if needed) (-f for foregraund start)"
41
35
  method_option :foregraund, :type => :boolean, :aliases => "-f"
42
- method_option :logger, :type => :string, :aliases => "-l"
43
36
  def load(*configs)
44
37
  configs.map!{ |c| File.expand_path(c) } if !configs.empty?
45
38
 
46
39
  if options[:foregraund]
47
40
  # in foregraund we stop another server, and run just 1 current config version
48
- if configs.size != 1
49
- say "foregraund expected only one config", :red
50
- exit 1
51
- end
52
-
41
+ error!("foregraund expected only one config") if configs.size != 1
53
42
  server_start_foregraund(configs.first)
54
43
 
55
44
  elsif server_started?
@@ -90,9 +79,7 @@ class Cli < Thor
90
79
 
91
80
  desc "history TARGET[,...]", "show process states history"
92
81
  def history(*targets)
93
- res = cmd(:history, *targets)
94
- puts res if res && !res.empty?
95
- puts
82
+ print cmd(:history, *targets)
96
83
  end
97
84
 
98
85
  desc "trace [TARGET]", "tracing log for app,group or process"
@@ -146,6 +133,16 @@ class Cli < Thor
146
133
 
147
134
  private
148
135
 
136
+ def error!(msg)
137
+ say msg, :red
138
+ exit 1
139
+ end
140
+
141
+ def print(msg, new_line = true)
142
+ say msg if msg && !msg.empty?
143
+ say if new_line
144
+ end
145
+
149
146
  def client
150
147
  @client ||= Eye::Client.new(Eye::Settings.socket_path)
151
148
  end
@@ -160,11 +157,9 @@ private
160
157
  res = _cmd(cmd, *args)
161
158
 
162
159
  if res == :not_started
163
- say "eye monitoring not found, did you start it?", :red
164
- exit 1
160
+ error! "eye monitoring not found, did you start it?"
165
161
  elsif res == :timeouted
166
- say "eye does not answer, timeouted...", :red
167
- exit 1
162
+ error! "eye does not answer, timeouted..."
168
163
  end
169
164
 
170
165
  res
@@ -175,18 +170,12 @@ private
175
170
  end
176
171
 
177
172
  def say_load_result(res = {}, opts = {})
178
- say(res) unless res.is_a?(Hash)
179
-
180
- if res.has_key?(:error) # TODO: remove that case, outdated
181
- show_load_message(res, opts)
182
- exit 1 if res[:error]
183
- else
184
- say_filename = (res.size > 1)
185
- say "eye started!", :green if opts[:started]
186
- res.each do |filename, _res|
187
- say "#{filename}: ", nil, true if say_filename
188
- show_load_message(_res, opts)
189
- end
173
+ error!(res) unless res.is_a?(Hash)
174
+ say_filename = (res.size > 1)
175
+ say "eye started!", :green if opts[:started]
176
+ res.each do |filename, _res|
177
+ say "#{filename}: ", nil, true if say_filename
178
+ show_load_message(_res, opts)
190
179
  end
191
180
  end
192
181
 
@@ -212,11 +201,11 @@ private
212
201
  def send_command(_cmd, *args)
213
202
  res = cmd(_cmd, *args)
214
203
  if res == :unknown_command
215
- say "unknown command :#{_cmd}", :red
204
+ error! "unknown command :#{_cmd}"
216
205
  elsif res == :corrupred_marshal
217
- say "something crazy wrong, check eye logs!", :red
206
+ error! "something crazy wrong, check eye logs!"
218
207
  elsif res == []
219
- say "command :#{_cmd}, targets not found!", :red
208
+ error! "command :#{_cmd}, targets not found!"
220
209
  else
221
210
  say "command :#{_cmd} sended to [#{res * ", "}]"
222
211
  end
@@ -227,7 +216,7 @@ private
227
216
  if log_file && File.exists?(log_file)
228
217
  Process.exec "tail -n 100 -f #{log_file} | grep '#{tag}'"
229
218
  else
230
- say "log file not found #{log_file.inspect}", :red
219
+ error! "log file not found #{log_file.inspect}"
231
220
  end
232
221
  end
233
222
 
@@ -250,8 +239,7 @@ private
250
239
 
251
240
  def ensure_loader_path
252
241
  unless loader_path
253
- say "start monitoring needs to run under ruby with installed gem 'eye'", :red
254
- exit 1
242
+ error! "start monitoring needs to run under ruby with installed gem 'eye'"
255
243
  end
256
244
  end
257
245
 
@@ -275,18 +263,15 @@ private
275
263
  Eye::Settings.ensure_eye_dir
276
264
 
277
265
  ensure_stop_previous_server
278
-
279
- args = []
280
- args += ['-l', options[:logger]] if options[:logger]
281
266
 
267
+ args = []
282
268
  pid = Process.spawn(ruby_path, loader_path, *args, :out => '/dev/null', :err => '/dev/null', :in => '/dev/null',
283
269
  :chdir => '/', :pgroup => true)
284
270
  Process.detach(pid)
285
271
  File.open(Eye::Settings.pid_path, 'w'){|f| f.write(pid) }
286
272
 
287
273
  unless wait_server
288
- say "server not runned in 15 seconds, something crazy wrong", :red
289
- exit 1
274
+ error! "server not runned in 15 seconds, something crazy wrong"
290
275
  end
291
276
 
292
277
  configs.unshift(Eye::Settings.eyeconfig) if File.exists?(Eye::Settings.eyeconfig)
data/examples/notify.eye CHANGED
@@ -11,8 +11,9 @@ Eye.application :some do
11
11
 
12
12
  process :some_process do
13
13
  notify :dev, :info
14
+ pid_file "1.pid"
14
15
 
15
- ...
16
+ #...
16
17
  end
17
18
 
18
- end
19
+ end
data/examples/puma.eye CHANGED
@@ -1,6 +1,6 @@
1
1
  RUBY = '/usr/local/ruby/1.9.3-p392/bin/ruby'
2
2
  RAILS_ENV = 'production'
3
- ROOT = '/var/www/super_app'
3
+ ROOT = File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
4
4
  CURRENT = File.expand_path(File.join(ROOT, %w{current}))
5
5
  LOGS = File.expand_path(File.join(ROOT, %w{shared log}))
6
6
  PIDS = File.expand_path(File.join(ROOT, %w{shared pids}))
@@ -12,7 +12,7 @@ end
12
12
 
13
13
  Eye.application :super_app do
14
14
  env 'RAILS_ENV' => RAILS_ENV
15
- working_dir CURRENT
15
+ working_dir ROOT
16
16
  triggers :flapping, :times => 10, :within => 1.minute
17
17
 
18
18
  process :puma do
data/examples/rbenv.eye CHANGED
@@ -1,6 +1,6 @@
1
1
  Eye.application "rbenv_example" do
2
2
  env 'RBENV_ROOT' => '/usr/local/rbenv', 'PATH' => "/usr/local/rbenv/shims:/usr/local/rbenv/bin:#{ENV['PATH']}"
3
- working_dir "/projects/some_project"
3
+ working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
4
4
 
5
5
  process "some_process" do
6
6
  pid_file "some.pid"
@@ -8,4 +8,4 @@ Eye.application "rbenv_example" do
8
8
  daemonize true
9
9
  stdall "some.log"
10
10
  end
11
- end
11
+ end
data/examples/sidekiq.eye CHANGED
@@ -16,8 +16,8 @@ def sidekiq_process(proxy, name)
16
16
  end
17
17
 
18
18
  Eye.application :sidekiq_test do
19
- working_dir '/some_dir'
19
+ working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
20
20
  env "RAILS_ENV" => 'production'
21
21
 
22
22
  sidekiq_process self, :sidekiq
23
- end
23
+ end
@@ -16,6 +16,7 @@ Eye.app 'thin-farm' do
16
16
 
17
17
  triggers :flapping, :times => 10, :within => 1.minute
18
18
  checks :memory, :below => 60.megabytes, :every => 30.seconds, :times => 5
19
+ start_timeout 30.seconds
19
20
 
20
21
  group :web do
21
22
  chain :action => :restart, :grace => 5.seconds
data/examples/unicorn.eye CHANGED
@@ -5,7 +5,7 @@ RAILS_ENV = 'production'
5
5
 
6
6
  Eye.application "rails_unicorn" do
7
7
  env "RAILS_ENV" => RAILS_ENV, "PATH" => "#{File.dirname(RUBY)}:#{ENV['PATH']}"
8
- working_dir "/projects/rails_unicorn"
8
+ working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
9
9
 
10
10
  process("unicorn") do
11
11
  pid_file "tmp/pids/unicorn.pid"
data/eye.gemspec CHANGED
@@ -30,7 +30,6 @@ Gem::Specification.new do |gem|
30
30
 
31
31
  gem.add_development_dependency 'rake'
32
32
  gem.add_development_dependency 'rspec'
33
- gem.add_development_dependency 'simplecov'
34
33
  gem.add_development_dependency 'rr'
35
34
  gem.add_development_dependency 'ruby-graphviz'
36
35
  gem.add_development_dependency 'forking'
@@ -39,4 +38,5 @@ Gem::Specification.new do |gem|
39
38
  gem.add_development_dependency 'sinatra'
40
39
  gem.add_development_dependency 'thin'
41
40
  gem.add_development_dependency 'xmpp4r'
41
+ gem.add_development_dependency 'coveralls'
42
42
  end
data/lib/eye.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Eye
2
- VERSION = "0.3.1"
2
+ VERSION = "0.3.2"
3
3
  ABOUT = "ReelEye v#{VERSION} (c) 2012-2013 @kostya"
4
4
  PROCLINE = "reel-eye monitoring v#{VERSION}"
5
5
 
@@ -50,7 +50,7 @@ class Eye::Application
50
50
  end
51
51
 
52
52
  def send_command(command, *args)
53
- debug "send_command #{command} #{args}"
53
+ info "send_command #{command}"
54
54
 
55
55
  @groups.each do |group|
56
56
  group.send_command(command, *args)
data/lib/eye/checker.rb CHANGED
@@ -13,6 +13,16 @@ class Eye::Checker
13
13
 
14
14
  attr_accessor :value, :values, :options, :pid, :type, :check_count
15
15
 
16
+ def self.name_and_class(type)
17
+ type = type.to_sym
18
+ return {:name => type, :type => type} if TYPES[type]
19
+
20
+ if type =~ /\A(.*?)_?[0-9]+\z/
21
+ ctype = $1.to_sym
22
+ return {:name => type, :type => ctype} if TYPES[ctype]
23
+ end
24
+ end
25
+
16
26
  def self.get_class(type)
17
27
  klass = eval("Eye::Checker::#{TYPES[type]}") rescue nil
18
28
  raise "Unknown checker #{type}" unless klass
@@ -59,15 +59,10 @@ private
59
59
 
60
60
  def quit
61
61
  info 'exiting...'
62
- delete
63
62
  sleep 1
64
63
  Eye::System.send_signal($$) # soft terminate
65
64
  sleep 2
66
65
  Eye::System.send_signal($$, 9)
67
66
  end
68
67
 
69
- def delete
70
- send_command(:delete)
71
- end
72
-
73
68
  end
@@ -6,10 +6,24 @@ module Eye::Controller::Helpers
6
6
  $0 = str
7
7
  end
8
8
 
9
+ def save_cache
10
+ File.open(Eye::Settings.cache_path, 'w') { |f| f.write(cache_str) }
11
+ rescue => ex
12
+ warn "save cache crashed with #{ex.message}"
13
+ end
14
+
15
+ def cache_str
16
+ all_processes.map{ |p| "#{p.full_name}=#{p.state}" } * "\n"
17
+ end
18
+
9
19
  def process_by_name(name)
10
20
  all_processes.detect{|c| c.name == name}
11
21
  end
12
22
 
23
+ def process_by_full_name(name)
24
+ all_processes.detect{|c| c.full_name == name }
25
+ end
26
+
13
27
  def group_by_name(name)
14
28
  all_groups.detect{|c| c.name == name}
15
29
  end
@@ -58,4 +72,4 @@ module Eye::Controller::Helpers
58
72
  res
59
73
  end
60
74
 
61
- end
75
+ end
@@ -22,6 +22,7 @@ module Eye::Controller::Load
22
22
  end
23
23
 
24
24
  set_proc_line
25
+ save_cache
25
26
 
26
27
  res
27
28
  end
@@ -35,6 +36,8 @@ private
35
36
  { :error => false, :config => yield }
36
37
 
37
38
  rescue Eye::Dsl::Error, Exception, NoMethodError => ex
39
+ raise if ex.class.to_s.include?('RR') # skip RR exceptions
40
+
38
41
  error "load: config error <#{filename}>: #{ex.message}"
39
42
 
40
43
  # filter backtrace for user output
@@ -18,7 +18,7 @@ module Eye::Controller::Options
18
18
  def set_opt_http(params = {})
19
19
  if params[:enable]
20
20
  if @http
21
- if params[:host] != @http.host || params[:host].to_i != @http.host
21
+ if params[:host] != @http.host || params[:port].to_i != @http.port
22
22
  stop_http
23
23
  start_http(params[:host], params[:port])
24
24
  end
@@ -4,7 +4,9 @@ module Eye::Controller::SendCommand
4
4
  matched_objects(*obj_strs) do |obj|
5
5
  if command.to_sym == :delete
6
6
  remove_object_from_tree(obj)
7
- set_proc_line # to sync proc line if was delete application
7
+
8
+ set_proc_line
9
+ save_cache
8
10
  end
9
11
 
10
12
  obj.send_command(command)
@@ -61,32 +63,44 @@ private
61
63
  return [] if obj_strs.blank?
62
64
  return @applications.dup if obj_strs.size == 1 && (obj_strs[0].strip == 'all' || obj_strs[0].strip == '*')
63
65
 
64
- res = []
66
+ res = Eye::Utils::AliveArray.new
65
67
  obj_strs.map{|c| c.split(",")}.flatten.each do |mask|
66
- res += find_objects_by_mask(mask)
68
+ res += find_objects_by_mask(mask.to_s.strip)
67
69
  end
70
+ res
71
+ end
72
+
73
+ def find_objects_by_mask(mask)
74
+ res = find_all_objects_by_mask(mask)
68
75
 
69
76
  if res.size > 1
70
- # remove inherited targets
77
+ final = Eye::Utils::AliveArray.new
71
78
 
72
- final = []
79
+ if mask[-1] != '*'
80
+ # try to find exactly matched
81
+ r = right_regexp(mask)
82
+ res.each do |obj|
83
+ final << obj if obj.full_name =~ r
84
+ end
85
+ end
86
+
87
+ return final if final.present?
88
+
89
+ # remove inherited targets
73
90
  res.each do |obj|
74
91
  sub_object = res.any?{|a| a.sub_object?(obj) }
75
92
  final << obj unless sub_object
76
93
  end
77
94
 
78
95
  res = final
79
- end
96
+ end
80
97
 
81
- res.present? ? Eye::Utils::AliveArray.new(res) : []
98
+ res
82
99
  end
83
100
 
84
- def find_objects_by_mask(mask)
85
- mask.strip!
86
-
87
- res = []
88
- str = Regexp.escape(mask).gsub('\*', '.*?')
89
- r = %r{\A#{str}}
101
+ def find_all_objects_by_mask(mask)
102
+ res = Eye::Utils::AliveArray
103
+ r = left_regexp(mask)
90
104
 
91
105
  # find app
92
106
  res = @applications.select{|a| a.name =~ r || a.full_name =~ r }
@@ -102,9 +116,16 @@ private
102
116
  gr.processes.each do |p|
103
117
  res << p if p.name =~ r || p.full_name =~ r
104
118
 
119
+ # child matching
105
120
  if p.childs.present?
106
- res += p.childs.values.select{|ch| ch.alive? && (ch.name =~ r || ch.full_name =~ r) }
121
+ childs = p.childs.values
122
+ res += childs.select do |ch|
123
+ name = ch.name rescue ''
124
+ full_name = ch.full_name rescue ''
125
+ name =~ r || full_name =~ r
126
+ end
107
127
  end
128
+
108
129
  end
109
130
  end
110
131
  end
@@ -112,4 +133,13 @@ private
112
133
  res
113
134
  end
114
135
 
136
+ def left_regexp(mask)
137
+ str = Regexp.escape(mask).gsub('\*', '.*?')
138
+ %r|\A#{str}|
139
+ end
140
+
141
+ def right_regexp(mask)
142
+ str = Regexp.escape(mask).gsub('\*', '.*?')
143
+ %r|#{str}\z|
144
+ end
115
145
  end