eye 0.5.2 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +13 -5
  2. data/.travis.yml +1 -6
  3. data/CHANGES.md +12 -0
  4. data/README.md +5 -0
  5. data/Rakefile +4 -4
  6. data/bin/loader_eye +14 -3
  7. data/bin/runner +16 -0
  8. data/examples/dependency.eye +17 -0
  9. data/examples/plugin/README.md +15 -0
  10. data/examples/plugin/main.eye +15 -0
  11. data/examples/plugin/plugin.rb +63 -0
  12. data/examples/unicorn.eye +1 -1
  13. data/eye.gemspec +1 -2
  14. data/lib/eye.rb +1 -1
  15. data/lib/eye/checker.rb +16 -4
  16. data/lib/eye/checker/children_count.rb +44 -0
  17. data/lib/eye/checker/children_memory.rb +12 -0
  18. data/lib/eye/checker/socket.rb +9 -2
  19. data/lib/eye/child_process.rb +6 -2
  20. data/lib/eye/cli.rb +13 -2
  21. data/lib/eye/cli/commands.rb +2 -2
  22. data/lib/eye/cli/server.rb +11 -3
  23. data/lib/eye/config.rb +2 -2
  24. data/lib/eye/controller/commands.rb +29 -2
  25. data/lib/eye/controller/helpers.rb +31 -6
  26. data/lib/eye/controller/load.rb +5 -6
  27. data/lib/eye/controller/options.rb +1 -1
  28. data/lib/eye/controller/send_command.rb +0 -1
  29. data/lib/eye/dsl.rb +2 -1
  30. data/lib/eye/dsl/application_opts.rb +4 -7
  31. data/lib/eye/dsl/group_opts.rb +2 -1
  32. data/lib/eye/dsl/helpers.rb +9 -1
  33. data/lib/eye/dsl/main.rb +11 -5
  34. data/lib/eye/dsl/opts.rb +5 -22
  35. data/lib/eye/dsl/process_opts.rb +20 -2
  36. data/lib/eye/dsl/pure_opts.rb +1 -1
  37. data/lib/eye/dsl/validation.rb +17 -2
  38. data/lib/eye/local.rb +79 -50
  39. data/lib/eye/notify.rb +5 -3
  40. data/lib/eye/notify/mail.rb +6 -2
  41. data/lib/eye/process.rb +3 -1
  42. data/lib/eye/process/children.rb +1 -1
  43. data/lib/eye/process/commands.rb +17 -6
  44. data/lib/eye/process/config.rb +6 -1
  45. data/lib/eye/process/data.rb +20 -0
  46. data/lib/eye/process/monitor.rb +10 -4
  47. data/lib/eye/process/states.rb +5 -2
  48. data/lib/eye/process/states_history.rb +1 -1
  49. data/lib/eye/process/system.rb +6 -2
  50. data/lib/eye/process/trigger.rb +0 -1
  51. data/lib/eye/process/validate.rb +8 -6
  52. data/lib/eye/process/watchers.rb +1 -7
  53. data/lib/eye/system.rb +14 -11
  54. data/lib/eye/system_resources.rb +8 -0
  55. data/lib/eye/trigger.rb +12 -4
  56. data/lib/eye/trigger/check_dependency.rb +30 -0
  57. data/lib/eye/trigger/stop_children.rb +4 -1
  58. data/lib/eye/trigger/wait_dependency.rb +49 -0
  59. data/lib/eye/utils.rb +13 -0
  60. metadata +41 -45
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: cafc3688088df4ed7d7e3dc818098b62409f048d
4
- data.tar.gz: 956fe8cad209a7eda5eb2fe8ddc9114d37ea32e3
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YTRhNjdkYTZkYmFlMmJmNGJmMGUzNWM4N2ViZjAyOWZiODhiOThkNA==
5
+ data.tar.gz: !binary |-
6
+ MzFiZDcwZjM4NTg4Y2QwNDI4NjgxZmU4MGQxZjEzMzQxYmJiZDY5Mw==
5
7
  SHA512:
