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,21 @@
1
+ class Eye::Dsl::ProcessOpts < Eye::Dsl::Opts
2
+
3
+ def monitor_children(&block)
4
+ opts = Eye::Dsl::ChildProcessOpts.new
5
+ opts.instance_eval(&block)
6
+ @config[:monitor_children] ||= {}
7
+ @config[:monitor_children].merge!(opts.config)
8
+ end
9
+
10
+ def xmonitor_children(&block); end
11
+
12
+ def application
13
+ parent.try(:parent)
14
+ end
15
+ alias :app :application
16
+
17
+ def group
18
+ parent
19
+ end
20
+
21
+ end
@@ -0,0 +1,110 @@
1
+ class Eye::Dsl::PureOpts
2
+
3
+ def self.create_options_methods(arr, types = nil)
4
+ m = Module.new do
5
+ arr.each do |opt|
6
+ define_method("set_#{opt}") do |arg|
7
+ key = opt.to_sym
8
+
9
+ if (disallow_options && disallow_options.include?(key)) || (allow_options && !allow_options.include?(key))
10
+ raise Eye::Dsl::Error, "disallow option #{key} for #{self.class.inspect}"
11
+ end
12
+
13
+ if types
14
+ good_type = Array(types).any?{|type| arg.is_a?(type) } || arg.nil?
15
+ raise Eye::Dsl::Error, "bad :#{opt} value #{arg.inspect}, type should be #{types.inspect}" unless good_type
16
+ end
17
+
18
+ @config[key] = arg
19
+ end
20
+
21
+ define_method("get_#{opt}") do
22
+ @config[ opt.to_sym ]
23
+ end
24
+
25
+ define_method(opt) do |*args|
26
+ if args.blank?
27
+ # getter
28
+ send "get_#{opt}"
29
+ else
30
+ send "set_#{opt}", *args
31
+ end
32
+ end
33
+
34
+ define_method("#{opt}=") do |arg|
35
+ send opt, arg
36
+ end
37
+ end
38
+ end
39
+
40
+ self.send :include, m
41
+ end
42
+
43
+ attr_reader :name, :full_name
44
+ attr_reader :config, :parent
45
+
46
+ def initialize(name = nil, parent = nil, merge_parent_config = true)
47
+ @name = name.to_s
48
+ @full_name = @name
49
+
50
+ if parent
51
+ @parent = parent
52
+ @config = merge_parent_config ? Eye::Utils::deep_clone(parent.config) : {}
53
+ @full_name = "#{parent.full_name}:#{@full_name}"
54
+ else
55
+ @config = {}
56
+ end
57
+
58
+ @config[:name] = @name if @name.present?
59
+ end
60
+
61
+ def allow_options
62
+ nil
63
+ end
64
+
65
+ def disallow_options
66
+ []
67
+ end
68
+
69
+ def with_condition(cond = true, &block)
70
+ self.instance_eval(&block) if cond && block
71
+ end
72
+
73
+ def include(proc, *args)
74
+ if proc.is_a?(String)
75
+ self.class.with_parsed_file(proc) do |path|
76
+ if File.exists?(path)
77
+ Eye::Dsl.debug "=> load #{path}"
78
+ self.instance_eval(File.read(path))
79
+ Eye::Dsl.debug "<= load #{path}"
80
+ end
81
+ end
82
+ else
83
+ ie = if args.present?
84
+ lambda{|i| proc[i, *args] }
85
+ else
86
+ proc
87
+ end
88
+
89
+ self.instance_eval(&ie)
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ def self.with_parsed_file(file_name)
96
+ saved_parsed_filename = Eye.parsed_filename
97
+
98
+ require 'pathname'
99
+
100
+ real_filename = Eye.parsed_filename && File.symlink?(Eye.parsed_filename) ? File.readlink(Eye.parsed_filename) : Eye.parsed_filename
101
+ dirname = File.dirname(real_filename) rescue nil
102
+ path = Pathname.new(file_name).expand_path(dirname).to_s
103
+
104
+ Eye.parsed_filename = path
105
+ yield path
106
+ ensure
107
+ Eye.parsed_filename = saved_parsed_filename
108
+ end
109
+
110
+ end
@@ -0,0 +1,59 @@
1
+ module Eye::Dsl::Validation
2
+ class Error < Exception; end
3
+
4
+ def inherited(subclass)
5
+ subclass.validates = self.validates.clone
6
+ subclass.should_bes = self.should_bes.clone
7
+ subclass.defaults = self.defaults.clone
8
+ subclass.variants = self.variants.clone
9
+ end
10
+
11
+ attr_accessor :validates, :should_bes, :defaults, :variants
12
+
13
+ def validates; @validates ||= {}; end
14
+ def should_bes; @should_bes ||= []; end
15
+ def defaults; @defaults ||= {}; end
16
+ def variants; @variants ||= {}; end
17
+
18
+ def param(param, types = [], should_be = false, default = nil, _variants = nil)
19
+ param = param.to_sym
20
+
21
+ validates[param] = types
22
+ should_bes << param if should_be
23
+ defaults[param] = default
24
+ variants[param] = _variants
25
+
26
+ define_method "#{param}" do
27
+ @options[param.to_sym] || default
28
+ end
29
+ end
30
+
31
+ def validate(options = {})
32
+ options.each do |param, value|
33
+ param = param.to_sym
34
+ types = validates[param]
35
+ unless types
36
+ if param != :type
37
+ raise Error, "#{self.name} unknown param :#{param} value #{value.inspect}"
38
+ end
39
+ end
40
+
41
+ if self.variants[param]
42
+ if value && !value.is_a?(Proc) && !self.variants[param].include?(value)
43
+ raise Error, "#{value.inspect} should within #{self.variants[param].inspect}"
44
+ end
45
+ end
46
+
47
+ next if types.blank?
48
+
49
+ types = Array(types)
50
+ good = types.any?{|type| value.is_a?(type) }
51
+ raise Error, "#{self.name} bad param :#{param} value #{value.inspect}, type #{types.inspect}" unless good
52
+ end
53
+
54
+ should_bes.each do |param|
55
+ raise Error, "#{self.name} for param :#{param} value should be" unless options[param.to_sym] || defaults[param.to_sym]
56
+ end
57
+ end
58
+
59
+ end
data/lib/eye/group.rb ADDED
@@ -0,0 +1,134 @@
1
+ require 'celluloid'
2
+
3
+ class Eye::Group
4
+ include Celluloid
5
+
6
+ autoload :Chain, 'eye/group/chain'
7
+
8
+ include Eye::Logger::Helpers
9
+ include Eye::Process::Scheduler
10
+ include Eye::Group::Chain
11
+
12
+ attr_reader :processes, :name, :hidden, :config
13
+
14
+ def initialize(name, config)
15
+ @name = name
16
+ @config = config
17
+ @logger = Eye::Logger.new(full_name)
18
+ @processes = Eye::Utils::AliveArray.new
19
+ @hidden = (name == '__default__')
20
+ debug 'created'
21
+ end
22
+
23
+ def full_name
24
+ @full_name ||= "#{@config[:application]}:#{@name}"
25
+ end
26
+
27
+ def update_config(cfg)
28
+ @config = cfg
29
+ @full_name = nil
30
+ end
31
+
32
+ def add_process(process)
33
+ @processes << process
34
+ end
35
+
36
+ # sort processes in name order
37
+ def resort_processes
38
+ @processes = @processes.sort_by(&:name)
39
+ end
40
+
41
+ def status_data(debug = false)
42
+ plist = @processes.map{|p| p.status_data(debug) }
43
+
44
+ h = { name: name, type: :group, subtree: plist }
45
+
46
+ h.merge!(debug: debug_data) if debug
47
+
48
+ # show current chain
49
+ if current_scheduled_command
50
+ h.update(current_command: current_scheduled_command)
51
+
52
+ if (chain_commands = scheduler_actions_list) && chain_commands.present?
53
+ h.update(chain_commands: chain_commands)
54
+ end
55
+
56
+ if @chain_processes_current && @chain_processes_count
57
+ h.update(chain_progress: [@chain_processes_current, @chain_processes_count])
58
+ end
59
+ end
60
+
61
+ h
62
+ end
63
+
64
+ def debug_data
65
+ {:queue => scheduler_actions_list, :chain => chain_status}
66
+ end
67
+
68
+ def send_command(command, *args)
69
+ info "send_command: #{command}"
70
+
71
+ case command
72
+ when :delete
73
+ delete *args
74
+ when :break_chain
75
+ break_chain *args
76
+ else
77
+ schedule command, *args, Eye::Reason::User.new(command)
78
+ end
79
+ end
80
+
81
+ def start
82
+ chain_command :start
83
+ end
84
+
85
+ def stop
86
+ async_schedule :stop
87
+ end
88
+
89
+ def restart
90
+ chain_command :restart
91
+ end
92
+
93
+ def delete
94
+ async_schedule :delete
95
+ terminate
96
+ end
97
+
98
+ def monitor
99
+ chain_command :monitor
100
+ end
101
+
102
+ def unmonitor
103
+ async_schedule :unmonitor
104
+ end
105
+
106
+ def signal(sig)
107
+ async_schedule :signal, sig
108
+ end
109
+
110
+ def break_chain
111
+ info "break chain"
112
+ scheduler.clear_pending_list
113
+ @chain_breaker = true
114
+ end
115
+
116
+ def clear
117
+ @processes = Eye::Utils::AliveArray.new
118
+ end
119
+
120
+ def sub_object?(obj)
121
+ @processes.include?(obj)
122
+ end
123
+
124
+ private
125
+
126
+ def async_schedule(command, *args)
127
+ info "send to all processes #{command} #{args.present? ? args*',' : nil}"
128
+
129
+ @processes.each do |process|
130
+ process.send_command(command, *args)
131
+ end
132
+ end
133
+
134
+ end
@@ -0,0 +1,81 @@
1
+ module Eye::Group::Chain
2
+
3
+ private
4
+
5
+ def chain_schedule(type, grace, command, *args)
6
+ info "start #{type} with #{grace}s chain #{command} #{args}"
7
+
8
+ @chain_processes_count = @processes.size
9
+ @chain_processes_current = 0
10
+ @chain_breaker = false
11
+
12
+ started_at = Time.now
13
+
14
+ @processes.each do | process |
15
+ chain_schedule_process(process, type, command, *args)
16
+
17
+ @chain_processes_current = @chain_processes_current.to_i + 1
18
+
19
+ # to skip last sleep
20
+ break if @chain_processes_current.to_i == @chain_processes_count.to_i
21
+ break if @chain_breaker
22
+
23
+ # wait next process
24
+ sleep grace.to_f
25
+
26
+ break if @chain_breaker
27
+ end
28
+
29
+ debug "chain finished #{Time.now - started_at}s"
30
+
31
+ @chain_processes_count = nil
32
+ @chain_processes_current = nil
33
+ end
34
+
35
+ def chain_schedule_process(process, type, command, *args)
36
+ debug "chain_schedule_process #{process.name} #{type} #{command}"
37
+
38
+ if type == :sync
39
+ # sync command, with waiting
40
+ # this is very hackety, because call method of the process without its scheduler
41
+ # need to provide some scheduler future
42
+ process.last_scheduled_reason = self.last_scheduled_reason
43
+ process.send(command, *args)
44
+ else
45
+ # async command
46
+ process.send_command(command, *args)
47
+ end
48
+ end
49
+
50
+ def chain_status
51
+ if @config[:chain]
52
+ [:start, :restart].map{|c| @config[:chain][c].try(:[], :grace) }
53
+ end
54
+ end
55
+
56
+ def chain_command(command, *args)
57
+ chain_opts = chain_options(command)
58
+ chain_schedule(chain_opts[:type], chain_opts[:grace], command, *args)
59
+ end
60
+
61
+ # with such delay will chained processes by default
62
+ DEFAULT_CHAIN = 0.2
63
+
64
+ def chain_options(command)
65
+ command = :start if command == :monitor # hack for monitor command, work as start
66
+
67
+ if @config[:chain] && @config[:chain][command]
68
+ type = @config[:chain][command].try :[], :type
69
+ type = [:async, :sync].include?(type) ? type : :async
70
+
71
+ grace = @config[:chain][command].try :[], :grace
72
+ grace = grace ? (grace.to_f rescue DEFAULT_CHAIN) : DEFAULT_CHAIN
73
+
74
+ {:type => type, :grace => grace}
75
+ else
76
+ # default chain case
77
+ {:type => :async, :grace => DEFAULT_CHAIN}
78
+ end
79
+ end
80
+
81
+ end
data/lib/eye/http.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'reel'
2
+
3
+ class Eye::Http
4
+ autoload :Router, 'eye/http/router'
5
+
6
+ attr_reader :server, :host, :port
7
+
8
+ def initialize(host, port)
9
+ @host = host
10
+ @port = port.to_i
11
+ @router = Router
12
+ end
13
+
14
+ def start
15
+ stop
16
+
17
+ @server = Reel::Server.supervise(@host, @port) do |connection|
18
+ while request = connection.request
19
+ status, headers, body = @router.call(Rack::MockRequest.env_for(request.url, :method => request.method, :input => request.body))
20
+ connection.respond(Reel::Response.new(status, headers, body))
21
+ end
22
+ end
23
+ end
24
+
25
+ def stop
26
+ if @server
27
+ @server.terminate
28
+ @server = nil
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ require 'cuba'
2
+ require 'json'
3
+
4
+ Eye::Http::Router = Cuba.new do
5
+
6
+ def json(result)
7
+ res.headers['Content-Type'] = 'application/json; charset=utf-8'
8
+ res.write({ result: result }.to_json)
9
+ end
10
+
11
+ on root do
12
+ res.write Eye::ABOUT
13
+ end
14
+
15
+ on "api/info", param("filter") do |filter|
16
+ json Eye::Control.command(:raw_info, filter)
17
+ end
18
+
19
+ [:start, :stop, :restart, :delete, :unmonitor, :monitor].each do |act|
20
+ on put, "api/#{act}", param("filter") do |filter|
21
+ json Eye::Control.command(act, filter)
22
+ end
23
+ end
24
+
25
+ end