eye 0.3.2 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +1 -1
  4. data/.travis.yml +3 -1
  5. data/CHANGES.md +11 -2
  6. data/Gemfile +1 -0
  7. data/README.md +18 -14
  8. data/Rakefile +10 -3
  9. data/bin/eye +41 -27
  10. data/examples/process_thin.rb +1 -1
  11. data/examples/processes/em.rb +2 -2
  12. data/examples/processes/forking.rb +2 -2
  13. data/examples/processes/sample.rb +5 -5
  14. data/examples/rbenv.eye +1 -1
  15. data/examples/sidekiq.eye +2 -2
  16. data/examples/test.eye +10 -6
  17. data/examples/thin-farm.eye +1 -1
  18. data/examples/unicorn.eye +1 -1
  19. data/eye.gemspec +13 -7
  20. data/lib/eye.rb +6 -6
  21. data/lib/eye/application.rb +9 -6
  22. data/lib/eye/checker.rb +51 -21
  23. data/lib/eye/checker/file_size.rb +1 -1
  24. data/lib/eye/checker/http.rb +3 -3
  25. data/lib/eye/checker/memory.rb +1 -1
  26. data/lib/eye/checker/socket.rb +6 -6
  27. data/lib/eye/child_process.rb +7 -11
  28. data/lib/eye/client.rb +6 -6
  29. data/lib/eye/config.rb +2 -2
  30. data/lib/eye/controller.rb +11 -8
  31. data/lib/eye/controller/commands.rb +8 -9
  32. data/lib/eye/controller/helpers.rb +1 -0
  33. data/lib/eye/controller/load.rb +11 -7
  34. data/lib/eye/controller/send_command.rb +44 -19
  35. data/lib/eye/controller/show_history.rb +8 -7
  36. data/lib/eye/controller/status.rb +39 -26
  37. data/lib/eye/dsl.rb +3 -3
  38. data/lib/eye/dsl/application_opts.rb +4 -4
  39. data/lib/eye/dsl/config_opts.rb +4 -4
  40. data/lib/eye/dsl/helpers.rb +2 -2
  41. data/lib/eye/dsl/main.rb +2 -2
  42. data/lib/eye/dsl/opts.rb +19 -14
  43. data/lib/eye/dsl/process_opts.rb +1 -1
  44. data/lib/eye/dsl/pure_opts.rb +2 -2
  45. data/lib/eye/dsl/validation.rb +7 -5
  46. data/lib/eye/group.rb +17 -11
  47. data/lib/eye/group/chain.rb +3 -3
  48. data/lib/eye/loader.rb +8 -6
  49. data/lib/eye/logger.rb +14 -5
  50. data/lib/eye/notify.rb +13 -7
  51. data/lib/eye/notify/jabber.rb +2 -2
  52. data/lib/eye/notify/mail.rb +2 -2
  53. data/lib/eye/process.rb +10 -13
  54. data/lib/eye/process/child.rb +1 -1
  55. data/lib/eye/process/commands.rb +34 -32
  56. data/lib/eye/process/config.rb +17 -12
  57. data/lib/eye/process/controller.rb +3 -6
  58. data/lib/eye/process/data.rb +16 -5
  59. data/lib/eye/process/monitor.rb +12 -5
  60. data/lib/eye/process/notify.rb +1 -1
  61. data/lib/eye/process/scheduler.rb +3 -3
  62. data/lib/eye/process/states.rb +10 -13
  63. data/lib/eye/process/states_history.rb +3 -3
  64. data/lib/eye/process/system.rb +17 -21
  65. data/lib/eye/process/trigger.rb +11 -30
  66. data/lib/eye/process/watchers.rb +9 -9
  67. data/lib/eye/server.rb +14 -6
  68. data/lib/eye/settings.rb +4 -4
  69. data/lib/eye/system.rb +10 -7
  70. data/lib/eye/system_resources.rb +4 -4
  71. data/lib/eye/trigger.rb +58 -21
  72. data/lib/eye/trigger/flapping.rb +24 -4
  73. data/lib/eye/trigger/state.rb +28 -0
  74. data/lib/eye/utils/alive_array.rb +1 -1
  75. data/lib/eye/utils/celluloid_klass.rb +5 -0
  76. data/lib/eye/utils/pmap.rb +7 -0
  77. data/lib/eye/utils/tail.rb +1 -1
  78. metadata +39 -23
  79. data/lib/eye/utils/leak_19.rb +0 -7
