eye 0.5.2 → 0.6

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