ace-eye 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +38 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +6 -0
  5. data/CHANGES.md +77 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE +22 -0
  8. data/README.md +212 -0
  9. data/Rakefile +35 -0
  10. data/bin/eye +5 -0
  11. data/bin/loader_eye +72 -0
  12. data/bin/runner +16 -0
  13. data/examples/dependency.eye +17 -0
  14. data/examples/notify.eye +19 -0
  15. data/examples/plugin/README.md +15 -0
  16. data/examples/plugin/main.eye +15 -0
  17. data/examples/plugin/plugin.rb +63 -0
  18. data/examples/process_thin.rb +29 -0
  19. data/examples/processes/em.rb +57 -0
  20. data/examples/processes/forking.rb +20 -0
  21. data/examples/processes/sample.rb +144 -0
  22. data/examples/processes/thin.ru +12 -0
  23. data/examples/puma.eye +29 -0
  24. data/examples/rbenv.eye +11 -0
  25. data/examples/sidekiq.eye +23 -0
  26. data/examples/test.eye +87 -0
  27. data/examples/thin-farm.eye +30 -0
  28. data/examples/unicorn.eye +39 -0
  29. data/eye.gemspec +40 -0
  30. data/lib/eye.rb +28 -0
  31. data/lib/eye/application.rb +73 -0
  32. data/lib/eye/checker.rb +258 -0
  33. data/lib/eye/checker/children_count.rb +44 -0
  34. data/lib/eye/checker/children_memory.rb +12 -0
  35. data/lib/eye/checker/cpu.rb +17 -0
  36. data/lib/eye/checker/cputime.rb +13 -0
  37. data/lib/eye/checker/file_ctime.rb +24 -0
  38. data/lib/eye/checker/file_size.rb +34 -0
  39. data/lib/eye/checker/file_touched.rb +15 -0
  40. data/lib/eye/checker/http.rb +96 -0
  41. data/lib/eye/checker/memory.rb +17 -0
  42. data/lib/eye/checker/nop.rb +6 -0
  43. data/lib/eye/checker/runtime.rb +18 -0
  44. data/lib/eye/checker/socket.rb +159 -0
  45. data/lib/eye/child_process.rb +101 -0
  46. data/lib/eye/cli.rb +185 -0
  47. data/lib/eye/cli/commands.rb +78 -0
  48. data/lib/eye/cli/render.rb +130 -0
  49. data/lib/eye/cli/server.rb +93 -0
  50. data/lib/eye/client.rb +32 -0
  51. data/lib/eye/config.rb +91 -0
  52. data/lib/eye/control.rb +2 -0
  53. data/lib/eye/controller.rb +54 -0
  54. data/lib/eye/controller/commands.rb +88 -0
  55. data/lib/eye/controller/helpers.rb +101 -0
  56. data/lib/eye/controller/load.rb +224 -0
  57. data/lib/eye/controller/options.rb +18 -0
  58. data/lib/eye/controller/send_command.rb +177 -0
  59. data/lib/eye/controller/status.rb +72 -0
  60. data/lib/eye/dsl.rb +53 -0
  61. data/lib/eye/dsl/application_opts.rb +39 -0
  62. data/lib/eye/dsl/chain.rb +12 -0
  63. data/lib/eye/dsl/child_process_opts.rb +13 -0
  64. data/lib/eye/dsl/config_opts.rb +55 -0
  65. data/lib/eye/dsl/group_opts.rb +32 -0
  66. data/lib/eye/dsl/helpers.rb +20 -0
  67. data/lib/eye/dsl/main.rb +51 -0
  68. data/lib/eye/dsl/opts.rb +151 -0
  69. data/lib/eye/dsl/process_opts.rb +36 -0
  70. data/lib/eye/dsl/pure_opts.rb +121 -0
  71. data/lib/eye/dsl/validation.rb +88 -0
  72. data/lib/eye/group.rb +140 -0
  73. data/lib/eye/group/chain.rb +81 -0
  74. data/lib/eye/loader.rb +10 -0
  75. data/lib/eye/local.rb +100 -0
  76. data/lib/eye/logger.rb +104 -0
  77. data/lib/eye/notify.rb +118 -0
  78. data/lib/eye/notify/jabber.rb +30 -0
  79. data/lib/eye/notify/mail.rb +48 -0
  80. data/lib/eye/process.rb +85 -0
  81. data/lib/eye/process/children.rb +60 -0
  82. data/lib/eye/process/commands.rb +280 -0
  83. data/lib/eye/process/config.rb +81 -0
  84. data/lib/eye/process/controller.rb +73 -0
  85. data/lib/eye/process/data.rb +78 -0
  86. data/lib/eye/process/monitor.rb +108 -0
  87. data/lib/eye/process/notify.rb +32 -0
  88. data/lib/eye/process/scheduler.rb +82 -0
  89. data/lib/eye/process/states.rb +86 -0
  90. data/lib/eye/process/states_history.rb +66 -0
  91. data/lib/eye/process/system.rb +97 -0
  92. data/lib/eye/process/trigger.rb +34 -0
  93. data/lib/eye/process/validate.rb +33 -0
  94. data/lib/eye/process/watchers.rb +66 -0
  95. data/lib/eye/reason.rb +20 -0
  96. data/lib/eye/server.rb +60 -0
  97. data/lib/eye/sigar.rb +5 -0
  98. data/lib/eye/system.rb +139 -0
  99. data/lib/eye/system_resources.rb +99 -0
  100. data/lib/eye/trigger.rb +136 -0
  101. data/lib/eye/trigger/check_dependency.rb +30 -0
  102. data/lib/eye/trigger/flapping.rb +41 -0
  103. data/lib/eye/trigger/stop_children.rb +17 -0
  104. data/lib/eye/trigger/transition.rb +15 -0
  105. data/lib/eye/trigger/wait_dependency.rb +49 -0
  106. data/lib/eye/utils.rb +45 -0
  107. data/lib/eye/utils/alive_array.rb +57 -0
  108. data/lib/eye/utils/celluloid_chain.rb +71 -0
  109. data/lib/eye/utils/celluloid_klass.rb +5 -0
  110. data/lib/eye/utils/leak_19.rb +10 -0
  111. data/lib/eye/utils/mini_active_support.rb +111 -0
  112. data/lib/eye/utils/pmap.rb +7 -0
  113. data/lib/eye/utils/tail.rb +20 -0
  114. metadata +398 -0
