eye 0.1.11 → 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. data/.rspec +1 -0
  2. data/Gemfile +2 -2
  3. data/README.md +41 -18
  4. data/bin/eye +4 -3
  5. data/examples/processes/em.rb +2 -1
  6. data/examples/processes/thin.ru +12 -0
  7. data/examples/sidekiq.eye +19 -0
  8. data/examples/test.eye +25 -16
  9. data/eye.gemspec +5 -3
  10. data/lib/eye.rb +2 -1
  11. data/lib/eye/checker/validation.rb +1 -1
  12. data/lib/eye/child_process.rb +27 -5
  13. data/lib/eye/controller/load.rb +13 -11
  14. data/lib/eye/controller/send_command.rb +32 -7
  15. data/lib/eye/controller/status.rb +4 -3
  16. data/lib/eye/dsl/config_opts.rb +38 -1
  17. data/lib/eye/dsl/opts.rb +12 -5
  18. data/lib/eye/dsl/pure_opts.rb +2 -2
  19. data/lib/eye/dsl/validate.rb +5 -0
  20. data/lib/eye/group.rb +1 -1
  21. data/lib/eye/group/chain.rb +2 -0
  22. data/lib/eye/notify.rb +86 -0
  23. data/lib/eye/notify/jabber.rb +30 -0
  24. data/lib/eye/notify/mail.rb +44 -0
  25. data/lib/eye/process.rb +5 -1
  26. data/lib/eye/process/child.rb +7 -8
  27. data/lib/eye/process/commands.rb +21 -18
  28. data/lib/eye/process/notify.rb +22 -7
  29. data/lib/eye/process/system.rb +18 -5
  30. data/lib/eye/process/validate.rb +23 -0
  31. data/lib/eye/system.rb +4 -1
  32. data/lib/eye/utils.rb +9 -0
  33. data/spec/checker_spec.rb +0 -1
  34. data/spec/client_server_spec.rb +0 -1
  35. data/spec/controller/controller_spec.rb +1 -1
  36. data/spec/controller/intergration_spec.rb +15 -0
  37. data/spec/controller/load_spec.rb +49 -4
  38. data/spec/dsl/chain_spec.rb +20 -14
  39. data/spec/dsl/checks_spec.rb +17 -0
  40. data/spec/dsl/notify_spec.rb +105 -0
  41. data/spec/dsl/process_spec.rb +50 -0
  42. data/spec/mock_spec.rb +0 -1
  43. data/spec/notify/jabber_spec.rb +25 -0
  44. data/spec/notify/mail_spec.rb +26 -0
  45. data/spec/notify_spec.rb +90 -0
  46. data/spec/process/config_spec.rb +0 -1
  47. data/spec/process/notify_spec.rb +27 -0
  48. data/spec/process/states_history_spec.rb +0 -1
  49. data/spec/process/stop_spec.rb +6 -0
  50. data/spec/process/system_spec.rb +34 -21
  51. data/spec/process/update_config_spec.rb +0 -1
  52. data/spec/spec_helper.rb +9 -2
  53. data/spec/support/spec_support.rb +0 -1
  54. data/spec/system_resources_spec.rb +0 -1
  55. data/spec/system_spec.rb +3 -6
  56. data/spec/utils/alive_array_spec.rb +0 -1
  57. data/spec/utils/celluloid_chain_spec.rb +0 -1
  58. data/spec/utils/tail_spec.rb +0 -1
  59. metadata +71 -7
@@ -2,13 +2,12 @@ module Eye::Controller::SendCommand
2
2
 
3
3
  def send_command(command, *obj_strs)
4
4
  matched_objects(*obj_strs) do |obj|
5
- obj.send_command(command)
6
-
7
5
  if command.to_sym == :delete
8
6
  remove_object_from_tree(obj)
9
7
  set_proc_line # to sync proc line if was delete application
10
- GC.start
11
8
  end
9
+
10
+ obj.send_command(command)
12
11
  end
