reel-eye 0.3.1 → 0.3.2

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.
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