eye 0.2 → 0.2.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b1592106a2ce3c26da8ad55f6b0036b10e5932fc
4
+ data.tar.gz: ff73a4ae40c4eeef57e9c50df0e50e8220eb187f
5
+ SHA512:
6
+ metadata.gz: b81127c859a0e48c6a08b7109582cddcdb12731b93eca191ae3fe65289703075e32c53f3552db24fcca3d693e61ada93ca7bfcae1bed4488724c54918ed6c880
7
+ data.tar.gz: 22e769a89a91343374045e502404f2fa26002f9a641807d4f772354774a3f88837cf486afb18da4880738548d9b8522f0c865e2b9c8ed2ae6cc9757d3da1ea78
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  Eye
2
2
  ===
3
3
 
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.
4
+ Process monitoring tool. An alternative to 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):
@@ -153,3 +153,8 @@ Quit monitoring:
153
153
  Config explain (for debug):
154
154
 
155
155
  $ eye explain examples/test.eye
156
+
157
+
158
+ ### Config api:
159
+
160
+ Waiting for pull requests ..., until that you can read `examples` and `spec/dsl` folders.
data/bin/eye CHANGED
@@ -266,13 +266,10 @@ private
266
266
  true
267
267
  end
268
268
 
269
- def wait_server
270
- Timeout.timeout(15) do
271
- while !server_started?
272
- sleep 0.3
273
- end
269
+ def wait_server(timeout = 15)
270
+ Timeout.timeout(timeout) do
271
+ sleep 0.3 while !server_started?
274
272
  end
275
-
276
273
  true
277
274
  rescue Timeout::Error
278
275
  false
data/examples/puma.eye ADDED
@@ -0,0 +1,34 @@
1
+ RUBY = '/usr/local/ruby/1.9.3-p392/bin/ruby'
2
+ RAILS_ENV = 'production'
3
+ ROOT = '/var/www/super_app'
4
+ CURRENT = File.expand_path(File.join(ROOT, %w{current}))
5
+ LOGS = File.expand_path(File.join(ROOT, %w{shared log}))
6
+ PIDS = File.expand_path(File.join(ROOT, %w{shared pids}))
7
+
8
+ Eye.config do
9
+ logger "#{LOGS}/eye.log"
10
+ logger_level Logger::ERROR
11
+ end
12
+
13
+ Eye.application :super_app do
14
+ env 'RAILS_ENV' => RAILS_ENV
15
+ working_dir CURRENT
16
+ triggers :flapping, :times => 10, :within => 1.minute
17
+
18
+ process :puma do
19
+ daemonize true
20
+ pid_file "#{PIDS}/puma.pid"
21
+ stdall "#{LOGS}/#{RAILS_ENV}.log"
22
+
23
+ start_command "#{RUBY} bin/puma --port 80 --pidfile #{PIDS}/puma.pid --environment #{RAILS_ENV} config.ru"
24
+ stop_command "kill -TERM {{PID}}"
25
+ restart_command "kill -USR2 {{PID}}"
26
+
27
+ start_timeout 15.seconds
28
+ stop_grace 10.seconds
29
+ restart_grace 10.seconds
30
+
31
+ checks :cpu, :every => 30, :below => 80, :times => 3
32
+ checks :memory, :every => 30, :below => 70.megabytes, :times => [3,5]
33
+ end
34
+ end
data/eye.gemspec CHANGED
@@ -5,7 +5,7 @@ Gem::Specification.new do |gem|
5
5
  gem.email = "kostya27@gmail.com"
6
6
 
7
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.}
8
+ %q{Process monitoring tool. An alternative to God and Bluepill. With Bluepill like config syntax. Requires MRI Ruby >= 1.9.2. Uses Celluloid and Celluloid::IO.}
9
9
  gem.homepage = "http://github.com/kostya/eye"
10
10
 
11
11
  gem.files = `git ls-files`.split($\)
data/lib/eye.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Eye
2
- VERSION = "0.2"
2
+ VERSION = "0.2.1"
3
3
  ABOUT = "Eye v#{VERSION} (c) 2012-2013 @kostya"
4
4
 
5
5
  autoload :Process, 'eye/process'
@@ -22,5 +22,4 @@ module Eye
22
22
  autoload :Control, 'eye/control'
23
23
  end
24
24
 
25
- ROOT_BINDING = binding
26
- ENV_LANG = ENV['LANG'] # save original LANG, because ruby somehow rewrite it
25
+ ENV_LANG = ENV['LANG'] # save original LANG, bug of celluloid 0.12
@@ -24,6 +24,11 @@ class Eye::Application
24
24
  @groups << group
25
25
  end
26
26
 
27
+ # sort processes in name order
28
+ def resort_groups
29
+ @groups = @groups.sort_by{|gr| gr.name == '__default__' ? 'zzzzz' : gr.name }
30
+ end
31
+
27
32
  def status_data(debug = false)
28
33
  h = { name: @name, type: :application, subtree: @groups.map{|gr| gr.status_data(debug) }}
29
34
  h.merge!(debug: debug_data) if debug
data/lib/eye/checker.rb CHANGED
@@ -51,6 +51,7 @@ class Eye::Checker
51
51
  end
52
52
 
53
53
  def check
54
+ tm1 = Time.now
54
55
  @value = get_value
55
56
  @values << {:value => @value, :good => good?(value)}
56
57
 
@@ -61,7 +62,7 @@ class Eye::Checker
61
62
  result = false if bad_count >= min_tries
62
63
  end
63
64
 
64
- info "#{last_human_values} => #{result ? 'OK' : 'Fail'}"
65
+ info "#{last_human_values} => #{result ? 'OK' : 'Fail'} (#{Time.now - tm1}s)"
65
66
  result
66
67
  end
67
68
 
