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,73 @@
1
+ module Eye::Controller::Commands
2
+
3
+ # Main method, answer for the client command
4
+ def command(cmd, *args)
5
+ debug "client command: #{cmd} #{args * ', '}"
6
+
7
+ start_at = Time.now
8
+ cmd = cmd.to_sym
9
+
10
+ res = case cmd
11
+ when :start, :stop, :restart, :unmonitor, :monitor
12
+ send_command(cmd, *args)
13
+ when :delete
14
+ exclusive{ send_command(cmd, *args) }
15
+ when :signal
16
+ signal(*args)
17
+ when :break_chain
18
+ break_chain(*args)
19
+ when :load
20
+ exclusive{ load(*args) }
21
+ when :info
22
+ info_string(*args)
23
+ when :xinfo
24
+ info_string_debug(*args)
25
+ when :oinfo
26
+ info_string_short
27
+ when :history
28
+ history_string(*args)
29
+ when :quit
30
+ quit
31
+ when :check
32
+ check(*args)
33
+ when :explain
34
+ explain(*args)
35
+ when :match
36
+ match(*args)
37
+ when :ping
38
+ :pong
39
+ when :logger_dev
40
+ Eye::Logger.dev
41
+
42
+ # object commands, for api
43
+ when :raw_info
44
+ info_data(*args)
45
+ when :raw_history
46
+ history_data(*args)
47
+
48
+ else
49
+ :unknown_command
50
+ end
51
+
52
+ GC.start
53
+ info "client command: #{cmd} #{args * ', '} (#{Time.now - start_at}s)"
54
+
55
+ res
56
+ end
57
+
58
+ private
59
+
60
+ def quit
61
+ info 'exiting...'
62
+ delete
63
+ sleep 1
64
+ Eye::System.send_signal($$) # soft terminate
65
+ sleep 2
66
+ Eye::System.send_signal($$, 9)
67
+ end
68
+
69
+ def delete
70
+ send_command(:delete)
71
+ end
72
+
73
+ end
@@ -0,0 +1,61 @@
1
+ module Eye::Controller::Helpers
2
+
3
+ def set_proc_line
4
+ str = Eye::PROCLINE
5
+ str += " (#{@applications.map(&:name) * ', '})" if @applications.present?
6
+ $0 = str
7
+ end
8
+
9
+ def process_by_name(name)
10
+ all_processes.detect{|c| c.name == name}
11
+ end
12
+
13
+ def group_by_name(name)
14
+ all_groups.detect{|c| c.name == name}
15
+ end
16
+
17
+ def application_by_name(name)
18
+ @applications.detect{|c| c.name == name}
19
+ end
20
+
21
+ def all_processes
22
+ processes = []
23
+ all_groups.each do |gr|
24
+ processes += gr.processes.to_a
25
+ end
26
+
27
+ processes
28
+ end
29
+
30
+ def all_groups
31
+ groups = []
32
+ @applications.each do |app|
33
+ groups += app.groups.to_a
34
+ end
35
+
36
+ groups
37
+ end
38
+
39
+ # {'app_name' => {'group_name' => {'process_name' => 'pid_file'}}}
40
+ def short_tree
41
+ res = {}
42
+ @applications.each do |app|
43
+ res2 = {}
44
+
45
+ app.groups.each do |group|
46
+ res3 = {}
47
+
48
+ group.processes.each do |process|
49
+ res3[process.name] = process[:pid_file_ex]
50
+ end
51
+
52
+ res2[group.name] = res3
53
+ end
54
+
55
+ res[app.name] = res2
56
+ end
57
+
58
+ res
59
+ end
60
+
61
+ end
@@ -0,0 +1,214 @@
1
+ module Eye::Controller::Load
2
+
3
+ def check(filename)
4
+ { filename => catch_load_error(filename) { parse_config(filename).to_h } }
5
+ end
6
+
7
+ def explain(filename)
8
+ { filename => catch_load_error(filename) { parse_config(filename).to_h } }
9
+ end
10
+
11
+ def load(*obj_strs)
12
+ info "load: #{obj_strs}"
13
+
14
+ res = Hash.new
15
+
16
+ globbing(*obj_strs).each do |filename|
17
+ res[filename] = catch_load_error(filename) do
18
+ cfg = parse_config(filename)
19
+ load_config(filename, cfg)
20
+ nil
21
+ end
22
+ end
23
+
24
+ set_proc_line
25
+
26
+ res
27
+ end
28
+
29
+ private
30
+
31
+ # regexp for clean backtrace to show for user
32
+ BT_REGX = %r[/lib/eye/|lib/celluloid|internal:prelude|logger.rb:|active_support/core_ext|shellwords.rb].freeze
33
+
34
+ def catch_load_error(filename = nil, &block)
35
+ { :error => false, :config => yield }
36
+
37
+ rescue Eye::Dsl::Error, Exception, NoMethodError => ex
38
+ error "load: config error <#{filename}>: #{ex.message}"
39
+
40
+ # filter backtrace for user output
41
+ bt = (ex.backtrace || [])
42
+ bt = bt.reject{|line| line.to_s =~ BT_REGX }
43
+ error bt.join("\n")
44
+
45
+ res = { :error => true, :message => ex.message }
46
+ res.merge!(:backtrace => bt) if bt.present?
47
+ res
48
+ end
49
+
50
+ def globbing(*obj_strs)
51
+ res = []
52
+ return res if obj_strs.empty?
53
+
54
+ obj_strs.each do |filename|
55
+ mask = if File.directory?(filename)
56
+ File.join filename, '{*.eye}'
57
+ else
58
+ filename
59
+ end
60
+
61
+ debug "load: globbing mask #{mask}"
62
+
63
+ sub = []
64
+ Dir[mask].each do |config_path|
65
+ sub << config_path
66
+ end
67
+ sub = [mask] if sub.empty?
68
+
69
+ res += sub
70
+ end
71
+
72
+ res
73
+ end
74
+
75
+ # return: result, config
76
+ def parse_config(filename)
77
+ raise Eye::Dsl::Error, "config file '#{filename}' not found!" unless File.exists?(filename)
78
+ debug "parse #{filename}"
79
+
80
+ cfg = Eye::Dsl.parse(nil, filename)
81
+ @current_config.merge(cfg).validate! # just validate summary config here
82
+ cfg
83
+ end
84
+
85
+ # !!! exclusive operation
86
+ def load_config(filename, config)
87
+ info "load #{filename}"
88
+ new_cfg = @current_config.merge(config)
89
+ new_cfg.validate!
90
+
91
+ load_options(new_cfg.settings)
92
+ create_objects(new_cfg.applications, config.application_names)
93
+ @current_config = new_cfg
94
+ end
95
+
96
+ # load global config options
97
+ def load_options(opts)
98
+ return if opts.blank?
99
+
100
+ opts.each do |key, value|
101
+ method = "set_opt_#{key}"
102
+ send(method, value) if value && respond_to?(method)
103
+ end
104
+ end
105
+
106
+ # create objects as diff, from configs
107
+ def create_objects(apps_config, changed_apps = [])
108
+ debug 'create objects'
109
+
110
+ apps_config.each do |app_name, app_cfg|
111
+ update_or_create_application(app_name, app_cfg.clone) if changed_apps.include?(app_name)
112
+ end
113
+
114
+ # sorting applications
115
+ @applications.sort_by!(&:name)
116
+ end
117
+
118
+ def update_or_create_application(app_name, app_config)
119
+ @old_groups = {}
120
+ @old_processes = {}
121
+
122
+ app = @applications.detect{|c| c.name == app_name}
123
+
124
+ if app
125
+ app.groups.each do |group|
126
+ @old_groups[group.name] = group
127
+ group.processes.each do |proc|
128
+ @old_processes[proc.name] = proc
129
+ end
130
+ end
131
+
132
+ @applications.delete(app)
133
+
134
+ debug "update app #{app_name}"
135
+ else
136
+ debug "create app #{app_name}"
137
+ end
138
+
139
+ app = Eye::Application.new(app_name, app_config)
140
+ @applications << app
141
+ @added_groups, @added_processes = [], []
142
+
143
+ new_groups = app_config.delete(:groups) || {}
144
+ new_groups.each do |group_name, group_cfg|
145
+ group = update_or_create_group(group_name, group_cfg.clone)
146
+ app.add_group(group)
147
+ group.resort_processes
148
+ end
149
+
150
+ # now, need to clear @old_groups, and @old_processes
151
+ @old_groups.each{|_, group| group.clear; group.send_command(:delete) }
152
+ @old_processes.each{|_, process| process.send_command(:delete) if process.alive? }
153
+
154
+ # schedule monitoring for new groups, processes
155
+ added_fully_groups = []
156
+ @added_groups.each do |group|
157
+ if group.processes.size > 0 && (group.processes.pure - @added_processes).size == 0
158
+ added_fully_groups << group
159
+ @added_processes -= group.processes.pure
160
+ end
161
+ end
162
+
163
+ added_fully_groups.each{|group| group.send_command :monitor }
164
+ @added_processes.each{|process| process.send_command :monitor }
165
+
166
+ # remove links to prevent memory leaks
167
+ @old_groups = nil
168
+ @old_processes = nil
169
+ @added_groups = nil
170
+ @added_processes = nil
171
+
172
+ app.resort_groups
173
+
174
+ app
175
+ end
176
+
177
+ def update_or_create_group(group_name, group_config)
178
+ group = if @old_groups[group_name]
179
+ debug "update group #{group_name}"
180
+ group = @old_groups.delete(group_name)
181
+ group.schedule :update_config, group_config, Eye::Reason::User.new(:'load config')
182
+ group.clear
183
+ group
184
+ else
185
+ debug "create group #{group_name}"
186
+ gr = Eye::Group.new(group_name, group_config)
187
+ @added_groups << gr
188
+ gr
189
+ end
190
+
191
+ processes = group_config.delete(:processes) || {}
192
+ processes.each do |process_name, process_cfg|
193
+ process = update_or_create_process(process_name, process_cfg.clone)
194
+ group.add_process(process)
195
+ end
196
+
197
+ group
198
+ end
199
+
200
+ def update_or_create_process(process_name, process_cfg)
201
+ if @old_processes[process_name]
202
+ debug "update process #{process_name}"
203
+ process = @old_processes.delete(process_name)
204
+ process.schedule :update_config, process_cfg, Eye::Reason::User.new(:'load config')
205
+ process
206
+ else
207
+ debug "create process #{process_name}"
208
+ process = Eye::Process.new(process_cfg)
209
+ @added_processes << process
210
+ process
211
+ end
212
+ end
213
+
214
+ end
@@ -0,0 +1,48 @@
1
+ module Eye::Controller::Options
2
+
3
+ def set_opt_logger(logger)
4
+ # do not apply logger, if in stdout state
5
+ if !%w{stdout stderr}.include?(Eye::Logger.dev)
6
+ if logger.blank?
7
+ Eye::Logger.link_logger(nil)
8
+ else
9
+ Eye::Logger.link_logger(logger)
10
+ end
11
+ end
12
+ end
13
+
14
+ def set_opt_logger_level(level)
15
+ Eye::Logger.log_level = level
16
+ end
17
+
18
+ def set_opt_http(params = {})
19
+ if params[:enable]
20
+ if @http
21
+ if params[:host] != @http.host || params[:host].to_i != @http.host
22
+ stop_http
23
+ start_http(params[:host], params[:port])
24
+ end
25
+ else
26
+ start_http(params[:host], params[:port])
27
+ end
28
+ else
29
+ stop_http if @http
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def stop_http
36
+ if @http
37
+ @http.stop
38
+ @http = nil
39
+ end
40
+ end
41
+
42
+ def start_http(host, port)
43
+ require 'eye/http' # ruby 2.0 autoload bug
44
+ @http = Eye::Http.new(host, port)
45
+ @http.start
46
+ end
47
+
48
+ end
@@ -0,0 +1,115 @@
1
+ module Eye::Controller::SendCommand
2
+
3
+ def send_command(command, *obj_strs)
4
+ matched_objects(*obj_strs) do |obj|
5
+ if command.to_sym == :delete
6
+ remove_object_from_tree(obj)
7
+ set_proc_line # to sync proc line if was delete application
8
+ end
9
+
10
+ obj.send_command(command)
11
+ end
12
+ end
13
+
14
+ def match(*obj_strs)
15
+ matched_objects(*obj_strs)
16
+ end
17
+
18
+ def signal(sig, *obj_strs)
19
+ matched_objects(*obj_strs) do |obj|
20
+ obj.send_command :signal, sig || 0
21
+ end
22
+ end
23
+
24
+ def break_chain(*obj_strs)
25
+ matched_objects(*obj_strs) do |obj|
26
+ obj.send_command(:break_chain)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def matched_objects(*obj_strs, &block)
33
+ objs = find_objects(*obj_strs)
34
+ res = objs.map(&:full_name)
35
+ objs.each{|obj| block[obj] } if block
36
+ res
37
+ end
38
+
39
+ def remove_object_from_tree(obj)
40
+ klass = obj.class
41
+
42
+ if klass == Eye::Application
43
+ @applications.delete(obj)
44
+ @current_config.delete_app(obj.name)
45
+ end
46
+
47
+ if klass == Eye::Group
48
+ @applications.each{|app| app.groups.delete(obj) }
49
+ @current_config.delete_group(obj.name)
50
+ end
51
+
52
+ if klass == Eye::Process
53
+ @applications.each{|app| app.groups.each{|gr| gr.processes.delete(obj) }}
54
+ @current_config.delete_process(obj.name)
55
+ end
56
+ end
57
+
58
+ # find object to action, restart ... (app, group or process)
59
+ # nil if not found
60
+ def find_objects(*obj_strs)
61
+ return [] if obj_strs.blank?
62
+ return @applications.dup if obj_strs.size == 1 && (obj_strs[0].strip == 'all' || obj_strs[0].strip == '*')
63
+
64
+ res = []
65
+ obj_strs.map{|c| c.split(",")}.flatten.each do |mask|
66
+ res += find_objects_by_mask(mask)
67
+ end
68
+
69
+ if res.size > 1
70
+ # remove inherited targets
71
+
72
+ final = []
73
+ res.each do |obj|
74
+ sub_object = res.any?{|a| a.sub_object?(obj) }
75
+ final << obj unless sub_object
76
+ end
77
+
78
+ res = final
79
+ end
80
+
81
+ res.present? ? Eye::Utils::AliveArray.new(res) : []
82
+ end
83
+
84
+ def find_objects_by_mask(mask)
85
+ mask.strip!
86
+
87
+ res = []
88
+ str = Regexp.escape(mask).gsub('\*', '.*?')
89
+ r = %r{\A#{str}}
90
+
91
+ # find app
92
+ res = @applications.select{|a| a.name =~ r || a.full_name =~ r }
93
+
94
+ # find group
95
+ @applications.each do |a|
96
+ res += a.groups.select{|gr| gr.name =~ r || gr.full_name =~ r }
97
+ end
98
+
99
+ # find process
100
+ @applications.each do |a|
101
+ a.groups.each do |gr|
102
+ gr.processes.each do |p|
103
+ res << p if p.name =~ r || p.full_name =~ r
104
+
105
+ if p.childs.present?
106
+ res += p.childs.values.select{|ch| ch.alive? && (ch.name =~ r || ch.full_name =~ r) }
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ res
113
+ end
114
+
115
+ end