eye 0.1.11 → 0.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.
Files changed (59) hide show
  1. data/.rspec +1 -0
  2. data/Gemfile +2 -2
  3. data/README.md +41 -18
  4. data/bin/eye +4 -3
  5. data/examples/processes/em.rb +2 -1
  6. data/examples/processes/thin.ru +12 -0
  7. data/examples/sidekiq.eye +19 -0
  8. data/examples/test.eye +25 -16
  9. data/eye.gemspec +5 -3
  10. data/lib/eye.rb +2 -1
  11. data/lib/eye/checker/validation.rb +1 -1
  12. data/lib/eye/child_process.rb +27 -5
  13. data/lib/eye/controller/load.rb +13 -11
  14. data/lib/eye/controller/send_command.rb +32 -7
  15. data/lib/eye/controller/status.rb +4 -3
  16. data/lib/eye/dsl/config_opts.rb +38 -1
  17. data/lib/eye/dsl/opts.rb +12 -5
  18. data/lib/eye/dsl/pure_opts.rb +2 -2
  19. data/lib/eye/dsl/validate.rb +5 -0
  20. data/lib/eye/group.rb +1 -1
  21. data/lib/eye/group/chain.rb +2 -0
  22. data/lib/eye/notify.rb +86 -0
  23. data/lib/eye/notify/jabber.rb +30 -0
  24. data/lib/eye/notify/mail.rb +44 -0
  25. data/lib/eye/process.rb +5 -1
  26. data/lib/eye/process/child.rb +7 -8
  27. data/lib/eye/process/commands.rb +21 -18
  28. data/lib/eye/process/notify.rb +22 -7
  29. data/lib/eye/process/system.rb +18 -5
  30. data/lib/eye/process/validate.rb +23 -0
  31. data/lib/eye/system.rb +4 -1
  32. data/lib/eye/utils.rb +9 -0
  33. data/spec/checker_spec.rb +0 -1
  34. data/spec/client_server_spec.rb +0 -1
  35. data/spec/controller/controller_spec.rb +1 -1
  36. data/spec/controller/intergration_spec.rb +15 -0
  37. data/spec/controller/load_spec.rb +49 -4
  38. data/spec/dsl/chain_spec.rb +20 -14
  39. data/spec/dsl/checks_spec.rb +17 -0
  40. data/spec/dsl/notify_spec.rb +105 -0
  41. data/spec/dsl/process_spec.rb +50 -0
  42. data/spec/mock_spec.rb +0 -1
  43. data/spec/notify/jabber_spec.rb +25 -0
  44. data/spec/notify/mail_spec.rb +26 -0
  45. data/spec/notify_spec.rb +90 -0
  46. data/spec/process/config_spec.rb +0 -1
  47. data/spec/process/notify_spec.rb +27 -0
  48. data/spec/process/states_history_spec.rb +0 -1
  49. data/spec/process/stop_spec.rb +6 -0
  50. data/spec/process/system_spec.rb +34 -21
  51. data/spec/process/update_config_spec.rb +0 -1
  52. data/spec/spec_helper.rb +9 -2
  53. data/spec/support/spec_support.rb +0 -1
  54. data/spec/system_resources_spec.rb +0 -1
  55. data/spec/system_spec.rb +3 -6
  56. data/spec/utils/alive_array_spec.rb +0 -1
  57. data/spec/utils/celluloid_chain_spec.rb +0 -1
  58. data/spec/utils/tail_spec.rb +0 -1
  59. metadata +71 -7
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --color
2
2
  --profile
3
+
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in eye.gemspec
4
- gemspec
4
+ gemspec
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  Eye
2
2
  ===
3
3
 
4
- Process monitoring tool. With Bluepill like config syntax. Requires MRI Ruby >= 1.9.2. Uses Celluloid and Celluloid::IO.
4
+ Process monitoring tool. Alternative for God and Bluepill. With Bluepill like config syntax. Requires MRI Ruby >= 1.9.2. Uses Celluloid and Celluloid::IO.
5
5
 
6
6
 
7
7
  Recommended installation on the server (system wide):
@@ -10,27 +10,30 @@ Recommended installation on the server (system wide):
10
10
  $ sudo ln -sf /usr/local/ruby/1.9.3/bin/eye /usr/local/bin/eye
11
11
 
12
12
 
13
- Config example, shows most of the options (examples/test.eye):
13
+ Config example, shows some typical processes and most of the options (see in exampes/ folder):
14
14
 