@@ -7,11 +7,11 @@ class Eye::Client
7
7
  def initialize(socket_path)
8
8
  @socket_path = socket_path
9
9
  end
10
-
10
+
11
11
  def command(cmd, *args)
12
- attempt_command([cmd, *args] * '|')
12
+ attempt_command(Marshal.dump([cmd, *args]))
13
13
  end
14
-
14
+
15
15
  def attempt_command(pack)
16
16
  Timeout.timeout(Eye::Settings.client_timeout) do
17
17
  return send_request(pack)
@@ -23,10 +23,10 @@ class Eye::Client
23
23
 
24
24
  def send_request(pack)
25
25
  UNIXSocket.open(@socket_path) do |socket|
26
- socket.puts(pack)
26
+ socket.write(pack)
27
27
  data = socket.read
28
- res = Marshal.load(data) rescue :corrupred_marshal
28
+ res = Marshal.load(data) rescue :corrupred_data
29
29
  end
30
30
  end
31
-
31
+
32
32
  end
@@ -32,7 +32,7 @@ class Eye::Config
32
32
 
33
33
  # Check dublicates of the full pid_file
34
34
 
35
- dubl_pids = all_processes.each_with_object(Hash.new(0)) do |o, h|
35
+ dubl_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
@@ -43,7 +43,7 @@ class Eye::Config
43
43
  end
44
44
 
45
45
  # Check dublicates of the full_name
46
- dubl_names = all_processes.each_with_object(Hash.new(0)) do |o, h|
46
+ dubl_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
@@ -6,10 +6,13 @@ require 'active_support/core_ext/object/blank'
6
6
  require 'active_support/core_ext/object/try'
7
7
  require 'active_support/core_ext/numeric'
8
8
  require 'active_support/core_ext/string/filters'
9
+ require 'active_support/core_ext/array/extract_options'
9
10
 
10
- require_relative 'utils/leak_19'
11
+ require_relative 'utils/celluloid_klass'
12
+ require_relative 'utils/pmap'
11
13
 
12
- Eye.send(:extend, Eye::Logger::Helpers)
14
+ # Extend all objects with logger
15
+ Object.send(:include, Eye::Logger::ObjectExt)
13
16
 
14
17
  class Eye::Controller
15
18
  include Celluloid
@@ -22,7 +25,6 @@ class Eye::Controller
22
25
  autoload :ShowHistory, 'eye/controller/show_history'
23
26
  autoload :Options, 'eye/controller/options'
24
27
 
25
- include Eye::Logger::Helpers
26
28
  include Eye::Controller::Load
27
29
  include Eye::Controller::Helpers
28
30
  include Eye::Controller::Commands
@@ -37,12 +39,9 @@ class Eye::Controller
37
39
  @applications = []
38
40
  @current_config = Eye::Config.new
39
41
 
40
- Eye.instance_variable_set(:@logger, Eye::Logger.new('eye'))
41
- @logger = Eye.logger
42
- Celluloid::logger = Eye.logger
43
-
42
+ Celluloid::logger = Eye::Logger.new('celluloid')
44
43
  Eye::SystemResources.setup
45
-
44
+
46
45
  info "starting #{Eye::ABOUT} (#{$$})"
47
46
  end
48
47
 
@@ -50,4 +49,8 @@ class Eye::Controller
50
49
  current_config.settings
51
50
  end
52
51
 
