reel-eye 0.4.1 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGES.md +17 -1
  5. data/README.md +3 -3
  6. data/Rakefile +8 -0
  7. data/bin/eye +0 -316
  8. data/bin/loader_eye +3 -3
  9. data/examples/test.eye +2 -2
  10. data/eye.gemspec +6 -10
  11. data/lib/eye.rb +4 -2
  12. data/lib/eye/application.rb +3 -3
  13. data/lib/eye/checker.rb +35 -7
  14. data/lib/eye/checker/cputime.rb +23 -0
  15. data/lib/eye/checker/file_touched.rb +15 -0
  16. data/lib/eye/checker/http.rb +7 -9
  17. data/lib/eye/checker/memory.rb +1 -1
  18. data/lib/eye/checker/runtime.rb +28 -0
  19. data/lib/eye/checker/socket.rb +4 -4
  20. data/lib/eye/cli.rb +166 -0
  21. data/lib/eye/cli/commands.rb +79 -0
  22. data/lib/eye/cli/render.rb +137 -0
  23. data/lib/eye/cli/server.rb +85 -0
  24. data/lib/eye/client.rb +3 -3
  25. data/lib/eye/config.rb +17 -14
  26. data/lib/eye/controller.rb +6 -10
  27. data/lib/eye/controller/commands.rb +6 -10
  28. data/lib/eye/controller/helpers.rb +1 -1
  29. data/lib/eye/controller/send_command.rb +5 -2
  30. data/lib/eye/controller/status.rb +38 -106
  31. data/lib/eye/dsl.rb +1 -1
  32. data/lib/eye/dsl/application_opts.rb +6 -2
  33. data/lib/eye/dsl/child_process_opts.rb +3 -2
  34. data/lib/eye/dsl/config_opts.rb +2 -2
  35. data/lib/eye/dsl/group_opts.rb +2 -1
  36. data/lib/eye/dsl/main.rb +4 -2
  37. data/lib/eye/dsl/opts.rb +11 -4
  38. data/lib/eye/dsl/validation.rb +49 -43
  39. data/lib/eye/group.rb +1 -1
  40. data/lib/eye/http.rb +4 -9
  41. data/lib/eye/http/router.rb +1 -1
  42. data/lib/eye/loader.rb +5 -9
  43. data/lib/eye/{settings.rb → local.rb} +1 -1
  44. data/lib/eye/logger.rb +5 -0
  45. data/lib/eye/notify.rb +12 -6
  46. data/lib/eye/notify/jabber.rb +2 -2
  47. data/lib/eye/process/child.rb +3 -1
  48. data/lib/eye/process/commands.rb +2 -2
  49. data/lib/eye/process/controller.rb +1 -1
  50. data/lib/eye/process/trigger.rb +1 -1
  51. data/lib/eye/sigar.rb +5 -0
  52. data/lib/eye/system.rb +8 -7
  53. data/lib/eye/system_resources.rb +46 -41
  54. data/lib/eye/trigger.rb +15 -8
  55. data/lib/eye/trigger/flapping.rb +1 -1
  56. data/lib/eye/trigger/stop_childs.rb +1 -1
  57. data/lib/eye/trigger/transition.rb +15 -0
  58. data/lib/eye/utils.rb +12 -0
  59. data/lib/eye/utils/leak_19.rb +7 -0
  60. data/lib/eye/utils/mini_active_support.rb +106 -0
  61. metadata +40 -17
  62. data/lib/eye/controller/show_history.rb +0 -63
  63. data/lib/eye/trigger/state.rb +0 -11
@@ -114,7 +114,7 @@ class Eye::Group
114
114
  end
115
115
 
116
116
  def break_chain
117
- info "break chain"
117
+ info 'break chain'
118
118
  scheduler_clear_pending_list
119
119
  @chain_breaker = true
120
120
  end
@@ -1,7 +1,8 @@
1
- gem 'reel', '~> 0.4.0.pre'
1
+ gem 'reel', '~> 0.4.0'
2
+ gem 'reel-rack'
2
3
  gem 'cuba'
3
4
 
4
- require 'reel'
5
+ require 'reel/rack/server'
5
6
 
6
7
  class Eye::Http