@@ -12,7 +12,7 @@ class Eye::Checker::Http < Eye::Checker
12
12
  param :open_timeout, [Fixnum, Float]
13
13
  param :read_timeout, [Fixnum, Float]
14
14
 
15
- attr_reader :session, :uri
15
+ attr_reader :uri
16
16
 
17
17
  def check_name
18
18
  'http'
@@ -29,49 +29,49 @@ class Eye::Checker::Http < Eye::Checker
29
29
  else
30
30
  Net::HTTPSuccess
31
31
  end
32
- @open_timeout = (open_timeout || timeout || 5).to_i
33
- @read_timeout = (read_timeout || timeout || 30).to_i
34
-
35
- @session = Net::HTTP.new(@uri.host, @uri.port)
36
- if @uri.scheme == 'https'
37
- require 'net/https'
38
- @session.use_ssl=true
39
- @session.verify_mode = OpenSSL::SSL::VERIFY_NONE
40
- end
41
- @session.open_timeout = @open_timeout
42
- @session.read_timeout = @read_timeout
32
+ @open_timeout = (open_timeout || 3).to_f
33
+ @read_timeout = (read_timeout || timeout || 15).to_f
43
34
  end
44
-
35
+
45
36
  def get_value
46
37
  Celluloid::Future.new{ get_value_sync }.value
47
38
  end
48
39
 
49
40
  def get_value_sync
50
- res = @session.start do |http|
51
- http.get(@uri.path)
52
- end
53
-
41
+ _session = session
42
+ res = _session.start{ |http| http.get(@uri.path) }
54
43
  {:result => res}
55
44
 
56
- rescue Timeout::Error
57
- debug 'Timeout error'
58
- {:exception => :timeout}
45
+ rescue Timeout::Error => ex
46
+ debug ex.inspect
47
+
48
+ if defined?(Net::OpenTimeout) # for ruby 2.0
49
+ mes = ex.class.is_a?(Net::OpenTimeout) ? "OpenTimeout<#{@open_timeout}>" : "ReadTimeout<#{@read_timeout}>"
50
+ {:exception => mes}
51
+ else
52
+ error "#{ex.message}"
53
+ {:exception => "Timeout<#{@open_timeout},#{@read_timeout}>"}
54
+ end
59
55
 
60
56
  rescue => ex
61
- error "Exception #{ex.message}"
62
- {:exception => ex.message}
57
+ {:exception => "Error<#{ex.message}>"}
63
58
  end
64
59
 
65
60
  def good?(value)
66
61
  return false unless value[:result]
67
- return false unless value[:result].kind_of?(@kind)
62
+
63
+ unless value[:result].kind_of?(@kind)
64
+ return false
65
+ end
68
66
 
69
67
  if @pattern
70
- if @pattern.is_a?(Regexp)
68
+ matched = if @pattern.is_a?(Regexp)
71
69
  @pattern === value[:result].body
72
70
  else
73
71
  value[:result].body.include?(@pattern.to_s)
74
72
  end
73
+ value[:notice] = "missing '#{@pattern.to_s}'" unless matched
74
+ matched
75
75
  else
76
76
  true
77
77
  end
@@ -81,13 +81,26 @@ class Eye::Checker::Http < Eye::Checker
81
81
  if !value.is_a?(Hash)
82
82
  '-'
83
83
  elsif value[:exception]
84
- if value[:exception] == :timeout
85
- 'T-out'
86
- else
87
- 'Err'
88
- end
84
+ value[:exception]
89
85
  else
90
- "#{value[:result].code}=#{value[:result].body.size/ 1024}Kb"
86
+ body_size = value[:result].body.size / 1024
87
+ msg = "#{value[:result].code}=#{body_size}Kb"
88
+ msg += "<#{value[:notice]}>" if value[:notice]
89
+ msg
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ def session
96
+ Net::HTTP.new(@uri.host, @uri.port).tap do |session|
97
+ if @uri.scheme == 'https'
98
+ require 'net/https'
99
+ session.use_ssl=true
100
+ session.verify_mode = OpenSSL::SSL::VERIFY_NONE
101
+ end
102
+ session.open_timeout = @open_timeout
103
+ session.read_timeout = @read_timeout
91
104
  end
92
105
  end
93
106
 
@@ -26,8 +26,8 @@ class Eye::Checker::Socket < Eye::Checker
26
26
 
27
27
  def initialize(*args)
28
28
  super
29
- @open_timeout = (open_timeout || 1).to_i
30
- @read_timeout = (read_timeout || timeout || 5).to_i
29
+ @open_timeout = (open_timeout || 1).to_f
30
+ @read_timeout = (read_timeout || timeout || 5).to_f
31
31
 