@@ -0,0 +1,104 @@
1
+ require 'logger'
2
+
3
+ class Eye::Logger
4
+ attr_accessor :prefix, :subprefix
5
+
6
+ class InnerLogger < Logger
7
+ FORMAT = '%d.%m.%Y %H:%M:%S'
8
+
9
+ def initialize(*args)
10
+ super
11
+
12
+ self.formatter = Proc.new do |s, d, p, m|
13
+ "#{d.strftime(FORMAT)} #{s.ljust(5)} -- #{m}\n"
14
+ end
15
+ end
16
+ end
17
+
18
+ module ObjectExt
19
+ def logger_tag
20
+ [Class, Module].include?(self.class) ? to_s : "<#{self.class.to_s}>"
21
+ end
22
+
23
+ def logger_sub_tag
24
+ end
25
+
26
+ def logger
27
+ @logger ||= Eye::Logger.new(logger_tag, logger_sub_tag)
28
+ end
29
+
30
+ Logger::Severity.constants.each do |level|
31
+ method_name = level.to_s.downcase
32
+ define_method method_name do |msg|
33
+ logger.send(method_name, msg)
34
+ end
35
+ end
36
+
37
+ def log_ex(ex)
38
+ error "#{ex.message} #{ex.backtrace}"
39
+ # notify here?
40
+ end
41
+ end
42
+
43
+ Logger::Severity.constants.each do |level|
44
+ method_name = level.to_s.downcase
45
+ define_method method_name do |msg|
46
+ self.class.inner_logger.send(method_name, "#{prefix_str}#{msg}")
47
+ end
48
+ end
49
+
50
+ def initialize(prefix = nil, subprefix = nil)
51
+ @prefix = prefix
52
+ @subprefix = subprefix
53
+ end
54
+
55
+ class << self
56
+ attr_reader :dev, :log_level, :args
57
+
58
+ def link_logger(dev, *args)
59
+ old_dev = @dev
60
+ @dev = dev ? dev.to_s : nil
61
+ @dev_fd = @dev
62
+ @args = args
63
+
64
+ @dev_fd = STDOUT if @dev.to_s.downcase == 'stdout'
65
+ @dev_fd = STDERR if @dev.to_s.downcase == 'stderr'
66
+
67
+ @inner_logger = InnerLogger.new(@dev_fd, *args)
68
+ @inner_logger.level = self.log_level || Logger::INFO
69
+
70
+ rescue Errno::ENOENT, Errno::EACCES
71
+ @dev = old_dev
72
+ raise
73
+ end
74
+
75
+ def reopen
76
+ link_logger(dev, *args)
77
+ end
78
+
79
+ def log_level=(level)
80
+ @log_level = level
81
+ @inner_logger.level = self.log_level if @inner_logger
82
+ end
83
+
84
+ def inner_logger
85
+ @inner_logger ||= InnerLogger.new(nil)
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def prefix_str
92
+ @pref_string ||= begin
93
+ pref_string = ''
94
+
95
+ if @prefix
96
+ pref_string = "[#{@prefix}] "
97
+ pref_string += "#{@subprefix} " if @subprefix
98
+ end
99
+
100
+ pref_string
101
+ end
102
+ end
103
+
104
+ end
@@ -0,0 +1,118 @@
1
+ require 'celluloid'
2
+
3
+ class Eye::Notify
4
+ include Celluloid
5
+ include Eye::Dsl::Validation
6
+
7
+ autoload :Mail, 'eye/notify/mail'
8
+ autoload :Jabber, 'eye/notify/jabber'
9
+
10
+ TYPES = {:mail => 'Mail', :jabber => 'Jabber'}
11
+
12
+ def self.get_class(type)
13
+ klass = eval("Eye::Notify::#{TYPES[type]}") rescue nil
14
+ raise "unknown notifier :#{type}" unless klass
15
+ if deps = klass.requires
16
+ Array(deps).each { |d| require d }
17
+ end
18
+ klass
19
+ end
20
+
21
+ def self.validate!(options)
22
+ get_class(options[:type]).validate(options)
23
+ end
24
+
25
+ def self.notify(contact, message_h)
26
+ contact = contact.to_s
27
+ settings = Eye::Control.settings
28
+ needed_hash = (settings[:contacts] || {})[contact]
29
+
30
+ if needed_hash.blank?
31
+ error "contact #{contact} not found; check your configuration"
32
+ return
33
+ end
34
+
35
+ create_proc = lambda do |nh|
36
+ type = nh[:type]
37
+ config = (settings[type] || {}).merge(nh[:opts] || {}).merge(:contact => nh[:contact])
38
+ klass = get_class(type)
39
+ notify = klass.new(config, message_h)
40
+ notify.async_notify if notify
41
+ end
42
+
43
+ if needed_hash.is_a?(Array)
44
+ needed_hash.each{|nh| create_proc[nh] }
45
+ else
46
+ create_proc[needed_hash]
47
+ end
48
+
49
+ rescue Exception, Timeout::Error => ex
50
+ log_ex(ex)
51
+ end
52
+
53
+ TIMEOUT = 1 * 60
54
+
55
+ def initialize(options = {}, message_h = {})
56
+ @message_h = message_h
57
+ @options = options
58
+
59
+ debug "created notifier #{options}"
60
+ end
61
+
62
+ def logger_sub_tag
63
+ @options[:contact]
64
+ end
65
+
66
+ def async_notify
67
+ async.notify
68
+ after(TIMEOUT){ terminate }
69
+ end
70
+
71
+ def notify
72
+ debug "start notify #{@message_h}"
73
+ execute
74
+ debug "end notify #{@message_h}"
75
+ terminate
76
+ end
77
+
78
+ def execute
79
+ raise NotImplementedError
80
+ end
81
+
82
+ param :contact, [String]
83
+
84
+ def message_subject
85
+ "[#{msg_host}] [#{msg_full_name}] #{msg_message}"
86
+ end
87
+
88
+ def message_body
89
+ "#{message_subject} at #{Eye::Utils.human_time2(msg_at)}"
90
+ end
91
+
92
+ def self.register(base)
93
+ name = base.to_s.gsub('Eye::Notify::', '')
94
+ type = name.underscore.to_sym
95
+ Eye::Notify::TYPES[type] = name
96
+ Eye::Notify.const_set(name, base)
97
+ Eye::Dsl::ConfigOpts.add_notify(type)
98
+ end
99
+
100
+ def self.requires
101
+ end
102
+
103
+ class Custom < Eye::Notify
104
+ def self.inherited(base)
105
+ super
106
+ register(base)
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ %w{at host message name full_name pid level}.each do |name|
113
+ define_method("msg_#{name}") do
114
+ @message_h[name.to_sym]
115
+ end
116
+ end
117
+
118
+ end
@@ -0,0 +1,30 @@
1
+ require 'xmpp4r'
2
+
3
+ class Eye::Notify::Jabber < Eye::Notify
4
+
5
+ # Eye.config do
6
+ # jabber :host => "some.host", :port => 12345, :user => "eye@some.host", :password => "123456"
7
+ # contact :vasya, :jabber, "vasya@some.host"
8
+ # end
9
+
10
+ param :host, String, true
11
+ param :port, [String, Fixnum], true
12
+ param :user, String, true
13
+ param :password, String
14
+
15
+ def execute
16
+ debug "send jabber #{[host, port, user, password]} - #{[contact, message_body]}"
17
+
18
+ mes = ::Jabber::Message.new(contact, message_body)
19
+ mes.set_type(:normal)
20
+ mes.set_id('1')
21
+ mes.set_subject(message_subject)
22
+
23
+ client = ::Jabber::Client.new(::Jabber::JID.new("#{user}/Eye"))
24
+ client.connect(host, port)
25
+ client.auth(password)
26
+ client.send(mes)
27
+ client.close
28
+ end
29
+
30
+ end
@@ -0,0 +1,48 @@
1
+ require 'net/smtp'
2
+
3
+ class Eye::Notify::Mail < Eye::Notify
4
+
5
+ # Eye.config do
6
+ # mail :host => "some.host", :port => 12345, :user => "eye@some.host", :password => "123456", :domain => "some.host"
7
+ # contact :vasya, :mail, "vasya@some.host"
8
+ # end
9
+
10
+ param :host, String, true
11
+ param :port, [String, Fixnum], true
12
+
13
+ param :domain, String
14
+ param :user, String
15
+ param :password, String
16
+ param :auth, Symbol, nil, nil, [:plain, :login, :cram_md5]
17
+
18
+ param :starttls, [TrueClass, FalseClass]
19
+
20
+ param :from_mail, String
21
+ param :from_name, String, nil, 'eye'
22
+
23
+ def execute
24
+ smtp
25
+ end
26
+
27
+ def smtp
28
+ args = [host, port, domain, user, password, auth]
29
+ debug "called smtp with #{args}"
30
+ smtp = Net::SMTP.new host, port
31
+ smtp.enable_starttls if starttls
32
+
33
+ smtp.start(domain, user, password, auth) do |s|
34
+ s.send_message(message, from_mail || user, contact)
35
+ end
36
+ end
37
+
38
+ def message
39
+ h = []
40
+ h << "From: #{from_name} <#{from_mail || user}>" if from_mail || user
41
+ h << "To: <#{contact}>"
42
+ h << "Subject: #{message_subject}"
43
+ h << "Date: #{msg_at.httpdate}"
44
+ h << "Message-Id: <#{rand(1000000000).to_s(36)}.#{$$}.#{contact}>"
45
+ "#{h * "\n"}\n#{message_body}"
46
+ end
47
+
48
+ end
@@ -0,0 +1,85 @@
1
+ require 'celluloid'
2
+
3
+ class Eye::Process
4
+ include Celluloid
5
+
6
+ autoload :Config, 'eye/process/config'
7
+ autoload :Commands, 'eye/process/commands'
8
+ autoload :Data, 'eye/process/data'
9
+ autoload :Watchers, 'eye/process/watchers'
10
+ autoload :Monitor, 'eye/process/monitor'
11
+ autoload :System, 'eye/process/system'
12
+ autoload :Controller, 'eye/process/controller'
13
+ autoload :StatesHistory, 'eye/process/states_history'
14
+ autoload :Children, 'eye/process/children'
15
+ autoload :Trigger, 'eye/process/trigger'
16
+ autoload :Notify, 'eye/process/notify'
17
+ autoload :Scheduler, 'eye/process/scheduler'
18
+ autoload :Validate, 'eye/process/validate'
19
+
20
+ attr_accessor :pid, :watchers, :config, :states_history,
21
+ :children, :triggers, :name, :state_reason, :flapping_times,
22
+ :parent_pid
23
+
24
+ def initialize(config)
25
+ raise 'you must supply a pid_file location' unless config[:pid_file]
26
+
27
+ @config = prepare_config(config)
28
+
29
+ @watchers = {}
30
+ @children = {}
31
+ @triggers = []
32
+ @name = @config[:name]
33
+
34
+ @flapping_times = 0
35
+
36
+ @states_history = Eye::Process::StatesHistory.new(100)
37
+ @states_history << :unmonitored
38
+
39
+ debug "creating with config: #{@config.inspect}"
40
+
41
+ add_triggers
42
+
43
+ super() # for statemachine
44
+ end
45
+
46
+ # c(), self[]
47
+ include Eye::Process::Config
48
+
49
+ # full_name, status_data
50
+ include Eye::Process::Data
51
+
52
+ # commands:
53
+ # start_process, stop_process, restart_process
54
+ include Eye::Process::Commands
55
+
56
+ # start, stop, restart, monitor, unmonit, delete
57
+ include Eye::Process::Controller
58
+
59
+ # add_watchers, remove_watchers:
60
+ include Eye::Process::Watchers
61
+
62
+ # check alive, crash methods:
63
+ include Eye::Process::Monitor
64
+
65
+ # system methods:
66
+ include Eye::Process::System
67
+
68
+ # manage child methods
69
+ include Eye::Process::Children
70
+
71
+ # manage triggers methods
72
+ include Eye::Process::Trigger
73
+
74
+ # manage notify methods
75
+ include Eye::Process::Notify
76
+
77
+ # scheduler
78
+ include Eye::Process::Scheduler
79
+
80
+ # validate
81
+ extend Eye::Process::Validate
82
+ end
83
+
84
+ # include state_machine states
85
+ require_relative 'process/states'
@@ -0,0 +1,60 @@
1
+ module Eye::Process::Children
2
+
3
+ def add_children
4
+ add_or_update_children
5
+ end
6
+
7
+ def add_or_update_children
8
+ return unless self[:monitor_children]
9
+ return unless self.up?
10
+ return if @updating_children
11
+ @updating_children = true
12
+
13
+ unless self.pid
14
+ warn "can't add children; pid not set"
15
+ return
16
+ end
17
+
18
+ now_children = Eye::SystemResources.children(self.pid)
19
+ new_children = []
20
+ exist_children = []
21
+
22
+ now_children.each do |child_pid|
23
+ if self.children[child_pid]
24
+ exist_children << child_pid
25
+ else
26
+ new_children << child_pid
27
+ end
28
+ end
29
+
30
+ removed_children = self.children.keys - now_children
31
+
32
+ if new_children.present?
33
+ new_children.each do |child_pid|
34
+ self.children[child_pid] = Eye::ChildProcess.new(child_pid, self[:monitor_children], logger.prefix, self.pid)
35
+ end
36
+ end
37
+
38
+ if removed_children.present?
39
+ removed_children.each{|child_pid| remove_child(child_pid) }
40
+ end
41
+
42
+ h = {:new => new_children.size, :removed => removed_children.size, :exists => exist_children.size }
43
+ debug "children info: #{ h.inspect }"
44
+
45
+ @updating_children = false
46
+ h
47
+ end
48
+
49
+ def remove_children
50
+ if children.present?
51
+ children.keys.each{|child_pid| remove_child(child_pid) }
52
+ end
53
+ end
54
+
55
+ def remove_child(child_pid)
56
+ child = self.children.delete(child_pid)
57
+ child.destroy if child && child.alive?
58
+ end
59
+
60
+ end