7
8
  autoload :Router, 'eye/http/router'
@@ -16,13 +17,7 @@ class Eye::Http
16
17
 
17
18
  def start
18
19
  stop
19
-
20
- @server = Reel::Server.supervise(@host, @port) do |connection|
21
- while request = connection.request
22
- status, headers, body = @router.call(Rack::MockRequest.env_for(request.url, :method => request.method, :input => request.body))
23
- connection.respond(Reel::Response.new(status, headers, body))
24
- end
25
- end
20
+ @server = Reel::Rack::Server.supervise(@router, :Host => @host, :Port => port)
26
21
  end
27
22
 
28
23
  def stop
@@ -13,7 +13,7 @@ Eye::Http::Router = Cuba.new do
13
13
  end
14
14
 
15
15
  on "api/info", param("filter") do |filter|
16
- json Eye::Control.command(:raw_info, filter)
16
+ json Eye::Control.command(:info_data, filter)
17
17
  end
18
18
 
19
19
  [:start, :stop, :restart, :delete, :unmonitor, :monitor].each do |act|
@@ -1,14 +1,10 @@
1
- # mini bundler, for embedded server gem installation
1
+ # add gems to $: by `gem` method
2
+ # this is only way when install eye as system wide
2
3
 
3
- gem 'celluloid', '~> 0.14.0'
4
- gem 'celluloid-io', '~> 0.14.0'
4
+ gem 'celluloid', '~> 0.15.0'
5
+ gem 'celluloid-io', '~> 0.15.0'
5
6
  gem 'nio4r'
6
7
  gem 'timers'
7
8
 
8
9
  gem 'state_machine'
9
-
10
- if RUBY_VERSION == '1.9.2'
11
- gem 'activesupport', '>= 3', '< 4.0'
12
- else
13
- gem 'activesupport', '>= 3'
14
- end
10
+ gem 'sigar', '~> 0.7.2'
@@ -1,6 +1,6 @@
1
1
  require 'fileutils'
2
2
 
3
- module Eye::Settings
3
+ module Eye::Local
4
4
  module_function
5
5
 
6
6
  def dir
@@ -56,6 +56,7 @@ class Eye::Logger
56
56
  attr_reader :dev, :log_level
57
57
 
58
58
  def link_logger(dev)
59
+ old_dev = @dev
59
60
  @dev = dev ? dev.to_s : nil
60
61
  @dev_fd = @dev
61
62
 
@@ -64,6 +65,10 @@ class Eye::Logger
64
65
 
65
66
  @inner_logger = InnerLogger.new(@dev_fd)
66
67
  @inner_logger.level = self.log_level || Logger::INFO
68
+
69
+ rescue Errno::ENOENT, Errno::EACCES
70
+ @dev = old_dev
71
+ raise
67
72
  end
68
73
 
69
74
  def log_level=(level)
@@ -1,15 +1,18 @@
1
1
  class Eye::Notify
2
2
  include Celluloid
3
- extend Eye::Dsl::Validation
3
+ include Eye::Dsl::Validation
4
4
 
5
5
  autoload :Mail, 'eye/notify/mail'
6
6
  autoload :Jabber, 'eye/notify/jabber'
7
7
 
8
- TYPES = {:mail => "Mail", :jabber => "Jabber"}
8
+ TYPES = {:mail => 'Mail', :jabber => 'Jabber'}
9
9
 
10
10
  def self.get_class(type)
11
11
  klass = eval("Eye::Notify::#{TYPES[type]}") rescue nil
12
12
  raise "Unknown notify #{type}" unless klass
13
+ if deps = klass.depends_on
14
+ Array(deps).each { |d| require d }
15
+ end
13
16
  klass
14
17
  end
15
18
 
@@ -71,7 +74,7 @@ class Eye::Notify
71
74
  end
72
75
 
73
76
  def execute
74
- raise "realize me"
77
+ raise 'realize me'
75
78
  end
76
79
 
77
80
  param :contact, [String]
@@ -81,17 +84,20 @@ class Eye::Notify
81
84
  end
82
85
 
83
86
  def message_body
84
- "#{message_subject} at #{msg_at.to_s(:short)}"
87
+ "#{message_subject} at #{msg_at.strftime(Eye::Cli::Render::DF)}"
85
88
  end