6
- metadata.gz: 6714b130ff87b60ad3f7ad768d4854e73b78aea6bf5f3c7374db183374cd608122cff1bc604e5f59e6e2482cc44aca303c12b40dc05b82f95ede1c88e89e0d3c
7
- data.tar.gz: d64d70d355dc8580d77d575d102eded7fe7dc25d78aff325db363a42a161fc73f55f210071ae30901849bf7ba724e78fa5d12eba56a2eb59532b94bd3c2c737f
8
+ metadata.gz: !binary |-
9
+ Zjk2MzM3ODlmMTA4ZTE4OTUzNTJkMTY3YzEzNTY5ZGM1ZmI4YWYzYjFiMjJk
10
+ N2RmMzJmNWE1ZDcyNzkyYmE1ZDhlNjM1ZTcxNzFmMmRlZmQ5YTVlNDE3NzNm
11
+ MGNlNmQwZDc1MzNjN2JlYzFjNWI5MDgyYWNmZDYyMjc0ZDRiNTE=
12
+ data.tar.gz: !binary |-
13
+ MzRkYjE1M2FhN2Q4YmQyMDk0Y2Q3ZjYxN2Y2YmQwYTMwODM5YmJjMzI3Nzlh
14
+ NTFmMjA2MTc1OTk2ODA4Y2FkZjRjYTMwZGNlNmEyYWMzYTJlMTJiYmI4Y2Iw
15
+ YmQyODZjMzVlMzliZWNmNzVjNDAxZGU5YTM4YmVkNmRiMmJiY2M=
data/.travis.yml CHANGED
@@ -1,11 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "1.9.2"
4
3
  - "1.9.3"
5
4
  - "2.0.0"
6
- - "2.1.0"
5
+ - "2.1"
7
6
  script: bundle exec rake N=15
