aggkit 0.3.8.9036 → 0.4.0.9098
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -3
- data/bin/aggconsul +5 -5
- data/bin/aggstart +4 -4
- data/bin/aggterm +29 -1
- data/bin/aggwrap +10 -6
- data/lib/aggkit.rb +2 -1
- data/lib/aggkit/childprocess/abstract_process.rb +2 -0
- data/lib/aggkit/childprocess/unix/process.rb +1 -0
- data/lib/aggkit/runner.rb +13 -2
- data/lib/aggkit/version.rb +1 -1
- data/lib/aggkit/watcher.rb +149 -177
- data/lib/aggkit/watcher/process_handler.rb +172 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d61521bee578abba7b80c9ad0182aae2483d42ca
|
4
|
+
data.tar.gz: a55d378ef1f3a44cd9a562cb9857aab3d7ab318f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 970126d8a53f24b3a75f3317d4bd3db962540cae8eedcaa74ba33e0fa9d726837e0d975484c9dc9166566027527a25de31197bdd761f521ea317e277c2957bfb
|
7
|
+
data.tar.gz: c3082b9794e11ded61f6a67ef2826dfa3626533f596a5dd3bf464527774c973f23cf4c82b81ad20c8bd81d664fe835fb6915d2d1c3f737ba15f9322499d5d124
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
aggkit (0.
|
4
|
+
aggkit (0.4.0.9098)
|
5
5
|
diplomat
|
6
6
|
dotenv
|
7
7
|
json
|
@@ -12,9 +12,8 @@ GEM
|
|
12
12
|
specs:
|
13
13
|
awesome_print (1.8.0)
|
14
14
|
diff-lcs (1.3)
|
15
|
-
diplomat (2.0.
|
15
|
+
diplomat (2.0.5)
|
16
16
|
faraday (~> 0.9)
|
17
|
-
json
|
18
17
|
dotenv (2.7.1)
|
19
18
|
faraday (0.15.4)
|
20
19
|
multipart-post (>= 1.2, < 3)
|
data/bin/aggconsul
CHANGED
@@ -13,7 +13,7 @@ if ENV['CONSUL_HTTP_ADDR'].to_s.empty?
|
|
13
13
|
end
|
14
14
|
|
15
15
|
@opts = {
|
16
|
-
consul:
|
16
|
+
consul: ENV['CONSUL_HTTP_ADDR']
|
17
17
|
}
|
18
18
|
|
19
19
|
@opts[:exec] = (begin
|
@@ -26,12 +26,12 @@ parser = OptionParser.new do |o|
|
|
26
26
|
o.banner = 'Usage: consul.rb [options] -- exec'
|
27
27
|
|
28
28
|
o.on('--consul url', 'Set up a custom Consul URL') do |consul|
|
29
|
-
if consul.to_s['http']
|
30
|
-
|
29
|
+
ENV['CONSUL_HTTP_ADDR'] = if consul.to_s['http']
|
30
|
+
consul.to_s
|
31
31
|
else
|
32
|
-
|
32
|
+
"http://#{consul}:8500"
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
@opts[:consul] = ENV['CONSUL_HTTP_ADDR']
|
36
36
|
end
|
37
37
|
|
data/bin/aggstart
CHANGED
@@ -31,12 +31,12 @@ parser = Aggkit::OptionParser.new do |o|
|
|
31
31
|
end
|
32
32
|
|
33
33
|
o.on("--consul=#{@opts[:consul]}", 'Set consul http address. CONSUL_HTTP_ADDR env can be used instead') do |consul|
|
34
|
-
if consul.to_s['http']
|
35
|
-
|
34
|
+
ENV['CONSUL_HTTP_ADDR'] = if consul.to_s['http']
|
35
|
+
consul.to_s
|
36
36
|
else
|
37
|
-
|
37
|
+
"http://#{consul}:8500"
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
@opts[:consul] = ENV['CONSUL_HTTP_ADDR']
|
41
41
|
end
|
42
42
|
|
data/bin/aggterm
CHANGED
@@ -12,7 +12,7 @@ STDERR.sync = true
|
|
12
12
|
}
|
13
13
|
|
14
14
|
def log(msg)
|
15
|
-
puts "[terminator]: #{msg}"
|
15
|
+
puts "[terminator:#{::Process.pid}][#{Time.now.strftime('%H:%M:%S.%L')}]: #{msg}"
|
16
16
|
end
|
17
17
|
|
18
18
|
log "started: #{ARGV.inspect}"
|
@@ -40,6 +40,10 @@ parser = OptionParser.new do |o|
|
|
40
40
|
o.on('--kill', 'SIGKILL self') do
|
41
41
|
@opts[:kill] = true
|
42
42
|
end
|
43
|
+
|
44
|
+
o.on('--fork NUM', 'fork NUM suprocesses and wait') do |num|
|
45
|
+
@opts[:fork] = Integer(num.to_s)
|
46
|
+
end
|
43
47
|
end
|
44
48
|
parser.parse!
|
45
49
|
|
@@ -53,6 +57,29 @@ parser.parse!
|
|
53
57
|
end
|
54
58
|
end
|
55
59
|
|
60
|
+
if @opts[:fork]
|
61
|
+
log 'forking...'
|
62
|
+
pids = @opts[:fork].times.map do |i|
|
63
|
+
fork do
|
64
|
+
exec 'echo', "#{i} forked"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
sleep 1
|
68
|
+
pids.each do |pid|
|
69
|
+
pid, status = ::Process.wait2(pid, Process::WNOHANG)
|
70
|
+
|
71
|
+
if status.nil?
|
72
|
+
log 'child already collected'
|
73
|
+
exit 1
|
74
|
+
end
|
75
|
+
|
76
|
+
unless status.success?
|
77
|
+
log 'child crashed'
|
78
|
+
exit 2
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
56
83
|
log 'sleep...'
|
57
84
|
sleep @opts[:sleep]
|
58
85
|
|
@@ -67,6 +94,7 @@ if @opts[:term]
|
|
67
94
|
::Process.kill('TERM', $PROCESS_ID)
|
68
95
|
end
|
69
96
|
|
97
|
+
|
70
98
|
log "normal exit with: #{@opts[:code]}"
|
71
99
|
exit @opts[:code]
|
72
100
|
|
data/bin/aggwrap
CHANGED
@@ -6,8 +6,10 @@ require 'aggkit'
|
|
6
6
|
require 'net/http'
|
7
7
|
|
8
8
|
module Diplomat
|
9
|
+
|
9
10
|
# Methods for interacting with the Consul check API endpoint
|
10
11
|
class Check < Diplomat::RestClient
|
12
|
+
|
11
13
|
# Update a TTL check
|
12
14
|
# @param check_id [String] the unique id of the check
|
13
15
|
# @param status [String] status of the check. Valid values are "passing", "warning", and "critical"
|
@@ -44,7 +46,9 @@ module Diplomat
|
|
44
46
|
def fail(check_id, output = nil)
|
45
47
|
update_ttl(check_id, 'critical', output)
|
46
48
|
end
|
49
|
+
|
47
50
|
end
|
51
|
+
|
48
52
|
end
|
49
53
|
|
50
54
|
|
@@ -61,7 +65,7 @@ end
|
|
61
65
|
@opts = {
|
62
66
|
service: ENV['AGGREDATOR_SERVICE'],
|
63
67
|
id: SecureRandom.hex(8),
|
64
|
-
consul: ENV['CONSUL_HTTP_ADDR']
|
68
|
+
consul: ENV['CONSUL_HTTP_ADDR']
|
65
69
|
}
|
66
70
|
|
67
71
|
@opts[:exec] = (begin
|
@@ -83,12 +87,12 @@ parser = Aggkit::OptionParser.new do |o|
|
|
83
87
|
end
|
84
88
|
|
85
89
|
o.on("--consul=#{@opts[:consul]}", 'Set consul host. CONSUL_HOST env used when missed') do |consul|
|
86
|
-
if consul.to_s['http']
|
87
|
-
|
90
|
+
ENV['CONSUL_HTTP_ADDR'] = if consul.to_s['http']
|
91
|
+
consul.to_s
|
88
92
|
else
|
89
|
-
|
93
|
+
"http://#{consul}:8500"
|
90
94
|
end
|
91
|
-
|
95
|
+
|
92
96
|
@opts[:consul] = ENV['CONSUL_HTTP_ADDR']
|
93
97
|
end
|
94
98
|
|
@@ -114,7 +118,7 @@ die 'service name must be provided' if @opts[:service].to_s.empty?
|
|
114
118
|
|
115
119
|
die 'script must be provided' if @opts[:exec].empty?
|
116
120
|
|
117
|
-
|
121
|
+
|
118
122
|
Diplomat.configure do |config|
|
119
123
|
config.url = @opts[:consul]
|
120
124
|
end
|
data/lib/aggkit.rb
CHANGED
data/lib/aggkit/runner.rb
CHANGED
@@ -47,14 +47,25 @@ module Aggkit
|
|
47
47
|
|
48
48
|
def execute(cmd)
|
49
49
|
puts "Executing: #{cmd}"
|
50
|
-
|
50
|
+
io = IO.popen(cmd)
|
51
|
+
output = io.read
|
52
|
+
begin
|
53
|
+
io.close
|
54
|
+
rescue StandardError
|
55
|
+
nil
|
56
|
+
end
|
51
57
|
@last_result = $?
|
52
58
|
output
|
53
59
|
end
|
54
60
|
|
55
61
|
def execute!(cmd, error = nil)
|
56
62
|
output = execute(cmd)
|
57
|
-
|
63
|
+
if @last_result
|
64
|
+
@last_result.success? || die(error || "Can't execute: #{cmd}")
|
65
|
+
else
|
66
|
+
die("Already collected: #{cmd}")
|
67
|
+
end
|
68
|
+
|
58
69
|
output
|
59
70
|
end
|
60
71
|
|
data/lib/aggkit/version.rb
CHANGED
data/lib/aggkit/watcher.rb
CHANGED
@@ -5,235 +5,207 @@ module Aggkit
|
|
5
5
|
|
6
6
|
class Watcher
|
7
7
|
|
8
|
+
require 'aggkit/watcher/process_handler'
|
9
|
+
|
10
|
+
attr_accessor :iolock
|
11
|
+
|
12
|
+
EXIT_SIGNALS = %w[EXIT QUIT].freeze
|
13
|
+
TERM_SIGNALS = %w[INT TERM].freeze
|
14
|
+
|
15
|
+
class Pipe
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@read, @write = IO.pipe
|
19
|
+
end
|
20
|
+
|
21
|
+
def puts(str)
|
22
|
+
@write.puts str.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def gets
|
26
|
+
@read.gets.strip.to_sym
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
8
30
|
|
9
31
|
def initialize
|
10
|
-
@
|
32
|
+
@iolock = Mutex.new
|
33
|
+
@pipe = Pipe.new
|
11
34
|
|
12
35
|
@procs = []
|
13
36
|
@code = 0
|
14
37
|
@crashed = false
|
15
|
-
@threads = []
|
16
38
|
@who = nil
|
17
|
-
|
18
|
-
end
|
39
|
+
end
|
19
40
|
|
20
|
-
def
|
21
|
-
|
41
|
+
def add(*cmd)
|
42
|
+
log "Starting #{cmd}..."
|
43
|
+
@procs.push ProcessHandler.new(self, *cmd)
|
44
|
+
log " * PID: #{@procs.last.pid}"
|
45
|
+
@procs.last
|
46
|
+
end
|
22
47
|
|
23
|
-
|
24
|
-
|
25
|
-
|
48
|
+
def log(msg)
|
49
|
+
@iolock.synchronize{ STDOUT.puts("[watcher][#{Time.now.strftime('%H:%M:%S.%L')}]: #{msg}") }
|
50
|
+
end
|
26
51
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
rescue StandardError
|
31
|
-
nil
|
32
|
-
end
|
33
|
-
if meta[:termcmd].is_a? String
|
34
|
-
log "Terminating by #{meta[:termcmd].gsub('%PID%', meta[:process].pid.to_s)}..."
|
35
|
-
output = `#{meta[:termcmd].gsub('%PID%', meta[:process].pid.to_s)}`
|
36
|
-
if $?.success?
|
37
|
-
log "ok: #{output}"
|
38
|
-
else
|
39
|
-
log "Error: #{output}"
|
40
|
-
::Process.kill 'TERM', meta[:process].pid
|
41
|
-
end
|
42
|
-
else
|
43
|
-
::Process.kill 'TERM', meta[:process].pid
|
44
|
-
end
|
45
|
-
end
|
52
|
+
def error(msg)
|
53
|
+
@iolock.synchronize{ STDERR.puts("[watcher][#{Time.now.strftime('%H:%M:%S.%L')}]: Error: #{msg}") }
|
54
|
+
end
|
46
55
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
meta[:stdout].close
|
53
|
-
meta[:stderr].close
|
54
|
-
end
|
56
|
+
def set_crash_report(pr)
|
57
|
+
unless @crashed
|
58
|
+
@crashed = true
|
59
|
+
@code = pr.exit_code
|
60
|
+
@who = pr.command
|
55
61
|
end
|
62
|
+
end
|
56
63
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
64
|
+
def quit!
|
65
|
+
@code = [@code || 0, 1].max if @crashed
|
66
|
+
exit!(@code || 0)
|
67
|
+
end
|
61
68
|
|
62
|
-
|
63
|
-
|
64
|
-
@code = meta[:process].exit_code
|
65
|
-
@who = meta[:cmd]
|
66
|
-
end
|
67
|
-
end
|
69
|
+
def terminate_all
|
70
|
+
raise 'Double termination occured!' if $terminating
|
68
71
|
|
69
|
-
|
70
|
-
|
71
|
-
meta[:stdout].close
|
72
|
-
rescue StandardError
|
73
|
-
nil
|
74
|
-
end
|
75
|
-
begin
|
76
|
-
meta[:stderr].close
|
77
|
-
rescue StandardError
|
78
|
-
nil
|
79
|
-
end
|
80
|
-
end
|
72
|
+
$terminating = true
|
73
|
+
running = @procs.reject(&:handled?)
|
81
74
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
75
|
+
running.each do |pr|
|
76
|
+
begin
|
77
|
+
pr.stdin.close
|
78
|
+
rescue StandardError
|
79
|
+
nil
|
86
80
|
end
|
87
|
-
|
88
|
-
def add(*cmd)
|
89
|
-
options = cmd.last
|
90
|
-
if options.is_a? Hash
|
91
|
-
cmd.pop
|
92
|
-
else
|
93
|
-
options = {}
|
81
|
+
pr.terminate
|
94
82
|
end
|
95
83
|
|
96
|
-
|
97
|
-
|
84
|
+
running.each do |pr|
|
85
|
+
pr.stop
|
86
|
+
collect_managed(pr)
|
87
|
+
end
|
88
|
+
end
|
98
89
|
|
99
|
-
|
100
|
-
|
90
|
+
def exec
|
91
|
+
install_signal_handlers(@pipe)
|
101
92
|
|
102
|
-
|
103
|
-
|
104
|
-
|
93
|
+
begin
|
94
|
+
yield(self)
|
95
|
+
rescue StandardError => e
|
96
|
+
@crashed = true
|
97
|
+
@code = 1
|
98
|
+
error e.inspect
|
99
|
+
error e.backtrace.last(20).join("\n")
|
100
|
+
log 'Try exits gracefully..'
|
101
|
+
terminate_all
|
102
|
+
exit!(@code)
|
103
|
+
end
|
105
104
|
|
106
|
-
|
107
|
-
|
108
|
-
process: process,
|
109
|
-
stdout: rout,
|
110
|
-
stderr: rerr,
|
111
|
-
stdin: process.io.stdin,
|
112
|
-
termcmd: options[:termcmd]
|
113
|
-
}
|
105
|
+
loop_childs
|
106
|
+
quit! if @procs.all?(&:handled?)
|
114
107
|
|
115
|
-
|
108
|
+
Thread.new(@pipe) do |pipe|
|
116
109
|
loop do
|
117
|
-
|
110
|
+
begin
|
111
|
+
sleep 10
|
112
|
+
pipe.puts :gc
|
113
|
+
rescue StandardError => e
|
114
|
+
log "GC thread exception: #{e.inspect}"
|
115
|
+
end
|
118
116
|
end
|
119
117
|
end
|
120
118
|
|
121
|
-
|
122
|
-
loop
|
123
|
-
|
119
|
+
loop do
|
120
|
+
log 'Main loop...'
|
121
|
+
case event = @pipe.gets
|
122
|
+
when :term
|
123
|
+
log ' * Child terminated: try exits gracefully...'
|
124
|
+
terminate_all
|
125
|
+
loop_childs
|
126
|
+
quit!
|
127
|
+
when *(EXIT_SIGNALS + TERM_SIGNALS).map(&:to_sym)
|
128
|
+
log " * Catch #{event}: try exits gracefully..."
|
129
|
+
terminate_all
|
130
|
+
loop_childs
|
131
|
+
quit!
|
132
|
+
when :child, :gc
|
133
|
+
log " * Main loop event: #{event}"
|
134
|
+
loop_childs
|
135
|
+
quit! if @procs.all?(&:handled?)
|
136
|
+
else
|
137
|
+
log " * Unknown event: #{event.inspect}"
|
138
|
+
loop_childs
|
139
|
+
quit! if @procs.all?(&:handled?)
|
124
140
|
end
|
125
141
|
end
|
126
|
-
|
127
|
-
log "Starting #{meta[:cmd]}"
|
128
|
-
meta[:pid] = meta[:process].start.pid
|
129
|
-
|
130
|
-
@procs.push(meta)
|
131
|
-
meta
|
132
142
|
end
|
133
143
|
|
134
|
-
|
135
|
-
str = io.gets
|
136
|
-
@lock.synchronize{ out.puts str }
|
137
|
-
true
|
138
|
-
rescue StandardError => e
|
139
|
-
false
|
140
|
-
end
|
144
|
+
private
|
141
145
|
|
142
|
-
|
143
|
-
|
144
|
-
|
146
|
+
def collect_managed(pr, status = nil)
|
147
|
+
log 'Child finished'
|
148
|
+
log " Process[#{pr.pid}]: #{pr.command}"
|
149
|
+
log " status: #{(status || pr.status).inspect}"
|
145
150
|
|
146
|
-
|
147
|
-
STDERR.puts "[watcher]: Error: #{msg}"
|
148
|
-
end
|
151
|
+
pr.stop!(status) if status
|
149
152
|
|
150
|
-
|
151
|
-
%w[EXIT QUIT].each do |sig|
|
152
|
-
trap(sig) do
|
153
|
-
stop_all
|
154
|
-
end
|
153
|
+
set_crash_report(pr) if pr.crashed?
|
155
154
|
end
|
156
155
|
|
156
|
+
def collect_processes
|
157
|
+
pid, status = Process.waitpid2(-1, ::Process::WNOHANG | ::Process::WUNTRACED)
|
157
158
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
159
|
+
return false if pid.nil? || pid == 0
|
160
|
+
|
161
|
+
if pr = @procs.find {|pr| pr.pid == pid }
|
162
|
+
collect_managed(pr, status)
|
163
|
+
@pipe.puts :term
|
164
|
+
else
|
165
|
+
log "Unmanaged process finished: #{status.inspect}"
|
162
166
|
end
|
167
|
+
|
168
|
+
true
|
169
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
170
|
+
false
|
163
171
|
end
|
164
172
|
|
165
|
-
|
166
|
-
|
167
|
-
|
173
|
+
def loop_childs
|
174
|
+
loop do
|
175
|
+
collected = collect_processes
|
176
|
+
break unless collected
|
168
177
|
end
|
178
|
+
end
|
169
179
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
180
|
+
def install_signal_handlers(pipe)
|
181
|
+
EXIT_SIGNALS.each do |sig|
|
182
|
+
trap(sig) do |*_args|
|
183
|
+
EXIT_SIGNALS.each do |sig|
|
184
|
+
trap(sig, 'IGNORE')
|
185
|
+
end
|
175
186
|
|
176
|
-
|
177
|
-
begin
|
178
|
-
meta[:stdin].close
|
179
|
-
rescue StandardError
|
180
|
-
nil
|
181
|
-
end
|
182
|
-
begin
|
183
|
-
meta[:stdout].close
|
184
|
-
rescue StandardError
|
185
|
-
nil
|
186
|
-
end
|
187
|
-
begin
|
188
|
-
meta[:stderr].close
|
189
|
-
rescue StandardError
|
190
|
-
nil
|
187
|
+
pipe.puts sig
|
191
188
|
end
|
189
|
+
end
|
192
190
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
191
|
+
TERM_SIGNALS.each do |sig|
|
192
|
+
trap(sig) do |*_args|
|
193
|
+
TERM_SIGNALS.each do |sig|
|
194
|
+
trap(sig) do |*_args|
|
195
|
+
STDERR.puts 'Forcing exit!'
|
196
|
+
exit!(1)
|
197
|
+
end
|
198
|
+
end
|
198
199
|
|
199
|
-
|
200
|
-
log 'Try exits gracefully..'
|
201
|
-
stop_all
|
200
|
+
pipe.puts sig
|
202
201
|
end
|
203
|
-
|
204
|
-
true
|
205
202
|
end
|
206
203
|
|
207
|
-
|
208
|
-
|
209
|
-
pid, status = Process.waitpid2(-1, Process::WNOHANG)
|
210
|
-
rescue StandardError
|
211
|
-
nil
|
212
|
-
end
|
213
|
-
log "Subprocess finished: #{status.inspect}" if status
|
204
|
+
trap('CLD') do |*_args|
|
205
|
+
pipe.puts :child
|
214
206
|
end
|
215
207
|
end
|
216
208
|
|
217
|
-
begin
|
218
|
-
yield(self)
|
219
|
-
rescue StandardError => e
|
220
|
-
@crashed = true
|
221
|
-
@code = 1
|
222
|
-
error e.inspect
|
223
|
-
error e.backtrace.last(20).join("\n")
|
224
|
-
log 'Try exits gracefully..'
|
225
|
-
stop_all
|
226
|
-
end
|
227
|
-
|
228
|
-
@threads.map(&:join)
|
229
|
-
|
230
|
-
@code = [@code || 0, 1].max if @crashed
|
231
|
-
|
232
|
-
exit(@code || 0)
|
233
|
-
end
|
234
|
-
|
235
|
-
|
236
|
-
|
237
209
|
end
|
238
210
|
|
239
211
|
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'English'
|
2
|
+
|
3
|
+
class Aggkit::Watcher::ProcessHandler
|
4
|
+
|
5
|
+
attr_accessor :watcher, :command, :options, :process, :stdin, :stdout, :stderr
|
6
|
+
|
7
|
+
def self.capture(cmd)
|
8
|
+
io = IO.popen(cmd)
|
9
|
+
output = io.read
|
10
|
+
begin
|
11
|
+
io.close
|
12
|
+
rescue StandardError
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
[output, $?]
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(watcher, *cmd)
|
19
|
+
@watcher = watcher
|
20
|
+
|
21
|
+
@options = if cmd.last.is_a? Hash
|
22
|
+
cmd.pop
|
23
|
+
else
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
|
27
|
+
@command = cmd.flatten.map{|c| c.to_s.strip }.reject(&:empty?)
|
28
|
+
@process = build_process(*command)
|
29
|
+
|
30
|
+
initialize_streams
|
31
|
+
|
32
|
+
@process.start
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize_streams
|
36
|
+
@threads = []
|
37
|
+
|
38
|
+
@threads << Thread.new do
|
39
|
+
loop do
|
40
|
+
break unless synchro_readline(stdout, STDOUT)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@threads << Thread.new do
|
45
|
+
loop do
|
46
|
+
break unless synchro_readline(stderr, STDERR)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def synchro_readline(io, out)
|
52
|
+
str = io.gets
|
53
|
+
@watcher.iolock.synchronize{ out.puts(str) }
|
54
|
+
true
|
55
|
+
rescue StandardError
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
def build_process(*cmd)
|
60
|
+
pr = ::Aggkit::ChildProcess.build(*cmd)
|
61
|
+
@stdout, wout = IO.pipe
|
62
|
+
@stderr, werr = IO.pipe
|
63
|
+
|
64
|
+
@stdin = pr.io.stdin
|
65
|
+
|
66
|
+
pr.io.stdout = wout
|
67
|
+
pr.io.stderr = werr
|
68
|
+
pr.duplex = true
|
69
|
+
pr
|
70
|
+
end
|
71
|
+
|
72
|
+
def handled?
|
73
|
+
!!@process.exit_code
|
74
|
+
end
|
75
|
+
|
76
|
+
def stop!(status)
|
77
|
+
@process.send(:set_exit_code, status)
|
78
|
+
stop
|
79
|
+
end
|
80
|
+
|
81
|
+
def terminate
|
82
|
+
return if exited?
|
83
|
+
return if @terminating
|
84
|
+
|
85
|
+
@terminating = true
|
86
|
+
|
87
|
+
if @options[:termcmd]
|
88
|
+
termcmd = @options[:termcmd].gsub('%PID%', pid.to_s)
|
89
|
+
@watcher.log "Terminating by #{termcmd}..."
|
90
|
+
output, status = ::Aggkit::Watcher::ProcessHandler.capture(termcmd)
|
91
|
+
|
92
|
+
if status.success?
|
93
|
+
@watcher.log "Success: #{output}"
|
94
|
+
else
|
95
|
+
@watcher.error "Failed: #{output}"
|
96
|
+
@process.send(:send_term)
|
97
|
+
end
|
98
|
+
else
|
99
|
+
@process.send(:send_term)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def stop(timeout = (@options[:timeout] || 5))
|
104
|
+
return if exited?
|
105
|
+
|
106
|
+
terminate
|
107
|
+
|
108
|
+
begin
|
109
|
+
return poll_for_exit(timeout)
|
110
|
+
rescue TimeoutError
|
111
|
+
# try next
|
112
|
+
end
|
113
|
+
|
114
|
+
begin
|
115
|
+
@process.send(:send_kill)
|
116
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
117
|
+
# handle race condition where process dies between timeout
|
118
|
+
# and send_kill
|
119
|
+
end
|
120
|
+
|
121
|
+
wait
|
122
|
+
ensure
|
123
|
+
clean_all
|
124
|
+
end
|
125
|
+
|
126
|
+
def method_missing(m, *args, &block)
|
127
|
+
if @process.respond_to? m
|
128
|
+
@process.send(m, *args, &block)
|
129
|
+
else
|
130
|
+
super
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def clean_all
|
137
|
+
begin
|
138
|
+
@stdin.close
|
139
|
+
rescue StandardError
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
|
143
|
+
begin
|
144
|
+
@stdout.close
|
145
|
+
rescue StandardError
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
|
149
|
+
begin
|
150
|
+
@stderr.close
|
151
|
+
rescue StandardError
|
152
|
+
nil
|
153
|
+
end
|
154
|
+
|
155
|
+
@threads.each do |th|
|
156
|
+
begin
|
157
|
+
th.terminate
|
158
|
+
rescue StandardError
|
159
|
+
nil
|
160
|
+
end
|
161
|
+
begin
|
162
|
+
th.join
|
163
|
+
rescue StandardError
|
164
|
+
nil
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
@threads = []
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aggkit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0.9098
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Godko Ivan
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-02-
|
12
|
+
date: 2019-02-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: diplomat
|
@@ -171,6 +171,7 @@ files:
|
|
171
171
|
- lib/aggkit/runner.rb
|
172
172
|
- lib/aggkit/version.rb
|
173
173
|
- lib/aggkit/watcher.rb
|
174
|
+
- lib/aggkit/watcher/process_handler.rb
|
174
175
|
homepage: https://br.rnds.pro/aggredator/support/aggkit
|
175
176
|
licenses:
|
176
177
|
- MIT
|