52
+ def logger_tag
53
+ 'Eye'
54
+ end
55
+
53
56
  end
@@ -6,8 +6,8 @@ module Eye::Controller::Commands
6
6
 
7
7
  start_at = Time.now
8
8
  cmd = cmd.to_sym
9
-
10
- res = case cmd
9
+
10
+ res = case cmd
11
11
  when :start, :stop, :restart, :unmonitor, :monitor
12
12
  send_command(cmd, *args)
13
13
  when :delete
@@ -23,7 +23,7 @@ module Eye::Controller::Commands
23
23
  when :xinfo
24
24
  info_string_debug(*args)
25
25
  when :oinfo
26
- info_string_short
26
+ info_string_short(*args)
27
27
  when :history
28
28
  history_string(*args)
29
29
  when :quit
@@ -47,22 +47,21 @@ module Eye::Controller::Commands
47
47
 
48
48
  else
49
49
  :unknown_command
50
- end
50
+ end
51
51
 
52
52
  GC.start
53
53
  info "client command: #{cmd} #{args * ', '} (#{Time.now - start_at}s)"
54
54
 
55
- res
55
+ res
56
56
  end
57
57
 
58
58
  private
59
59
 
60
60
  def quit
61
- info 'exiting...'
61
+ info 'Quit!'
62
+ Eye::System.send_signal($$, :TERM)
62
63
  sleep 1
63
- Eye::System.send_signal($$) # soft terminate
64
- sleep 2
65
- Eye::System.send_signal($$, 9)
64
+ Eye::System.send_signal($$, :KILL)
66
65
  end
67
66
 
68
67
  end
@@ -3,6 +3,7 @@ module Eye::Controller::Helpers
3
3
  def set_proc_line
4
4
  str = Eye::PROCLINE
5
5
  str += " (#{@applications.map(&:name) * ', '})" if @applications.present?
6
+ str += " [#{ENV['EYE_V']}]" if ENV['EYE_V']
6
7
  $0 = str
7
8
  end
8
9
 
@@ -8,7 +8,9 @@ module Eye::Controller::Load
8
8
  { filename => catch_load_error(filename) { parse_config(filename).to_h } }
9
9
  end
10
10
 
11
- def load(*obj_strs)
11
+ def load(*args)
12
+ h = args.extract_options!
13
+ obj_strs = args.flatten
12
14
  info "load: #{obj_strs}"
13
15
 
14
16
  res = Hash.new
@@ -24,6 +26,8 @@ module Eye::Controller::Load
24
26
  set_proc_line
25
27
  save_cache
26
28
 
29
+ info "loaded: #{obj_strs}, selfpid <#{$$}>"
30
+
27
31
  res
28
32
  end
29
33
 
@@ -68,7 +72,7 @@ private
68
72
  sub << config_path
69
73
  end
70
74
  sub = [mask] if sub.empty?
71
-
75
+
72
76
  res += sub
73
77
  end
74
78
 
@@ -76,7 +80,7 @@ private
76
80
  end
77
81
 
78
82
  # return: result, config
79
- def parse_config(filename)
83
+ def parse_config(filename)
80
84
  raise Eye::Dsl::Error, "config file '#{filename}' not found!" unless File.exists?(filename)
81
85
  debug "parse #{filename}"
82
86
 
@@ -90,7 +94,7 @@ private
90
94
  info "load #{filename}"
91
95
  new_cfg = @current_config.merge(config)
92
96
  new_cfg.validate!
93
-
97
+
94
98
  load_options(new_cfg.settings)
95
99
  create_objects(new_cfg.applications, config.application_names)
96
100
  @current_config = new_cfg
@@ -150,7 +154,7 @@ private
150
154
  group.resort_processes
151
155
  end
152
156
 
153
- # now, need to clear @old_groups, and @old_processes
157
+ # now, need to clear @old_groups, and @old_processes
154
158
  @old_groups.each{|_, group| group.clear; group.send_command(:delete) }
