eye 0.1.11
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.
- data/.gitignore +31 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +132 -0
- data/Rakefile +18 -0
- data/bin/eye +282 -0
- data/bin/loader_eye +56 -0
- data/examples/processes/em.rb +56 -0
- data/examples/processes/forking.rb +20 -0
- data/examples/processes/sample.rb +144 -0
- data/examples/rbenv.eye +11 -0
- data/examples/test.eye +65 -0
- data/examples/unicorn.eye +29 -0
- data/eye.gemspec +37 -0
- data/lib/eye.rb +25 -0
- data/lib/eye/application.rb +65 -0
- data/lib/eye/checker.rb +118 -0
- data/lib/eye/checker/cpu.rb +27 -0
- data/lib/eye/checker/file_ctime.rb +29 -0
- data/lib/eye/checker/file_size.rb +38 -0
- data/lib/eye/checker/http.rb +94 -0
- data/lib/eye/checker/memory.rb +27 -0
- data/lib/eye/checker/socket.rb +148 -0
- data/lib/eye/checker/validation.rb +49 -0
- data/lib/eye/child_process.rb +75 -0
- data/lib/eye/client.rb +32 -0
- data/lib/eye/control.rb +2 -0
- data/lib/eye/controller.rb +43 -0
- data/lib/eye/controller/commands.rb +64 -0
- data/lib/eye/controller/helpers.rb +61 -0
- data/lib/eye/controller/load.rb +224 -0
- data/lib/eye/controller/send_command.rb +88 -0
- data/lib/eye/controller/status.rb +136 -0
- data/lib/eye/dsl.rb +52 -0
- data/lib/eye/dsl/application_opts.rb +33 -0
- data/lib/eye/dsl/chain.rb +12 -0
- data/lib/eye/dsl/child_process_opts.rb +7 -0
- data/lib/eye/dsl/config_opts.rb +11 -0
- data/lib/eye/dsl/group_opts.rb +27 -0
- data/lib/eye/dsl/helpers.rb +12 -0
- data/lib/eye/dsl/main.rb +58 -0
- data/lib/eye/dsl/opts.rb +88 -0
- data/lib/eye/dsl/process_opts.rb +21 -0
- data/lib/eye/dsl/pure_opts.rb +132 -0
- data/lib/eye/dsl/validate.rb +41 -0
- data/lib/eye/group.rb +125 -0
- data/lib/eye/group/chain.rb +68 -0
- data/lib/eye/io/unix_server.rb +44 -0
- data/lib/eye/io/unix_socket.rb +39 -0
- data/lib/eye/loader.rb +13 -0
- data/lib/eye/logger.rb +80 -0
- data/lib/eye/process.rb +83 -0
- data/lib/eye/process/child.rb +61 -0
- data/lib/eye/process/commands.rb +256 -0
- data/lib/eye/process/config.rb +70 -0
- data/lib/eye/process/controller.rb +72 -0
- data/lib/eye/process/data.rb +46 -0
- data/lib/eye/process/monitor.rb +97 -0
- data/lib/eye/process/notify.rb +17 -0
- data/lib/eye/process/scheduler.rb +50 -0
- data/lib/eye/process/states.rb +92 -0
- data/lib/eye/process/states_history.rb +62 -0
- data/lib/eye/process/system.rb +60 -0
- data/lib/eye/process/trigger.rb +32 -0
- data/lib/eye/process/watchers.rb +67 -0
- data/lib/eye/server.rb +51 -0
- data/lib/eye/settings.rb +35 -0
- data/lib/eye/system.rb +145 -0
- data/lib/eye/system_resources.rb +83 -0
- data/lib/eye/trigger.rb +53 -0
- data/lib/eye/trigger/flapping.rb +24 -0
- data/lib/eye/utils.rb +5 -0
- data/lib/eye/utils/alive_array.rb +31 -0
- data/lib/eye/utils/celluloid_chain.rb +51 -0
- data/lib/eye/utils/leak_19.rb +7 -0
- data/lib/eye/utils/tail.rb +20 -0
- data/spec/checker/cpu_spec.rb +58 -0
- data/spec/checker/file_ctime_spec.rb +34 -0
- data/spec/checker/file_size_spec.rb +107 -0
- data/spec/checker/http_spec.rb +109 -0
- data/spec/checker/memory_spec.rb +64 -0
- data/spec/checker/socket_spec.rb +116 -0
- data/spec/checker_spec.rb +188 -0
- data/spec/child_process/child_process_spec.rb +46 -0
- data/spec/client_server_spec.rb +34 -0
- data/spec/controller/commands_spec.rb +92 -0
- data/spec/controller/controller_spec.rb +133 -0
- data/spec/controller/find_objects_spec.rb +150 -0
- data/spec/controller/group_spec.rb +110 -0
- data/spec/controller/intergration_spec.rb +327 -0
- data/spec/controller/load_spec.rb +326 -0
- data/spec/controller/races_spec.rb +70 -0
- data/spec/controller/stop_on_delete_spec.rb +157 -0
- data/spec/dsl/chain_spec.rb +140 -0
- data/spec/dsl/checks_spec.rb +202 -0
- data/spec/dsl/config_spec.rb +44 -0
- data/spec/dsl/dsl_spec.rb +73 -0
- data/spec/dsl/getter_spec.rb +223 -0
- data/spec/dsl/integration_spec.rb +311 -0
- data/spec/dsl/load_spec.rb +52 -0
- data/spec/dsl/process_spec.rb +330 -0
- data/spec/dsl/sub_procs_spec.rb +93 -0
- data/spec/dsl/with_server_spec.rb +104 -0
- data/spec/example/em.rb +57 -0
- data/spec/example/forking.rb +20 -0
- data/spec/example/sample.rb +154 -0
- data/spec/fixtures/dsl/0.rb +8 -0
- data/spec/fixtures/dsl/0a.rb +8 -0
- data/spec/fixtures/dsl/0c.rb +8 -0
- data/spec/fixtures/dsl/1.rb +5 -0
- data/spec/fixtures/dsl/bad.eye +6 -0
- data/spec/fixtures/dsl/configs/1.eye +3 -0
- data/spec/fixtures/dsl/configs/2.eye +1 -0
- data/spec/fixtures/dsl/configs/3.eye +1 -0
- data/spec/fixtures/dsl/configs/4.eye +3 -0
- data/spec/fixtures/dsl/empty.eye +20 -0
- data/spec/fixtures/dsl/include_test.eye +5 -0
- data/spec/fixtures/dsl/include_test/1.rb +6 -0
- data/spec/fixtures/dsl/include_test/ha.rb +4 -0
- data/spec/fixtures/dsl/include_test2.eye +5 -0
- data/spec/fixtures/dsl/integration.eye +30 -0
- data/spec/fixtures/dsl/integration2.eye +32 -0
- data/spec/fixtures/dsl/integration_locks.eye +30 -0
- data/spec/fixtures/dsl/integration_sor.eye +32 -0
- data/spec/fixtures/dsl/integration_sor2.eye +27 -0
- data/spec/fixtures/dsl/integration_sor3.eye +32 -0
- data/spec/fixtures/dsl/load.eye +25 -0
- data/spec/fixtures/dsl/load2.eye +7 -0
- data/spec/fixtures/dsl/load2_dup2.eye +13 -0
- data/spec/fixtures/dsl/load2_dup_pid.eye +7 -0
- data/spec/fixtures/dsl/load3.eye +10 -0
- data/spec/fixtures/dsl/load4.eye +7 -0
- data/spec/fixtures/dsl/load5.eye +8 -0
- data/spec/fixtures/dsl/load6.eye +17 -0
- data/spec/fixtures/dsl/load_dubls.eye +36 -0
- data/spec/fixtures/dsl/load_dup_ex_names.eye +15 -0
- data/spec/fixtures/dsl/load_error.eye +5 -0
- data/spec/fixtures/dsl/load_error_folder/load3.eye +10 -0
- data/spec/fixtures/dsl/load_error_folder/load4.eye +7 -0
- data/spec/fixtures/dsl/load_folder/load3.eye +10 -0
- data/spec/fixtures/dsl/load_folder/load4.eye +7 -0
- data/spec/fixtures/dsl/load_int.eye +8 -0
- data/spec/fixtures/dsl/load_int2.eye +13 -0
- data/spec/fixtures/dsl/load_logger.eye +26 -0
- data/spec/fixtures/dsl/load_logger2.eye +3 -0
- data/spec/fixtures/dsl/long_load.eye +5 -0
- data/spec/fixtures/dsl/subfolder1/proc1.rb +3 -0
- data/spec/fixtures/dsl/subfolder2.eye +9 -0
- data/spec/fixtures/dsl/subfolder2/common.rb +1 -0
- data/spec/fixtures/dsl/subfolder2/proc2.rb +3 -0
- data/spec/fixtures/dsl/subfolder2/sub/proc3.rb +6 -0
- data/spec/fixtures/dsl/subfolder3.eye +8 -0
- data/spec/fixtures/dsl/subfolder3/common.rb +1 -0
- data/spec/fixtures/dsl/subfolder3/proc4.rb +3 -0
- data/spec/fixtures/dsl/subfolder3/sub/proc5.rb +6 -0
- data/spec/fixtures/dsl/subfolder4.eye +6 -0
- data/spec/fixtures/dsl/subfolder4/a.rb +2 -0
- data/spec/fixtures/dsl/subfolder4/b.rb +1 -0
- data/spec/fixtures/dsl/subfolder4/c.rb +1 -0
- data/spec/mock_spec.rb +32 -0
- data/spec/process/checks/child_checks_spec.rb +79 -0
- data/spec/process/checks/cpu_spec.rb +114 -0
- data/spec/process/checks/ctime_spec.rb +43 -0
- data/spec/process/checks/fsize_spec.rb +22 -0
- data/spec/process/checks/http_spec.rb +52 -0
- data/spec/process/checks/intergration_spec.rb +32 -0
- data/spec/process/checks/memory_spec.rb +113 -0
- data/spec/process/child_process_spec.rb +125 -0
- data/spec/process/config_spec.rb +75 -0
- data/spec/process/controller_spec.rb +173 -0
- data/spec/process/monitoring_spec.rb +180 -0
- data/spec/process/restart_spec.rb +174 -0
- data/spec/process/scheduler_spec.rb +150 -0
- data/spec/process/start_spec.rb +261 -0
- data/spec/process/states_history_spec.rb +118 -0
- data/spec/process/stop_spec.rb +150 -0
- data/spec/process/system_spec.rb +100 -0
- data/spec/process/triggers/flapping_spec.rb +81 -0
- data/spec/process/update_config_spec.rb +63 -0
- data/spec/spec_helper.rb +120 -0
- data/spec/support/rr_celluloid.rb +36 -0
- data/spec/support/scheduler_hack.rb +16 -0
- data/spec/support/spec_support.rb +164 -0
- data/spec/system_resources_spec.rb +59 -0
- data/spec/system_spec.rb +170 -0
- data/spec/utils/alive_array_spec.rb +50 -0
- data/spec/utils/celluloid_chain_spec.rb +82 -0
- data/spec/utils/tail_spec.rb +21 -0
- metadata +558 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
class Eye::Process::StatesHistory < Eye::Utils::Tail
|
2
|
+
|
3
|
+
def push(state, reason = nil, tm = Time.now)
|
4
|
+
super(state: state, at: tm, reason: reason)
|
5
|
+
end
|
6
|
+
|
7
|
+
def states
|
8
|
+
self.map{|c| c[:state] }
|
9
|
+
end
|
10
|
+
|
11
|
+
def states_for_period(period)
|
12
|
+
tm = Time.now - period
|
13
|
+
self.select do |s|
|
14
|
+
s[:at] >= tm
|
15
|
+
end.map{|c| c[:state] }
|
16
|
+
end
|
17
|
+
|
18
|
+
def last_state
|
19
|
+
last[:state]
|
20
|
+
end
|
21
|
+
|
22
|
+
def last_state_changed_at
|
23
|
+
last[:at]
|
24
|
+
end
|
25
|
+
|
26
|
+
def seq?(*seq)
|
27
|
+
str = states * ','
|
28
|
+
substr = seq.flatten * ','
|
29
|
+
str.include?(substr)
|
30
|
+
end
|
31
|
+
|
32
|
+
def end?(*seq)
|
33
|
+
str = states * ','
|
34
|
+
substr = seq.flatten * ','
|
35
|
+
str.end_with?(substr)
|
36
|
+
end
|
37
|
+
|
38
|
+
def any?(*seq)
|
39
|
+
states.any? do |st|
|
40
|
+
seq.flatten.include?(st)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def noone?(*seq)
|
45
|
+
!states.all? do |st|
|
46
|
+
seq.flatten.include?(st)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def all?(*seq)
|
51
|
+
states.all? do |st|
|
52
|
+
seq.flatten.include?(st)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def state_count(state)
|
57
|
+
states.count do |st|
|
58
|
+
st == state
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module Eye::Process::System
|
4
|
+
|
5
|
+
def load_pid_from_file
|
6
|
+
if File.exists?(self[:pid_file_ex])
|
7
|
+
_pid = File.read(self[:pid_file_ex]).to_i
|
8
|
+
_pid > 0 ? _pid : nil
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_pid_from_file
|
13
|
+
self.pid = load_pid_from_file
|
14
|
+
end
|
15
|
+
|
16
|
+
def save_pid_to_file
|
17
|
+
if self.pid
|
18
|
+
File.open(self[:pid_file_ex], 'w') do |f|
|
19
|
+
f.write self.pid
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def clear_pid_file
|
27
|
+
File.unlink(self[:pid_file_ex])
|
28
|
+
true
|
29
|
+
rescue
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def pid_file_ctime
|
34
|
+
File.ctime(self[:pid_file_ex]) rescue Time.now
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_realy_running?
|
38
|
+
Eye::System.pid_alive?(self.pid) if self.pid
|
39
|
+
end
|
40
|
+
|
41
|
+
def send_signal(code)
|
42
|
+
debug "send signal #{code}"
|
43
|
+
|
44
|
+
res = Eye::System.send_signal(self.pid, code)
|
45
|
+
error(res[:message]) if res[:status] != :ok
|
46
|
+
|
47
|
+
res[:status] == :ok
|
48
|
+
end
|
49
|
+
|
50
|
+
def with_timeout(time, &block)
|
51
|
+
Timeout.timeout(time.to_f, &block)
|
52
|
+
rescue Timeout::Error
|
53
|
+
:timeout
|
54
|
+
end
|
55
|
+
|
56
|
+
def execute(cmd, cfg = {})
|
57
|
+
defer{ Eye::System::execute cmd, cfg }
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Eye::Process::Trigger
|
2
|
+
|
3
|
+
def add_triggers
|
4
|
+
if self[:triggers]
|
5
|
+
self[:triggers].each do |type, cfg|
|
6
|
+
add_trigger(cfg)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def remove_triggers
|
12
|
+
self.triggers = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def check_triggers
|
16
|
+
return if unmonitored?
|
17
|
+
|
18
|
+
self.triggers.each do |trigger|
|
19
|
+
if !trigger.check(self.states_history)
|
20
|
+
notify :crit, 'flapping!'
|
21
|
+
@flapping = true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def add_trigger(cfg = {})
|
29
|
+
self.triggers << Eye::Trigger.create(cfg, logger.prefix)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Eye::Process::Watchers
|
2
|
+
|
3
|
+
def add_watchers(force = false)
|
4
|
+
return unless self.up?
|
5
|
+
|
6
|
+
remove_watchers if force
|
7
|
+
|
8
|
+
if @watchers.blank?
|
9
|
+
# default watcher :check_alive
|
10
|
+
add_watcher(:check_alive, self[:check_alive_period]) do
|
11
|
+
check_alive
|
12
|
+
GC.start if rand(1000) == 0
|
13
|
+
end
|
14
|
+
|
15
|
+
# monitor childs pids
|
16
|
+
if self[:monitor_children]
|
17
|
+
add_watcher(:check_childs, self[:childs_update_period]) do
|
18
|
+
add_or_update_childs
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# monitor conditional watchers
|
23
|
+
start_checkers
|
24
|
+
else
|
25
|
+
warn 'try add_watchers, but its already here'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_watchers
|
30
|
+
@watchers.each{|_, h| h[:timer].cancel }
|
31
|
+
@watchers = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def add_watcher(type, period = 2, subject = nil, &block)
|
37
|
+
return if @watchers[type]
|
38
|
+
|
39
|
+
debug "add watcher #{type}(#{period})"
|
40
|
+
|
41
|
+
timer = every(period.to_f) do
|
42
|
+
debug "check #{type}"
|
43
|
+
block.call(subject)
|
44
|
+
end
|
45
|
+
|
46
|
+
@watchers[type] ||= {:timer => timer, :subject => subject}
|
47
|
+
end
|
48
|
+
|
49
|
+
def start_checkers
|
50
|
+
self[:checks].each{|type, cfg| start_checker(cfg) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def start_checker(cfg)
|
54
|
+
subject = Eye::Checker.create(pid, cfg, logger.prefix)
|
55
|
+
|
56
|
+
# ex: {:type => :memory, :every => 5.seconds, :below => 100.megabytes, :times => [3,5]}
|
57
|
+
add_watcher("check_#{cfg[:type]}".to_sym, subject.every, subject, &method(:watcher_tick).to_proc)
|
58
|
+
end
|
59
|
+
|
60
|
+
def watcher_tick(subject)
|
61
|
+
unless subject.check
|
62
|
+
notify :crit, "Bounded #{subject.check_name}: #{subject.last_human_values}"
|
63
|
+
schedule :restart, "bounded #{subject.check_name}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/eye/server.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'celluloid/io'
|
2
|
+
require_relative 'io/unix_socket'
|
3
|
+
require_relative 'io/unix_server'
|
4
|
+
|
5
|
+
class Eye::Server
|
6
|
+
include Celluloid::IO
|
7
|
+
|
8
|
+
attr_reader :socket_path, :server
|
9
|
+
|
10
|
+
def initialize(socket_path)
|
11
|
+
@socket_path = socket_path
|
12
|
+
@server = begin
|
13
|
+
UNIXServer.open(socket_path)
|
14
|
+
rescue Errno::EADDRINUSE
|
15
|
+
unlink_socket_file
|
16
|
+
UNIXServer.open(socket_path)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def finalize
|
21
|
+
@server.close if @server
|
22
|
+
unlink_socket_file
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
loop { handle_connection! @server.accept }
|
27
|
+
end
|
28
|
+
|
29
|
+
def handle_connection(socket)
|
30
|
+
command, *args = socket.readline.strip.split('|')
|
31
|
+
response = command(command, *args)
|
32
|
+
socket.write(Marshal.dump(response))
|
33
|
+
|
34
|
+
rescue Errno::EPIPE
|
35
|
+
# client timeouted
|
36
|
+
# do nothing
|
37
|
+
|
38
|
+
ensure
|
39
|
+
socket.close
|
40
|
+
end
|
41
|
+
|
42
|
+
def command(cmd, *args)
|
43
|
+
Eye::Control.command(cmd, *args)
|
44
|
+
end
|
45
|
+
|
46
|
+
def unlink_socket_file
|
47
|
+
File.delete(@socket_path) if @socket_path
|
48
|
+
rescue
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/lib/eye/settings.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Eye::Settings
|
4
|
+
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def dir
|
8
|
+
if Process::UID.eid == 0 # root
|
9
|
+
'/var/run/eye'
|
10
|
+
else
|
11
|
+
File.expand_path(File.join(ENV['HOME'], '.eye'))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def path(path)
|
16
|
+
File.join(dir, path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ensure_eye_dir
|
20
|
+
FileUtils.mkdir_p( dir )
|
21
|
+
end
|
22
|
+
|
23
|
+
def socket_path
|
24
|
+
path('sock')
|
25
|
+
end
|
26
|
+
|
27
|
+
def pid_path
|
28
|
+
path('pid')
|
29
|
+
end
|
30
|
+
|
31
|
+
def client_timeout
|
32
|
+
5
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/lib/eye/system.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Eye::System
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# Check that pid realy exits
|
8
|
+
# very fast
|
9
|
+
def pid_alive?(pid)
|
10
|
+
pid ? ::Process.kill(0, pid) && true : false
|
11
|
+
rescue Errno::ESRCH, Errno::EPERM
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
# Send signal to process (uses for kill)
|
16
|
+
# code: TERM(15), KILL(9), QUIT(3), ...
|
17
|
+
def send_signal(pid, code = :TERM)
|
18
|
+
code = 0 if code == '0'
|
19
|
+
if code.to_s.to_i != 0
|
20
|
+
code = code.to_i
|
21
|
+
code = -code if code < 0
|
22
|
+
end
|
23
|
+
code = code.to_s.upcase if code.is_a?(String) || code.is_a?(Symbol)
|
24
|
+
|
25
|
+
::Process.kill(code, pid) if pid
|
26
|
+
{:status => :ok}
|
27
|
+
|
28
|
+
rescue Errno::ESRCH
|
29
|
+
{:status => :error, :message => 'process not found'}
|
30
|
+
|
31
|
+
rescue Errno::EPERM
|
32
|
+
{:status => :error, :message => 'wrong permissions to kill'}
|
33
|
+
|
34
|
+
rescue => e
|
35
|
+
{:status => :error, :message => "failed signal #{code}: #{e.message}"}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Daemonize cmd, and detach
|
39
|
+
# options:
|
40
|
+
# :pid_file
|
41
|
+
# :working_dir
|
42
|
+
# :environment
|
43
|
+
# :stdin, :stdout, :stderr
|
44
|
+
def daemonize(cmd, cfg = {})
|
45
|
+
opts = spawn_options(cfg)
|
46
|
+
pid = Process::spawn(prepare_env(cfg), *Shellwords.shellwords(cmd), opts)
|
47
|
+
Process.detach(pid)
|
48
|
+
{:pid => pid}
|
49
|
+
|
50
|
+
rescue Errno::ENOENT, Errno::EACCES => ex
|
51
|
+
{:error => ex}
|
52
|
+
end
|
53
|
+
|
54
|
+
# Execute cmd with blocking, return status (be careful: inside actor blocks it mailbox, use with defer)
|
55
|
+
# options
|
56
|
+
# :working_dir
|
57
|
+
# :environment
|
58
|
+
# :stdin, :stdout, :stderr
|
59
|
+
def execute(cmd, cfg = {})
|
60
|
+
opts = spawn_options(cfg)
|
61
|
+
pid = Process::spawn(prepare_env(cfg), *Shellwords.shellwords(cmd), opts)
|
62
|
+
|
63
|
+
Timeout.timeout(cfg[:timeout] || 1.second) do
|
64
|
+
Process.waitpid(pid)
|
65
|
+
end
|
66
|
+
|
67
|
+
{:pid => pid}
|
68
|
+
|
69
|
+
rescue Timeout::Error => ex
|
70
|
+
send_signal(pid, 9)
|
71
|
+
{:error => ex}
|
72
|
+
|
73
|
+
rescue Errno::ENOENT, Errno::EACCES => ex
|
74
|
+
{:error => ex}
|
75
|
+
end
|
76
|
+
|
77
|
+
# get table
|
78
|
+
# {pid => {:rss =>, :cpu =>, :ppid => , :cmd => , :start_time}}
|
79
|
+
# slow
|
80
|
+
def ps_aux
|
81
|
+
cmd = if RUBY_PLATFORM.include?('darwin')
|
82
|
+
'ps axo pid,ppid,pcpu,rss,start,command'
|
83
|
+
else
|
84
|
+
'ps axo pid,ppid,pcpu,rss,start_time,command'
|
85
|
+
end
|
86
|
+
|
87
|
+
str = Process.send('`', cmd).force_encoding('binary')
|
88
|
+
lines = str.split("\n")
|
89
|
+
lines.shift # remove first line
|
90
|
+
lines.inject(Hash.new) do |mem, line|
|
91
|
+
chunk = line.strip.split(/\s+/).map(&:strip)
|
92
|
+
mem[chunk[0].to_i] = {
|
93
|
+
:rss => chunk[3].to_i,
|
94
|
+
:cpu => chunk[2].to_i,
|
95
|
+
:ppid => chunk[1].to_i,
|
96
|
+
:start_time => chunk[4],
|
97
|
+
:cmd => chunk[5..-1].join(' ')
|
98
|
+
}
|
99
|
+
mem
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# normalize file
|
104
|
+
def normalized_file(file, working_dir = nil)
|
105
|
+
Pathname.new(file).expand_path(working_dir).to_s
|
106
|
+
end
|
107
|
+
|
108
|
+
def host
|
109
|
+
@host ||= `hostname`.chomp
|
110
|
+
end
|
111
|
+
|
112
|
+
# set host for tests
|
113
|
+
def host=(hostname)
|
114
|
+
@host = hostname
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def spawn_options(config = {})
|
120
|
+
o = {}
|
121
|
+
o = {chdir: config[:working_dir]} if config[:working_dir]
|
122
|
+
o.update(out: [config[:stdout], 'a']) if config[:stdout]
|
123
|
+
o.update(err: [config[:stderr], 'a']) if config[:stderr]
|
124
|
+
o.update(in: config[:stdin]) if config[:stdin]
|
125
|
+
o
|
126
|
+
end
|
127
|
+
|
128
|
+
def prepare_env(config = {})
|
129
|
+
env = {}
|
130
|
+
|
131
|
+
(config[:environment] || {}).each do |k,v|
|
132
|
+
env[k.to_s] = v.to_s if v
|
133
|
+
end
|
134
|
+
|
135
|
+
# return original LANG env, because ruby loose it (needs for unicorn)
|
136
|
+
env['LANG'] = ENV_LANG unless env['LANG']
|
137
|
+
|
138
|
+
# set PWD for unicorn respawn
|
139
|
+
env['PWD'] = config[:working_dir] if config[:working_dir]
|
140
|
+
|
141
|
+
env
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|