reel-eye 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +32 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +22 -0
  7. data/README.md +170 -0
  8. data/Rakefile +20 -0
  9. data/bin/eye +322 -0
  10. data/bin/loader_eye +58 -0
  11. data/examples/notify.eye +18 -0
  12. data/examples/process_thin.rb +29 -0
  13. data/examples/processes/em.rb +57 -0
  14. data/examples/processes/forking.rb +20 -0
  15. data/examples/processes/sample.rb +144 -0
  16. data/examples/processes/thin.ru +12 -0
  17. data/examples/puma.eye +34 -0
  18. data/examples/rbenv.eye +11 -0
  19. data/examples/sidekiq.eye +23 -0
  20. data/examples/test.eye +81 -0
  21. data/examples/thin-farm.eye +29 -0
  22. data/examples/unicorn.eye +31 -0
  23. data/eye.gemspec +42 -0
  24. data/lib/eye.rb +28 -0
  25. data/lib/eye/application.rb +74 -0
  26. data/lib/eye/checker.rb +138 -0
  27. data/lib/eye/checker/cpu.rb +27 -0
  28. data/lib/eye/checker/file_ctime.rb +25 -0
  29. data/lib/eye/checker/file_size.rb +34 -0
  30. data/lib/eye/checker/http.rb +98 -0
  31. data/lib/eye/checker/memory.rb +27 -0
  32. data/lib/eye/checker/socket.rb +152 -0
  33. data/lib/eye/child_process.rb +101 -0
  34. data/lib/eye/client.rb +32 -0
  35. data/lib/eye/config.rb +88 -0
  36. data/lib/eye/control.rb +2 -0
  37. data/lib/eye/controller.rb +53 -0
  38. data/lib/eye/controller/commands.rb +73 -0
  39. data/lib/eye/controller/helpers.rb +61 -0
  40. data/lib/eye/controller/load.rb +214 -0
  41. data/lib/eye/controller/options.rb +48 -0
  42. data/lib/eye/controller/send_command.rb +115 -0
  43. data/lib/eye/controller/show_history.rb +62 -0
  44. data/lib/eye/controller/status.rb +131 -0
  45. data/lib/eye/dsl.rb +48 -0
  46. data/lib/eye/dsl/application_opts.rb +33 -0
  47. data/lib/eye/dsl/chain.rb +12 -0
  48. data/lib/eye/dsl/child_process_opts.rb +8 -0
  49. data/lib/eye/dsl/config_opts.rb +48 -0
  50. data/lib/eye/dsl/group_opts.rb +27 -0
  51. data/lib/eye/dsl/helpers.rb +12 -0
  52. data/lib/eye/dsl/main.rb +40 -0
  53. data/lib/eye/dsl/opts.rb +140 -0
  54. data/lib/eye/dsl/process_opts.rb +21 -0
  55. data/lib/eye/dsl/pure_opts.rb +110 -0
  56. data/lib/eye/dsl/validation.rb +59 -0
  57. data/lib/eye/group.rb +134 -0
  58. data/lib/eye/group/chain.rb +81 -0
  59. data/lib/eye/http.rb +31 -0
  60. data/lib/eye/http/router.rb +25 -0
  61. data/lib/eye/loader.rb +23 -0
  62. data/lib/eye/logger.rb +80 -0
  63. data/lib/eye/notify.rb +86 -0
  64. data/lib/eye/notify/jabber.rb +30 -0
  65. data/lib/eye/notify/mail.rb +44 -0
  66. data/lib/eye/process.rb +86 -0
  67. data/lib/eye/process/child.rb +58 -0
  68. data/lib/eye/process/commands.rb +256 -0
  69. data/lib/eye/process/config.rb +70 -0
  70. data/lib/eye/process/controller.rb +76 -0
  71. data/lib/eye/process/data.rb +47 -0
  72. data/lib/eye/process/monitor.rb +95 -0
  73. data/lib/eye/process/notify.rb +32 -0
  74. data/lib/eye/process/scheduler.rb +78 -0
  75. data/lib/eye/process/states.rb +86 -0
  76. data/lib/eye/process/states_history.rb +66 -0
  77. data/lib/eye/process/system.rb +97 -0
  78. data/lib/eye/process/trigger.rb +54 -0
  79. data/lib/eye/process/validate.rb +23 -0
  80. data/lib/eye/process/watchers.rb +69 -0
  81. data/lib/eye/reason.rb +20 -0
  82. data/lib/eye/server.rb +52 -0
  83. data/lib/eye/settings.rb +46 -0
  84. data/lib/eye/system.rb +154 -0
  85. data/lib/eye/system_resources.rb +86 -0
  86. data/lib/eye/trigger.rb +53 -0
  87. data/lib/eye/trigger/flapping.rb +28 -0
  88. data/lib/eye/utils.rb +14 -0
  89. data/lib/eye/utils/alive_array.rb +31 -0
  90. data/lib/eye/utils/celluloid_chain.rb +70 -0
  91. data/lib/eye/utils/leak_19.rb +7 -0
  92. data/lib/eye/utils/tail.rb +20 -0
  93. metadata +390 -0