32
32
  if addr =~ %r[\Atcp://(.*?):(.*?)\z]
33
33
  @socket_family = :tcp
@@ -44,32 +44,29 @@ class Eye::Checker::Socket < Eye::Checker
44
44
  end
45
45
 
46
46
  def get_value_sync
47
- sock = Timeout::timeout(@open_timeout) do
48
- if @socket_family == :tcp
49
- TCPSocket.open(@socket_addr, @socket_port)
50
- elsif @socket_family == :unix
51
- UNIXSocket.open(@socket_path)
52
- else
53
- raise "Unknown socket addr #{addr}"
54
- end
47
+ sock = begin
48
+ Timeout::timeout(@open_timeout){ open_socket }
49
+ rescue Timeout::Error
50
+ return { :exception => "OpenTimeout<#{@open_timeout}>" }
55
51
  end
56
52
 
57
53
  if send_data
58
- Timeout::timeout(@read_timeout) do
59
- _write_data(sock, send_data)
60
- { :result => _read_data(sock) }
54
+ begin
55
+ Timeout::timeout(@read_timeout) do
56
+ _write_data(sock, send_data)
57
+ result = _read_data(sock)
58
+
59
+ { :result => result }
60
+ end
61
+ rescue Timeout::Error
62
+ return { :exception => "ReadTimeout<#{@read_timeout}>" }
61
63
  end
62
64
  else
63
65
  { :result => :listen }
64
66
  end
65
67
 
66
- rescue Timeout::Error
67
- debug 'Timeout error'
68
- { :exception => :timeout }
69
-
70
68
  rescue Exception => e
71
- warn "Exception #{e.message}"
72
- { :exception => e.message }
69
+ { :exception => "Error<#{e.message}>" }
73
70
 
74
71
  ensure
75
72
  sock.close if sock
@@ -83,18 +80,25 @@ class Eye::Checker::Socket < Eye::Checker
83
80
  match = begin
84
81
  !!expect_data[value[:result]]
85
82
  rescue Timeout::Error, Exception => ex
86
- error "proc match failed with #{ex.message}"
83
+ mes = "proc match failed with '#{ex.message}'"
84
+ error(mes)
85
+ value[:notice] = mes
87
86
  return false
88
87
  end
89
88
 
90
- warn "proc #{expect_data} not matched (#{value[:result].truncate(30)}) answer" unless match
89
+ unless match
90
+ warn "proc #{expect_data} not matched (#{value[:result].truncate(30)}) answer"
91
+ value[:notice] = "missing proc validation"
92
+ end
93
+
91
94
  return match
92
95
  end
93
96
 
94
97
  return true if expect_data.is_a?(Regexp) && expect_data.match(value[:result])
95
98
  return true if value[:result].to_s == expect_data.to_s
96
99
 
97
- warn "#{expect_data} not matched (#{value[:result].truncate(30)}) answer"
100
+ warn "#{expect_data} not matched (#{value[:result].truncate(30)}) answer"
101
+ value[:notice] = "missing '#{expect_data.to_s}'"
98
102
  return false
99
103
  end
100
104
 
@@ -105,22 +109,30 @@ class Eye::Checker::Socket < Eye::Checker
105
109
  if !value.is_a?(Hash)
106
110
  '-'
107
111
  elsif value[:exception]
108
- if value[:exception] == :timeout
109
- 'T-out'
110
- else
111
- "Err(#{value[:exception]})"
112
- end
112
+ value[:exception]
113
113
  else
114
114
  if value[:result] == :listen
115
115
  "listen"
116
116
  else
117
- "#{value[:result].to_s.size}b"
117
+ res = "#{value[:result].to_s.size}b"
118
+ res += "<#{value[:notice]}>" if value[:notice]
119
+ res
118
120
  end
119
121
  end
120
122
  end
121
123
 
122
124
  private
123
125
 
126
+ def open_socket
127
+ if @socket_family == :tcp
128
+ TCPSocket.open(@socket_addr, @socket_port)
129
+ elsif @socket_family == :unix
130
+ UNIXSocket.open(@socket_path)
131
+ else
132
+ raise "Unknown socket addr #{addr}"
133
+ end
134
+ end
135
+
124
136
  def _write_data(socket, data)
125
137
  case protocol
126
138
  when :em_object
@@ -50,6 +50,10 @@ class Eye::ChildProcess
50
50
  :up
51
51
  end
52
52
 
53
+ def up?
54
+ state == :up
55
+ end
56
+
53
57
  def send_command(command, *args)
54
58
  schedule command, *args, "#{command} by user"
55
59
  end
@@ -38,6 +38,8 @@ class Eye::Controller
38
38
  Celluloid::logger = Eye.logger
39
39
 
40
40
  Eye::SystemResources.setup
41
+
42
+ info "starting #{Eye::ABOUT}"
41
43
  end
42
44
 
43
45
  end
@@ -183,6 +183,8 @@ private
183
183
  @added_groups = nil
184
184
  @added_processes = nil
185
185
 
186
+ app.resort_groups
187
+
186
188
  app
187
189
  end
188
190
 
data/lib/eye/dsl.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require_relative 'dsl/helpers'
2
2
 
3
+ Eye::BINDING = binding
4
+
3
5
  class Eye::Dsl
4
6
 
5
7
  autoload :Main, 'eye/dsl/main'
@@ -34,7 +36,7 @@ class Eye::Dsl
34
36
  content = File.read(filename) if content.blank?
35
37
 
36
38
  silence_warnings do
37
- Kernel.eval(content, ROOT_BINDING, filename.to_s)
39
+ Kernel.eval(content, Eye::BINDING, filename.to_s)
38
40
  end
39
41
 
40
42
  validate(Eye.parsed_config)
data/lib/eye/dsl/opts.rb CHANGED
@@ -92,4 +92,13 @@ class Eye::Dsl::Opts < Eye::Dsl::PureOpts
92
92
  set_stderr value
93
93
  end
94
94
 
95
+ def scoped(&block)
96
+ h = self.class.new(self.name, self)
97
+ h.instance_eval(&block)
98
+ groups = h.config.delete :groups
99
+ processes = h.config.delete :processes
100
+ self.config[:groups].merge!(groups) if groups.present?
101
+ self.config[:processes].merge!(processes) if processes.present?
102
+ end
103
+
95
104
  end
data/lib/eye/process.rb CHANGED
@@ -58,7 +58,7 @@ class Eye::Process
58
58
  # add_watchers, remove_watchers:
59
59
  include Eye::Process::Watchers
60
60
 
61
- # check alive, crush methods:
61
+ # check alive, crash methods:
62
62
  include Eye::Process::Monitor
63
63
 
64
64
  # system methods:
@@ -25,7 +25,7 @@ module Eye::Process::Commands
25
25
  end
26
26
 
27
27
  self.pid = nil
28
- switch :crushed
28
+ switch :crashed
29
29
  end
30
30
 
31
31
  result
@@ -75,7 +75,7 @@ module Eye::Process::Commands
75
75
  execute_restart_command
76
76
  sleep self[:restart_timeout].to_f
77
77
  result = check_alive_with_refresh_pid_if_needed
78
- switch(result ? :restarted : :crushed)
78
+ switch(result ? :restarted : :crashed)
79
79
  else
80
80
  stop_process
81
81
  start_process
@@ -166,8 +166,7 @@ private
166
166
  res = Eye::System.daemonize(self[:start_command], config)
167
167
  start_time = Time.now - time_before
168
168
 
169
- info "daemonizing: `#{self[:start_command]}` with start_grace: #{self[:start_grace].to_f}s, env: #{self[:environment].inspect}, working_dir: #{self[:working_dir]}"
170
-
169
+ 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]})"
171
170
 
