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
@@ -1,10 +1,10 @@
1
1
  class Eye::Notify::Jabber < Eye::Notify
2
-
2
+
3
3
  # Eye.config do
4
4
  # jabber :host => "some.host", :port => 12345, :user => "eye@some.host", :password => "123456"
5
5
  # contact :vasya, :jabber, "vasya@some.host"
6
6
  # end
7
-
7
+
8
8
  param :host, String, true
9
9
  param :port, [String, Fixnum], true
10
10
  param :user, String, true
@@ -14,10 +14,10 @@ class Eye::Notify::Mail < Eye::Notify
14
14
  param :user, String
15
15
  param :password, String
16
16
  param :auth, Symbol, nil, nil, [:plain, :login, :cram_md5]
17
-
17
+
18
18
  param :from_mail, String
19
19
  param :from_name, String, nil, 'eye'
20
-
20
+
21
21
  def execute
22
22
  smtp
23
23
  end
@@ -2,7 +2,7 @@ require 'celluloid'
2
2
 
3
3
  class Eye::Process
4
4
  include Celluloid
5
-
5
+
6
6
  autoload :Config, 'eye/process/config'
7
7
  autoload :Commands, 'eye/process/commands'
8
8
  autoload :Data, 'eye/process/data'
@@ -17,19 +17,19 @@ class Eye::Process
17
17
  autoload :Scheduler, 'eye/process/scheduler'
18
18
  autoload :Validate, 'eye/process/validate'
19
19
 
20
- attr_accessor :pid, :watchers, :config, :states_history,
21
- :childs, :triggers, :name, :state_reason
22
-
20
+ attr_accessor :pid, :watchers, :config, :states_history,
21
+ :childs, :triggers, :name, :state_reason, :flapping_times
22
+
23
23
  def initialize(config)
24
24
  raise 'pid file should be' unless config[:pid_file]
25
25
 
26
26
  @config = prepare_config(config)
27
- @logger = Eye::Logger.new(full_name)
28
27
 
29
28
  @watchers = {}
30
29
  @childs = {}
31
30
  @triggers = []
32
31
  @name = @config[:name]
32
+ @flapping_times = 0
33
33
 
34
34
  @states_history = Eye::Process::StatesHistory.new(100)
35
35
  @states_history << :unmonitored
@@ -41,22 +41,22 @@ class Eye::Process
41
41
  super() # for statemachine
42
42
  end
43
43
 
44
- # c(), self[]
44
+ # c(), self[]
45
45
  include Eye::Process::Config
46
-
46
+
47
47
  # full_name, status_data
48
48
  include Eye::Process::Data
49
-
49
+
50
50
  # commands:
51
51
  # start_process, stop_process, restart_process
52
52
  include Eye::Process::Commands
53
53
 
54
54
  # start, stop, restart, monitor, unmonit, delete
55
55
  include Eye::Process::Controller
56
-
56
+
57
57
  # add_watchers, remove_watchers:
58
58
  include Eye::Process::Watchers
59
-
59
+
60
60
  # check alive, crash methods:
61
61
  include Eye::Process::Monitor
62
62
 
@@ -72,9 +72,6 @@ class Eye::Process
72
72
  # manage notify methods
73
73
  include Eye::Process::Notify
74
74
 
75
- # logger methods
76
- include Eye::Logger::Helpers
77
-
78
75
  # scheduler
79
76
  include Eye::Process::Scheduler
80
77
 
@@ -31,7 +31,7 @@ module Eye::Process::Child
31
31
  if new_childs.present?
32
32
  new_childs.each do |child_pid|
33
33
  self.childs[child_pid] = Eye::ChildProcess.new(child_pid, self[:monitor_children], logger.prefix)
34
- end
34
+ end
35
35
  end
36
36
 
37
37
  if removed_childs.present?
@@ -7,7 +7,7 @@ module Eye::Process::Commands
7
7
 
8
8
  unless self[:start_command]
9
9
  warn 'no start command, so unmonitoring'
10
- switch :unmonitoring
10
+ switch :unmonitoring, Eye::Reason.new(:no_start_command)
11
11
  return :no_start_command
12
12
  end
13
13
 
@@ -35,7 +35,7 @@ module Eye::Process::Commands
35
35
 
36
36
  :state_error
37
37
  end
38
-
38
+
39
39
  def stop_process
40
40
  debug 'stop_process command'
41
41
 
@@ -46,19 +46,15 @@ module Eye::Process::Commands
46
46
  if process_realy_running?
