eye 0.7.pre → 0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGES.md +10 -2
  4. data/README.md +5 -0
  5. data/examples/delayed_job.eye +1 -1
  6. data/examples/dependency.eye +1 -1
  7. data/examples/plugin/plugin.rb +1 -1
  8. data/examples/process_thin.rb +2 -1
  9. data/examples/processes/forking.rb +6 -2
  10. data/examples/processes/sample.rb +4 -4
  11. data/examples/syslog.eye +15 -0
  12. data/examples/test.eye +3 -0
  13. data/examples/thin-farm.eye +2 -2
  14. data/examples/unicorn.eye +1 -1
  15. data/eye.gemspec +1 -1
  16. data/lib/eye.rb +1 -1
  17. data/lib/eye/application.rb +3 -10
  18. data/lib/eye/checker.rb +2 -1
  19. data/lib/eye/checker/children_count.rb +1 -7
  20. data/lib/eye/checker/file_touched.rb +1 -1
  21. data/lib/eye/checker/http.rb +2 -3
  22. data/lib/eye/checker/ssl_socket.rb +28 -0
  23. data/lib/eye/child_process.rb +4 -4
  24. data/lib/eye/cli.rb +12 -6
  25. data/lib/eye/cli/commands.rb +8 -8
  26. data/lib/eye/cli/render.rb +2 -8
  27. data/lib/eye/cli/server.rb +3 -2
  28. data/lib/eye/client.rb +2 -5
  29. data/lib/eye/config.rb +33 -3
  30. data/lib/eye/controller.rb +0 -1
  31. data/lib/eye/controller/load.rb +5 -10
  32. data/lib/eye/controller/status.rb +1 -1
  33. data/lib/eye/dsl.rb +2 -2
  34. data/lib/eye/dsl/config_opts.rb +8 -1
  35. data/lib/eye/dsl/opts.rb +8 -4
  36. data/lib/eye/dsl/pure_opts.rb +1 -3
  37. data/lib/eye/dsl/validation.rb +4 -4
  38. data/lib/eye/group.rb +12 -3
  39. data/lib/eye/local.rb +5 -15
  40. data/lib/eye/notify.rb +0 -3
  41. data/lib/eye/process/children.rb +1 -1
  42. data/lib/eye/process/commands.rb +1 -1
  43. data/lib/eye/process/data.rb +2 -2
  44. data/lib/eye/process/scheduler.rb +4 -0
  45. data/lib/eye/process/system.rb +14 -8
  46. data/lib/eye/system_resources.rb +18 -3
  47. data/lib/eye/utils.rb +1 -1
  48. data/lib/eye/utils/pmap.rb +1 -2
  49. metadata +7 -6
  50. data/lib/eye/utils/celluloid_klass.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 488ed21561da07936dd5fbc3e0d869e4665ba4a5
4
- data.tar.gz: 12b6d6c69ee5e0bf9409bb61e700a6525d1d386a
3
+ metadata.gz: af465bb7737a6f1891bc3ab6ee5d9ebe9547530a
4
+ data.tar.gz: 929efa8c2932925ef6aa2974377982a16e50494e
5
5
  SHA512:
6
- metadata.gz: 54a0993fb03ecf29c9f8a572c977aeb6856da53b972f6d3555c1e17fef13bc807fdfb819ed968f4571f5ee3dd05c8faf0cf766a77be6017ac81e007927605438
7
- data.tar.gz: 3023586158006d3c76ba25675b88bfc2eb0977cafc748b928437ab1cf74178d4494e229e6028615ae8c1efc667188566b0801f5739fa565a94026e6cd737fb56
6
+ metadata.gz: 9bb24f0bc0d47d2345aa0a1f708ad76486cfbe72363cdd6b6410133b912fc8cf47bddde0995422272c7688338ca986386ec3e8b4e9234b09d82adcc9dda155a6
7
+ data.tar.gz: edf8b2a9874f4d58423d501bbe130e75109317be53a05d418118782f127ba5b4637d5109674c1621f21ceb5a04022cbaa6ba8864f2ab34a690c1b1b277798b44
@@ -3,5 +3,5 @@ rvm:
3
3
  - "1.9.3"
4
4
  - "2.0.0"
5
5
  - "2.1"
6
- - "2.2"
6
+ - "2.2.2"
7
7
  script: bundle exec rake split_test N=15
data/CHANGES.md CHANGED
@@ -1,10 +1,18 @@
1
- 0.7.pre
1
+ 0.7
2
2
  -------
3
- * add trigger starting_guard
3
+ * add `stdall syslog`, example: https://github.com/kostya/eye/blob/master/examples/syslog.eye
4
+ * added check `ssl_socket` #125
5
+ * some fixes with `eye q -s`
6
+ * fixed `__default__` apps
7
+ * default loaded configs with first eye start, is: `/etc/eye.conf`, and `~/.eyeconfig`
8
+ * add trigger `starting_guard`
9
+ * fix `load_env` function
4
10
  * fix multiple contacts #118
5
11
  * add slack notifier #115
6
12
  * some fixes in depend_on
7
13
  * some fixes in flapping
14
+ * add proxy_url to http check
15
+ * process with children, shows children history now
8
16
  * Update Celluloid to 0.16.0
9
17
 
10
18
  0.6.4
data/README.md CHANGED
@@ -43,6 +43,9 @@ Eye.application 'test' do
43
43
  # All options inherits down to the config leafs.
44
44
  # except `env`, which merging down
45
45
 
46
+ # uid "user_name" # run app as a user_name (optional) - available only on ruby >= 2.0
47
+ # gid "group_name" # run app as a group_name (optional) - available only on ruby >= 2.0
48
+
46
49
  working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
47
50
  stdall 'trash.log' # stdout,err logs for processes by default
48
51
  env 'APP_ENV' => 'production' # global env for each processes
@@ -152,6 +155,8 @@ test
152
155
  thin ............................ up (21:53, 2%, 54Mb, <4228>)