172
171
  if res[:error]
173
172
  error "raised with #{res[:error].inspect}"
@@ -189,7 +188,7 @@ private
189
188
  sleep self[:start_grace].to_f
190
189
 
191
190
  unless process_realy_running?
192
- error "process with pid (#{self.pid}) not found, may be crushed (#{check_logs_str})"
191
+ error "process with pid (#{self.pid}) not found, may be crashed (#{check_logs_str})"
193
192
  return {:error => :not_realy_running}
194
193
  end
195
194
 
@@ -232,11 +231,12 @@ private
232
231
  end
233
232
 
234
233
  unless process_realy_running?
235
- error "process in pid_file(#{self[:pid_file]})(#{self.pid}) not found, maybe process do not write there actual pid, or just crushed (#{check_logs_str})"
234
+ error "process in pid_file(#{self[:pid_file]})(#{self.pid}) not found, maybe process do not write there actual pid, or just crashed (#{check_logs_str})"
236
235
  return {:error => :not_realy_running}
237
236
  end
238
237
 
239
238
  res[:pid] = self.pid
239
+ info "process get pid:#{res[:pid]}"
240
240
  res
241
241
  end
242
242
 
@@ -1,7 +1,7 @@
1
1
  module Eye::Process::Config
2
2
 
3
3
  DEFAULTS = {
4
- :keep_alive => true, # restart when crushed
4
+ :keep_alive => true, # restart when crashed
5
5
  :check_alive_period => 5.seconds,
6
6
 
7
7
  :start_timeout => 15.seconds,
@@ -39,16 +39,16 @@ private
39
39
  # check that process runned
40
40
  unless process_realy_running?
41
41
  warn 'check_alive: process not found'
42
- notify :warn, 'crushed!'
43
- switch :crushed
42
+ notify :warn, 'crashed!'
43
+ switch :crashed
44
44
  else
45
45
  # check that pid_file still here
46
46
  ppid = failsafe_load_pid
47
47
 
48
48
  if ppid != self.pid
49
- msg = "check_alive: pid_file(#{self[:pid_file]}) changes by itself (#{self.pid}) => (#{ppid})"
49
+ msg = "check_alive: pid_file(#{self[:pid_file]}) changes by itself (pid:#{self.pid}) => (pid:#{ppid})"
50
50
  if control_pid?
51
- msg += ", not correct, pid_file is under eye control, so rewrited back #{self.pid}"
51
+ msg += ", not correct, pid_file is under eye control, so rewrited back pid:#{self.pid}"
52
52
  save_pid_to_file rescue msg += ', (Can`t rewrite pid_file O_o)'
53
53
  else
54
54
  if ppid == nil
@@ -80,18 +80,18 @@ private
80
80
  pid
81
81
  end
82
82
 
83
- def check_crush
83
+ def check_crash
84
84
  if down?
85
85
  if self[:keep_alive] && !@flapping
86
- warn 'check crushed: process is down'
87
- schedule :start, 'crushed'
86
+ warn 'check crashed: process is down'
87
+ schedule :start, 'crashed'
88
88
  else
89
- warn 'check crushed: process is down, and flapping happens (or not keep_alive option)'
89
+ warn 'check crashed: process is down, and flapping happens (or not keep_alive option)'
90
90
  schedule :unmonitor, 'flapping'
91
91
  end
92
92
  end
93
93
  end
94
94
 
95
- public :check_crush # bug of celluloid 0.12
95
+ public :check_crash # bug of celluloid 0.12
96
96
 
97
97
  end
@@ -1,7 +1,7 @@
1
1
  module Eye::Process::Notify
2
2
 
3
3
  # notify to user:
4
- # 1) process crushed by itself, and we restart it [:warn]
4
+ # 1) process crashed by itself, and we restart it [:warn]
5
5
  # 2) checker bounded to restart process [:crit]
6
6
  # 3) flapping + switch to unmonitored [:crit]
7
7
 
@@ -25,7 +25,7 @@ class Eye::Process
25
25
  transition :starting => :up
26
26
  end
27
27
 
28
- event :crushed do
28
+ event :crashed do
29
29
  transition [:starting, :restarting, :up] => :down
30
30
  end
31
31
 
@@ -53,7 +53,7 @@ class Eye::Process
53
53
  transition any => :unmonitored
54
54
  end
55
55
 
56
- after_transition :on => :crushed, :do => :on_crushed
56
+ after_transition :on => :crashed, :do => :on_crashed
57
57
  after_transition any => :unmonitored, :do => :on_unmonitored
58
58
  after_transition any-:up => :up, :do => :on_up
59
59
  after_transition :up => any-:up, :do => :from_up
@@ -61,8 +61,8 @@ class Eye::Process
61
61
  after_transition any => any, :do => :upd_for_triggers
62
62
  end
63
63
 
64
- def on_crushed
65
- schedule :check_crush, 'crushed'
64
+ def on_crashed
65
+ schedule :check_crash, 'crashed'
66
66
  end
67
67
 
68
68
  def on_unmonitored
@@ -59,6 +59,8 @@ private
59
59
 
60
60
  def watcher_tick(subject)
61
61
  unless subject.check
62
+ return unless up?
63
+
62
64
  notify :crit, "Bounded #{subject.check_name}: #{subject.last_human_values}"
63
65
  schedule :restart, "bounded #{subject.check_name}"
64
66
  end
data/lib/eye/utils.rb CHANGED
@@ -10,5 +10,5 @@ module Eye::Utils
10
10
  else value
11
11
  end
12
12
  end
13
-
13
+
14
14
  end
@@ -18,7 +18,7 @@ describe "Eye::Checker::Http" do
18
18
  it "initialize" do
19
19
  subject.instance_variable_get(:@kind).should == Net::HTTPSuccess
20
20
  subject.instance_variable_get(:@pattern).should == /OK/
21
- subject.instance_variable_get(:@open_timeout).should == 2
21
+ subject.instance_variable_get(:@open_timeout).should == 3
22
22
  subject.instance_variable_get(:@read_timeout).should == 2
23
23
  end
24
24
 
@@ -34,17 +34,22 @@ describe "Eye::Checker::Http" do
34
34
  end
35
35
 
36
36
  it "get_value exception" do
37
+ a = ""
38
+ stub(subject).session{ a }
37
39
  stub(subject.session).start{ raise Timeout::Error, "timeout" }
38
- subject.get_value.should == {:exception => :timeout}
40
+ mes = RUBY_VERSION < '2.0' ? "Timeout<3.0,2.0>" : "ReadTimeout<2.0>"
39
41
 
40
- subject.human_value(subject.get_value).should == "T-out"
42
+ subject.get_value.should == {:exception => mes}
43
+ subject.human_value(subject.get_value).should == mes
41
44
  end
42
45
 
43
46
  it "get_value raised" do
47
+ a = ""
48
+ stub(subject).session{ a }
44
49
  stub(subject.session).start{ raise "something" }
45
- subject.get_value.should == {:exception => "something"}
50
+ subject.get_value.should == {:exception => "Error<something>"}
46
51
 
47
- subject.human_value(subject.get_value).should == "Err"
52
+ subject.human_value(subject.get_value).should == "Error<something>"
48
53
  end
49
54
 
50
55
  end
@@ -30,7 +30,7 @@ describe "Socket Checker" do
30
30
 
31
31
  it "timeouted" do
32
32
  c = chsock(:addr => addr, :send_data => "timeout")
33
- c.get_value.should == {:exception => :timeout}
33
+ c.get_value.should == {:exception => "ReadTimeout<2.0>"}
34
34
  c.check.should == false
35
35
  end
36
36
 
@@ -47,7 +47,7 @@ describe "Socket Checker" do
47
47
  @process.stop
48
48
  c = chsock(:addr => addr.chop)
49
49
  if addr =~ /tcp/
50
- c.get_value.should == {:exception => "Connection refused - connect(2)"}
50
+ c.get_value.should == {:exception => "Error<Connection refused - connect(2)>"}
51
51
  else
52
52
  c.get_value[:exception].should include("No such file or directory")
53
53
  end
@@ -63,7 +63,7 @@ describe "Socket Checker" do
63
63
  it "check responding without send_data" do
64
64
  c = chsock(:addr => addr.chop, :send_data => nil, :expect_data => nil)
65
65
  if addr =~ /tcp/
66
- c.get_value.should == {:exception => "Connection refused - connect(2)"}
66
+ c.get_value.should == {:exception => "Error<Connection refused - connect(2)>"}
67
67
  else
68
68
  c.get_value[:exception].should include("No such file or directory")
69
69
  end
@@ -10,10 +10,12 @@ def app_check(app, name, gr_size)
10
10
  app.name.should == name
11
11
  app.class.should == Eye::Application
12
12
  app.groups.size.should == gr_size
13
+ app.groups.class.should == Eye::Utils::AliveArray
13
14
  end
14
15
 
15
16
  def gr_check(gr, name, p_size, hidden = false)
16
17
  gr.class.should == Eye::Group
18
+ gr.processes.class.should == Eye::Utils::AliveArray
17
19
  gr.processes.size.should == p_size
18
20
  gr.name.should == name
19
21
  gr.hidden.should == hidden
data/spec/dsl/dsl_spec.rb CHANGED
@@ -69,5 +69,5 @@ describe "Eye::Dsl" do
69
69
  cfg['bla'][:groups]['__default__'][:some].should == nil
70
70
  cfg['bla'][:groups]['__default__'][:processes][:some].should == nil
71
71
  end
72
-
72
+
73
73
  end
@@ -308,4 +308,69 @@ describe "Eye::Dsl" do
308
308
  :environment=>{"P"=>"1"}}}}}