86
89
 
87
90
  def self.register(base)
88
- name = base.to_s.gsub("Eye::Notify::", '')
91
+ name = base.to_s.gsub('Eye::Notify::', '')
89
92
  type = name.underscore.to_sym
90
93
  Eye::Notify::TYPES[type] = name
91
94
  Eye::Notify.const_set(name, base)
92
95
  Eye::Dsl::ConfigOpts.add_notify(type)
93
96
  end
94
97
 
98
+ def self.depends_on
99
+ end
100
+
95
101
  class Custom < Eye::Notify
96
102
  def self.inherited(base)
97
103
  super
@@ -107,4 +113,4 @@ private
107
113
  end
108
114
  end
109
115
 
110
- end
116
+ end
@@ -1,3 +1,5 @@
1
+ require 'xmpp4r'
2
+
1
3
  class Eye::Notify::Jabber < Eye::Notify
2
4
 
3
5
  # Eye.config do
@@ -11,8 +13,6 @@ class Eye::Notify::Jabber < Eye::Notify
11
13
  param :password, String
12
14
 
13
15
  def execute
14
- require 'xmpp4r'
15
-
16
16
  debug "send jabber #{[host, port, user, password]} - #{[contact, message_body]}"
17
17
 
18
18
  mes = ::Jabber::Message.new(contact, message_body)
@@ -6,8 +6,9 @@ module Eye::Process::Child
6
6
 
7
7
  def add_or_update_childs
8
8
  return unless self[:monitor_children]
9
-
10
9
  return unless self.up?
10
+ return if @updating_childs
11
+ @updating_childs = true
11
12
 
12
13
  unless self.pid
13
14
  warn 'Cant add childs, because no pid'
@@ -41,6 +42,7 @@ module Eye::Process::Child
41
42
  h = {:new => new_childs.size, :removed => removed_childs.size, :exists => exist_childs.size }
42
43
  debug "childs info: #{ h.inspect }"
43
44
 
45
+ @updating_childs = false
44
46
  h
45
47
  end
46
48
 
@@ -88,7 +88,7 @@ private
88
88
 
89
89
  def kill_process
90
90
  unless self.pid
91
- error "try to kill process without pid"
91
+ error 'try to kill process without pid'
92
92
  return
93
93
  end
94
94
 
@@ -145,7 +145,7 @@ private
145
145
 
146
146
  def execute_restart_command
147
147
  unless self.pid
148
- error "try to execute restart_command without pid"
148
+ error 'try to execute restart_command without pid'
149
149
  return
150
150
  end
151
151
 
@@ -43,7 +43,7 @@ module Eye::Process::Controller
43
43
  info "process from pid_file(#{self.pid}) found and already running, so :up"
44
44
  switch :already_running
45
45
  else
46
- warn "process not found, so :unmonitor"
46
+ warn 'process not found, so :unmonitor'
47
47
  schedule :unmonitor, Eye::Reason.new(:'not found')
48
48
  end
49
49
  end
@@ -14,7 +14,7 @@ module Eye::Process::Trigger
14
14
 
15
15
  def check_triggers(transition)
16
16
  return if unmonitored?
17
- self.triggers.each { |trigger| trigger.notify(transition) }
17
+ self.triggers.each { |trigger| trigger.notify(transition, state_reason) }
18
18
  end
19
19
 
20
20
  def retry_start_after_flapping
@@ -0,0 +1,5 @@
1
+ require 'sigar'
2
+ require 'logger'
3
+
4
+ Eye::Sigar = ::Sigar.new
5
+ Eye::Sigar.logger = ::Logger.new(nil)
@@ -55,13 +55,15 @@ module Eye::System
55
55
  # :environment
56
56
  # :stdin, :stdout, :stderr
57
57
  def daemonize(cmd, cfg = {})
58
- opts = spawn_options(cfg)
59
- pid = Process::spawn(prepare_env(cfg), *Shellwords.shellwords(cmd), opts)
60
- Process.detach(pid)
58
+ pid = ::Process::spawn(prepare_env(cfg), *Shellwords.shellwords(cmd), spawn_options(cfg))
59
+
61
60
  {:pid => pid, :exitstatus => 0}