153
156
  ```
154
157
 
158
+ $ eye i -j # show info in JSON format
159
+
155
160
  ### Commands:
156
161
 
157
162
  start, stop, restart, delete, monitor, unmonitor
@@ -2,7 +2,7 @@ cwd = File.expand_path(File.join(File.dirname(__FILE__), %w[ ../ ../ ]))
2
2
 
3
3
  config_path = File.join(cwd, %w{ config dj.yml } )
4
4
 
5
- workers_count = if File.exists?(config_path)
5
+ workers_count = if File.exist?(config_path)
6
6
  YAML.load_file(config_path).try(:[], :workers) || 5
7
7
  else
8
8
  5
@@ -1,4 +1,4 @@
1
- # process dependencies :b -> :a
1
+ # process dependencies example
2
2
 
3
3
  Eye.app :dependency do
4
4
  process(:a) do
@@ -13,7 +13,7 @@ class Reactor
13
13
  end
14
14
 
15
15
  def read_file
16
- if File.exists?(@filename)
16
+ if File.exist?(@filename)
17
17
  cmd = File.read(@filename).chop
18
18
  File.delete(@filename) rescue nil
19
19
  cmd
@@ -1,3 +1,4 @@
1
+ # part of thin-farm.eye config
1
2
 
2
3
  def thin(proxy, port)
3
4
  name = "thin-#{port}"
@@ -10,7 +11,7 @@ def thin(proxy, port)
10
11
  "-R thin.ru",
11
12
  "--tag #{proxy.app.name}.#{proxy.name}",
12
13
  "-t 60",
13
- "-e #{proxy.env["RAILS_ENV"]}",
14
+ "-e #{proxy.env['RAILS_ENV']}",
14
15
  "-c #{proxy.working_dir}",
15
16
  "-a 127.0.0.1"
16
17
  ]
@@ -2,19 +2,23 @@ require 'bundler/setup'
2
2
  require 'forking'
3
3
 
4
4
  root = File.expand_path(File.dirname(__FILE__))
5
+ cnt = (ENV['FORKING_COUNT'] || 3).to_i
5
6
 
6
7
  f = Forking.new(:name => 'forking', :working_dir => root,
7
8
  :log_file => "#{root}/forking.log",
8
9
  :pid_file => "#{root}/forking.pid", :sync_log => true)
9
10
 
10
- 3.times do |i|
11
+ cnt.times do |i|
11
12
  f.spawn(:log_file => "#{root}/child#{i}.log", :sync_log => true) do
12
13
  $0 = "forking child"
14
+ t = 0
13
15
  loop do
14
16
  p "#{Time.now} - #{Time.now.to_f} - #{i} - tick"
15
17
  sleep 0.1
18
+ t += 0.1
19
+ exit if t > 300
16
20
  end
17
21
  end
18
22
  end
19
23
 
20
- f.run!
24
+ f.run!
@@ -53,7 +53,7 @@ optparse.parse!
53
53
  module Sample
54
54
  def puts(mes = "")
55
55
  tm = Time.now
56
- STDOUT.puts "#{tm.to_s} (#{tm.to_f}) - #{mes}"
56
+ STDOUT.puts "#{tm} (#{tm.to_f}) - #{mes}"
57
57
  STDOUT.flush
58
58
  end
59
59
 
@@ -92,10 +92,10 @@ if options[:daemonize]
92
92
  daemonize(options[:pid_file], options[:log_file], options[:daemonize_delay])
93
93
  end
94
94
 
95
- puts "Started #{ARGV.inspect}, #{options.inspect}, #{ENV["ENV1"]}"
95
+ puts "Started #{ARGV.inspect}, #{options.inspect}, #{ENV['ENV1']}"
96
96
 
97
97
  if options[:lock_file]
98
- if File.exists?(options[:lock_file])
98
+ if File.exist?(options[:lock_file])
99
99
  puts "Lock file exists, exiting"
100
100
  exit 1
101
101
  else
@@ -124,7 +124,7 @@ loop do
124
124
  puts "tick"
125
125
 
126
126
  if options[:watch_file]
127
- if File.exists?(options[:watch_file])
127
+ if File.exist?(options[:watch_file])
128
128
  puts "watch file finded"
129
129
  File.unlink(options[:watch_file])
130
130
 
@@ -0,0 +1,15 @@
1
+ # output eye logger to syslog, and process stdout to syslog too
2
+ # experimental feature, in some cases may be unstable
3
+
4
+ Eye.config do
5
+ logger syslog
6
+ end
7
+
8
+ Eye.app :syslog_test do
9
+ process :some do
10
+ pid_file "/tmp/syslog_test.pid"
11
+ start_command "ruby -e 'loop { p Time.now; sleep 1 }'"
12
+ daemonize!
13
+ stdall syslog
14
+ end
15
+ end
@@ -11,6 +11,9 @@ Eye.application 'test' do
11
11
  # All options inherits down to the config leafs.
12
12
  # except `env`, which merging down
13
13
 
14
+ # uid "user_name" # run app as a user_name (optional) - available only on ruby >= 2.0
15
+ # gid "group_name" # run app as a group_name (optional) - available only on ruby >= 2.0
16
+
14
17
  working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
15
18
  stdall 'trash.log' # stdout,err logs for processes by default
16
19
  env 'APP_ENV' => 'production' # global env for each processes
@@ -11,8 +11,8 @@ Eye.app 'thin-farm' do
11
11
  working_dir File.expand_path(File.join(File.dirname(__FILE__), %w[ processes ]))
12
12
  env "RAILS_ENV" => "production"
13
13
 
14
- stop_on_delete true # this option means, when we change pids and load config,
15
- # deleted processes will be stops
14
+ # more about stop_on_delete: https://github.com/kostya/eye/wiki/About-stop_on_delete-=-true
15
+ stop_on_delete true
16
16
 
17
17
  trigger :flapping, :times => 10, :within => 1.minute
18
18
  check :memory, :below => 60.megabytes, :every => 30.seconds, :times => 5
@@ -1,4 +1,4 @@
1
- # Example: now to run unicorn, and monitor its child processes
1
+ # Example: how to run unicorn, and monitor its child processes
2
2
 
3
3
  RUBY = '/usr/local/ruby/1.9.3/bin/ruby' # ruby on the server
4
4
  RAILS_ENV = 'production'
@@ -23,7 +23,7 @@ Gem::Specification.new do |gem|
23
23
  gem.add_dependency 'celluloid-io', '~> 0.16.0'
24
24
  gem.add_dependency 'state_machine'
25
25
  gem.add_dependency 'thor'
26
- gem.add_dependency 'sigar', '~> 0.7.2'
26
+ gem.add_dependency 'sigar', '~> 0.7.3'
27
27
 
28
28
  gem.add_development_dependency 'rake'
29
29
  gem.add_development_dependency 'rspec', '< 2.14'
data/lib/eye.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Eye
2
- VERSION = "0.7.pre"
2
+ VERSION = "0.7"
3
3
  ABOUT = "Eye v#{VERSION} (c) 2012-2015 @kostya"
4
4
  PROCLINE = "eye monitoring v#{VERSION}"
5
5
 
@@ -1,6 +1,6 @@
1
1
  class Eye::Application
2
2
 
3
- attr_reader :groups, :name
3
+ attr_reader :groups, :name, :config
4
4
 
5
5
  def initialize(name, config = {})
6
6
  @groups = Eye::Utils::AliveArray.new
@@ -28,19 +28,12 @@ class Eye::Application
28
28
 
29
29
  def status_data(debug = false)
30
30
  h = { name: @name, type: :application, subtree: @groups.map{|gr| gr.status_data(debug) }}
31
- h.merge!(debug: debug_data) if debug
31
+ h[:debug] = debug_data if debug
32
32
  h
33
33
  end
34
34
 
35
35
  def status_data_short
36
- h = Hash.new
37
- @groups.each do |c|
38
- c.processes.each do |p|
39
- h[p.state] ||= 0
40
- h[p.state] += 1
41
- end
42
- end
43
- { name: @name, type: :application, states: h}
36
+ { name: @name, type: :application, subtree: @groups.map(&:status_data_short) }
44
37
  end
45
38
 
46
39
  def debug_data
@@ -8,6 +8,7 @@ class Eye::Checker
8
8
  autoload :FileSize, 'eye/checker/file_size'
9
9
  autoload :FileTouched,'eye/checker/file_touched'
10
10
  autoload :Socket, 'eye/checker/socket'
11
+ autoload :SslSocket, 'eye/checker/ssl_socket'
11
12
  autoload :Nop, 'eye/checker/nop'
12
13
  autoload :Runtime, 'eye/checker/runtime'
13
14
  autoload :Cputime, 'eye/checker/cputime'
@@ -17,7 +18,7 @@ class Eye::Checker
17
18
  TYPES = {:memory => 'Memory', :cpu => 'Cpu', :http => 'Http',
18
19
  :ctime => 'FileCTime', :fsize => 'FileSize', :file_touched => 'FileTouched',
19
20
  :socket => 'Socket', :nop => 'Nop', :runtime => 'Runtime', :cputime => 'Cputime',
20
- :children_count => "ChildrenCount", :children_memory => "ChildrenMemory" }
21
+ :children_count => "ChildrenCount", :children_memory => "ChildrenMemory", :ssl_socket => 'SslSocket' }
21
22
 
22
23
  attr_accessor :value, :values, :options, :pid, :type, :check_count, :process
23
24
 
@@ -14,13 +14,7 @@ class Eye::Checker::ChildrenCount < Eye::Checker::Measure
14
14
  super
15
15
  else
16
16
  pids = ordered_by_date_children_pids
17
-
18
- pids = if strategy == :kill_old
19
- pids[0...-below]
20
- else
21
- pids[below..-1]
22
- end
23
-
17
+ pids = (strategy == :kill_old) ? pids[0...-below] : pids[below..-1]
24
18
  kill_pids(pids)
25
19
  end
26
20
  end
@@ -9,7 +9,7 @@ class Eye::Checker::FileTouched < Eye::Checker
9
9
  end
10
10
 
11
11
  def get_value
12
- File.exists?(file)
12
+ File.exist?(file)
13
13
  end
14
14
 
15
15
  def good?(value)
@@ -21,10 +21,9 @@ class Eye::Checker::Http < Eye::Checker::Defer
21
21
  @uri = URI.parse(url)
22
22
  @proxy_uri = URI.parse(proxy_url) if proxy_url
23
23
  @kind = case kind
24
- when Fixnum then Net::HTTPResponse::CODE_TO_OBJ[kind]
24
+ when Fixnum then Net::HTTPResponse::CODE_TO_OBJ[kind.to_s]
25
25
  when String, Symbol then Net.const_get("HTTP#{kind.to_s.camelize}") rescue Net::HTTPSuccess
26
- else
27
- Net::HTTPSuccess
26
+ else Net::HTTPSuccess
28
27
  end
29
28
  @open_timeout = (open_timeout || 3).to_f
30
29
  @read_timeout = (read_timeout || timeout || 15).to_f
@@ -0,0 +1,28 @@
1
+ require 'openssl'
2
+
3
+ class Eye::Checker::SslSocket < Eye::Checker::Socket
4
+ param :ctx, Hash, nil, {ssl_version: :SSLv23, verify_mode: OpenSSL::SSL::VERIFY_NONE}
5
+
6
+ # other params inherits from socket check
7
+ #
8
+ # examples:
9
+ #
10
+ # check :ssl_socket, :addr => "tcp://127.0.0.1:443", :every => 5.seconds, :times => 1, :timeout => 1.second,
11
+ # :ctx => {ssl_version: :SSLv23, verify_mode: OpenSSL::SSL::VERIFY_NONE}
12
+ #
13
+ #
14
+ # ctx_params from http://ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html
15
+
16
+ private
17
+
18
+ def open_socket
19
+ OpenSSL::SSL::SSLSocket.new(super, ctx_params).tap do |socket|
20
+ socket.sync_close = true
21
+ socket.connect
22
+ end
23
+ end
24
+
25
+ def ctx_params
26
+ @ctx_params ||= OpenSSL::SSL::SSLContext.new().tap { |c| c.set_params(ctx) }
27
+ end
28
+ end
@@ -24,13 +24,13 @@ class Eye::ChildProcess
24
24
  # scheduler
25
25
  include Eye::Process::Scheduler
26
26
 
27
- attr_reader :pid, :name, :full_name, :config, :watchers
27
+ attr_reader :pid, :name, :full_name, :config, :watchers, :parent
28
28
 
29
- def initialize(pid, config = {}, logger_prefix = nil, parent_pid = nil)
29
+ def initialize(pid, config = {}, logger_prefix = nil, parent = nil)
30
30
  raise 'Empty pid' unless pid
31
31
 
32
32
  @pid = pid
33
- @parent_pid = parent_pid
33
+ @parent = parent
34
34
  @config = prepare_config(config)
35
35
  @name = "child-#{pid}"
36
36
  @full_name = [logger_prefix, @name] * ':'
@@ -96,6 +96,6 @@ class Eye::ChildProcess
96
96
  end
97
97
 
98
98
  def prepare_command(command) # override
99
- super.gsub('{PARENT_PID}', @parent_pid.to_s)
99
+ super.gsub('{PARENT_PID}', @parent.pid.to_s)
100
100
  end
101
101
  end
@@ -98,18 +98,24 @@ class Eye::Cli < Thor
98
98
  ensure_stop_previous_server if res != :corrupted_data
99
99
 
100
100
  # remove pid_file
101
- File.delete(Eye::Local.pid_path) if File.exists?(Eye::Local.pid_path)
101
+ File.delete(Eye::Local.pid_path) if File.exist?(Eye::Local.pid_path)
102
102
 
103
103
  say "Quit ಠ╭╮ಠ", :yellow
104
104
  end
105
105
 
106
- [:start, :stop, :restart, :unmonitor, :monitor, :delete, :match].each do |_cmd|
107
- desc "#{_cmd} MASK[,...]", "#{_cmd} app,group or process"
108
- define_method(_cmd) do |*masks|
109
- send_command(_cmd, *masks)
106
+ [:start, :stop, :restart, :unmonitor, :monitor, :delete, :match].each do |command|
107
+ desc "#{command} MASK[,...]", "#{command} app,group or process"
108
+ define_method(command) do |*masks|
109
+ send_command(command, *masks)
110
110
  end
111
111
  end
112
112
 
113
+ desc "force_restart MASK[,...]", "restart by stop;start (not by restart_command)"
114
+ def force_restart(*masks)
115
+ send_command(:stop, *masks)
116
+ send_command(:start, *masks)
117
+ end
118
+
113
119
  desc "signal SIG MASK[,...]", "send signal to app,group or process"
114
120
  def signal(sig, *masks)
115
121
  send_command(:signal, sig, *masks)
@@ -189,7 +195,7 @@ private
189
195
 
190
196
  def log_trace(tag = '')
191
197
  log_file = cmd(:logger_dev)
192
- if log_file && File.exists?(log_file)
198
+ if log_file && File.exist?(log_file)
193
199
  Process.exec "tail -n 100 -f #{log_file} | grep '#{tag}'"
194
200
  else
195
201
  error! "log file not found #{log_file.inspect}"
@@ -27,10 +27,10 @@ private
27
27
  error!(res) unless res.is_a?(Hash)
28
28
  say_filename = (res.size > 1)
29
29
  error = false
30
- res.each do |filename, _res|
30
+ res.each do |filename, res2|
31
31
  say "#{filename}: ", nil, true if say_filename
32
- show_load_message(_res, opts)
33
- error = true if _res[:error]
32
+ show_load_message(res2, opts)
33
+ error = true if res2[:error]
34
34
  end
35
35
 
36
36
  exit(1) if error
@@ -54,10 +54,10 @@ private
54
54
  end
55
55
  end
56
56
 
57
- def send_command(_cmd, *args)
58
- res = cmd(_cmd, *args)
57
+ def send_command(command, *args)
58
+ res = cmd(command, *args)
59
59
  if res == :unknown_command
60
- error! "unknown command :#{_cmd}"
60
+ error! "unknown command :#{command}"
61
61
  elsif res == :corrupted_data
62
62
  error! 'something crazy wrong, check eye logs!'
63
63
  elsif res.is_a?(Hash)
@@ -65,9 +65,9 @@ private
65
65
  error! "Error: #{res[:error]}"
66
66
  elsif res = res[:result]
67
67
  if res == []
68
- error! "command :#{_cmd}, objects not found!"
68
+ error! "command :#{command}, objects not found!"
69
69
  else
70
- say "command :#{_cmd} sent to [#{res * ", "}]"
70
+ say "command :#{command} sent to [#{res * ', '}]"
71
71
  end
72
72
  end
73
73
  else
@@ -34,7 +34,7 @@ private
34
34
  off = level * 2
35
35
  off_str = ' ' * off
36
36
 
37
- short_state = (data[:type] == :application && data[:states])
37
+ short_state = ((data[:type] == :application || data[:type] == :group) && data[:states])
38
38
  is_text = data[:state] || data[:states]
39
39
 
40
40
  name = (data[:type] == :application && !is_text) ? "\033[1m#{data[:name]}\033[0m" : data[:name].to_s
@@ -105,13 +105,7 @@ private
105
105
 
106
106
  def render_history(data)
107
107
  error!("unexpected server response #{data.inspect}") unless data.is_a?(Hash)
108
-
109
- res = []
110
- data.each do |name, data|
111
- res << detail_process_info(name, data)
112
- end
113
-
114
- res * "\n"
108
+ data.map { |name, history| detail_process_info(name, history) }.join("\n")
115
109
  end
116
110
 
117
111
  def detail_process_info(name, history)
@@ -9,7 +9,7 @@ private
9
9
 
10
10
  def loader_path
11
11
  filename = File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. .. bin loader_eye]))
12
- File.exists?(filename) ? filename : nil
12
+ File.exist?(filename) ? filename : nil
13
13
  end
14
14
 
15
15
  def ruby_path
@@ -62,7 +62,8 @@ private
62
62
  error! 'server has not started in 15 seconds, something is very wrong'
63
63
  end
64
64
 
65
- configs.unshift(Eye::Local.eyeconfig) if File.exists?(Eye::Local.eyeconfig)
65
+ configs.unshift(Eye::Local.global_eyeconfig) if File.exist?(Eye::Local.global_eyeconfig)
66
+ configs.unshift(Eye::Local.eyeconfig) if File.exist?(Eye::Local.eyeconfig)
66
67
  configs << Eye::Local.eyefile if Eye::Local.local_runner
67
68
 
68
69
  say 'Eye started! ㋡', :green
@@ -13,10 +13,7 @@ class Eye::Client
13
13
  end
14
14
 
15
15
  def attempt_command(pack)
16
- Timeout.timeout(Eye::Local.client_timeout) do
17
- return send_request(pack)
18
- end
19
-
16
+ Timeout.timeout(Eye::Local.client_timeout) { send_request(pack) }
20
17
  rescue Timeout::Error, EOFError
21
18
  :timeouted
22
19
  end
@@ -25,7 +22,7 @@ class Eye::Client
25
22
  UNIXSocket.open(@socket_path) do |socket|
26
23
  socket.write(pack)
27
24
  data = socket.read
28
- res = Marshal.load(data) rescue :corrupted_data
25
+ Marshal.load(data) rescue :corrupted_data
29
26
  end
30
27
  end
31
28
 
@@ -20,11 +20,18 @@ class Eye::Config
20
20
  end
21
21
 
22
22
  def to_h
23
- {:settings => @settings, :applications => @applications}
23
+ h = {}
24
+ h[:settings] = @settings
25
+ if Eye.respond_to?(:parsed_default_app)
26
+ d = Eye.parsed_default_app
27
+ h[:defaults] = d ? d.config : {}
28
+ end
29
+ h[:applications] = @applications
30
+ h
24
31
  end
25
32
 
26
33
  # raise an error if config wrong
27
- def validate!(localize = true)
34
+ def validate!(validate_apps = [])
28
35
  all_processes = processes
29
36
 
30
37
  # Check blank pid_files
@@ -58,13 +65,36 @@ class Eye::Config
58
65
 
59
66
  # validate processes with their own validate
60
67
  all_processes.each do |process_cfg|
61
- Eye::Process.validate process_cfg, localize
68
+ Eye::Process.validate process_cfg, validate_apps.include?(process_cfg[:application])
62
69
  end
63
70
 
64
71
  # just to be sure ENV was not removed
65
72
  ENV[''] rescue raise Eye::Dsl::Error.new("ENV is not a hash '#{ENV.inspect}'")
66
73
  end
67
74
 
75
+ def transform!
76
+ all_processes = processes
77
+
78
+ # transform syslog option
79
+ all_processes.each do |process|
80
+ out = process[:stdout] && process[:stdout].start_with?(':syslog')
81
+ err = process[:stderr] && process[:stderr].start_with?(':syslog')
82
+ if err || out
83
+ redir = err ? '2>&1' : ''
84
+ process[:stdout] = nil if out
85
+ process[:stderr] = nil if err
86
+
87
+ escaped_start_command = process[:start_command].to_s.gsub(%{"}, %{\\"})
88
+
89
+ names = [process[:application], process[:group] == '__default__' ? nil : process[:group], process[:name]].compact
90
+ logger = "logger -t \"#{names.join(':')}\""
91
+
92
+ process[:start_command] = %{sh -c "#{escaped_start_command} #{redir} | #{logger}"}
93
+ process[:use_leaf_child] = true if process[:daemonize]
94
+ end
95
+ end
96
+ end
97
+
68
98
  def processes
69
99
  applications.values.map{|e| (e[:groups] || {}).values.map{|c| (c[:processes] || {}).values} }.flatten
70
100
  end
@@ -1,7 +1,6 @@
1
1
  require 'celluloid'
2
2
  require 'yaml'
3
3
 
4
- require_relative 'utils/celluloid_klass'
5
4
  require_relative 'utils/pmap'
6
5
 
7
6
  require_relative 'utils/leak_19'
@@ -9,7 +9,7 @@ module Eye::Controller::Load
9
9
  end
10
10
 
11
11
  def load(*args)
12
- h = args.extract_options!
12
+ args.extract_options!
13
13
  obj_strs = args.flatten
14
14
  info "=> loading: #{obj_strs}"
15
15
 
@@ -49,7 +49,7 @@ private
49
49
  error bt.join("\n")
50
50
 
51
51
  res = { :error => true, :message => ex.message }
52
- res.merge!(:backtrace => bt) if bt.present?
52
+ res[:backtrace] = bt if bt.present?
53
53
  res
54
54
  end
55
55
 
@@ -58,12 +58,7 @@ private
58
58
  return res if obj_strs.empty?
59
59
 
60
60
  obj_strs.each do |filename|
61
- mask = if File.directory?(filename)
62
- File.join filename, '{*.eye}'
63
- else
64
- filename
65
- end
66
-
61
+ mask = File.directory?(filename) ? File.join(filename, '{*.eye}') : filename
67
62
  debug { "loading: globbing mask #{mask}" }
68
63
 
69
64
  sub = []
@@ -83,7 +78,7 @@ private
83
78
  debug { "parsing: #{filename}" }
84
79
 
85
80
  cfg = Eye::Dsl.parse(nil, filename)
86
- @current_config.merge(cfg).validate!(false) # just validate summary config here
81
+ @current_config.merge(cfg).validate! # just validate summary config here
87
82
  Eye.parsed_config = nil # remove link on config, for better gc
88
83
  cfg
89
84
  end
@@ -92,7 +87,7 @@ private
92
87
  def load_config(filename, config)
93
88
  info "loading: #{filename}"
94
89
  new_cfg = @current_config.merge(config)
95
- new_cfg.validate!
90
+ new_cfg.validate!(config.application_names)
96
91
 
97
92
  load_options(new_cfg.settings)
98
93
  create_objects(new_cfg.applications, config.application_names)
@@ -2,7 +2,7 @@ module Eye::Controller::Status
2
2
 
3
3
  def debug_data(*args)
4
4
  h = args.extract_options!
5
- actors = Celluloid::Actor.all.map{|actor| actor.__klass__ }.group_by{|a| a}.map{|k,v| [k, v.size]}.sort_by{|a|a[1]}.reverse
5
+ actors = Celluloid::Actor.all.map{|actor| actor.wrapped_object.class.to_s }.group_by{|a| a}.map{|k,v| [k, v.size]}.sort_by{ |a| a[1] }.reverse
6
6
 
7
7
  res = {
8
8
  :about => Eye::ABOUT,
@@ -27,7 +27,6 @@ class Eye::Dsl
27
27
  def parse(content = nil, filename = nil)
28
28
  Eye.parsed_config = Eye::Config.new
29
29
  Eye.parsed_filename = filename
30
- Eye.parsed_default_app = nil
31
30
 
32
31
  content = File.read(filename) if content.blank?
33
32
 
@@ -35,7 +34,8 @@ class Eye::Dsl
35
34
  Kernel.eval(content, Eye::BINDING, filename.to_s)
36
35
  end
37
36
 
38
- Eye.parsed_config.validate!(false)
37
+ Eye.parsed_config.transform!
38
+ Eye.parsed_config.validate!
39
39
  Eye.parsed_config
40
40
  end
41
41
 
@@ -12,6 +12,13 @@ class Eye::Dsl::ConfigOpts < Eye::Dsl::PureOpts
12
12
  end
13
13
  alias logger= logger
14
14
 
15
+ def syslog(name = 'eye', *args)
16
+ require 'syslog/logger'
17
+ Syslog::Logger.new(name, *args)
18
+ rescue LoadError
19
+ raise Eye::Dsl::Error, "logger syslog requires Ruby >= 2.0"
20
+ end
21
+
15
22
  # ==== contact options ==============================
16
23
  def self.add_notify(type)
17
24
  create_options_methods([type], Hash)
@@ -23,7 +30,7 @@ class Eye::Dsl::ConfigOpts < Eye::Dsl::PureOpts
23
30
  end
24
31
  end
25
32
 
26
- Eye::Notify::TYPES.keys.each { |name| add_notify(name) }
33
+ Eye::Notify::TYPES.each_key { |name| add_notify(name) }
27
34
 
28
35
  def contact(contact_name, contact_type, contact, contact_opts = {})
29
36
  raise Eye::Dsl::Error, "unknown contact_type #{contact_type}" unless Eye::Notify::TYPES[contact_type]
@@ -19,7 +19,7 @@ class Eye::Dsl::Opts < Eye::Dsl::PureOpts
19
19
  def initialize(name = nil, parent = nil)
20
20
  super(name, parent)
21
21
 
22
- @config[:application] = parent.name if parent.is_a?(Eye::Dsl::ApplicationOpts)
22
+ @config[:application] = parent.name if parent.is_a?(Eye::Dsl::ApplicationOpts) && parent.name != '__default__'
23
23
  @config[:group] = parent.name if parent.is_a?(Eye::Dsl::GroupOpts)
24
24
 
25
25
  # hack for full name
@@ -30,7 +30,7 @@ class Eye::Dsl::Opts < Eye::Dsl::PureOpts
30
30
  nac = Eye::Checker.name_and_class(type.to_sym)
31
31
  raise Eye::Dsl::Error, "unknown checker type #{type}" unless nac
32
32
 
33
- opts.merge!(:type => nac[:type])
33
+ opts[:type] = nac[:type]
34
34
  Eye::Checker.validate!(opts)
35
35
 
36
36
  @config[:checks] ||= {}
@@ -41,7 +41,7 @@ class Eye::Dsl::Opts < Eye::Dsl::PureOpts
41
41
  nac = Eye::Trigger.name_and_class(type.to_sym)
42
42
  raise Eye::Dsl::Error, "unknown trigger type #{type}" unless nac
43
43
 
44
- opts.merge!(:type => nac[:type])
44
+ opts[:type] = nac[:type]
45
45
  Eye::Trigger.validate!(opts)
46
46
 
47
47
  @config[:triggers] ||= {}
@@ -184,7 +184,7 @@ class Eye::Dsl::Opts < Eye::Dsl::PureOpts
184
184
  def load_env(filename = '~/.env', raise_when_no_file = true)
185
185
  fnames = [File.expand_path(filename, @config[:working_dir]),
186
186
  File.expand_path(filename)].uniq
187
- filenames = fnames.select { |f| File.exists?(f) }
187
+ filenames = fnames.select { |f| File.exist?(f) }
188
188
 
189
189
  if filenames.size < 1
190
190
  unless raise_when_no_file
@@ -205,6 +205,10 @@ class Eye::Dsl::Opts < Eye::Dsl::PureOpts
205
205
  @config[:skip_group_actions][act] = val
206
206
  end
207
207
 
208
+ def syslog
209
+ ':syslog'
210
+ end
211
+
208
212
  private
209
213
 
210
214
  def validate_signals(signals = nil)
@@ -82,7 +82,7 @@ class Eye::Dsl::PureOpts
82
82
  def use(proc, *args)
83
83
  if proc.is_a?(String)
84
84
  self.class.with_parsed_file(proc) do |path|
85
- if File.exists?(path)
85
+ if File.exist?(path)
86
86
  Eye::Dsl.debug { "=> load #{path}" }
87
87
  self.instance_eval(File.read(path))
88
88
  Eye::Dsl.debug { "<= load #{path}" }
@@ -101,8 +101,6 @@ class Eye::Dsl::PureOpts
101
101
 
102
102
  def nop(*args, &block); end
103
103
 
104
- private
105
-
106
104
  def self.with_parsed_file(file_name)
107
105
  saved_parsed_filename = Eye.parsed_filename
108
106
 
@@ -20,13 +20,13 @@ module Eye::Dsl::Validation
20
20
  def defaults; @defaults ||= {}; end
21
21
  def variants; @variants ||= {}; end
22
22
 
23
- def param(param, types = [], should_be = false, default = nil, _variants = nil)
23
+ def param(param, types = [], should_be = false, default = nil, variants = nil)
24
24
  param = param.to_sym
25
25
 
26
- validates[param] = types
27
- should_bes << param if should_be
26
+ self.validates[param] = types
27
+ self.should_bes << param if should_be
28
28
  param_default(param, default)
29
- variants[param] = _variants
29
+ self.variants[param] = variants
30
30
 
31
31
  return if param == :do
32
32
 
@@ -49,7 +49,7 @@ class Eye::Group
49
49
 
50
50
  h = { name: name, type: :group, subtree: plist }
51
51
 
52
- h.merge!(debug: debug_data) if debug
52
+ h[:debug] = debug_data if debug
53
53
 
54
54
  # show current chain
55
55
  if current_scheduled_command
@@ -67,6 +67,15 @@ class Eye::Group
67
67
  h
68
68
  end
69
69
 
70
+ def status_data_short
71
+ h = Hash.new
72
+ @processes.each do |p|
73
+ h[p.state] ||= 0
74
+ h[p.state] += 1
75
+ end
76
+ { name: (@name == '__default__' ? 'default' : @name), type: :group, states: h }
77
+ end
78
+
70
79
  def debug_data
71
80
  {:queue => scheduler_actions_list, :chain => chain_status}
72
81
  end
@@ -76,9 +85,9 @@ class Eye::Group
76
85
 
77
86
  case command
78
87
  when :delete
79
- delete *args
88
+ delete(*args)
80
89
  when :break_chain
81
- break_chain *args
90
+ break_chain(*args)
82
91
  else
83
92
  schedule command, *args, Eye::Reason::User.new(command)
84
93
  end
@@ -12,16 +12,14 @@ module Eye::Local
12
12
  end
13
13
  end
14
14
 
15
- def dir=(d)
16
- @dir = d
15
+ attr_writer :dir, :client_timeout, :host
16
+
17
+ def global_eyeconfig
18
+ '/etc/eye.conf'
17
19
  end
18
20
 
19
21
  def eyeconfig
20
- if root?
21
- '/etc/eye.conf'
22
- else
23
- File.expand_path(File.join(home, '.eyeconfig'))
24
- end
22
+ File.expand_path(File.join(home, '.eyeconfig'))
25
23
  end
26
24
 
27
25
  def root?
@@ -62,10 +60,6 @@ module Eye::Local
62
60
  @client_timeout ||= default_client_timeout
63
61
  end
64
62
 
65
- def client_timeout=(cl)
66
- @client_timeout = cl
67
- end
68
-
69
63
  def supported_setsid?
70
64
  RUBY_VERSION >= '2.0'
71
65
  end
@@ -77,10 +71,6 @@ module Eye::Local
77
71
  end
78
72
  end
79
73
 
80
- def host=(hostname)
81
- @host = hostname
82
- end
83
-
84
74
  def eyefile
85
75
  @eyefile ||= find_eyefile('.')
86
76
  end
@@ -108,12 +108,9 @@ class Eye::Notify
108
108
  end
109
109
  end
110
110
 
111
- private
112
-
113
111
  %w{at host message name full_name pid level}.each do |name|
114
112
  define_method("msg_#{name}") do
115
113
  @message_h[name.to_sym]
116
114
  end
117
115
  end
118
-
119
116
  end
@@ -32,7 +32,7 @@ module Eye::Process::Children
32
32
  if new_children.present?
33
33
  new_children.each do |child_pid|
34
34
  cfg = self[:monitor_children].try :update, :notify => self[:notify]
35
- self.children[child_pid] = Eye::ChildProcess.new(child_pid, cfg, logger.prefix, self.pid)
35
+ self.children[child_pid] = Eye::ChildProcess.new(child_pid, cfg, logger.prefix, current_actor)
36
36
  end
37
37
  end
38
38
 
@@ -170,7 +170,7 @@ private
170
170
 
171
171
  def daemonize_process
172
172
  time_before = Time.now
173
- res = Eye::System.daemonize(self[:start_command], config)
173
+ res = daemonize(self[:start_command], config)
174
174
  start_time = Time.now - time_before
175
175
 
176
176
  info "daemonizing: `#{self[:start_command]}` with start_grace: #{self[:start_grace].to_f}s, env: '#{environment_string}', <#{res[:pid]}> (in #{self[:working_dir]})"