309
309
  end
310
310
 
311
+
312
+ describe "scoped" do
313
+ it "scoped" do
314
+ conf = <<-E
315
+ Eye.application("bla") do
316
+ group :gr do
317
+ env "A" => '1', "B" => '2'
318
+
319
+ process :a do
320
+ pid_file "1.pid"
321
+ end
322
+
323
+ scoped do
324
+ env "A" => '2'
325
+
326
+ process :b do
327
+ pid_file "2.pid"
328
+ end
329
+ end
330
+
331
+ process :c do
332
+ pid_file "3.pid"
333
+ end
334
+ end
335
+ end
336
+ E
337
+
338
+ Eye::Dsl.parse_apps(conf).should == {
339
+ "bla" => {:name=>"bla", :groups=>{
340
+ "gr"=>{:name=>"gr", :application=>"bla", :environment=>{"A"=>"1", "B"=>"2"},
341
+ :processes=>{
342
+ "a"=>{:name=>"a", :application=>"bla", :environment=>{"A"=>"1", "B"=>"2"}, :group=>"gr", :pid_file=>"1.pid"},
343
+ "b"=>{:name=>"b", :application=>"bla", :environment=>{"A"=>"2", "B"=>"2"}, :group=>"gr", :pid_file=>"2.pid"},
344
+ "c"=>{:name=>"c", :application=>"bla", :environment=>{"A"=>"1", "B"=>"2"}, :group=>"gr", :pid_file=>"3.pid"}}}}}}
345
+ end
346
+
347
+ it "scoped" do
348
+ conf = <<-E
349
+ Eye.application("bla") do
350
+ start_timeout 10.seconds
351
+
352
+ group(:a){}
353
+
354
+ scoped do
355
+ start_timeout 15.seconds
356
+ group(:b){
357
+ scoped do
358
+
359
+ end
360
+ }
361
+ end
362
+
363
+ group(:c){}
364
+ end
365
+ E
366
+
367
+ Eye::Dsl.parse_apps(conf).should == {
368
+ "bla" => {:name=>"bla", :start_timeout=>10, :groups=>{
369
+ "a"=>{:name=>"a", :start_timeout=>10, :application=>"bla", :processes=>{}},
370
+ "b"=>{:name=>"b", :start_timeout=>15, :application=>"bla", :processes=>{}},
371
+ "c"=>{:name=>"c", :start_timeout=>10, :application=>"bla", :processes=>{}}}}}
372
+ end
373
+
374
+ end
375
+
311
376
  end