@@ -0,0 +1,27 @@
1
+ class Eye::Checker::Memory < Eye::Checker
2
+
3
+ # checks :memory, :every => 3.seconds, :below => 80.megabytes, :times => [3,5]
4
+
5
+ param :below, [Fixnum, Float], true
6
+
7
+ def check_name
8
+ @check_name ||= "memory(#{human_value(below)})"
9
+ end
10
+
11
+ def get_value
12
+ Eye::SystemResources.memory(@pid).to_i * 1024
13
+ end
14
+
15
+ def human_value(value)
16
+ "#{value.to_i / 1024 / 1024}Mb"
17
+ end
18
+
19
+ def good?(value)
20
+ if below
21
+ value < below
22
+ else
23
+ true
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,152 @@
1
+ class Eye::Checker::Socket < Eye::Checker::Defer
2
+
3
+ # checks :socket, :every => 5.seconds, :times => 1,
4
+ # :addr => "unix:/var/run/daemon.sock", :timeout => 3.seconds,
5
+ #
6
+ # Available parameters:
7
+ # :addr the socket addr to open. The format is tcp://<host>:<port> or unix:<path>
8
+ # :timeout generic timeout for reading data from socket
9
+ # :open_timeout override generic timeout for the connection
10
+ # :read_timeout override generic timeout for data read/write
11
+ # :send_data after connection send this data
12
+ # :expect_data after sending :send_data expect this response. Can be a string, Regexp or a Proc
13
+ # :protocol way of pack,unpack messages (default = socket default), example: :protocol => :em_object
14
+
15
+ param :addr, String, true
16
+ param :timeout, [Fixnum, Float]
17
+ param :open_timeout, [Fixnum, Float]
18
+ param :read_timeout, [Fixnum, Float]
19
+ param :send_data
20
+ param :expect_data, [String, Regexp, Proc]
21
+ param :protocol, [Symbol], nil, nil, [:default, :em_object]
22
+
23
+ def initialize(*args)
24
+ super
25
+ @open_timeout = (open_timeout || 1).to_f
26
+ @read_timeout = (read_timeout || timeout || 5).to_f
27
+
28
+ if addr =~ %r[\Atcp://(.*?):(.*?)\z]
29
+ @socket_family = :tcp
30
+ @socket_addr = $1
31
+ @socket_port = $2.to_i
32
+ elsif addr =~ %r[\Aunix:(.*)\z]
33
+ @socket_family = :unix
34
+ @socket_path = $1
35
+ end
36
+ end
37
+
38
+ def get_value
39
+ sock = begin
40
+ Timeout::timeout(@open_timeout){ open_socket }
41
+ rescue Timeout::Error
42
+ return { :exception => "OpenTimeout<#{@open_timeout}>" }
43
+ end
44
+
45
+ if send_data
46
+ begin
47
+ Timeout::timeout(@read_timeout) do
48
+ _write_data(sock, send_data)
49
+ result = _read_data(sock)
50
+
51
+ { :result => result }
52
+ end
53
+ rescue Timeout::Error
54
+ return { :exception => "ReadTimeout<#{@read_timeout}>" }
55
+ end
56
+ else
57
+ { :result => :listen }
58
+ end
59
+
60
+ rescue Exception => e
61
+ { :exception => "Error<#{e.message}>" }
62
+
63
+ ensure
64
+ sock.close if sock
65
+ end
66
+
67
+ def good?(value)
68
+ return false if !value[:result]
69
+
70
+ if expect_data
71
+ if expect_data.is_a?(Proc)
72
+ match = begin
73
+ !!expect_data[value[:result]]
74
+ rescue Timeout::Error, Exception => ex
75
+ mes = "proc match failed with '#{ex.message}'"
76
+ error(mes)
77
+ value[:notice] = mes
78
+ return false
79
+ end
80
+
81
+ unless match
82
+ warn "proc #{expect_data} not matched (#{value[:result].truncate(30)}) answer"
83
+ value[:notice] = "missing proc validation"
84
+ end
85
+
86
+ return match
87
+ end
88
+
89
+ return true if expect_data.is_a?(Regexp) && expect_data.match(value[:result])
90
+ return true if value[:result].to_s == expect_data.to_s
91
+
92
+ warn "#{expect_data} not matched (#{value[:result].truncate(30)}) answer"
93
+ value[:notice] = "missing '#{expect_data.to_s}'"
94
+ return false
95
+ end
96
+
97
+ return true
98
+ end
99
+
100
+ def human_value(value)
101
+ if !value.is_a?(Hash)
102
+ '-'
103
+ elsif value[:exception]
104
+ value[:exception]
105
+ else
106
+ if value[:result] == :listen
107
+ "listen"
108
+ else
109
+ res = "#{value[:result].to_s.size}b"
110
+ res += "<#{value[:notice]}>" if value[:notice]
111
+ res
112
+ end
113
+ end
114
+ end
115
+
116
+ private
117
+
118
+ def open_socket
119
+ if @socket_family == :tcp
120
+ TCPSocket.open(@socket_addr, @socket_port)
121
+ elsif @socket_family == :unix
122
+ UNIXSocket.open(@socket_path)
123
+ else
124
+ raise "Unknown socket addr #{addr}"
125
+ end
126
+ end
127
+
128
+ def _write_data(socket, data)
129
+ case protocol
130
+ when :em_object
131
+ data = Marshal.dump(data)
132
+ socket.write([data.bytesize, data].pack('Na*'))
133
+ else
134
+ socket.write(data.to_s)
135
+ end
136
+ end
137
+
138
+ def _read_data(socket)
139
+ case protocol
140
+ when :em_object
141
+ content = ""
142
+ msg_size = socket.recv(4).unpack('N')[0] rescue 0
143
+ content << socket.recv(msg_size - content.length) while content.length < msg_size
144
+ if content.present?
145
+ Marshal.load(content) rescue 'corrupted_marshal'
146
+ end
147
+ else
148
+ socket.readline.chop
149
+ end
150
+ end
151
+
152
+ end
@@ -0,0 +1,101 @@
1
+ require 'celluloid'
2
+
3
+ class Eye::ChildProcess
4
+ include Celluloid
5
+
6
+ # needs: kill_process
7
+ include Eye::Process::Commands
8
+
9
+ # easy config + defaults: prepare_config, c, []
10
+ include Eye::Process::Config
11
+
12
+ # conditional watchers: start_checkers
13
+ include Eye::Process::Watchers
14
+
15
+ # system methods: send_signal
16
+ include Eye::Process::System
17
+
18
+ # logger methods: info, ...
19
+ include Eye::Logger::Helpers
20
+
21
+ # self_status_data
22
+ include Eye::Process::Data
23
+
24
+ # manage notify methods
25
+ include Eye::Process::Notify
26
+
27
+ # scheduler
28
+ include Eye::Process::Scheduler
29
+
30
+ attr_reader :pid, :name, :full_name, :config, :watchers
31
+
32
+ def initialize(pid, config = {}, logger_prefix = nil)
33
+ raise 'Empty pid' unless pid
34
+
35
+ @pid = pid
36
+ @config = prepare_config(config)
37
+ @name = "child-#{pid}"
38
+ @full_name = [logger_prefix, @name] * ':'
39
+
40
+ @logger = Eye::Logger.new(@full_name)
41
+
42
+ @watchers = {}
43
+
44
+ debug "start monitoring CHILD config: #{@config.inspect}"
45
+
46
+ start_checkers
47
+ end
48
+
49
+ def state
50
+ :up
51
+ end
52
+
53
+ def up?
54
+ state == :up
55
+ end
56
+
57
+ def send_command(command, *args)
58
+ schedule command, *args, Eye::Reason::User.new(command)
59
+ end
60
+
61
+ def start
62
+ end
63
+
64
+ def stop
65
+ kill_process
66
+ end
67
+
68
+ def restart
69
+ if self[:restart_command]
70
+ execute_restart_command
71
+ else
72
+ stop
73
+ end
74
+ end
75
+
76
+ def monitor
77
+ end
78
+
79
+ def unmonitor
80
+ end
81
+
82
+ def delete
83
+ end
84
+
85
+ def destroy
86
+ remove_watchers
87
+ terminate
88
+ end
89
+
90
+ def signal(sig)
91
+ if self.pid
92
+ res = send_signal(sig)
93
+ info "send signal #{sig} to #{self.pid} = #{res}"
94
+ end
95
+ end
96
+
97
+ def status_data(debug = false)
98
+ self_status_data(debug)
99
+ end
100
+
101
+ end
data/lib/eye/client.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+
4
+ class Eye::Client
5
+ attr_reader :socket_path
6
+
7
+ def initialize(socket_path)
8
+ @socket_path = socket_path
9
+ end
10
+
11
+ def command(cmd, *args)
12
+ attempt_command([cmd, *args] * '|')
13
+ end
14
+
15
+ def attempt_command(pack)
16
+ Timeout.timeout(Eye::Settings.client_timeout) do
17
+ return send_request(pack)
18
+ end
19
+
20
+ rescue Timeout::Error, EOFError
21
+ :timeouted
22
+ end
23
+
24
+ def send_request(pack)
25
+ UNIXSocket.open(@socket_path) do |socket|
26
+ socket.puts(pack)
27
+ data = socket.read
28
+ res = Marshal.load(data) rescue :corrupred_marshal
29
+ end
30
+ end
31
+
32
+ end
data/lib/eye/config.rb ADDED
@@ -0,0 +1,88 @@
1
+ class Eye::Config
2
+
3
+ attr_reader :settings, :applications
4
+
5
+ def initialize(settings = {}, applications = {})
6
+ @settings = settings
7
+ @applications = applications
8
+ end
9
+
10
+ def merge(other_config)
11
+ Eye::Config.new(@settings.merge(other_config.settings), @applications.merge(other_config.applications))
12
+ end
13
+
14
+ def merge!(other_config)
15
+ @settings.merge!(other_config.settings)
16
+ @applications.merge!(other_config.applications)
17
+ end
18
+
19
+ def to_h
20
+ {:settings => @settings, :applications => @applications}
21
+ end
22
+
23
+ # raise an error if config wrong
24
+ def validate!
25
+ all_processes = processes
26
+
27
+ # Check blank pid_files
28
+ no_pid_file = all_processes.select{|c| c[:pid_file].blank? }
29
+ if no_pid_file.present?
30
+ raise Eye::Dsl::Error, "blank pid_file for: #{no_pid_file.map{|c| c[:name]} * ', '}"
31
+ end
32
+
33
+ # Check dublicates of the full pid_file
34
+
35
+ dubl_pids = all_processes.each_with_object(Hash.new(0)) do |o, h|
36
+ ex_pid_file = Eye::System.normalized_file(o[:pid_file], o[:working_dir])
37
+ h[ex_pid_file] += 1
38
+ end
39
+ dubl_pids = dubl_pids.select{|k,v| v>1}
40
+
41
+ if dubl_pids.present?
42
+ raise Eye::Dsl::Error, "dublicate pid_files: #{dubl_pids.inspect}"
43
+ end
44
+
45
+ # Check dublicates of the full_name
46
+ dubl_names = all_processes.each_with_object(Hash.new(0)) do |o, h|
47
+ full_name = "#{o[:application]}:#{o[:group]}:#{o[:name]}"
48
+ h[full_name] += 1
49
+ end
50
+ dubl_names = dubl_names.select{|k,v| v>1}
51
+
52
+ if dubl_names.present?
53
+ raise Eye::Dsl::Error, "dublicate names: #{dubl_names.inspect}"
54
+ end
55
+
56
+ # validate processes with their own validate
57
+ all_processes.each do |process_cfg|
58
+ Eye::Process.validate process_cfg
59
+ end
60
+ end
61
+
62
+ def processes
63
+ applications.values.map{|e| (e[:groups] || {}).values.map{|c| (c[:processes] || {}).values} }.flatten
64
+ end
65
+
66
+ def application_names
67
+ applications.keys
68
+ end
69
+
70
+ def delete_app(name)
71
+ applications.delete(name)
72
+ end
73
+
74
+ def delete_group(name)
75
+ applications.each do |app_name, app_cfg|
76
+ app_cfg[:groups].delete(name)
77
+ end
78
+ end
79
+
80
+ def delete_process(name)
81
+ applications.each do |app_name, app_cfg|
82
+ app_cfg[:groups].each do |gr_name, gr_cfg|
83
+ gr_cfg[:processes].delete(name)
84
+ end
85
+ end
86
+ end
87
+
88
+ end
@@ -0,0 +1,2 @@
1
+ # controller global singlton
2
+ Eye::Control = Eye::Controller.new
@@ -0,0 +1,53 @@
1
+ require 'celluloid'
2
+
3
+ require 'yaml'
4
+ require 'active_support'
5
+ require 'active_support/core_ext/object/blank'
6
+ require 'active_support/core_ext/object/try'
7
+ require 'active_support/core_ext/numeric'
8
+ require 'active_support/core_ext/string/filters'
9
+
10
+ require_relative 'utils/leak_19'
11
+
12
+ Eye.send(:extend, Eye::Logger::Helpers)
13
+
14
+ class Eye::Controller
15
+ include Celluloid
16
+
17
+ autoload :Load, 'eye/controller/load'
18
+ autoload :Helpers, 'eye/controller/helpers'
19
+ autoload :Commands, 'eye/controller/commands'
20
+ autoload :Status, 'eye/controller/status'
21
+ autoload :SendCommand, 'eye/controller/send_command'
22
+ autoload :ShowHistory, 'eye/controller/show_history'
23
+ autoload :Options, 'eye/controller/options'
24
+
25
+ include Eye::Logger::Helpers
26
+ include Eye::Controller::Load
27
+ include Eye::Controller::Helpers
28
+ include Eye::Controller::Commands
29
+ include Eye::Controller::Status
30
+ include Eye::Controller::SendCommand
31
+ include Eye::Controller::ShowHistory
32
+ include Eye::Controller::Options
33
+
34
+ attr_reader :applications, :current_config
35
+
36
+ def initialize
37
+ @applications = []
38
+ @current_config = Eye::Config.new
39
+
40
+ Eye.instance_variable_set(:@logger, Eye::Logger.new('eye'))
41
+ @logger = Eye.logger
42
+ Celluloid::logger = Eye.logger
43
+
44
+ Eye::SystemResources.setup
45
+
46
+ info "starting #{Eye::ABOUT} (#{$$})"
47
+ end
48
+
49
+ def settings
50
+ current_config.settings
51
+ end
52
+
53
+ end