155
159
  @old_processes.each{|_, process| process.send_command(:delete) if process.alive? }
156
160
 
@@ -159,7 +163,7 @@ private
159
163
  @added_groups.each do |group|
160
164
  if group.processes.size > 0 && (group.processes.pure - @added_processes).size == 0
161
165
  added_fully_groups << group
162
- @added_processes -= group.processes.pure
166
+ @added_processes -= group.processes.pure
163
167
  end
164
168
  end
165
169
 
@@ -205,7 +209,7 @@ private
205
209
  debug "update process #{process_name}"
206
210
  process = @old_processes.delete(process_name)
207
211
  process.schedule :update_config, process_cfg, Eye::Reason::User.new(:'load config')
208
- process
212
+ process
209
213
  else
210
214
  debug "create process #{process_name}"
211
215
  process = Eye::Process.new(process_cfg)
@@ -1,9 +1,9 @@
1
1
  module Eye::Controller::SendCommand
2
2
 
3
- def send_command(command, *obj_strs)
4
- matched_objects(*obj_strs) do |obj|
3
+ def send_command(command, *args)
4
+ matched_objects(*args) do |obj|
5
5
  if command.to_sym == :delete
6
- remove_object_from_tree(obj)
6
+ remove_object_from_tree(obj)
7
7
 
8
8
  set_proc_line
9
9
  save_cache
@@ -13,29 +13,39 @@ module Eye::Controller::SendCommand
13
13
  end
14
14
  end
15
15
 
16
- def match(*obj_strs)
17
- matched_objects(*obj_strs)
18
- end
16
+ def match(*args)
17
+ matched_objects(*args)
18
+ end
19
19
 
20
- def signal(sig, *obj_strs)
21
- matched_objects(*obj_strs) do |obj|
22
- obj.send_command :signal, sig || 0
20
+ def signal(signal, *args)
21
+ matched_objects(*args) do |obj|
22
+ obj.send_command :signal, signal || 0
23
23
  end
24
24
  end
25
25
 
26
- def break_chain(*obj_strs)
27
- matched_objects(*obj_strs) do |obj|
26
+ def break_chain(*args)
27
+ matched_objects(*args) do |obj|
28
28
  obj.send_command(:break_chain)
29
29
  end
30
30
  end
31
31
 
32
32
  private
33
33
 
34
- def matched_objects(*obj_strs, &block)
34
+ class Error < Exception; end
35
+
36
+ def matched_objects(*args, &block)
37
+ h = args.extract_options!
38
+ obj_strs = args
39
+
35
40
  objs = find_objects(*obj_strs)
36
41
  res = objs.map(&:full_name)
37
42
  objs.each{|obj| block[obj] } if block
38
- res
43
+ {:result => res}
44
+
45
+ rescue Error => ex
46
+ error ex.message
47
+
48
+ {:error => ex.message}
39
49
  end
40
50
 
41
51
  def remove_object_from_tree(obj)
@@ -54,17 +64,17 @@ private
54
64
  if klass == Eye::Process
55
65
  @applications.each{|app| app.groups.each{|gr| gr.processes.delete(obj) }}
56
66
  @current_config.delete_process(obj.name)
57
- end
67
+ end
58
68
  end
59
69
 
60
70
  # find object to action, restart ... (app, group or process)
61
71
  # nil if not found
62
72
  def find_objects(*obj_strs)
63
73
  return [] if obj_strs.blank?
64
- return @applications.dup if obj_strs.size == 1 && (obj_strs[0].strip == 'all' || obj_strs[0].strip == '*')
74
+ return @applications.dup if obj_strs.size == 1 && (obj_strs[0].to_s.strip == 'all' || obj_strs[0].to_s.strip == '*')
65
75
 
66
76
  res = Eye::Utils::AliveArray.new
67
- obj_strs.map{|c| c.split(",")}.flatten.each do |mask|
77
+ obj_strs.map{|c| c.to_s.split(",")}.flatten.each do |mask|
68
78
  res += find_objects_by_mask(mask.to_s.strip)