@@ -124,7 +124,7 @@ describe "Process Controller" do
124
124
  end
125
125
  end
126
126
 
127
- describe "process cant start, crush each time" do
127
+ describe "process cant start, crash each time" do
128
128
  before :each do
129
129
  @process = process(C.p2.merge(:start_command => C.p2[:start_command] + " -r" ))
130
130
  @process.send_command :start
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/../spec_helper'
3
3
  describe "Process Monitoring" do
4
4
 
5
5
  [C.p1, C.p2].each do |cfg|
6
- it "process crushed, should restart #{cfg[:name]}" do
6
+ it "process crashed, should restart #{cfg[:name]}" do
7
7
  start_ok_process(cfg)
8
8
  old_pid = @pid
9
9
 
@@ -6,7 +6,7 @@ describe "Process Restart" do
6
6
  start_ok_process(cfg)
7
7
  old_pid = @pid
8
8
 
9
- dont_allow(@process).check_crush
9
+ dont_allow(@process).check_crash
10
10
  @process.restart
11
11
 
12
12
  @process.pid.should_not == old_pid
@@ -26,7 +26,7 @@ describe "Process Restart" do
26
26
  start_ok_process(cfg.merge(:stop_command => "kill -9 {{PID}}"))
27
27
  old_pid = @pid
28
28
 
29
- dont_allow(@process).check_crush
29
+ dont_allow(@process).check_crash
30
30
  @process.restart
31
31
 
32
32
  @process.pid.should_not == old_pid
@@ -46,7 +46,7 @@ describe "Process Restart" do
46
46
  start_ok_process(cfg.merge(:restart_command => "kill -USR1 {{PID}}"))
47
47
  old_pid = @pid
48
48
 
49
- dont_allow(@process).check_crush
49
+ dont_allow(@process).check_crash
50
50
  @process.restart
51
51
 
52
52
  sleep 3
@@ -71,7 +71,7 @@ describe "Process Restart" do
71
71
  start_ok_process(cfg.merge(:stop_command => "kill -USR1 {{PID}}"))
72
72
  old_pid = @pid
73
73
 
74
- dont_allow(@process).check_crush
74
+ dont_allow(@process).check_crash
75
75
  @process.restart
76
76
 
77
77
  sleep 3
@@ -93,7 +93,7 @@ describe "Process Restart" do
93
93
  # so monitor should see that process died, and up it
94
94
  start_ok_process(cfg.merge(:restart_command => "kill -9 {{PID}}"))
95
95
 
96
- mock(@process).check_crush
96
+ mock(@process).check_crash
97
97
 
98
98
  @process.restart
99
99
  Eye::System.pid_alive?(@pid).should == false
@@ -103,7 +103,7 @@ describe "Process Restart" do
103
103
  it "Bad restart command, invalid" do
104
104
  start_ok_process(cfg.merge(:restart_command => "asdfasdf sdf asd fasdf asdf"))
105
105
 
106
- dont_allow(@process).check_crush
106
+ dont_allow(@process).check_crash
107
107
 
108
108
  @process.restart
109
109
  Eye::System.pid_alive?(@pid).should == true
@@ -133,7 +133,7 @@ describe "Process Restart" do
133
133
  @process.state = st.to_s
134
134
  old_pid = @pid
135
135
 
136
- dont_allow(@process).check_crush
136
+ dont_allow(@process).check_crash
137
137
  @process.restart
138
138
 
139
139
  @process.pid.should_not == old_pid
@@ -69,7 +69,7 @@ describe "Process Start" do
69
69
  @process.state_name.should == :up
70
70
  end
71
71
 
72
- it "process crushed, with config #{c[:name]}" do
72
+ it "process crashed, with config #{c[:name]}" do
73
73
  @process = process(c.merge(:start_command => c[:start_command] + " -r" ))
74
74
  @process.start.should == {:error=>:not_realy_running}
75
75
 
@@ -89,7 +89,7 @@ describe "Process Start" do
89
89
 
90
90
  it "start with invalid command" do
91
91
  @process = process(c.merge(:start_command => "asdf asdf1 r f324 f324f 32f44f"))