@@ -43,8 +43,8 @@ module Eye::Process::Data
43
43
  state_reason: @states_history.last_reason.to_s )
44
44
  end
45
45
 
46
- h.merge!(debug: debug_data) if debug
47
- h.merge!(current_command: current_scheduled_command) if current_scheduled_command
46
+ h[:debug] = debug_data if debug
47
+ h[:current_command] = current_scheduled_command if current_scheduled_command
48
48
 
49
49
  h
50
50
  end
@@ -53,6 +53,10 @@ module Eye::Process::Scheduler
53
53
  info "<= #{command}"
54
54
 
55
55
  schedule_history.push(command, reason, @last_scheduled_at.to_i)
56
+
57
+ if parent = self.try(:parent)
58
+ parent.schedule_history.push("#{command}_child", reason, @last_scheduled_at.to_i)
59
+ end
56
60
  end
57
61
 
58
62
  def scheduler_actions_list
@@ -3,7 +3,7 @@ require 'timeout'
3
3
  module Eye::Process::System
4
4
 
5
5
  def load_pid_from_file
6
- res = if File.exists?(self[:pid_file_ex])
6
+ res = if File.exist?(self[:pid_file_ex])
7
7
  _pid = File.read(self[:pid_file_ex]).to_i
8
8
  _pid > 0 ? _pid : nil