15
+ examples/test.eye
15
16
  ```ruby
16
17
  Eye.load("./eye/*.rb") # load submodules
18
+
17
19
  Eye.config do
18
20
  logger "/tmp/eye.log" # eye logger
21
+ logger_level Logger::DEBUG
19
22
  end
20
23
 
21
- Eye.app "test" do
24
+ Eye.application "test" do
22
25
  working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
23
- stdall "trash.log" # stdout + stderr
24
- env "APP_ENV" => "production"
26
+ stdall "trash.log" # stdout,err logs for processes by default
27
+ env "APP_ENV" => "production" # global env for each processes
25
28
  triggers :flapping, :times => 10, :within => 1.minute
26
29
 
27
30
  group "samples" do
28
- env "A" => "1" # env merging
31
+ env "A" => "1" # merging to app env
29
32
  chain :grace => 5.seconds, :action => :restart # restarting with 5s interval, one by one.
30
33
 
31
34
  # eye daemonized process
32
35
  process("sample1") do
33
- pid_file "1.pid" # expanded with working_dir
36
+ pid_file "1.pid" # will be expanded with working_dir
34
37
  start_command "ruby ./sample.rb"
35
38
  daemonize true
36
39
  stdall "sample1.log"
@@ -42,7 +45,7 @@ Eye.app "test" do
42
45
  process("sample2") do
43
46
  pid_file "2.pid"
44
47
  start_command "ruby ./sample.rb -d --pid 2.pid --log sample2.log"
45
- stop_command "kill -9 {{PID}}"
48
+ stop_command "kill -9 {PID}"
46
49
 
47
50
  checks :memory, :below => 300.megabytes, :times => 3
48
51
  end
@@ -59,12 +62,30 @@ Eye.app "test" do
59
62
  stop_grace 5.seconds
60
63
 
61
64
  monitor_children do
62
- childs_update_period 5.seconds
63
-
64
- restart_command "kill -2 {{PID}}"
65
+ restart_command "kill -2 {PID}" # for this child process
65
66
  checks :memory, :below => 300.megabytes, :times => 3
66
67
  end
67
68
  end
69
+
70
+ process :event_machine do |p|
71
+ p.pid_file = 'em.pid'
72
+ p.start_command = 'ruby em.rb'
73
+ p.stdout = 'em.log'
74
+ p.daemonize = true
75
+ p.stop_signals = [:QUIT, 2.seconds, :KILL]
76
+
77
+ p.checks :socket, :addr => "tcp://127.0.0.1:33221", :every => 10.seconds, :times => 2,
78
+ :timeout => 1.second, :send_data => "ping", :expect_data => /pong/
79
+ end
80
+
81
+ process :thin do
82
+ pid_file "thin.pid"
83
+ start_command "bundle exec thin start -R thin.ru -p 33233 -d -l thin.log -P thin.pid"
84
+ stop_signals [:QUIT, 2.seconds, :TERM, 1.seconds, :KILL]
85
+
86
+ checks :http, :url => "http://127.0.0.1:33233/hello", :pattern => /World/, :every => 5.seconds,
87
+ :times => [2, 3], :timeout => 1.second
88
+ end
68
89
 
69
90
  end
70
91
  ```
@@ -87,14 +108,16 @@ Process statuses:
87
108
  $ eye i(nfo)
88
109
 
89
110
  ```
90
- test
111
+ test
91
112
  samples
92
- sample1 ....................... up (21:38, 0%, 15Mb, <4813>)
93
- sample2 ....................... up (21:36, 0%, 14Mb, <4530>)
94
- forking ......................... up (21:34, 0%, 19Mb, <4272>)
95
- =child= ....................... up (21:34, 0%, 22Mb, <4275>)
96
- =child= ....................... up (21:34, 0%, 21Mb, <4278>)
97
- =child= ....................... up (21:34, 0%, 21Mb, <4281>)
113
+ sample1 ....................... up (21:52, 0%, 13Mb, <4107>)
114
+ sample2 ....................... up (21:52, 0%, 12Mb, <4142>)
115
+ event_machine ................... up (21:52, 3%, 26Mb, <4112>)
116
+ forking ......................... up (21:52, 0%, 41Mb, <4203>)
117
+ child-4206 .................... up (21:52, 0%, 41Mb, <4206>)
118
+ child-4211 .................... up (21:52, 0%, 41Mb, <4211>)
119
+ child-4214 .................... up (21:52, 0%, 41Mb, <4214>)
120
+ thin ............................ up (21:53, 2%, 54Mb, <4228>)
98
121
  ```
99
122
 
100
123
  ### Commands:
data/bin/eye CHANGED
@@ -23,8 +23,9 @@ class Cli < Thor
23
23
 
24
24
  desc "xinfo", "extended eye info, debug data"
25
25
  method_option :config, :type => :boolean, :aliases => "-c"
26
+ method_option :show_processes, :type => :boolean, :aliases => "-p"
26
27
  def xinfo
27
- res = cmd(:xinfo, options[:config])
28
+ res = cmd(:xinfo, options[:config], options[:show_processes])
28
29
  puts res if res && !res.empty?
29
30
  puts
30
31
  end
@@ -167,8 +168,8 @@ private
167
168
  end
168
169
 
169
170
  if opts[:print_config]
170
- require 'yaml'
171
- puts YAML.dump(res[:config])
171
+ require 'pp'
172
+ PP.pp res[:config]
172
173
  end
173
174
  end
174
175
  end
@@ -44,7 +44,8 @@ class EchoObj < EM::Connection
44
44
  end
45
45
  end
46
46
 
47
- trap "TERM" do
47
+ trap "QUIT" do
48
+ puts "quit signal, stopping"
48
49
  EM.stop
49
50
  end
50
51
 
@@ -0,0 +1,12 @@
1
+ require 'bundler/setup'
2
+ require 'sinatra'
3
+
4
+ class Test < Sinatra::Base
5
+
6
+ get '/hello' do
7
+ sleep 0.5
8
+ "Hello World!"
9
+ end
10
+ end
11
+
12
+ run Test.new
@@ -0,0 +1,19 @@
1
+ def sidekiq_process(proxy, name)
2
+ proxy.process(name) do
3
+ start_command "ruby ./bin/sidekiq -e #{proxy.env['RAILS_ENV']} -C ./config/sidekiq.#{proxy.env['RAILS_ENV']}.yml"
4
+ pid_file "tmp/pids/#{name}.pid"
5
+ stdall "log/#{name}.log"
6
+ daemonize true
7
+ stop_signals [:QUIT, 5.seconds, :TERM, 5.seconds, :KILL]
8
+
9
+ checks :cpu, :every => 30, :below => 100, :times => 5
10
+ checks :memory, :every => 30, :below => 300.megabytes, :times => 5
11
+ end
12
+ end
13
+
14
+ Eye.application :sidekiq_test do
15
+ working_dir '/some_dir'
16
+ env "RAILS_ENV" => 'production'
17
+
18
+ sidekiq_process self, :sidekiq
19
+ end
@@ -1,22 +1,23 @@
1
1
  Eye.load("./eye/*.rb") # load submodules
2
+
2
3
  Eye.config do
3
4
  logger "/tmp/eye.log" # eye logger
4
5
  logger_level Logger::DEBUG
5
6
  end
6
7
 
7
- Eye.app "test" do
8
+ Eye.application "test" do
8
9
  working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
9
- stdall "trash.log" # stdout + stderr
10
- env "APP_ENV" => "production"
10
+ stdall "trash.log" # stdout,err logs for processes by default
11
+ env "APP_ENV" => "production" # global env for each processes
11
12
  triggers :flapping, :times => 10, :within => 1.minute
12
13
 
13
14
  group "samples" do
14
- env "A" => "1" # env merging
15
+ env "A" => "1" # merging to app env
15
16
  chain :grace => 5.seconds, :action => :restart # restarting with 5s interval, one by one.
16
17
 
17
18
  # eye daemonized process
18
19
  process("sample1") do
19
- pid_file "1.pid" # expanded with working_dir
20
+ pid_file "1.pid" # will be expanded with working_dir
20
21
  start_command "ruby ./sample.rb"
21
22
  daemonize true
22
23
  stdall "sample1.log"
@@ -28,7 +29,7 @@ Eye.app "test" do
28
29
  process("sample2") do
29
30
  pid_file "2.pid"
30
31
  start_command "ruby ./sample.rb -d --pid 2.pid --log sample2.log"
31
- stop_command "kill -9 {{PID}}"
32
+ stop_command "kill -9 {PID}"
32
33
 
33
34
  checks :memory, :below => 300.megabytes, :times => 3
34
35
  end
@@ -45,21 +46,29 @@ Eye.app "test" do
45
46
  stop_grace 5.seconds
46
47
 
47
48
  monitor_children do
48
- childs_update_period 5.seconds
49
-
50
- restart_command "kill -2 {{PID}}"
49
+ restart_command "kill -2 {PID}" # for this child process
51
50
  checks :memory, :below => 300.megabytes, :times => 3