47
47
  warn 'NOT STOPPED, check command/signals, or tune stop_timeout/stop_grace, seems it was really soft'
48
48
 
49
- switch :unmonitoring
49
+ switch :unmonitoring, Eye::Reason.new(:'not stopped (soft command)')
50
50
  nil
51
51
 
52
52
  else
53
53
  switch :stopped
54
54
 
55
- if control_pid?
56
- info "delete pid_file: #{self[:pid_file_ex]}"
57
- clear_pid_file
58
- end
59
-
60
- true
55
+ clear_pid_file if self[:clear_pid] # by default for all
61
56
 
57
+ true
62
58
  end
63
59
 
64
60
  rescue StateMachine::InvalidTransition => e
@@ -73,7 +69,7 @@ module Eye::Process::Commands
73
69
 
74
70
  if self[:restart_command]
75
71
  execute_restart_command
76
- sleep self[:restart_timeout].to_f
72
+ sleep_grace(:restart_grace)
77
73
  result = check_alive_with_refresh_pid_if_needed
78
74
  switch(result ? :restarted : :crashed)
79
75
  else
@@ -106,7 +102,7 @@ private
106
102
  end
107
103
  end
108
104
 
109
- sleep self[:stop_grace].to_f
105
+ sleep_grace(:stop_grace)
110
106
 
111
107
  elsif self[:stop_signals]
112
108
  info "executing stop_signals #{self[:stop_signals].inspect}"
@@ -119,7 +115,7 @@ private
119
115
  delay = stop_signals.shift
120
116
  signal = stop_signals.shift
121
117
 
122
- if wait_for_condition(delay.to_f, 0.1){ !process_realy_running? }
118
+ if wait_for_condition(delay.to_f, 0.3){ !process_realy_running? }
123
119
  info 'has terminated'
124
120
  break
125
121
  end
@@ -127,17 +123,17 @@ private
127
123
  send_signal(signal)
128
124
  end
129
125
 
130
- sleep self[:stop_grace].to_f
126
+ sleep_grace(:stop_grace)
131
127
 
132
128
  else # default command
133
- info "executing: `kill -TERM #{self.pid}` with stop_grace: #{self[:stop_grace].to_f}s"
129
+ debug "executing: `kill -TERM #{self.pid}` with stop_grace: #{self[:stop_grace].to_f}s"
134
130
  send_signal(:TERM)
135
-
136
- sleep self[:stop_grace].to_f
131
+
132
+ sleep_grace(:stop_grace)
137
133
 
138
134
  # if process not die here, by default we force kill it
139
135
  if process_realy_running?
140
- warn "process not die after TERM and stop_grace #{self[:stop_grace].to_f}s, so send KILL"
136
+ warn "process not die after TERM and stop_grace #{self[:stop_grace].to_f}s, so send KILL(#{self.pid})"
141
137
  send_signal(:KILL)
142
138
  sleep 0.1 # little grace
143
139
  end
@@ -160,7 +156,7 @@ private
160
156
 
161
157
  res
162
158
  end
163
-
159
+
164
160
  def daemonize_process
165
161
  time_before = Time.now
166
162
  res = Eye::System.daemonize(self[:start_command], config)
@@ -169,15 +165,15 @@ private
169
165
  info "daemonizing: `#{self[:start_command]}` with start_grace: #{self[:start_grace].to_f}s, env: #{self[:environment].inspect}, working_dir: #{self[:working_dir]} (pid:#{res[:pid]})"
170
166
 
171
167
  if res[:error]
172
- error "raised with #{res[:error].inspect}"
173
-
174
168
  if res[:error].message == 'Permission denied - open'
175
- error 'seems stdout/err/all files is not writable'
169
+ error "raised with #{res[:error].inspect}, seems #{[self[:stdout], self[:stderr]]} files are not writable"
170
+ else
171
+ error "raised with #{res[:error].inspect}"
176
172
  end
177
173
 
178
174
  return {:error => res[:error].inspect}
179
175
  end
180
-
176
+
181
177
  self.pid = res[:pid]
182
178
 
183
179
  unless self.pid
@@ -185,7 +181,7 @@ private
185
181
  return {:error => :empty_pid}
186
182
  end
187
183
 
188
- sleep self[:start_grace].to_f
184
+ sleep_grace(:start_grace)
189
185
 
190
186
  unless process_realy_running?
191
187
  error "process with pid(#{self.pid}) not found, may be crashed (#{check_logs_str})"
@@ -193,9 +189,9 @@ private
193
189
  end
194
190
 
195
191
  unless failsafe_save_pid