13
12
  end
14
13
 
@@ -32,9 +31,29 @@ private
32
31
  end
33
32
 
34
33
  def remove_object_from_tree(obj)
35
- @applications.delete(obj)
36
- @applications.each{|app| app.groups.delete(obj) }
37
- @applications.each{|app| app.groups.each{|gr| gr.processes.delete(obj) }}
34
+ klass = obj.class
35
+
36
+ if klass == Eye::Application
37
+ @applications.delete(obj)
38
+ @current_config[:applications].delete(obj.name)
39
+ end
40
+
41
+ if klass == Eye::Group
42
+ @applications.each{|app| app.groups.delete(obj) }
43
+ @current_config[:applications].each do |app_name, app_cfg|
44
+ app_cfg[:groups].delete(obj.name)
45
+ end
46
+ end
47
+
48
+ if klass == Eye::Process
49
+ @applications.each{|app| app.groups.each{|gr| gr.processes.delete(obj) }}
50
+
51
+ @current_config[:applications].each do |app_name, app_cfg|
52
+ app_cfg[:groups].each do |gr_name, gr_cfg|
53
+ gr_cfg[:processes].delete(obj.name)
54
+ end
55
+ end
56
+ end
38
57
  end
39
58
 
40
59
  # find object to action, restart ... (app, group or process)
@@ -78,7 +97,13 @@ private
78
97
  # find process
79
98
  @applications.each do |a|
80
99
  a.groups.each do |gr|
81
- res += gr.processes.select{|p| p.name =~ r || p.full_name =~ r }
100
+ gr.processes.each do |p|
101
+ res << p if p.name =~ r || p.full_name =~ r
102
+
103
+ if p.childs.present?
104
+ res += p.childs.values.select{|ch| ch.name =~ r || ch.full_name =~ r }
105
+ end
106
+ end
82
107
  end
83
108
  end
84
109
 
@@ -23,7 +23,7 @@ module Eye::Controller::Status
23
23
  make_str({:subtree => @applications.map{|a| a.status_data_short } }).to_s
24
24
  end
25
25
 
26
- def info_string_debug(show_config = false)
26
+ def info_string_debug(show_config = false, show_processes = false)
27
27
  actors = Celluloid::Actor.all.map{|actor| actor.instance_variable_get(:@klass) }.group_by{|a| a}.map{|k,v| [k, v.size]}.sort_by{|a|a[1]}.reverse
28
28
 
29
29
  str = <<-S
@@ -34,10 +34,11 @@ Socket: #{Eye::Settings::socket_path}
34
34
  PidPath: #{Eye::Settings::pid_path}
35
35
  Actors: #{actors.inspect}
36
36
 
37
- #{make_str(info_data_debug)}
38
37
  S
39
38
 
40
- if show_config
39
+ str += make_str(info_data_debug) + "\n" if show_processes.present?
40
+
41
+ if show_config.present?
41
42
  str += "\nCurrent config: \n"
42
43
  str += YAML.dump(current_config)
43
44
  end
@@ -3,9 +3,46 @@ class Eye::Dsl::ConfigOpts < Eye::Dsl::PureOpts
3
3
  create_options_methods([:logger], String)
4
4
  create_options_methods([:logger_level], Fixnum)
5
5
  create_options_methods([:http], Hash)
6
-
6
+
7
7
  def set_logger(logger)
8
8
  logger.blank? ? super('') : super
9
9
  end
