eye 0.3.2 → 0.4

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