69
79
  end
70
80
  res
@@ -76,15 +86,16 @@ private
76
86
  if res.size > 1
77
87
  final = Eye::Utils::AliveArray.new
78
88
 
89
+ # try to find exactly matched
79
90
  if mask[-1] != '*'
80
- # try to find exactly matched
81
91
  r = right_regexp(mask)
82
92
  res.each do |obj|
83
93
  final << obj if obj.full_name =~ r
84
94
  end
85
95
  end
86
96
 
87
- return final if final.present?
97
+ res = final if final.present?
98
+ final = Eye::Utils::AliveArray.new
88
99
 
89
100
  # remove inherited targets
90
101
  res.each do |obj|
@@ -93,7 +104,21 @@ private
93
104
  end
94
105
 
95
106
  res = final
96
- end
107
+
108
+ # try to remove objects with different applications
109
+ fapps, apps = [], []
110
+ res.each do |obj|
111
+ if obj.is_a?(Eye::Application)
112
+ fapps << obj.name
113
+ else
114
+ apps << obj.app_name
115
+ end
116
+ end
117
+
118
+ if (apps).uniq.size > 1 || (fapps.size > 0 && (apps | fapps).size > fapps.size)
119
+ raise Error, "cant match targets from different applications: #{res.map(&:full_name)}"
120
+ end
121
+ end
97
122
 
98
123
  res
99
124
  end
@@ -1,7 +1,7 @@
1
1
  module Eye::Controller::ShowHistory
2
2
 
3
- def history_string(*obj_strs)
4
- data = history_data(*obj_strs)
3
+ def history_string(*args)
4
+ data = history_data(*args)
5
5
 
6
6
  res = []
7
7
  data.each do |name, data|
@@ -11,9 +11,9 @@ module Eye::Controller::ShowHistory
11
11
  res * "\n"
12
12
  end
13
13
 
14
- def history_data(*obj_strs)
14
+ def history_data(*args)
15
15
  res = {}
16
- get_processes_for_history(*obj_strs).each do |process|
16
+ get_processes_for_history(*args).each do |process|
17
17
  res[process.full_name] = process.schedule_history.reject{|c| c[:state] == :check_crash }
18
18
  end
19
19
  res
@@ -21,11 +21,12 @@ module Eye::Controller::ShowHistory
21
21
 
22
22
  private
23
23
 
24
- def get_processes_for_history(*obj_strs)
24
+ def get_processes_for_history(*args)
25
+ args = ['*'] if args.empty?
25
26
  res = []
26
- matched_objects(*obj_strs) do |obj|
27
+ matched_objects(*args) do |obj|
27
28
  if (obj.is_a?(Eye::Process) || obj.is_a?(Eye::ChildProcess))
28
- res << obj
29
+ res << obj
29
30
  else
30
31
  res += obj.processes.to_a
31
32
  end
@@ -1,35 +1,23 @@
1
1
  module Eye::Controller::Status
2
2
 
3
- def info_objects(mask = nil)
4
- res = []
5
- return @applications unless mask
6
- matched_objects(mask){|obj| res << obj }
7
- res
8
- end
9
-
10
- def info_data(mask = nil)
11
- {:subtree => info_objects(mask).map{|a| a.status_data } }
12
- end
13
-
14
- def info_data_debug(mask = nil)
15
- {:subtree => info_objects(mask).map{|a| a.status_data(true) } }
3
+ def info_string(*args)
4
+ make_str(info_data(*args)).to_s
16
5
  end
17
6
 
18
- def info_string(mask = nil)
19
- make_str(info_data(mask)).to_s
20
- end
21
-
22
- def info_string_short
7
+ def info_string_short(*args)
23
8
  make_str({:subtree => @applications.map{|a| a.status_data_short } }).to_s
24
9
  end
25
10
 