10
+
11
+ # ==== contact options ==============================
12
+
13
+ Eye::Notify::TYPES.keys.each do |not_system|
14
+ create_options_methods([not_system], Hash)
15
+
16
+ define_method("set_#{not_system}") do |value|
17
+ value = value.merge(:type => not_system)
18
+ super(value)
19
+ Eye::Notify.validate!(value)
20
+ end
21
+ end
22
+
23
+ def contact(contact_name, contact_type, contact, contact_opts = {})
24
+ raise Eye::Dsl::Error, "unknown contact_type #{contact_type}" unless Eye::Notify::TYPES[contact_type]
25
+ raise Eye::Dsl::Error, "contact should be a String" unless contact.is_a?(String)
26
+
27
+ notify_hash = @config[contact_type] || (@parent && @parent.config[contact_type]) || Eye::parsed_config[:config][contact_type] || {}
28
+ validate_hash = notify_hash.merge(contact_opts).merge(:type => contact_type)
29
+
30
+ Eye::Notify.validate!(validate_hash)
31
+
32
+ @config[:contacts] ||= {}
33
+ @config[:contacts][contact_name.to_s] = {name: contact_name.to_s, type: contact_type,
34
+ contact: contact, opts: contact_opts}
35
+ end
36
+
37
+ def contact_group(contact_group_name, &block)
38
+ c = Eye::Dsl::ConfigOpts.new nil, self, false
39
+ c.instance_eval(&block)
40
+ cfg = c.config
41
+ @config[:contacts] ||= {}
42
+ if cfg[:contacts].present?
43
+ @config[:contacts][contact_group_name.to_s] = cfg[:contacts].values
44
+ @config[:contacts].merge!(cfg[:contacts])
45
+ end
46
+ end
10
47
 
11
48
  end
@@ -2,24 +2,19 @@ class Eye::Dsl::Opts < Eye::Dsl::PureOpts
2
2
 
3
3
  STR_OPTIONS = [ :pid_file, :working_dir, :stdout, :stderr, :stdall, :start_command,
4
4
  :stop_command, :restart_command ]
5
-
6
5
  create_options_methods(STR_OPTIONS, String)
7
6
 
8
7
  BOOL_OPTIONS = [ :daemonize, :keep_alive, :control_pid, :auto_start, :stop_on_delete]
9
-
10
8
  create_options_methods(BOOL_OPTIONS, [TrueClass, FalseClass])
11
9
 
12
10
  INTERVAL_OPTIONS = [ :check_alive_period, :start_timeout, :restart_timeout, :stop_timeout, :start_grace,
13
11
  :restart_grace, :stop_grace, :childs_update_period ]
14
-
15
12
  create_options_methods(INTERVAL_OPTIONS, [Fixnum, Float])
16
13
 
17
14
  OTHER_OPTIONS = [ :environment, :stop_signals ]
18
-
19
15
  create_options_methods(OTHER_OPTIONS)
20
16
 
21
17
 
22
-
23
18
  def initialize(name = nil, parent = nil)
24
19
  super(name, parent)
25
20
 
@@ -70,6 +65,18 @@ class Eye::Dsl::Opts < Eye::Dsl::PureOpts
70
65
  @config[:triggers].try :delete, type
71
66
  end
72
67
 
68
+ def notify(contact, level = :crit)
69
+ raise Eye::Dsl::Error, "level should be in #{[:warn, :crit]}" unless Eye::Process::Notify::LEVELS[level]
70
+
71
+ @config[:notify] ||= {}
72
+ @config[:notify][contact.to_s] = level
73
+ end
74
+
75
+ def nonotify(contact)
76
+ @config[:notify] ||= {}
77
+ @config[:notify].delete(contact.to_s)
78
+ end
79
+
73
80
  def set_environment(value)
74
81
  raise Eye::Dsl::Error, "environment should be a hash, but not #{value.inspect}" unless value.is_a?(Hash)
75
82
  @config[:environment] ||= {}
@@ -43,13 +43,13 @@ class Eye::Dsl::PureOpts
43
43
  attr_reader :name, :full_name
44
44
  attr_reader :config, :parent
45
45
 
46
- def initialize(name = nil, parent = nil)
46
+ def initialize(name = nil, parent = nil, merge_parent_config = true)
47
47
  @name = name.to_s
48
48
  @full_name = @name
49
49
 
50
50
  if parent
51
51
  @parent = parent
