eye 0.1.11 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
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