reel-eye 0.4.1 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGES.md +17 -1
  5. data/README.md +3 -3
  6. data/Rakefile +8 -0
  7. data/bin/eye +0 -316
  8. data/bin/loader_eye +3 -3
  9. data/examples/test.eye +2 -2
  10. data/eye.gemspec +6 -10
  11. data/lib/eye.rb +4 -2
  12. data/lib/eye/application.rb +3 -3
  13. data/lib/eye/checker.rb +35 -7
  14. data/lib/eye/checker/cputime.rb +23 -0
  15. data/lib/eye/checker/file_touched.rb +15 -0
  16. data/lib/eye/checker/http.rb +7 -9
  17. data/lib/eye/checker/memory.rb +1 -1
  18. data/lib/eye/checker/runtime.rb +28 -0
  19. data/lib/eye/checker/socket.rb +4 -4
  20. data/lib/eye/cli.rb +166 -0
  21. data/lib/eye/cli/commands.rb +79 -0
  22. data/lib/eye/cli/render.rb +137 -0
  23. data/lib/eye/cli/server.rb +85 -0
  24. data/lib/eye/client.rb +3 -3
  25. data/lib/eye/config.rb +17 -14
  26. data/lib/eye/controller.rb +6 -10
  27. data/lib/eye/controller/commands.rb +6 -10
  28. data/lib/eye/controller/helpers.rb +1 -1
  29. data/lib/eye/controller/send_command.rb +5 -2
  30. data/lib/eye/controller/status.rb +38 -106
  31. data/lib/eye/dsl.rb +1 -1
  32. data/lib/eye/dsl/application_opts.rb +6 -2
  33. data/lib/eye/dsl/child_process_opts.rb +3 -2
  34. data/lib/eye/dsl/config_opts.rb +2 -2
  35. data/lib/eye/dsl/group_opts.rb +2 -1
  36. data/lib/eye/dsl/main.rb +4 -2
  37. data/lib/eye/dsl/opts.rb +11 -4
  38. data/lib/eye/dsl/validation.rb +49 -43
  39. data/lib/eye/group.rb +1 -1
  40. data/lib/eye/http.rb +4 -9
  41. data/lib/eye/http/router.rb +1 -1
  42. data/lib/eye/loader.rb +5 -9
  43. data/lib/eye/{settings.rb → local.rb} +1 -1
  44. data/lib/eye/logger.rb +5 -0
  45. data/lib/eye/notify.rb +12 -6
  46. data/lib/eye/notify/jabber.rb +2 -2
  47. data/lib/eye/process/child.rb +3 -1
  48. data/lib/eye/process/commands.rb +2 -2
  49. data/lib/eye/process/controller.rb +1 -1
  50. data/lib/eye/process/trigger.rb +1 -1
  51. data/lib/eye/sigar.rb +5 -0
  52. data/lib/eye/system.rb +8 -7
  53. data/lib/eye/system_resources.rb +46 -41
  54. data/lib/eye/trigger.rb +15 -8
  55. data/lib/eye/trigger/flapping.rb +1 -1
  56. data/lib/eye/trigger/stop_childs.rb +1 -1
  57. data/lib/eye/trigger/transition.rb +15 -0
  58. data/lib/eye/utils.rb +12 -0
  59. data/lib/eye/utils/leak_19.rb +7 -0
  60. data/lib/eye/utils/mini_active_support.rb +106 -0
  61. metadata +40 -17
  62. data/lib/eye/controller/show_history.rb +0 -63
  63. data/lib/eye/trigger/state.rb +0 -11