62
61
 
63
62
  rescue Errno::ENOENT, Errno::EACCES => ex
64
63
  {:error => ex}
64
+
65
+ ensure
66
+ Process.detach(pid) if pid
65
67
  end
66
68
 
67
69
  # Execute cmd with blocking, return status (be careful: inside actor blocks it mailbox, use with defer)
@@ -70,8 +72,7 @@ module Eye::System
70
72
  # :environment
71
73
  # :stdin, :stdout, :stderr
72
74
  def execute(cmd, cfg = {})
73
- opts = spawn_options(cfg)
74
- pid = Process::spawn(prepare_env(cfg), *Shellwords.shellwords(cmd), opts)
75
+ pid = ::Process::spawn(prepare_env(cfg), *Shellwords.shellwords(cmd), spawn_options(cfg))
75
76
 
76
77
  timeout = cfg[:timeout] || 1.second
77
78
  status = 0
@@ -142,7 +143,7 @@ module Eye::System
142
143
  o.update(err: [config[:stderr], 'a']) if config[:stderr]
143
144
  o.update(in: config[:stdin]) if config[:stdin]
144
145
 
145
- if Eye::Settings.root?
146
+ if Eye::Local.root?
146
147
  o.update(uid: Etc.getpwnam(config[:uid]).uid) if config[:uid]
147
148
  o.update(gid: Etc.getpwnam(config[:gid]).gid) if config[:gid]
148
149
  end
@@ -156,7 +157,7 @@ module Eye::System
156
157
  env = {}
157
158
 
158
159
  (config[:environment] || {}).each do |k,v|
159
- env[k.to_s] = v.to_s if v
160
+ env[k.to_s] = v && v.to_s
160
161
  end
161
162
 
162
163
  env
@@ -6,31 +6,33 @@ class Eye::SystemResources
6
6
  class << self
7
7
 
8
8
  def memory(pid)
9
- ps_aux[pid].try :[], :rss
9
+ cache.proc_mem(pid).try(:resident)
10
10
  end
11
11
 
12
12
  def cpu(pid)
13
- ps_aux[pid].try :[], :cpu
13
+ if cpu = cache.proc_cpu(pid)
14
+ cpu.percent * 100
15
+ end
14
16
  end
15
17
 
16
18
  def childs(parent_pid)
17
- parent_pid = parent_pid.to_i
19
+ cache.childs(parent_pid)
20
+ end
18
21
 
19
- childs = []
20
- ps_aux.each do |pid, h|
21
- childs << pid if h[:ppid] == parent_pid
22
+ def start_time(pid) # unixtime
23
+ if cpu = cache.proc_cpu(pid)
24
+ cpu.start_time.to_i / 1000
22
25
  end
23
-
24
- childs
25
26
  end
26
27
 
27
- def start_time(pid)
28
- ps_aux[pid].try :[], :start_time
28
+ # total cpu usage in seconds
29
+ def cputime(pid)
30
+ if cpu = cache.proc_cpu(pid)
31
+ cpu.total.to_f / 1000
32
+ end
29
33
  end
30
34
 
31
35
  def resources(pid)
32
- return {} unless ps_aux[pid]
33
-
34
36
  { :memory => memory(pid),
35
37
  :cpu => cpu(pid),
36
38
  :start_time => start_time(pid),
@@ -38,49 +40,52 @@ class Eye::SystemResources
38
40
  }
39
41
  end
40
42
 
41
- # initialize actor, call 1 time before using
42
- def setup
43
- @actor ||= PsAxActor.new
44
- end
45
-
46
- private
47
-
48
- def reset!
49
- setup.terminate
50
- @actor = nil
51
- end
52
-
53
- def ps_aux
54
- setup
55
- @actor.get
43
+ def cache
44
+ @cache ||= Cache.new
56
45
  end
57
-
58
46
  end
59
47
 
60
- class PsAxActor
48
+ class Cache
61
49
  include Celluloid
62
50
 
63
- UPDATE_INTERVAL = 5 # seconds
51
+ attr_reader :expire
64
52
 
65
53
  def initialize
66
- set
54
+ clear
55
+ setup_expire
67
56
  end
68
57
 