52
- @config = Marshal.load(Marshal.dump(parent.config)) # O_o ruby recommended deep clone
52
+ @config = merge_parent_config ? Eye::Utils::deep_clone(parent.config) : {}
53
53
  @full_name = "#{parent.full_name}:#{@full_name}"
54
54
  else
55
55
  @config = {}
@@ -36,6 +36,11 @@ module Eye::Dsl::Validate
36
36
  if dubl_names.present?
37
37
  raise Eye::Dsl::Error, "dublicate names: #{dubl_names.inspect}"
38
38
  end
39
+
40
+ # validate processes with their own validate
41
+ all_processes.each do |process_cfg|
42
+ Eye::Process.validate process_cfg
43
+ end
39
44
  end
40
45
 
41
46
  end
@@ -105,7 +105,7 @@ class Eye::Group
105
105
  end
106
106
 
107
107
  def clear
108
- @processes.clear
108
+ @processes = Eye::Utils::AliveArray.new
109
109
  end
110
110
 
111
111
  def sub_object?(obj)
@@ -25,6 +25,8 @@ private
25
25
  end
26
26
 
27
27
  def chain_schedule_process(process, type, command, *args)
28
+ debug "chain_schedule_process #{process.name} #{type} #{command}"
29
+
28
30
  if type == :sync
29
31
  # sync command, with waiting
30
32
  process.send(command, *args)
@@ -0,0 +1,86 @@
1
+ class Eye::Notify
2
+ include Celluloid
3
+ include Eye::Logger::Helpers
4
+ extend Eye::Checker::Validation
5
+
6
+ autoload :Mail, 'eye/notify/mail'
7
+ autoload :Jabber, 'eye/notify/jabber'
8
+
9
+ TYPES = {:mail => "Mail", :jabber => "Jabber"}
10
+
11
+ def self.get_class(type)
12
+ klass = eval("Eye::Notify::#{TYPES[type]}") rescue nil
13
+ raise "Unknown notify #{type}" unless klass
14
+ klass
15
+ end
16
+
17
+ def self.validate!(options)
18
+ get_class(options[:type]).validate(options)
19
+ end
20
+
21
+ def self.notify(contact, message_h)
22
+ current_config = Eye::Control.current_config[:config] # Warning, using global reference here !!! (not so nice)
23
+ needed_hash = (current_config[:contacts] || {})[contact.to_s]
24
+
25
+ return if needed_hash.blank?
26
+
27
+ create_proc = lambda do |nh|
28
+ type = nh[:type]
29
+ config = (current_config[type] || {}).merge(nh[:opts] || {}).merge(:contact => nh[:contact])
30
+ klass = get_class(type)
31
+ notify = klass.new(config, message_h)
32
+ notify.async_notify if notify
33
+ end
34
+
35
+ if needed_hash.is_a?(Array)
36
+ needed_hash.each{|nh| create_proc[nh] }
37
+ else
38
+ create_proc[needed_hash]
39
+ end
40
+ end
41
+
42
+ TIMEOUT = 1.minute
43
+
44
+ def initialize(options = {}, message_h = {})
45
+ @logger = Eye::Logger.new("#{self.class.name.downcase} - #{options[:contact]}")
46
+ debug "created notifier #{options}"
47
+
48
+ @message_h = message_h
49
+ @options = options
50
+ end
51
+
52
+ def async_notify
53
+ async.notify
54
+ after(TIMEOUT){ terminate }
55
+ end
56
+
57
+ def notify
58
+ debug "start notify #{@message_h}"
59
+ execute
60
+ debug "end notify #{@message_h}"
61
+ terminate
62
+ end
63
+
64
+ def execute
65
+ raise "realize me"
66
+ end
67
+
68
+ param :contact, [String]
69
+
70
+ def message_subject
71
+ "[#{msg_host}] [#{msg_full_name}] #{msg_message}"
72
+ end
73
+
74
+ def message_body
75
+ "#{message_subject} at #{msg_at.to_s(:short)}"
76
+ end
77
+
78
+ private
79
+
80
+ %w{at host message name full_name pid level}.each do |name|
81
+ define_method("msg_#{name}") do
82
+ @message_h[name.to_sym]
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,30 @@
1
+ class Eye::Notify::Jabber < Eye::Notify
2
+
3
+ # Eye.config do
4
+ # jabber :host => "some.host", :port => 12345, :user => "eye@some.host", :password => "123456"
5
+ # contact :vasya, :jabber, "vasya@some.host"
6
+ # end
7
+
8
+ param :host, String, true
9
+ param :port, [String, Fixnum], true
10
+ param :user, String, true
11
+ param :password, String
12
+
13
+ def execute
14
+ require 'xmpp4r'
15
+
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,44 @@
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 # :plain, :login, :cram_md5
17
+
18
+ param :from_mail, String
19
+ param :from_name, String, nil, 'eye'
20
+
21
+ def execute
22
+ smtp
23
+ end
24
+
25
+ def smtp
26
+ args = [host, port, domain, user, password, auth]
27
+ debug "called smtp with #{args}"
28
+
29
+ Net::SMTP.start(*args) do |smtp|
30
+ smtp.send_message(message, from_mail || user, contact)
31
+ end
32
+ end
33
+
34
+ def message
35
+ h = []
36
+ h << "From: #{from_name} <#{from_mail || user}>" if from_mail || user
37
+ h << "To: <#{contact}>"
38
+ h << "Subject: #{message_subject}"
39
+ h << "Date: #{msg_at.httpdate}"
40
+ h << "Message-Id: <#{rand(1000000000).to_s(36)}.#{$$}.#{contact}>"
41
+ "#{h * "\n"}\n#{message_body}"
42
+ end
43
+
44
+ end
@@ -15,6 +15,7 @@ class Eye::Process
15
15
  autoload :Trigger, 'eye/process/trigger'