9
9
  end
@@ -73,19 +73,25 @@ module Eye::Process::System
73
73
  end
74
74
 
75
75
  def execute(cmd, cfg = {})
76
- defer{ Eye::System::execute cmd, cfg }
76
+ defer { Eye::System::execute cmd, cfg }.tap do |res|
77
+ notify(:debug, "Bad exit status of command #{cmd.inspect}(#{res[:exitstatus].inspect})") if res[:exitstatus] != 0
78
+ end
79
+ end
80
+
81
+ def daemonize(cmd, cfg = {})
82
+ Eye::System.daemonize(cmd, cfg)
77
83
  end
78
84
 
79
85
  def execute_sync(cmd, opts = {:timeout => 1.second})
80
- res = execute cmd, self.config.merge(opts)
81
- info "execute_sync `#{cmd}` with res: #{res}"
82
- res
86
+ execute(cmd, self.config.merge(opts)).tap do |res|
87
+ info "execute_sync `#{cmd}` with res: #{res}"
88
+ end
83
89
  end
84
90
 
85
91
  def execute_async(cmd, opts = {})
86
- res = Eye::System.daemonize(cmd, self.config.merge(opts))
87
- info "execute_async `#{cmd}` with res: #{res}"
88
- res
92
+ daemonize(cmd, self.config.merge(opts)).tap do |res|
93
+ info "execute_async `#{cmd}` with res: #{res}"
94
+ end
89
95
  end