52
51
  end
53
52
  end
54
53
 
55
- process :event_machine do
56
- pid_file 'em.pid'
57
- start_command 'ruby em.rb'
58
- stdall 'em.log'
59
- daemonize true
54
+ process :event_machine do |p|
55
+ p.pid_file = 'em.pid'
56
+ p.start_command = 'ruby em.rb'
57
+ p.stdout = 'em.log'
58
+ p.daemonize = true
59
+ p.stop_signals = [:QUIT, 2.seconds, :KILL]
60
60
 
61
- checks :socket, :addr => "tcp://127.0.0.1:33221", :send_data => "ping", :expect_data => /pong/,
62
- :every => 10.seconds, :times => 2, :timeout => 1.second
61
+ p.checks :socket, :addr => "tcp://127.0.0.1:33221", :every => 10.seconds, :times => 2,
62
+ :timeout => 1.second, :send_data => "ping", :expect_data => /pong/
63
+ end
64
+
65
+ process :thin do
66
+ pid_file "thin.pid"
67
+ start_command "bundle exec thin start -R thin.ru -p 33233 -d -l thin.log -P thin.pid"
68
+ stop_signals [:QUIT, 2.seconds, :TERM, 1.seconds, :KILL]
69
+
70
+ checks :http, :url => "http://127.0.0.1:33233/hello", :pattern => /World/, :every => 5.seconds,
71
+ :times => [2, 3], :timeout => 1.second
63
72
  end
64
73
 
65
74
  end
@@ -1,12 +1,11 @@
1
- # -*- encoding: utf-8 -*-
2
1
  require File.expand_path('../lib/eye', __FILE__)
3
2
 
4
3
  Gem::Specification.new do |gem|
5
4
  gem.authors = "Konstantin Makarchev"
6
5
  gem.email = "kostya27@gmail.com"
7
6
 
8
- gem.description = %q{Process monitoring tool. With Bluepill like config syntax. Requires MRI Ruby >= 1.9.2. Uses Celluloid and Celluloid::IO.}
9
- gem.summary = %q{Process monitoring tool. With Bluepill like config syntax. Requires MRI Ruby >= 1.9.2. Uses Celluloid and Celluloid::IO.}
7
+ gem.description = gem.summary = \
8
+ %q{Process monitoring tool. Alternative for God and Bluepill. With Bluepill like config syntax. Requires MRI Ruby >= 1.9.2. Uses Celluloid and Celluloid::IO.}
10
9
  gem.homepage = "http://github.com/kostya/eye"
11
10
 
12
11
  gem.files = `git ls-files`.split($\)
@@ -34,4 +33,7 @@ Gem::Specification.new do |gem|
34
33
  gem.add_development_dependency 'forking'
35
34
  gem.add_development_dependency 'fakeweb'
36
35
  gem.add_development_dependency 'eventmachine'
36
+ gem.add_development_dependency 'sinatra'
37
+ gem.add_development_dependency 'thin'
38
+ gem.add_development_dependency 'xmpp4r'
37
39
  end
data/lib/eye.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Eye
2
- VERSION = "0.1.11"
2
+ VERSION = "0.2"
3
3
  ABOUT = "Eye v#{VERSION} (c) 2012-2013 @kostya"
4
4
 
5
5
  autoload :Process, 'eye/process'
@@ -16,6 +16,7 @@ module Eye
16
16
  autoload :Settings, 'eye/settings'
17
17
  autoload :Client, 'eye/client'
18
18
  autoload :Utils, 'eye/utils'
19
+ autoload :Notify, 'eye/notify'
19
20
 
20
21
  autoload :Controller, 'eye/controller'
21
22
  autoload :Control, 'eye/control'
@@ -42,7 +42,7 @@ module Eye::Checker::Validation
42
42
  end
43
43
 
44
44
  should_bes.each do |param|
45
- raise Error, "#{self.name} bad param :#{param}, value should be" unless options[param.to_sym] || defaults[param.to_sym]
45
+ raise Error, "#{self.name} for param :#{param} value should be" unless options[param.to_sym] || defaults[param.to_sym]
46
46
  end
47
47
  end
48
48
 
@@ -27,16 +27,17 @@ class Eye::ChildProcess
27
27
  # scheduler
28
28
  include Eye::Process::Scheduler
29
29
 
30
- attr_reader :pid, :name, :config, :watchers
30
+ attr_reader :pid, :name, :full_name, :config, :watchers
31
31
 
32
32
  def initialize(pid, config = {}, logger_prefix = nil)