196
- return {:error => :cant_write_pid}
192
+ return {:error => :cant_write_pid}
197
193
  end
198
-
194
+
199
195
  res
200
196
  end
201
197
 
@@ -204,13 +200,13 @@ private
204
200
  time_before = Time.now
205
201
 
206
202
  res = execute(self[:start_command], config.merge(:timeout => config[:start_timeout]))
207
- start_time = Time.now - time_before
203
+ start_time = Time.now - time_before
208
204
 
209
205
  if res[:error]
210
- error "raised with #{res[:error].inspect}"
211
-
212
206
  if res[:error].message == 'Permission denied - open'
213
- error 'seems stdout/err/all files is not writable'
207
+ error "raised with #{res[:error].inspect}, seems #{[self[:stdout], self[:stderr]]} files are not writable"
208
+ else
209
+ error "raised with #{res[:error].inspect}"
214
210
  end
215
211
 
216
212
  if res[:error].class == Timeout::Error
@@ -220,7 +216,7 @@ private
220
216
  return {:error => res[:error].inspect}
221
217
  end
222
218
 
223
- sleep self[:start_grace].to_f
219
+ sleep_grace(:start_grace)
224
220
 
225
221
  unless set_pid_from_file
226
222
  error "pid_file(#{self[:pid_file_ex]}) does not appears after start_grace #{self[:start_grace].to_f}, check start_command, or tune start_grace (eye dont know what to monitor without pid)"
@@ -241,8 +237,8 @@ private
241
237
  if !self[:stdout] && !self[:stderr]
242
238
  'maybe should add stdout/err/all logs'
243
239
  else
244
- "check also it stdout/err/all logs #{[self[:stdout], self[:stderr]].inspect}"
245
- end
240
+ "check also it stdout/err/all logs #{[self[:stdout], self[:stderr]]}"
241
+ end
246
242
  end
247
243
 
248
244
  def prepare_command(command)
@@ -253,4 +249,10 @@ private
253
249
  end
254
250
  end
255
251
 
252
+ def sleep_grace(grace_name)
253
+ grace = self[grace_name].to_f
254
+ info "sleeping for :#{grace_name} #{grace}"
255
+ sleep grace
256
+ end
257
+
256
258
  end
@@ -8,14 +8,15 @@ module Eye::Process::Config
8
8
  :stop_timeout => 10.seconds,
9
9
  :restart_timeout => 10.seconds,
10
10
 
11
- :start_grace => 2.5.seconds,
12
- :stop_grace => 0.5.seconds,
13
- :restart_grace => 0.5.seconds,
11
+ :start_grace => 2.5.seconds,
12
+ :stop_grace => 0.5.seconds,
13
+ :restart_grace => 1.second,
14
14
 
15
15
  :daemonize => false,
16
16
  :auto_start => true, # auto start on monitor action
17
17
 
18
- :childs_update_period => 30.seconds
18
+ :childs_update_period => 30.seconds,
19
+ :clear_pid => true # by default clear pid on stop
19
20
  }
20
21
 
21
22
  def prepare_config(new_config)
@@ -30,25 +31,26 @@ module Eye::Process::Config
30
31
  h[:triggers] ||= {}
31
32
  h[:triggers][:flapping] = {:type => :flapping, :times => 10, :within => 10.seconds}
32
33
  end
33
-
34
+
34
35
  h[:stdout] = Eye::System.normalized_file(h[:stdout], h[:working_dir]) if h[:stdout]
35
36
  h[:stderr] = Eye::System.normalized_file(h[:stderr], h[:working_dir]) if h[:stderr]
36
37
 
37
- h
38
+ h
38
39
  end
39
40
 
40
41
  def c(name)
41
42
  @config[name]
42
43
  end
43
-
44
+
44
45
  def [](name)
45
46
  @config[name]
46
47
  end
47
-
48
+
48
49
  def update_config(new_config = {})
49
50
  new_config = prepare_config(new_config)
50
51
  @config = new_config
51
52
  @full_name = nil
53
+ @logger = nil
52
54
 
53
55
  debug "update config to: #{@config.inspect}"
54
56
 
@@ -57,14 +59,17 @@ module Eye::Process::Config
57
59
 
58
60
  if up?
59
61
  # rebuild checks for this process
60
- from_up; on_up
61
- end
62
+ remove_watchers
63
+ remove_childs
64
+
65
+ add_watchers
66
+ add_childs
67
+ end
62
68
  end
63
69
 
64
70
  # is pid_file under Eye::Process control, or not
65
71
  def control_pid?