90
96
 
91
97
  def failsafe_load_pid
@@ -36,10 +36,25 @@ class Eye::SystemResources
36
36
 
37
37
  # last child in a children tree
38
38
  def leaf_child(pid)
39
+ if dc = deep_children(pid)
40
+ dc.detect do |child|
41
+ args = Eye::Sigar.proc_args(child)[0] rescue ''
42
+ !args.start_with?('logger') && child != pid
43
+ end
44
+ end
45
+ end
46
+
47
+ def deep_children(pid)
48
+ Array(pid_or_children(pid)).flatten.sort_by { |pid| -pid }
49
+ end
50
+
51
+ def pid_or_children(pid)
39
52
  c = children(pid)
40
- return if c.empty?
41
- c += children(c.shift) while c.size > 1
42
- c[0]
53
+ if !c || c.empty?
54
+ pid
55
+ else
56
+ c.map { |ppid| pid_or_children(ppid) }
57
+ end
43
58
  end
44
59
 
45
60
  def resources(pid)
@@ -33,7 +33,7 @@ module Eye::Utils
33
33
  time = Time.at(unix_time.to_i)
34
34
  d1 = time.to_date
35
35
  d2 = Time.now.to_date
36
- time.strftime (d1 == d2) ? D1 : D2
36
+ time.strftime((d1 == d2) ? D1 : D2)
37
37
  end