33
33
  raise 'Empty pid' unless pid
34
34
 
35
35
  @pid = pid
36
36
  @config = prepare_config(config)
37
- @name = '=child='
37
+ @name = "child-#{pid}"
38
+ @full_name = [logger_prefix, @name] * ':'
38
39
 
39
- @logger = Eye::Logger.new("#{logger_prefix} child:#{pid}")
40
+ @logger = Eye::Logger.new(@full_name)
40
41
 
41
42
  @watchers = {}
42
43
 
@@ -49,12 +50,23 @@ class Eye::ChildProcess
49
50
  :up
50
51
  end
51
52
 
53
+ def send_command(command, *args)
54
+ schedule command, *args, "#{command} by user"
55
+ end
56
+
57
+ def start
58
+ end
59
+
52
60
  def stop
53
61
  kill_process
54
62
  end
55
63
 
56
64
  def restart
57
- stop
65
+ if self[:restart_command]
66
+ execute_restart_command
67
+ else
68
+ stop
69
+ end
58
70
  end
59
71
 
60
72
  def monitor
@@ -63,11 +75,21 @@ class Eye::ChildProcess
63
75
  def unmonitor
64
76
  end
65
77
 
66
- def delete
78
+ def delete
79
+ end
80
+
81
+ def destroy
67
82
  remove_watchers
68
83
  terminate
69
84
  end
70
85
 
86
+ def signal(sig)
87
+ if self.pid
88
+ res = send_signal(sig)
89
+ info "send signal #{sig} to #{self.pid} = #{res}"
90
+ end
91
+ end
92
+
71
93
  def status_data(debug = false)
72
94
  self_status_data(debug)
73
95
  end
@@ -24,7 +24,7 @@ module Eye::Controller::Load
24
24
  private
25
25
 
26
26
  # regexp for clean backtrace to show for user
27
- BT_REGX = %r[/lib/eye/|lib/celluloid|internal:prelude|logger.rb:].freeze
27
+ BT_REGX = %r[/lib/eye/|lib/celluloid|internal:prelude|logger.rb:|active_support/core_ext|shellwords.rb].freeze
28
28
 
29
29
  def catch_load_error(filename, &block)
30
30
  res = block.call
@@ -34,7 +34,8 @@ private
34
34
  error "load: config error <#{filename}>: #{ex.message}"
35
35
 
36
36
  # filter backtrace for user output
37
- bt = (ex.backtrace || []).reject{|line| line.to_s =~ BT_REGX }
37
+ bt = (ex.backtrace || [])
38
+ bt = bt.reject{|line| line.to_s =~ BT_REGX }
38
39
  error bt.join("\n")
39
40
 
40
41
  res = {:error => true, :message => ex.message}
@@ -69,27 +70,28 @@ private
69
70
 
70
71
  raise Eye::Dsl::Error, "config file '#{mask}' not found!" if configs.blank?
71
72
 
72
- new_cfg = @current_config
73
+ @loaded_config = Eye::Dsl.initial_config
73
74
  configs.each do |cfg|
74
- new_cfg = merge_configs(new_cfg, cfg)
75
+ @loaded_config = merge_configs(@loaded_config, cfg)
75
76
  end
76
77
 
78
+ new_cfg = merge_configs(@current_config, @loaded_config)
77
79
  validate(new_cfg)
78
-
79
80
  new_cfg
80
81
  end
81
82
 
82
83
  def _load(filename)
83
84
  new_cfg = parse_set_of_configs(filename)
84
-
85
- load_config(new_cfg)
85
+
86
+ load_config(new_cfg, @loaded_config[:applications].keys)
87
+ @loaded_config = nil
86
88
 
87
89
  GC.start
88
90
  end
89
91
 
90
- def load_config(new_config)
92
+ def load_config(new_config, changed_apps = [])
91
93
  load_options(new_config[:config])
92
- create_objects(new_config[:applications])
94
+ create_objects(new_config[:applications], changed_apps)
93
95
  @current_config = new_config
94
96
  end
95
97
 
@@ -117,10 +119,10 @@ private
117
119
  end
118
120
 
119
121
  # create objects as diff, from configs
120
- def create_objects(apps_config)
122
+ def create_objects(apps_config, changed_apps = [])
121
123
  debug 'create objects'
122
124
  apps_config.each do |app_name, app_cfg|
123
- update_or_create_application(app_name, app_cfg.clone)
125
+ update_or_create_application(app_name, app_cfg.clone) if changed_apps.include?(app_name)
124
126
  end
125
127
 
126
128
  # sorting applications