26
- def info_string_debug(show_config = false, show_processes = false)
27
- actors = Celluloid::Actor.all.map{|actor| actor.instance_variable_get(:@klass) }.group_by{|a| a}.map{|k,v| [k, v.size]}.sort_by{|a|a[1]}.reverse
11
+ def info_string_debug(*args)
12
+ h = args.extract_options!
13
+ actors = Celluloid::Actor.all.map{|actor| actor.__klass__ }.group_by{|a| a}.map{|k,v| [k, v.size]}.sort_by{|a|a[1]}.reverse
28
14
 
29
15
  str = <<-S
30
16
  About: #{Eye::ABOUT}
31
17
  Info: #{resources_str(Eye::SystemResources.resources($$))}
32
18
  Ruby: #{RUBY_DESCRIPTION}
19
+ Gems: #{%w|Celluloid Celluloid::IO ActiveSupport StateMachine NIO|.map{|c| gem_version(c) }}
20
+ Checks: #{Eye::Checker::TYPES}, #{Eye::Trigger::TYPES}
33
21
  Logger: #{Eye::Logger.dev}
34
22
  Socket: #{Eye::Settings::socket_path}
35
23
  Pid: #{Eye::Settings::pid_path}
@@ -37,9 +25,9 @@ Actors: #{actors.inspect}
37
25
 
38
26
  S
39
27
 
40
- str += make_str(info_data_debug) + "\n" if show_processes.present?
28
+ str += make_str(info_data_debug) + "\n" if h[:processes].present?
41
29
 
42
- if show_config.present?
30
+ if h[:config].present?
43
31
  str += "\nCurrent config: \n"
44
32
  str += YAML.dump(current_config.to_h)
45
33
  end
@@ -48,7 +36,22 @@ Actors: #{actors.inspect}
48
36
  str
49
37
  end
50
38
 
51
- private
39
+ def info_data(*args)
40
+ {:subtree => info_objects(*args).map{|a| a.status_data } }
41
+ end
42
+
43
+ private
44
+
45
+ def info_data_debug(*args)
46
+ {:subtree => info_objects(*args).map{|a| a.status_data(true) } }
47
+ end
48
+
49
+ def info_objects(*args)
50
+ res = []
51
+ return @applications if args.empty?
52
+ matched_objects(*args){|obj| res << obj }
53
+ res
54
+ end
52
55
 
53
56
  def make_str(data, level = -1)
54
57
  return nil if data.blank?
@@ -75,7 +78,7 @@ private
75
78
  str += " (chain: #{data[:debug][:chain].map(&:to_i)})"
76
79
  end
77
80
  elsif data[:state]
78
- str += ' ' + data[:state].to_s
81
+ str += ' ' + data[:state].to_s
79
82
  str += ' (' + resources_str(data[:resources]) + ')' if data[:resources].present? && data[:state].to_sym == :up
80
83
  str += " (#{data[:state_reason]} at #{data[:state_changed_at].to_s(:short)})" if data[:state_reason] && data[:state] == 'unmonitored'
81
84
  elsif data[:current_command]
@@ -100,11 +103,11 @@ private
100
103
 
101
104
  def resources_str(r)
102
105
  return '' if r.blank?
103
-
106
+
104
107
  res = "#{r[:start_time]}, #{r[:cpu]}%"
105
108
  res += ", #{r[:memory] / 1024}Mb" if r[:memory]
106
109
  res += ", <#{r[:pid]}>"
107
-
110
+
108
111
  res
109
112
  end
110
113
 
@@ -123,4 +126,14 @@ private
123
126
  apps
124
127
  end
125
128
 
129
+ def gem_version(klass)
130
+ v = nil
131
+ begin
132
+ v = eval("#{klass}::VERSION::STRING")
133
+ rescue
134
+ v = eval("#{klass}::VERSION") rescue ''
135
+ end
136
+ "#{klass}=#{v}"
137
+ end
138
+
126
139
  end