38
38
 
39
39
  DF = '%d %b %H:%M'
@@ -1,7 +1,6 @@
1
1
  module Enumerable
2
2
  # Simple parallel map using Celluloid::Futures
3
3
  def pmap(&block)
4
- futures = map { |elem| Celluloid::Future.new(elem, &block) }
5
- futures.map { |future| future.value }
4
+ map { |elem| Celluloid::Future.new(elem, &block) }.map(&:value)
6
5
  end
7
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eye
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.pre
4
+ version: '0.7'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Makarchev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-10 00:00:00.000000000 Z
11
+ date: 2015-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: celluloid
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.7.2
75
+ version: 0.7.3
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.7.2
82
+ version: 0.7.3
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rake
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -326,6 +326,7 @@ files:
326
326
  - examples/rbenv.eye
327
327
  - examples/sidekiq.eye
328
328
  - examples/stress_test.eye
329
+ - examples/syslog.eye
329
330
  - examples/test.eye
330
331
  - examples/thin-farm.eye
331
332
  - examples/triggers.eye
@@ -346,6 +347,7 @@ files:
346
347
  - lib/eye/checker/nop.rb
347
348
  - lib/eye/checker/runtime.rb
348
349
  - lib/eye/checker/socket.rb
350
+ - lib/eye/checker/ssl_socket.rb
349
351
  - lib/eye/child_process.rb
350
352
  - lib/eye/cli.rb
351
353
  - lib/eye/cli/commands.rb
@@ -412,7 +414,6 @@ files:
412
414
  - lib/eye/utils.rb
413
415
  - lib/eye/utils/alive_array.rb
414
416
  - lib/eye/utils/celluloid_chain.rb
415
- - lib/eye/utils/celluloid_klass.rb
416
417
  - lib/eye/utils/leak_19.rb
417
418
  - lib/eye/utils/mini_active_support.rb
418
419
  - lib/eye/utils/pmap.rb
@@ -437,7 +438,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
437
438
  version: 1.3.6
438
439
  requirements: []
439
440
  rubyforge_project:
440
- rubygems_version: 2.4.5
441
+ rubygems_version: 2.4.7
441
442
  signing_key:
442
443
  specification_version: 4
443
444
  summary: Process monitoring tool. Inspired from Bluepill and God. Requires Ruby(MRI)
@@ -1,5 +0,0 @@
1
- class Celluloid::SyncProxy
2
- def __klass__
3
- @klass
4
- end
5
- end