16
16
  autoload :Notify, 'eye/process/notify'
17
17
  autoload :Scheduler, 'eye/process/scheduler'
18
+ autoload :Validate, 'eye/process/validate'
18
19
 
19
20
  attr_accessor :pid, :watchers, :config, :states_history,
20
21
  :childs, :triggers, :flapping, :name
@@ -24,7 +25,7 @@ class Eye::Process
24
25
 
25
26
  @config = prepare_config(config)
26
27
  @logger = Eye::Logger.new(full_name)
27
-
28
+
28
29
  @watchers = {}
29
30
  @childs = {}
30
31
  @triggers = []
@@ -77,6 +78,9 @@ class Eye::Process
77
78
 
78
79
  # scheduler
79
80
  include Eye::Process::Scheduler
81
+
82
+ # validate
83
+ extend Eye::Process::Validate
80
84
  end
81
85
 
82
86
  # include state_machine states
@@ -35,10 +35,7 @@ module Eye::Process::Child
35
35
  end
36
36
 
37
37
  if removed_childs.present?
38
- removed_childs.each do |child_pid|
39
- child = self.childs.delete(child_pid)
40
- child.delete if child && child.alive?
41
- end
38
+ removed_childs.each{|child_pid| remove_child(child_pid) }
42
39
  end
43
40
 
44
41
  h = {:new => new_childs.size, :removed => removed_childs.size, :exists => exist_childs.size }
@@ -49,13 +46,15 @@ module Eye::Process::Child
49
46
 
50
47
  def remove_childs
51
48
  if childs.present?
52
- childs.keys.each do |child_pid|
53
- child = childs.delete(child_pid)
54
- child.delete if child && child.alive?
55
- end
49
+ childs.keys.each{|child_pid| remove_child(child_pid) }
56
50
  else
57
51
  debug 'No childs to clear'
58
52
  end
59
53
  end
60
54
 
55
+ def remove_child(child_pid)
56
+ child = self.childs.delete(child_pid)
57
+ child.destroy if child && child.alive?
58
+ end
59
+
61
60
  end