92
- mock(@process).check_crush
92
+ mock(@process).check_crash
93
93
  res = @process.start
94
94
  res.should == {:error=>"#<Errno::ENOENT: No such file or directory - asdf>"}
95
95
 
@@ -103,7 +103,7 @@ describe "Process Start" do
103
103
 
104
104
  it "start PROBLEM with stdout permissions" do
105
105
  @process = process(c.merge(:stdout => "/var/run/1.log"))
106
- mock(@process).check_crush
106
+ mock(@process).check_crash
107
107
  res = @process.start
108
108
  res.should == {:error=>"#<Errno::EACCES: Permission denied - open>"}
109
109
 
@@ -117,7 +117,7 @@ describe "Process Start" do
117
117
 
118
118
  it "start PROBLEM binary permissions" do
119
119
  @process = process(c.merge(:start_command => "./sample.rb"))
120
- mock(@process).check_crush
120
+ mock(@process).check_crash
121
121
  res = @process.start
122
122
  res.should == {:error=>"#<Errno::EACCES: Permission denied - ./sample.rb>"}
123
123
 
@@ -40,7 +40,7 @@ describe "Process Stop" do
40
40
  it "stop process by default command" do
41
41
  start_ok_process
42
42
 
43
- dont_allow(@process).check_crush
43
+ dont_allow(@process).check_crash
44
44
  @process.stop_process
45
45
 
46
46
  Eye::System.pid_alive?(@pid).should == false
@@ -54,7 +54,7 @@ describe "Process Stop" do
54
54
  start_ok_process(C.p2.merge(:start_command => C.p2[:start_command] + " -T"))
55
55
  Eye::System.pid_alive?(@pid).should == true
56
56
 
57
- dont_allow(@process).check_crush
57
+ dont_allow(@process).check_crash
58
58
  @process.stop_process
59
59
 
60
60
  Eye::System.pid_alive?(@pid).should == false
@@ -67,7 +67,7 @@ describe "Process Stop" do
67
67
  it "stop process by specific command" do
68
68
  start_ok_process(C.p1.merge(:stop_command => "kill -9 {{PID}}"))
69
69
 
70
- dont_allow(@process).check_crush
70
+ dont_allow(@process).check_crash
71
71
  @process.stop_process
72
72
 
73
73
  Eye::System.pid_alive?(@pid).should == false
@@ -64,7 +64,7 @@ describe "Flapping" do
64
64
  @process.start!
65
65
 
66
66
  proxy(@process).schedule(:start, anything)
67
- proxy(@process).schedule(:check_crush, anything)
67
+ proxy(@process).schedule(:check_crash, anything)
68
68
  dont_allow(@process).schedule(:unmonitor)
69
69
 
70
70
 
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eye
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.2'
5
- prerelease:
4
+ version: 0.2.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Konstantin Makarchev
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-03-09 00:00:00.000000000 Z
11
+ date: 2013-03-17 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: celluloid
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: celluloid-io
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
@@ -46,23 +41,20 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: state_machine
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: activesupport
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
59
  - - ~>
68
60
  - !ruby/object:Gem::Version
@@ -70,7 +62,6 @@ dependencies:
70
62
  type: :runtime
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
66
  - - ~>
76
67
  - !ruby/object:Gem::Version
@@ -78,196 +69,172 @@ dependencies:
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: thor
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - '>='
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0'
86
76
  type: :runtime
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - '>='
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0'
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: rake
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ! '>='
87
+ - - '>='
100
88
  - !ruby/object:Gem::Version
101
89
  version: '0'
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ! '>='
94
+ - - '>='
108
95
  - !ruby/object:Gem::Version
109
96
  version: '0'
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: rspec
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
- - - ! '>='
101
+ - - '>='
116
102
  - !ruby/object:Gem::Version
117
103
  version: '0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
- - - ! '>='
108
+ - - '>='
124
109
  - !ruby/object:Gem::Version
125
110
  version: '0'
126
111
  - !ruby/object:Gem::Dependency
127
112
  name: simplecov
128
113
  requirement: !ruby/object:Gem::Requirement
129
- none: false
130
114
  requirements:
131
- - - ! '>='
115
+ - - '>='
132
116
  - !ruby/object:Gem::Version
133
117
  version: '0'
134
118
  type: :development
135
119
  prerelease: false
136
120
  version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
121
  requirements:
139
- - - ! '>='
122
+ - - '>='
140
123
  - !ruby/object:Gem::Version
141
124
  version: '0'
142
125
  - !ruby/object:Gem::Dependency
143
126
  name: rr
144
127
  requirement: !ruby/object:Gem::Requirement
145
- none: false
146
128
  requirements:
147
- - - ! '>='
129
+ - - '>='
148
130
  - !ruby/object:Gem::Version
149
131
  version: '0'
150
132
  type: :development
151
133
  prerelease: false
152
134
  version_requirements: !ruby/object:Gem::Requirement
153
- none: false
154
135
  requirements:
155
- - - ! '>='
136
+ - - '>='
156
137
  - !ruby/object:Gem::Version
157
138
  version: '0'
158
139
  - !ruby/object:Gem::Dependency
159
140
  name: ruby-graphviz
160
141
  requirement: !ruby/object:Gem::Requirement
161
- none: false
162
142
  requirements:
163
- - - ! '>='
143
+ - - '>='
164
144
  - !ruby/object:Gem::Version
165
145
  version: '0'
166
146
  type: :development
167
147
  prerelease: false
168
148
  version_requirements: !ruby/object:Gem::Requirement
169
- none: false
170
149
  requirements:
171
- - - ! '>='
150
+ - - '>='
172
151
  - !ruby/object:Gem::Version
173
152
  version: '0'
174
153
  - !ruby/object:Gem::Dependency