66
- return self[:control_pid] unless self[:control_pid].nil?
67
72
  !!self[:daemonize]
68
73
  end
69
-
74
+
70
75
  end
@@ -52,7 +52,7 @@ module Eye::Process::Controller
52
52
  def unmonitor
53
53
  switch :unmonitoring
54
54
  end
55
-
55
+
56
56
  def delete
57
57
  if self[:stop_on_delete]
58
58
  info 'process has stop_on_delete option, so sync-stop it first'
@@ -67,10 +67,7 @@ module Eye::Process::Controller
67
67
  end
68
68
 
69
69
  def signal(sig = 0)
70
- if self.pid
71
- res = send_signal(sig)
72
- info "send signal #{sig} to #{self.pid} = #{res}"
73
- end
70
+ send_signal(sig) if self.pid
74
71
  end
75
-
72
+
76
73
  end
@@ -1,8 +1,19 @@
1
1
  module Eye::Process::Data
2
2
 
3
- # logger tag
3
+ def logger_tag
4
+ full_name
5
+ end
6
+
7
+ def app_name
8
+ self[:application]
9
+ end
10
+
11
+ def group_name
12
+ (self[:group] == '__default__') ? nil : self[:group]
13
+ end
14
+
4
15
  def full_name
5
- @full_name ||= [self[:application], (self[:group] == '__default__') ? nil : self[:group], self[:name]].compact.join(':')
16
+ @full_name ||= [app_name, group_name, self[:name]].compact.join(':')
6
17
  end
7
18
 
8
19
  def status_data(debug = false)
@@ -19,8 +30,8 @@ module Eye::Process::Data
19
30
  end
20
31
 
21
32
  def self_status_data(debug = false)
22
- h = { name: name, state: state,
23
- type: (self.class == Eye::ChildProcess ? :child_process : :process),
33
+ h = { name: name, state: state,
34
+ type: (self.class == Eye::ChildProcess ? :child_process : :process),
24
35
  resources: Eye::SystemResources.resources(pid) }
25
36
 
26
37
  if @states_history
@@ -31,7 +42,7 @@ module Eye::Process::Data
31
42
  h.merge!(debug: debug_data) if debug
32
43
  h.merge!(current_command: current_scheduled_command) if current_scheduled_command
33
44
 
34
- h
45
+ h
35
46
  end
36
47
 
37
48
  def debug_data
@@ -15,7 +15,7 @@ private
15
15
  def try_update_pid_from_file
16
16
  # if pid file was rewrited
17
17
  newpid = load_pid_from_file
18
- if newpid != self.pid
18
+ if newpid != self.pid
19
19
  info "process changed pid to #{newpid}, updating..." if self.pid
20
20
  self.pid = newpid
21
21
 
@@ -23,7 +23,7 @@ private
23
23
  return true
24
24
  else
25
25
  warn "process with new_pid #{newpid} not found"
26
- return false
26
+ return false
27
27
  end
28
28
  else
29
29
  debug 'process not found'
@@ -32,7 +32,7 @@ private
32
32
  end
33
33
 
34
34
  REWRITE_FACKUP_PIDFILE_PERIOD = 2.minutes
35
-
35
+
36
36
  def check_alive
37
37
  if up?
38
38
 
@@ -40,11 +40,13 @@ private
40
40
  unless process_realy_running?
41
41
  warn "check_alive: process(#{self.pid}) not found!"
42
42
  notify :info, 'crashed!'
43
+ clear_pid_file if control_pid? && self.pid && load_pid_from_file == self.pid
44
+
43
45
  switch :crashed, Eye::Reason.new(:crashed)
44
46
  else
45
47
  # check that pid_file still here
46
48
  ppid = failsafe_load_pid
47
-
49
+
48
50
  if ppid != self.pid
49
51
  msg = "check_alive: pid_file(#{self[:pid_file]}) changes by itself (pid:#{self.pid}) => (pid:#{ppid})"
50
52
  if control_pid?
@@ -78,7 +80,12 @@ private
78
80
  if down?
79
81
  if self[:keep_alive]
80
82
  warn 'check crashed: process is down'
81
- schedule :restore, Eye::Reason.new(:crashed)
83
+
84
+ if self[:restore_in]
85
+ schedule_in self[:restore_in].to_f, :restore, Eye::Reason.new(:crashed)
86
+ else
87
+ schedule :restore, Eye::Reason.new(:crashed)
88
+ end
82
89
  else
83
90
  warn 'check crashed: process without keep_alive'
84
91
  schedule :unmonitor, Eye::Reason.new(:crashed)