69
- def get
70
- if @at + UPDATE_INTERVAL < Time.now
71
- @at = Time.now # for minimize races
72
- async.set
73
- end
74
- @ps_aux
58
+ def setup_expire(expire = 5)
59
+ @expire = expire
60
+ @timer.cancel if @timer
61
+ @timer = every(@expire) { clear }
75
62
  end
76
63
 
77
- private
64
+ def clear
65
+ @memory = {}
66
+ @cpu = {}
67
+ @ppids = {}
68
+ end
78
69
 
79
- def set
80
- @ps_aux = Eye::System.ps_aux
81
- @at = Time.now
70
+ def proc_mem(pid)
71
+ @memory[pid] ||= Eye::Sigar.proc_mem(pid) if pid
72
+
73
+ rescue ArgumentError # when incorrect PID
82
74
  end
83
75
 
76
+ def proc_cpu(pid)
77
+ @cpu[pid] ||= Eye::Sigar.proc_cpu(pid) if pid
78
+
79
+ rescue ArgumentError # when incorrect PID
80
+ end
81
+
82
+ def childs(pid)
83
+ if pid
84
+ @ppids[pid] ||= Eye::Sigar.proc_list("State.Ppid.eq=#{pid}")
85
+ else
86
+ []
87
+ end
88
+ end
84
89
  end
85
90
 
86
91
  end
@@ -1,16 +1,16 @@
1
1
  class Eye::Trigger
2
+ include Eye::Dsl::Validation
3
+
2
4
  autoload :Flapping, 'eye/trigger/flapping'
3
- autoload :State, 'eye/trigger/state'
5
+ autoload :Transition, 'eye/trigger/transition'
4
6
  autoload :StopChilds, 'eye/trigger/stop_childs'
5
7
 
6
8
  # ex: { :type => :flapping, :times => 2, :within => 30.seconds}
7
9
 
8
- TYPES = {:flapping => "Flapping", :state => "State", :stop_childs => "StopChilds"}
10
+ TYPES = {:flapping => 'Flapping', :transition => 'Transition', :stop_childs => 'StopChilds'}
9
11
 
10
12
  attr_reader :message, :options, :process
11
13
 
12
- extend Eye::Dsl::Validation
13
-
14
14
  def self.name_and_class(type)
15
15
  type = type.to_sym
16
16
  return {:name => type, :type => type} if TYPES[type]
@@ -24,6 +24,9 @@ class Eye::Trigger
24
24
  def self.get_class(type)
25
25
  klass = eval("Eye::Trigger::#{TYPES[type]}") rescue nil
26
26
  raise "Unknown trigger #{type}" unless klass
27
+ if deps = klass.depends_on
28
+ Array(deps).each { |d| require d }
29
+ end
27
30
  klass
28
31
  end
29
32
 
@@ -59,8 +62,9 @@ class Eye::Trigger
59
62
  "trigger(#{@options[:type]})"
60
63
  end
61
64
 
62
- def notify(transition)
65
+ def notify(transition, reason)
63
66
  debug "check (:#{transition.event}) :#{transition.from} => :#{transition.to}"
67
+ @reason = reason
64
68
  @transition = transition
65
69
 
66
70
  check(transition) if filter_transition(transition)
@@ -82,7 +86,7 @@ class Eye::Trigger
82
86
  end
83
87
 
84
88
  def check(transition)
85
- raise "realize me"
89
+ raise 'realize me'
86
90
  end
87
91
 
88
92
  def run_in_process_context(p)
@@ -94,12 +98,15 @@ class Eye::Trigger
94
98
  end
95
99
 
96
100
  def self.register(base)
97
- name = base.to_s.gsub("Eye::Trigger::", '')
101
+ name = base.to_s.gsub('Eye::Trigger::', '')
98
102
  type = name.underscore.to_sym
99
103
  Eye::Trigger::TYPES[type] = name
100
104
  Eye::Trigger.const_set(name, base)
101
105
  end
102
106
 
107
+ def self.depends_on
108
+ end
109
+
103
110
  class Custom < Eye::Trigger
104
111
  def self.inherited(base)
105
112
  super
@@ -120,4 +127,4 @@ private
120
127
  end
121
128
  end
122
129
 
123
- end
130
+ end