175
154
  name: forking
176
155
  requirement: !ruby/object:Gem::Requirement
177
- none: false
178
156
  requirements:
179
- - - ! '>='
157
+ - - '>='
180
158
  - !ruby/object:Gem::Version
181
159
  version: '0'
182
160
  type: :development
183
161
  prerelease: false
184
162
  version_requirements: !ruby/object:Gem::Requirement
185
- none: false
186
163
  requirements:
187
- - - ! '>='
164
+ - - '>='
188
165
  - !ruby/object:Gem::Version
189
166
  version: '0'
190
167
  - !ruby/object:Gem::Dependency
191
168
  name: fakeweb
192
169
  requirement: !ruby/object:Gem::Requirement
193
- none: false
194
170
  requirements:
195
- - - ! '>='
171
+ - - '>='
196
172
  - !ruby/object:Gem::Version
197
173
  version: '0'
198
174
  type: :development
199
175
  prerelease: false
200
176
  version_requirements: !ruby/object:Gem::Requirement
201
- none: false
202
177
  requirements:
203
- - - ! '>='
178
+ - - '>='
204
179
  - !ruby/object:Gem::Version
205
180
  version: '0'
206
181
  - !ruby/object:Gem::Dependency
207
182
  name: eventmachine
208
183
  requirement: !ruby/object:Gem::Requirement
209
- none: false
210
184
  requirements:
211
- - - ! '>='
185
+ - - '>='
212
186
  - !ruby/object:Gem::Version
213
187
  version: '0'
214
188
  type: :development
215
189
  prerelease: false
216
190
  version_requirements: !ruby/object:Gem::Requirement
217
- none: false
218
191
  requirements:
219
- - - ! '>='
192
+ - - '>='
220
193
  - !ruby/object:Gem::Version
221
194
  version: '0'
222
195
  - !ruby/object:Gem::Dependency
223
196
  name: sinatra
224
197
  requirement: !ruby/object:Gem::Requirement
225
- none: false
226
198
  requirements:
227
- - - ! '>='
199
+ - - '>='
228
200
  - !ruby/object:Gem::Version
229
201
  version: '0'
230
202
  type: :development
231
203
  prerelease: false
232
204
  version_requirements: !ruby/object:Gem::Requirement
233
- none: false
234
205
  requirements:
235
- - - ! '>='
206
+ - - '>='
236
207
  - !ruby/object:Gem::Version
237
208
  version: '0'
238
209
  - !ruby/object:Gem::Dependency
239
210
  name: thin
240
211
  requirement: !ruby/object:Gem::Requirement
241
- none: false
242
212
  requirements:
243
- - - ! '>='
213
+ - - '>='
244
214
  - !ruby/object:Gem::Version
245
215
  version: '0'
246
216
  type: :development
247
217
  prerelease: false
248
218
  version_requirements: !ruby/object:Gem::Requirement
249
- none: false
250
219
  requirements:
251
- - - ! '>='
220
+ - - '>='
252
221
  - !ruby/object:Gem::Version
253
222
  version: '0'
254
223
  - !ruby/object:Gem::Dependency
255
224
  name: xmpp4r
256
225
  requirement: !ruby/object:Gem::Requirement
257
- none: false
258
226
  requirements:
259
- - - ! '>='
227
+ - - '>='
260
228
  - !ruby/object:Gem::Version
261
229
  version: '0'
262
230
  type: :development
263
231
  prerelease: false
264
232
  version_requirements: !ruby/object:Gem::Requirement
265
- none: false
266
233
  requirements:
267
- - - ! '>='
234
+ - - '>='
268
235
  - !ruby/object:Gem::Version
269
236
  version: '0'
270
- description: Process monitoring tool. Alternative for God and Bluepill. With Bluepill
237
+ description: Process monitoring tool. An alternative to God and Bluepill. With Bluepill
271
238
  like config syntax. Requires MRI Ruby >= 1.9.2. Uses Celluloid and Celluloid::IO.
272
239
  email: kostya27@gmail.com
273
240
  executables:
@@ -288,6 +255,7 @@ files:
288
255
  - examples/processes/forking.rb
289
256
  - examples/processes/sample.rb
290
257
  - examples/processes/thin.ru
258
+ - examples/puma.eye
291
259
  - examples/rbenv.eye
292
260
  - examples/sidekiq.eye
293
261
  - examples/test.eye
@@ -479,28 +447,27 @@ files:
479
447
  homepage: http://github.com/kostya/eye
480
448
  licenses:
481
449
  - MIT
450
+ metadata: {}
482
451
  post_install_message:
483
452
  rdoc_options: []
484
453
  require_paths:
485
454
  - lib
486
455
  required_ruby_version: !ruby/object:Gem::Requirement
487
- none: false
488
456
  requirements:
489
- - - ! '>='
457
+ - - '>='
490
458
  - !ruby/object:Gem::Version
491
459
  version: 1.9.2
492
460
  required_rubygems_version: !ruby/object:Gem::Requirement
493
- none: false
494
461
  requirements:
495
- - - ! '>='
462
+ - - '>='
496
463
  - !ruby/object:Gem::Version
497
464
  version: 1.3.6
498
465
  requirements: []
499
466
  rubyforge_project:
500
- rubygems_version: 1.8.25
467
+ rubygems_version: 2.0.2
501
468
  signing_key:
502
- specification_version: 3
503
- summary: Process monitoring tool. Alternative for God and Bluepill. With Bluepill
469
+ specification_version: 4
470
+ summary: Process monitoring tool. An alternative to God and Bluepill. With Bluepill
504
471
  like config syntax. Requires MRI Ruby >= 1.9.2. Uses Celluloid and Celluloid::IO.
505
472
  test_files:
506
473
  - spec/checker/cpu_spec.rb