@@ -0,0 +1,79 @@
1
+ module Eye::Cli::Commands
2
+ private
3
+
4
+ def client
5
+ @client ||= Eye::Client.new(Eye::Local.socket_path)
6
+ end
7
+
8
+ def _cmd(cmd, *args)
9
+ client.command(cmd, *args)
10
+ rescue Errno::ECONNREFUSED, Errno::ENOENT
11
+ :not_started
12
+ end
13
+
14
+ def cmd(cmd, *args)
15
+ res = _cmd(cmd, *args)
16
+
17
+ if res == :not_started
18
+ error! "socket(#{Eye::Local.socket_path}) not found, did you `eye load`?"
19
+ elsif res == :timeouted
20
+ error! 'eye does not answer, timeouted...'
21
+ end
22
+
23
+ res
24
+ end
25
+
26
+ def say_load_result(res = {}, opts = {})
27
+ error!(res) unless res.is_a?(Hash)
28
+ say_filename = (res.size > 1)
29
+ say 'eye started!', :green if opts[:started]
30
+ error = false
31
+ res.each do |filename, _res|
32
+ say "#{filename}: ", nil, true if say_filename
33
+ show_load_message(_res, opts)
34
+ error = true if _res[:error]
35
+ end
36
+
37
+ exit(1) if error
38
+ end
39
+
40
+ def show_load_message(res, opts = {})
41
+ if res[:error]
42
+ say res[:message], :red
43
+ res[:backtrace].to_a.each{|line| say line, :red }
44
+ else
45
+ if opts[:syntax]
46
+ say 'config ok!', :green if !res[:empty]
47
+ else
48
+ say 'config loaded!', :green if !res[:empty]
49
+ end
50
+
51
+ if opts[:print_config]
52
+ require 'pp'
53
+ PP.pp res[:config], STDOUT, 150
54
+ end
55
+ end
56
+ end
57
+
58
+ def send_command(_cmd, *args)
59
+ res = cmd(_cmd, *args)
60
+ if res == :unknown_command
61
+ error! "unknown command :#{_cmd}"
62
+ elsif res == :corrupted_data
63
+ error! 'something crazy wrong, check eye logs!'
64
+ elsif res.is_a?(Hash)
65
+ if res[:error]
66
+ error! "Error: #{res[:error]}"
67
+ elsif res = res[:result]
68
+ if res == []
69
+ error! "command :#{_cmd}, objects not found!"
70
+ else
71
+ say "command :#{_cmd} sended to [#{res * ", "}]"
72
+ end
73
+ end
74
+ else
75
+ error! "unknown result #{res.inspect}"
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,137 @@
1
+ module Eye::Cli::Render
2
+ private
3
+ DF = '%d %b %H:%M'
4
+
5
+ def render_info(data)
6
+ error!("unexpected server answer #{data.inspect}") unless data.is_a?(Hash)
7
+
8
+ make_str data
9
+ end
10
+
11
+ def make_str(data, level = -1)
12
+ return nil if !data || data.empty?
13
+
14
+ if data.is_a?(Array)
15
+ data.map{|el| make_str(el, level) }.compact * "\n"
16
+ else
17
+ str = nil
18
+
19
+ if data[:name]
20
+ return make_str(data[:subtree], level) if data[:name] == '__default__'
21
+
22
+ off = level * 2
23
+ off_str = ' ' * off
24
+
25
+ short_state = (data[:type] == :application && data[:states])
26
+ is_text = data[:state] || data[:states]
27
+
28
+ name = (data[:type] == :application && !is_text) ? "\033[1m#{data[:name]}\033[0m" : data[:name].to_s
29
+ off_len = short_state ? 20 : 35
30
+ str = off_str + (name + ' ').ljust(off_len - off, is_text ? '.' : ' ')
31
+
32
+ if short_state
33
+ str += ' ' + data[:states].map { |k, v| "#{k}:#{v}" }.join(', ')
34
+ elsif data[:state]
35
+ str += ' ' + data[:state].to_s
36
+ str += ' (' + resources_str(data[:resources]) + ')' if data[:resources] && data[:state].to_sym == :up
37
+ str += " (#{data[:state_reason]} at #{data[:state_changed_at].strftime(DF)})" if data[:state_reason] && data[:state] == 'unmonitored'
38
+ elsif data[:current_command]
39
+ chain_progress = if data[:chain_progress]
40
+ " #{data[:chain_progress][0]} of #{data[:chain_progress][1]}" rescue ''
41
+ end
42
+ str += " \e[1;33m[#{data[:current_command]}#{chain_progress}]\033[0m"
43
+ str += " (#{data[:chain_commands] * ', '})" if data[:chain_commands]
44
+ end
45
+
46
+ end
47
+
48
+ if data[:subtree].nil?
49
+ str
50
+ elsif !data[:subtree] && data[:type] != :application
51
+ nil
52
+ else
53
+ [str, make_str(data[:subtree], level + 1)].compact * "\n"
54
+ end
55
+ end
56
+ end
57
+
58
+ def resources_str(r)
59
+ return '' if !r || r.empty?
60
+ memory, cpu, start_time, pid = r[:memory], r[:cpu], r[:start_time], r[:pid]
61
+ return '' unless memory && cpu && start_time
62
+
63
+ res = "#{Eye::Utils.human_time(start_time)}, #{cpu.to_i}%"
64
+ res += ", #{memory / 1024 / 1024}Mb"
65
+ res += ", <#{pid}>"
66
+
67
+ res
68
+ end
69
+
70
+ def render_debug_info(data)
71
+ error!("unexpected server answer #{data.inspect}") unless data.is_a?(Hash)
72
+
73
+ s = ""
74
+
75
+ config_yaml = data.delete(:config_yaml)
76
+
77
+ data.each do |k, v|
78
+ s << "#{"#{k}:".ljust(10)} "
79
+
80
+ case k
81
+ when :resources
82
+ s << resources_str(v)
83
+ else
84
+ s << "#{v}"
85
+ end
86
+
87
+ s << "\n"
88
+ end
89
+
90
+ s << "\n"
91
+
92
+ if config_yaml
93
+ s << "Current config:\n"
94
+ s << config_yaml
95
+ end
96
+
97
+ s
98
+ end
99
+
100
+ def render_history(data)
101
+ error!("unexpected server answer #{data.inspect}") unless data.is_a?(Hash)
102
+
103
+ res = []
104
+ data.each do |name, data|
105
+ res << detail_process_info(name, data)
106
+ end
107
+
108
+ res * "\n"
109
+ end
110
+
111
+ def detail_process_info(name, history)
112
+ return if history.empty?
113
+
114
+ res = "\033[1m#{name}\033[0m:\n"
115
+ history = history.reverse
116
+
117
+ history.chunk{|h| [h[:state], h[:reason].to_s] }.each do |_, hist|
118
+ if hist.size >= 3
119
+ res << detail_process_info_string(hist[0])
120
+ res << detail_process_info_string(:state => "... #{hist.size - 2} times", :reason => '...', :at => hist[-1][:at])
121
+ res << detail_process_info_string(hist[-1])
122
+ else
123
+ hist.each do |h|
124
+ res << detail_process_info_string(h)
125
+ end
126
+ end
127
+ end
128
+
129
+ res
130
+ end
131
+
132
+ def detail_process_info_string(h)
133
+ state = h[:state].to_s.ljust(14)
134
+ "#{Time.at(h[:at]).strftime(DF)} - #{state} (#{h[:reason]})\n"
135
+ end
136
+
137
+ end
@@ -0,0 +1,85 @@
1
+ module Eye::Cli::Server
2
+ private
3
+
4
+ def server_started?
5
+ _cmd(:ping) == :pong
6
+ end
7
+
8
+ def loader_path
9
+ filename = File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. .. bin loader_eye]))
10
+ File.exists?(filename) ? filename : nil
11
+ end
12
+
13
+ def ruby_path
14
+ require 'rbconfig'
15
+ RbConfig::CONFIG['bindir'] + '/ruby'
16
+ end
17
+
18
+ def ensure_loader_path
19
+ unless loader_path
20
+ error! "start monitoring needs to run under ruby with installed gem 'eye'"
21
+ end
22
+ end
23
+
24
+ def server_start_foreground(conf = nil)
25
+ ensure_loader_path
26
+ Eye::Local.ensure_eye_dir
27
+
28
+ if server_started?
29
+ _cmd(:quit) && sleep(1) # stop previous server
30
+ end
31
+
32
+ args = []
33
+ args += ['-c', conf] if conf
34
+ args += ['-l', 'stdout']
35
+
36
+ Process.exec(ruby_path, loader_path, *args)
37
+ end
38
+
39
+ def server_start(configs)
40
+ ensure_loader_path
41
+ Eye::Local.ensure_eye_dir
42
+
43
+ ensure_stop_previous_server
44
+
45
+ args = []
46
+ opts = {:out => '/dev/null', :err => '/dev/null', :in => '/dev/null',
47
+ :chdir => '/', :pgroup => true}
48
+
49
+ pid = Process.spawn(ruby_path, loader_path, *args, opts)
50
+ Process.detach(pid)
51
+ File.open(Eye::Local.pid_path, 'w'){|f| f.write(pid) }
52
+
53
+ unless wait_server
54
+ error! 'server not runned in 15 seconds, something crazy wrong'
55
+ end
56
+
57
+ configs.unshift(Eye::Local.eyeconfig) if File.exists?(Eye::Local.eyeconfig)
58
+
59
+ if !configs.empty?
60
+ say_load_result cmd(:load, *configs), :started => true
61
+ else
62
+ say 'started!', :green
63
+ end
64
+ end
65
+
66
+ def ensure_stop_previous_server
67
+ Eye::Local.ensure_eye_dir
68
+ pid = File.read(Eye::Local.pid_path).to_i rescue nil
69
+ if pid
70
+ Process.kill(9, pid) rescue nil
71
+ end
72
+ File.delete(Eye::Local.pid_path) rescue nil
73
+ true
74
+ end
75
+
76
+ def wait_server(timeout = 15)
77
+ Timeout.timeout(timeout) do
78
+ sleep 0.3 while !server_started?
79
+ end
80
+ true
81
+ rescue Timeout::Error
82
+ false
83
+ end
84
+
85
+ end
@@ -13,7 +13,7 @@ class Eye::Client
13
13
  end