8
- #before_script:
9
- # - bundle exec rake remove_coverage
10
- #after_script:
11
- # - bundle exec rake coveralls:push
data/CHANGES.md CHANGED
@@ -1,3 +1,15 @@
1
+ 0.6
2
+ ------
3
+ * add processes dependencies (#43)
4
+ * add eye-http gem (https://github.com/kostya/eye-http)
5
+ * add eye plugin example (https://github.com/kostya/eye/tree/master/examples/plugin)
6
+ * add quit option --stop_all (#39)
7
+ * add local eye runner (like foreman, used Eyefile)
8
+ * add use_leaf_child monitoring strategy (to daemonize sh -c '...') (788488a)
9
+ * add children_count, children_memory checks
10
+ * add dsl default application options (__default__)
11
+ * trusting external pid_file changes (#52)
12
+
1
13
  0.5.2
2
14
  -----
3
15
  * rename dsl :childs_update_period to :children_update_period
data/README.md CHANGED
@@ -185,6 +185,7 @@ Log tracing (tail and grep):
185
185
  Quit monitoring:
186
186
 
187
187
  $ eye q(uit)
188
+ $ eye q -s # stop all processes and quit
188
189
 
189
190
  Interactive info:
190
191
 
@@ -204,4 +205,8 @@ Process states and events:
204
205
  [![Eye](https://raw.github.com/kostya/stuff/master/eye/mprocess.png)](https://raw.github.com/kostya/stuff/master/eye/process.png)
205
206
 
206
207
 
208
+ EyeHttp Gem:
209
+
210
+ https://github.com/kostya/eye-http
211
+
207
212
  Thanks `Bluepill` for the nice config ideas.
data/Rakefile CHANGED
@@ -7,10 +7,10 @@ require 'coveralls/rake/task'
7
7
 
8
8
  Coveralls::RakeTask.new
9
9
 
10
- task :default => :pspecs
10
+ task :default => :pspec
11
11
 
12
- task :pspecs do
13
- Rake::Task['parallel:spec'].invoke(ENV['N'] || 8)
12
+ task :pspec do
13
+ Rake::Task['parallel:spec'].invoke(ENV['N'] || 10)
14
14
  end
15
15
 
16
16
  RSpec::Core::RakeTask.new(:spec) do |t|
@@ -26,7 +26,7 @@ task :env do
26
26
  require 'bundler/setup'
27
27
  require 'eye'
28
28
  Eye::Controller
29
- Eye::Process # preload
29
+ Eye::Process
30
30
  end
31
31
 
32
32
  desc "graph"
data/bin/loader_eye CHANGED
@@ -24,6 +24,15 @@ OptionParser.new do |opts|
24
24
  options[:logger] = logger
25
25
  end
26
26
 
27
+ opts.on( '-dr', '--dir DIR', 'Dir for local runner' ) do |dir|
28
+ Eye::Local.dir = dir
29
+ Eye::Local.local_runner = true
30
+ end
31
+
32
+ opts.on( '-st', '--stop_all', 'Stop all on exit' ) do |stop_all|
33
+ options[:stop_all] = true
34
+ end
35
+
27
36
  opts.on( '-d', '--debug', 'debug info to logger' ) do
28
37
  options[:debug] = true
29
38
  end
@@ -52,8 +61,10 @@ Eye::Control.set_proc_line
52
61
 
53
62
  server.async.run
54
63
 
55
- trap("INT"){ exit }
56
- trap("USR1"){ Eye::Logger.reopen }
57
- trap("USR2"){ GC.start }
64
+ trap("USR1") { Eye::Logger.reopen }
65
+ trap("USR2") { GC.start }
66
+ trap("INT") { exit }
67
+
68
+ at_exit { Eye::Control.command(:stop_all) } if options[:stop_all]
58
69
 
59
70
  sleep
data/bin/runner ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib]))
3
+ require 'eye'
4
+
5
+ # Local version of eye
6
+ # which looking for Eyefile
7
+ # like foreman
8
+
9
+ unless Eye::Local.eyefile
10
+ puts "Not found Eyefile"
11
+ exit 1
12
+ end
13
+
14
+ Eye::Local.dir = File.dirname(Eye::Local.eyefile)
15
+ Eye::Local.local_runner = true
16
+ Eye::Cli.start
@@ -0,0 +1,17 @@
1
+ # process dependencies :b -> :a
2
+
3
+ Eye.app :dependency do
4
+ process(:a) do
5
+ start_command "sleep 100"
6
+ daemonize true
7
+ pid_file "/tmp/test_process_a.pid"
8
+ end
9
+
10
+ process(:b) do
11
+ start_command "sleep 100"
12
+ daemonize true
13
+ pid_file "/tmp/test_process_b.pid"
14
+ depend_on :a
15
+ end
16
+
17
+ end
@@ -0,0 +1,15 @@
1
+ Eye Plugin Example
2
+ ------------------
3
+
4
+ This plugin adds reactor which try to reads command from file "/tmp/cmd.txt" every 1.second (then execute it and delete file). Also plugin add trigger to save every process state transition into "/tmp/saver.log".
5
+
6
+ To test it:
7
+
8
+ bundle exec eye l examples/plugin/main.eye
9
+ tail -f /tmp/eye.log
10
+ tail -f /tmp/saver.log
11
+ echo 'restart' > /tmp/cmd.txt
12
+
13
+ Also, here http example of gem:
14
+
15
+ https://github.com/kostya/eye-http
@@ -0,0 +1,15 @@
1
+ Eye.load('./plugin.rb')
2
+
3
+ Eye.config do
4
+ logger "/tmp/eye.log"
5
+ enable_reactor(1.second, "/tmp/cmd.txt")
6
+ enable_saver("/tmp/saver.log")
7
+ end
8
+
9
+ Eye.app :app do
10
+ process :process do
11
+ pid_file "/tmp/p.pid"
12
+ start_command "sleep 10"
13
+ daemonize true
14
+ end
15
+ end
@@ -0,0 +1,63 @@
1
+ class Reactor
2
+ include Celluloid
3
+
4
+ def initialize(interval, filename)
5
+ @interval = interval
6
+ @filename = filename
7
+ every(@interval) do
8
+ info "check file #{@filename}"
9
+ if cmd = read_file
10
+ execute_command cmd
11
+ end
12
+ end
13
+ end
14
+
15
+ def read_file
16
+ if File.exists?(@filename)
17
+ cmd = File.read(@filename).chop
18
+ File.delete(@filename) rescue nil
19
+ cmd
20
+ end
21
+ end
22
+
23
+ def execute_command(cmd)
24
+ Eye::Control.command(cmd, 'all') if %w{restart start stop}.include?(cmd)
25
+ end
26
+ end
27
+
28
+ class Saver < Eye::Trigger::Custom
29
+ param :log_name, String, true
30
+
31
+ def check(trans)
32
+ tlogger.info "#{process.full_name} transition from #{trans.from_name} to #{trans.to_name}"
33
+ end
34
+
35
+ def tlogger
36
+ @tlogger ||= Logger.new(log_name)
37
+ end
38
+ end
39
+
40
+ def reactor
41
+ Celluloid::Actor[:reactor]
42
+ end
43
+
44
+ # Extend config options, add enable_reactor
45
+ class Eye::Dsl::ConfigOpts
46
+ def enable_reactor(*args)
47
+ @config[:reactor] = args
48
+ end
49
+
50
+ def enable_saver(save_log)
51
+ Eye.application '__default__' do
52
+ trigger :saver, :log_name => save_log
53
+ end
54
+ end
55
+ end
56
+
57
+ # extend controller to execute method, and config loads
58
+ class Eye::Controller
59
+ def set_opt_reactor(args)
60
+ reactor.terminate if reactor
61
+ Celluloid::Actor[:reactor] = Reactor.supervise(*args)
62
+ end
63
+ end
data/examples/unicorn.eye CHANGED
@@ -26,7 +26,7 @@ Eye.application "rails_unicorn" do
26
26
  check :cpu, :every => 30, :below => 80, :times => 3
27
27
  check :memory, :every => 30, :below => 150.megabytes, :times => [3,5]
28
28
 
29
- start_timeout 30.seconds
29
+ start_timeout 100.seconds
30
30
  restart_grace 30.seconds
31
31
 
32
32
  monitor_children do
data/eye.gemspec CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.version = Eye::VERSION
17
17
  gem.license = "MIT"
18
18
 
19
- gem.required_ruby_version = '>= 1.9.2' # because of celluloid
19
+ gem.required_ruby_version = '>= 1.9.2'
20
20
  gem.required_rubygems_version = '>= 1.3.6'
21
21
 
22
22
  gem.add_dependency 'celluloid', '~> 0.15.0'
@@ -37,5 +37,4 @@ Gem::Specification.new do |gem|
37
37
  gem.add_development_dependency 'xmpp4r'
38
38
  gem.add_development_dependency 'coveralls'
39
39
  gem.add_development_dependency 'simplecov', '>= 0.8.1'
40
- gem.add_development_dependency 'parallel_tests'
41
40
  end
data/lib/eye.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Eye
2
- VERSION = "0.5.2"
2
+ VERSION = "0.6"
3
3
  ABOUT = "Eye v#{VERSION} (c) 2012-2014 @kostya"
4
4
  PROCLINE = "eye monitoring v#{VERSION}"
5
5
 
data/lib/eye/checker.rb CHANGED
@@ -11,16 +11,19 @@ class Eye::Checker
11
11
  autoload :Nop, 'eye/checker/nop'
12
12
  autoload :Runtime, 'eye/checker/runtime'
13
13
  autoload :Cputime, 'eye/checker/cputime'
14
+ autoload :ChildrenCount, 'eye/checker/children_count'
15
+ autoload :ChildrenMemory,'eye/checker/children_memory'
14
16
 
15
17
  TYPES = {:memory => 'Memory', :cpu => 'Cpu', :http => 'Http',
16
18
  :ctime => 'FileCTime', :fsize => 'FileSize', :file_touched => 'FileTouched',
17
- :socket => 'Socket', :nop => 'Nop', :runtime => 'Runtime', :cputime => 'Cputime' }
19
+ :socket => 'Socket', :nop => 'Nop', :runtime => 'Runtime', :cputime => 'Cputime',
20
+ :children_count => "ChildrenCount", :children_memory => "ChildrenMemory" }
18
21
 
19
22
  attr_accessor :value, :values, :options, :pid, :type, :check_count, :process
20
23
 
21
24
  param :every, [Fixnum, Float], false, 5
22
25
  param :times, [Fixnum, Array], nil, 1
23
- param :fires, [Symbol, Array], nil, nil, [:stop, :restart, :unmonitor, :nothing, :start, :delete]
26
+ param :fires, [Symbol, Array], nil, nil, [:stop, :restart, :unmonitor, :start, :delete, :nothing, :notify]
24
27
  param :initial_grace, [Fixnum, Float]
25
28
  param :skip_initial_fails, [TrueClass, FalseClass]
26
29
 
@@ -37,7 +40,7 @@ class Eye::Checker
37
40
  def self.get_class(type)
38
41
  klass = eval("Eye::Checker::#{TYPES[type]}") rescue nil
39
42
  raise "Unknown checker #{type}" unless klass
40
- if deps = klass.depends_on
43
+ if deps = klass.requires
41
44
  Array(deps).each { |d| require d }
42
45
  end
43
46
  klass
@@ -180,6 +183,15 @@ class Eye::Checker
180
183
  process.instance_exec(&p) if process.alive?
181
184
  end
182
185
 
186
+ def fire
187
+ actions = fires ? Array(fires) : [:restart]
188
+ process.notify :warn, "Bounded #{check_name}: #{last_human_values} send to #{actions}"
189
+
190
+ actions.each do |action|
191
+ process.schedule action, Eye::Reason.new("bounded #{check_name}")
192
+ end
193
+ end
194
+
183
195
  def defer(&block)
184
196
  Celluloid::Future.new(&block).value
185
197
  end
@@ -197,7 +209,7 @@ class Eye::Checker
197
209
  Eye::Checker.const_set(name, base)
198
210
  end
199
211
 
200
- def self.depends_on
212
+ def self.requires
201
213
  end
202
214
 
203
215
  class CustomCell < Eye::Checker
@@ -0,0 +1,44 @@
1
+ class Eye::Checker::ChildrenCount < Eye::Checker::Measure
2
+
3
+ # check :children_count, :every => 30.seconds, :below => 10, :strategy => :kill_old
4
+ # monitor_children should be enabled
5
+
6
+ param :strategy, Symbol, nil, :restart, [:restart, :kill_old, :kill_new]
7
+
8
+ def get_value
9
+ process.children.size
10
+ end
11
+
12
+ def fire
13
+ if strategy == :restart
14
+ super
15
+ else
16
+ pids = ordered_by_date_children_pids
17
+
18
+ pids = if strategy == :kill_old
19
+ pids[0...-below]
20
+ else
21
+ pids[below..-1]
22
+ end
23
+
24
+ kill_pids(pids)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def kill_pids(pids)
31
+ info "killing pids: #{pids.inspect} for strategy: #{strategy}"
32
+ pids.each do |pid|
33
+ if child = process.children[pid]
34
+ child.schedule :stop, Eye::Reason.new("bounded #{check_name}")
35
+ end
36
+ end
37
+ end
38
+
39
+ def ordered_by_date_children_pids
40
+ children = process.children.values
41
+ children.sort_by { |ch| Eye::SystemResources.start_time(ch.pid).to_i }.map &:pid
42
+ end
43
+
44
+ end
@@ -0,0 +1,12 @@
1
+ class Eye::Checker::ChildrenMemory < Eye::Checker::Measure
2
+
3
+ # check :children_memory, :every => 30.seconds, :below => 400.megabytes
4
+ # monitor_children should be enabled
5
+
6
+ def get_value
7
+ process.children.values.inject(0) do |sum, ch|
8
+ sum + Eye::SystemResources.memory(ch.pid).to_i
9
+ end
10
+ end
11
+
12
+ end
@@ -18,7 +18,7 @@ class Eye::Checker::Socket < Eye::Checker::Defer
18
18
  param :read_timeout, [Fixnum, Float]
19
19
  param :send_data
20
20
  param :expect_data, [String, Regexp, Proc]
21
- param :protocol, [Symbol], nil, nil, [:default, :em_object]
21
+ param :protocol, [Symbol], nil, nil, [:default, :em_object, :raw]
22
22
 
23
23
  def initialize(*args)
24
24
  super
@@ -51,7 +51,11 @@ class Eye::Checker::Socket < Eye::Checker::Defer
51
51
  { :result => result }
52
52
  end
53
53
  rescue Timeout::Error
54
- return { :exception => "ReadTimeout<#{@read_timeout}>" }
54
+ if protocol == :raw
55
+ return { :result => @buffer }
56
+ else
57
+ return { :exception => "ReadTimeout<#{@read_timeout}>" }
58
+ end
55
59
  end
56
60
  else
57
61
  { :result => :listen }
@@ -144,6 +148,9 @@ private
144
148
  if content.present?
145
149
  Marshal.load(content) rescue 'corrupted_marshal'
146
150
  end
151
+ when :raw
152
+ @buffer = ''
153
+ loop { @buffer << socket.recv(1) }
147
154
  else
148
155
  socket.readline.chop
149
156
  end
@@ -26,10 +26,11 @@ class Eye::ChildProcess
26
26
 
27
27
  attr_reader :pid, :name, :full_name, :config, :watchers
28
28
 
29
- def initialize(pid, config = {}, logger_prefix = nil)
29
+ def initialize(pid, config = {}, logger_prefix = nil, parent_pid = nil)
30
30
  raise 'Empty pid' unless pid
31
31
 
32
32
  @pid = pid
33
+ @parent_pid = parent_pid
33
34
  @config = prepare_config(config)
34
35
  @name = "child-#{pid}"
35
36
  @full_name = [logger_prefix, @name] * ':'
@@ -94,4 +95,7 @@ class Eye::ChildProcess
94
95
  self_status_data(debug)
95
96
  end
96
97
 
97
- end
98
+ def prepare_command(command) # override
99
+ super.gsub('{PARENT_PID}', @parent_pid.to_s)
100
+ end
101
+ end