14
14
 
15
15
  def attempt_command(pack)
16
- Timeout.timeout(Eye::Settings.client_timeout) do
16
+ Timeout.timeout(Eye::Local.client_timeout) do
17
17
  return send_request(pack)
18
18
  end
19
19
 
@@ -25,8 +25,8 @@ class Eye::Client
25
25
  UNIXSocket.open(@socket_path) do |socket|
26
26
  socket.write(pack)
27
27
  data = socket.read
28
- res = Marshal.load(data) rescue :corrupred_data
28
+ res = Marshal.load(data) rescue :corrupted_data
29
29
  end
30
30
  end
31
31
 
32
- end
32
+ end
@@ -30,33 +30,36 @@ class Eye::Config
30
30
  raise Eye::Dsl::Error, "blank pid_file for: #{no_pid_file.map{|c| c[:name]} * ', '}"
31
31
  end
32
32
 
33
- # Check dublicates of the full pid_file
33
+ # Check duplicates of the full pid_file
34
34
 
35
- dubl_pids = all_processes.each_with_object(Hash.new(0)) do |o, h|
35
+ dupl_pids = all_processes.each_with_object(Hash.new(0)) do |o, h|
36
36
  ex_pid_file = Eye::System.normalized_file(o[:pid_file], o[:working_dir])
37
37
  h[ex_pid_file] += 1
38
38
  end
39
- dubl_pids = dubl_pids.select{|k,v| v>1}
39
+ dupl_pids = dupl_pids.select{|k,v| v>1}
40
40
 
41
- if dubl_pids.present?
42
- raise Eye::Dsl::Error, "dublicate pid_files: #{dubl_pids.inspect}"
41
+ if dupl_pids.present?
42
+ raise Eye::Dsl::Error, "duplicate pid_files: #{dupl_pids.inspect}"
43
43
  end
44
44
 
45
- # Check dublicates of the full_name
46
- dubl_names = all_processes.each_with_object(Hash.new(0)) do |o, h|
45
+ # Check duplicates of the full_name
46
+ dupl_names = all_processes.each_with_object(Hash.new(0)) do |o, h|
47
47
  full_name = "#{o[:application]}:#{o[:group]}:#{o[:name]}"
48
48
  h[full_name] += 1
49
49
  end
50
- dubl_names = dubl_names.select{|k,v| v>1}
50
+ dupl_names = dupl_names.select{|k,v| v>1}
51
51
 
52
- if dubl_names.present?
53
- raise Eye::Dsl::Error, "dublicate names: #{dubl_names.inspect}"
52
+ if dupl_names.present?
53
+ raise Eye::Dsl::Error, "duplicate names: #{dupl_names.inspect}"
54
54
  end
55
55
 
56
56
  # validate processes with their own validate
57
57
  all_processes.each do |process_cfg|
58
58
  Eye::Process.validate process_cfg
59
59
  end
60
+
61
+ # just to be sure ENV was not removed
62
+ ENV[''] rescue raise Eye::Dsl::Error.new("ENV is not a hash '#{ENV.inspect}'")
60
63
  end
61
64
 
62
65
  def processes
@@ -73,16 +76,16 @@ class Eye::Config
73
76
 
74
77
  def delete_group(name)
75
78
  applications.each do |app_name, app_cfg|
76
- app_cfg[:groups].delete(name)
79
+ (app_cfg[:groups] || {}).delete(name)
77
80
  end
78
81
  end
79
82
 
80
83
  def delete_process(name)
81
84
  applications.each do |app_name, app_cfg|
82
- app_cfg[:groups].each do |gr_name, gr_cfg|
83
- gr_cfg[:processes].delete(name)
85
+ (app_cfg[:groups] || {}).each do |gr_name, gr_cfg|
86
+ (gr_cfg[:processes] || {}).delete(name)
84
87
  end
85
88
  end
86
89
  end
87
90
 
88
- end
91
+ end
@@ -1,19 +1,17 @@
1
1
  require 'celluloid'
2
-
3
2
  require 'yaml'
4
- require 'active_support'
5
- require 'active_support/core_ext/object/blank'
6
- require 'active_support/core_ext/object/try'
7
- require 'active_support/core_ext/numeric'
8
- require 'active_support/core_ext/string/filters'
9
- require 'active_support/core_ext/array/extract_options'
10
3
 
11
4
  require_relative 'utils/celluloid_klass'
12
5
  require_relative 'utils/pmap'
13
6
 
7
+ require_relative 'utils/leak_19'
8
+ require_relative 'utils/mini_active_support'
9
+
14
10
  # Extend all objects with logger
15
11
  Object.send(:include, Eye::Logger::ObjectExt)
16
12
 
13
+ Eye::Sigar # needs to preload
14
+
17
15
  class Eye::Controller
18
16
  include Celluloid
19
17
 
@@ -22,7 +20,6 @@ class Eye::Controller
22
20
  autoload :Commands, 'eye/controller/commands'
23
21
  autoload :Status, 'eye/controller/status'
24
22
  autoload :SendCommand, 'eye/controller/send_command'
25
- autoload :ShowHistory, 'eye/controller/show_history'
26
23
  autoload :Options, 'eye/controller/options'
27
24
 
28
25
  include Eye::Controller::Load
@@ -30,7 +27,6 @@ class Eye::Controller
30
27
  include Eye::Controller::Commands
31
28
  include Eye::Controller::Status
32
29
  include Eye::Controller::SendCommand
33
- include Eye::Controller::ShowHistory
34
30
  include Eye::Controller::Options
35
31
 
36
32
  attr_reader :applications, :current_config
@@ -42,7 +38,7 @@ class Eye::Controller
42
38
  @current_config = Eye::Config.new
43
39
 
44
40
  Celluloid::logger = Eye::Logger.new('celluloid')
45
- Eye::SystemResources.setup
41
+ Eye::SystemResources.cache
46
42
 
47
43
  info "starting #{Eye::ABOUT} (#{$$})"
48
44
  end
@@ -16,14 +16,6 @@ module Eye::Controller::Commands
16
16
  signal(*args)
17
17
  when :load
18
18
  load(*args)
19
- when :info
20
- info_string(*args)
21
- when :xinfo
22
- info_string_debug(*args)
23
- when :oinfo
24
- info_string_short(*args)
25
- when :history
26
- history_string(*args)
27
19
  when :quit
28
20
  quit
29
21
  when :check
@@ -38,9 +30,13 @@ module Eye::Controller::Commands
38
30
  Eye::Logger.dev
39
31
 
40
32
  # object commands, for api
41
- when :raw_info
33
+ when :info_data
42
34
  info_data(*args)
43
- when :raw_history
35
+ when :short_data
36
+ short_data(*args)
37
+ when :debug_data
38
+ debug_data(*args)
39
+ when :history_data
44
40
  history_data(*args)
45
41
 
46
42
  else