parallel_cucumber 0.2.0.pre.36 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -0
- data/lib/parallel_cucumber.rb +1 -0
- data/lib/parallel_cucumber/cli.rb +57 -13
- data/lib/parallel_cucumber/dsl.rb +18 -0
- data/lib/parallel_cucumber/helper/command.rb +147 -56
- data/lib/parallel_cucumber/helper/cucumber.rb +28 -22
- data/lib/parallel_cucumber/helper/processes.rb +53 -17
- data/lib/parallel_cucumber/helper/queue.rb +3 -3
- data/lib/parallel_cucumber/hooks.rb +18 -0
- data/lib/parallel_cucumber/logger.rb +38 -0
- data/lib/parallel_cucumber/main.rb +122 -54
- data/lib/parallel_cucumber/version.rb +1 -1
- data/lib/parallel_cucumber/worker.rb +153 -68
- metadata +11 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c446c47f0dd453f9b1a8f66e11d374fbf6e16b2e
|
4
|
+
data.tar.gz: 7a15cd91d8a712344a63d843899278d52a8d86b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c24151c5b51f480adcfc7641b9e5f784ac397c875c7568d60a36451af2ac7569d67f95ddc68ce31fd0f5331323d8bf6aa5fc58b70bcb86e28b0071cc4ecaaa9
|
7
|
+
data.tar.gz: 4f2a5adf38c27af996b465fb60da139aee157c3becc975332e1e95a3a0fec857fb5ff027ee1145bd5ee453abcf634d1335184e3b160a19660332c9e00e0cb4e0
|
data/README.md
CHANGED
@@ -7,17 +7,31 @@ Usage: parallel_cucumber [options] [ [FILE|DIR|URL][:LINE[:LINE]*] ]
|
|
7
7
|
Example: parallel_cucumber -n 4 -o "-f pretty -f html -o report.html" examples/i18n/en/features
|
8
8
|
-n WORKERS How many workers to use. Default is 1 or longest list in -e
|
9
9
|
-o, --cucumber-options "OPTIONS" Run cucumber with these options
|
10
|
+
-r, --require "file_path" Load files for parallel_cucumber
|
11
|
+
--directed-tests JSON Direct tests to specific workers, e.g. {"0": "-t @head"}
|
10
12
|
--test-command COMMAND Command to run for test phase, default cucumber
|
11
13
|
--pre-batch-check COMMAND Command causing worker to quit on exit failure
|
14
|
+
--on-batch-error COMMAND Command to call on any error happened while running batch of tests.
|
15
|
+
It will receive as argument json file with list of tests and error happened.
|
16
|
+
--no-pretty Now a no-op for compatibility there is no default pretty
|
17
|
+
--log-dir DIR Directory for worker logfiles
|
18
|
+
--log-decoration JSON Block quoting for logs, e.g. {start: "#start %s", end: "#end %s"}
|
19
|
+
--summary JSON Summary files, e.g. {failed: "./failed.txt", unknown: "./unknown.txt"}
|
12
20
|
-e, --env-variables JSON Set additional environment variables to processes
|
13
21
|
--batch-size SIZE How many tests each worker takes from queue at once. Default is 1
|
22
|
+
--group-by ENV_VAR Key for cumulative report
|
14
23
|
-q ARRAY, `url,name` Url for TCP connection: `redis://[password]@[hostname]:[port]/[db]` (password, port and database are optional), for unix socket connection: `unix://[path to Redis socket]`. Default is redis://127.0.0.1:6379 and name is `queue`
|
15
24
|
--queue-connection-params
|
16
25
|
--setup-worker SCRIPT Execute SCRIPT before each worker
|
17
26
|
--teardown-worker SCRIPT Execute SCRIPT after each worker
|
18
27
|
--worker-delay SECONDS Delay before next worker starting. Could be used for avoiding 'spikes' in CPU and RAM usage Default is 0
|
19
28
|
--batch-timeout SECONDS Timeout for each batch of tests. Default is 600
|
29
|
+
--precheck-timeout SECONDS Timeout for each test precheck. Default is 600
|
30
|
+
--batch-error-timeout SECONDS
|
31
|
+
Timeout for each batch_error script. Default is 30
|
32
|
+
--setup-timeout SECONDS Timeout for each worker's set-up phase. Default is 30
|
20
33
|
--debug Print more debug information
|
34
|
+
--long-running-tests STRING Cucumber arguments for long-running-tests
|
21
35
|
-v, --version Show version
|
22
36
|
-h, --help Show this
|
23
37
|
```
|
data/lib/parallel_cucumber.rb
CHANGED
@@ -8,8 +8,11 @@ module ParallelCucumber
|
|
8
8
|
batch_size: 1,
|
9
9
|
batch_timeout: 600,
|
10
10
|
setup_timeout: 30,
|
11
|
+
precheck_timeout: 30,
|
12
|
+
batch_error_timeout: 30,
|
11
13
|
cucumber_options: '',
|
12
14
|
debug: false,
|
15
|
+
directed_tests: {},
|
13
16
|
log_dir: '.',
|
14
17
|
log_decoration: {},
|
15
18
|
env_variables: {},
|
@@ -62,6 +65,21 @@ module ParallelCucumber
|
|
62
65
|
options[:cucumber_options] = cucumber_options
|
63
66
|
end
|
64
67
|
|
68
|
+
opts.on('-r', '--require "file_path"', 'Load files for parallel_cucumber') do |load_file|
|
69
|
+
raise(ArgumentError, "No such file to load: #{load_file}") unless File.exist?(load_file)
|
70
|
+
options[:load_files] ||= []
|
71
|
+
options[:load_files] << load_file
|
72
|
+
end
|
73
|
+
|
74
|
+
opts.on('--directed-tests JSON', 'Direct tests to specific workers, e.g. {"0": "-t @head"}') do |json|
|
75
|
+
options[:directed_tests] = begin
|
76
|
+
JSON.parse(json)
|
77
|
+
rescue JSON::ParserError
|
78
|
+
puts 'Log block quoting not in JSON format. Did you forget to escape the quotes?'
|
79
|
+
raise
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
65
83
|
opts.on('--test-command COMMAND',
|
66
84
|
"Command to run for test phase, default #{DEFAULTS[:test_command]}") do |test_command|
|
67
85
|
options[:test_command] = test_command
|
@@ -71,11 +89,14 @@ module ParallelCucumber
|
|
71
89
|
options[:pre_check] = pre_check
|
72
90
|
end
|
73
91
|
|
74
|
-
|
75
|
-
|
76
|
-
|
92
|
+
opts.on('--on-batch-error COMMAND',
|
93
|
+
'Command to call on any error happened while running batch of tests.
|
94
|
+
It will receive as argument json file with list of tests and error happened.') do |on_batch_error|
|
95
|
+
options[:on_batch_error] = on_batch_error
|
77
96
|
end
|
78
97
|
|
98
|
+
opts.on('--no-pretty', 'Now a no-op for compatibility there is no default pretty') {}
|
99
|
+
|
79
100
|
opts.on('--log-dir DIR', 'Directory for worker logfiles') do |log_dir|
|
80
101
|
options[:log_dir] = log_dir
|
81
102
|
end
|
@@ -108,7 +129,7 @@ module ParallelCucumber
|
|
108
129
|
end
|
109
130
|
|
110
131
|
help_message = "How many tests each worker takes from queue at once. Default is #{DEFAULTS[:batch_size]}"
|
111
|
-
opts.on('--batch-size SIZE', Integer, help_message
|
132
|
+
opts.on('--batch-size SIZE', Integer, help_message) do |batch_size|
|
112
133
|
if batch_size < 1
|
113
134
|
puts "The minimum batch size is 1 but given: '#{batch_size}'"
|
114
135
|
exit 1
|
@@ -116,14 +137,18 @@ module ParallelCucumber
|
|
116
137
|
options[:batch_size] = batch_size
|
117
138
|
end
|
118
139
|
|
119
|
-
|
140
|
+
opts.on('--group-by ENV_VAR', 'Key for cumulative report') do |group_by|
|
141
|
+
options[:group_by] = group_by
|
142
|
+
end
|
143
|
+
|
144
|
+
help_message = <<-TEXT.gsub(/\s+/, ' ').strip
|
120
145
|
`url,name`
|
121
146
|
Url for TCP connection:
|
122
147
|
`redis://[password]@[hostname]:[port]/[db]` (password, port and database are optional),
|
123
148
|
for unix socket connection: `unix://[path to Redis socket]`.
|
124
149
|
Default is redis://127.0.0.1:6379 and name is `queue`
|
125
150
|
TEXT
|
126
|
-
opts.on('-q', '--queue-connection-params ARRAY', Array, help_message
|
151
|
+
opts.on('-q', '--queue-connection-params ARRAY', Array, help_message) do |params|
|
127
152
|
options[:queue_connection_params] = params
|
128
153
|
end
|
129
154
|
|
@@ -135,26 +160,40 @@ module ParallelCucumber
|
|
135
160
|
options[:teardown_worker] = script
|
136
161
|
end
|
137
162
|
|
138
|
-
help_message = <<-TEXT
|
163
|
+
help_message = <<-TEXT.gsub(/\s+/, ' ').strip
|
139
164
|
Delay before next worker starting.
|
140
165
|
Could be used for avoiding 'spikes' in CPU and RAM usage
|
141
166
|
Default is #{DEFAULTS[:worker_delay]}
|
142
167
|
TEXT
|
143
|
-
opts.on('--worker-delay SECONDS', Float, help_message
|
168
|
+
opts.on('--worker-delay SECONDS', Float, help_message) do |worker_delay|
|
144
169
|
options[:worker_delay] = worker_delay
|
145
170
|
end
|
146
171
|
|
147
|
-
help_message = <<-TEXT
|
172
|
+
help_message = <<-TEXT.gsub(/\s+/, ' ').strip
|
148
173
|
Timeout for each batch of tests. Default is #{DEFAULTS[:batch_timeout]}
|
149
174
|
TEXT
|
150
|
-
opts.on('--batch-timeout SECONDS', Float, help_message
|
175
|
+
opts.on('--batch-timeout SECONDS', Float, help_message) do |batch_timeout|
|
151
176
|
options[:batch_timeout] = batch_timeout
|
152
177
|
end
|
153
178
|
|
154
|
-
help_message = <<-TEXT
|
179
|
+
help_message = <<-TEXT.gsub(/\s+/, ' ').strip
|
180
|
+
Timeout for each test precheck. Default is #{DEFAULTS[:batch_timeout]}
|
181
|
+
TEXT
|
182
|
+
opts.on('--precheck-timeout SECONDS', Float, help_message) do |timeout|
|
183
|
+
options[:precheck_timeout] = timeout
|
184
|
+
end
|
185
|
+
|
186
|
+
help_message = <<-TEXT.gsub(/\s+/, ' ').strip
|
187
|
+
Timeout for each batch_error script. Default is #{DEFAULTS[:batch_error_timeout]}
|
188
|
+
TEXT
|
189
|
+
opts.on('--batch-error-timeout SECONDS', Float, help_message) do |timeout|
|
190
|
+
options[:batch_error_timeout] = timeout
|
191
|
+
end
|
192
|
+
|
193
|
+
help_message = <<-TEXT.gsub(/\s+/, ' ').strip
|
155
194
|
Timeout for each worker's set-up phase. Default is #{DEFAULTS[:setup_timeout]}
|
156
195
|
TEXT
|
157
|
-
opts.on('--setup-timeout SECONDS', Float, help_message
|
196
|
+
opts.on('--setup-timeout SECONDS', Float, help_message) do |setup_timeout|
|
158
197
|
options[:setup_timeout] = setup_timeout
|
159
198
|
end
|
160
199
|
|
@@ -162,6 +201,11 @@ module ParallelCucumber
|
|
162
201
|
options[:debug] = debug
|
163
202
|
end
|
164
203
|
|
204
|
+
help_message = 'Cucumber arguments for long-running-tests'
|
205
|
+
opts.on('--long-running-tests STRING', String, help_message) do |cucumber_long_run_args|
|
206
|
+
options[:long_running_tests] = cucumber_long_run_args
|
207
|
+
end
|
208
|
+
|
165
209
|
opts.on('-v', '--version', 'Show version') do
|
166
210
|
puts ParallelCucumber::VERSION
|
167
211
|
exit 0
|
@@ -174,7 +218,7 @@ module ParallelCucumber
|
|
174
218
|
end
|
175
219
|
|
176
220
|
option_parser.parse!(argv)
|
177
|
-
options[:cucumber_args] = argv
|
221
|
+
options[:cucumber_args] = argv.join(' ')
|
178
222
|
|
179
223
|
options
|
180
224
|
rescue OptionParser::InvalidOption => e
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'hooks'
|
2
|
+
|
3
|
+
module ParallelCucumber
|
4
|
+
module DSL
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# Registers a callback hook which will be called at the end of every batch
|
8
|
+
# There can be more than one after_batch, they will be invoked sequentially
|
9
|
+
# If one hook fails, rest all will be skipped
|
10
|
+
# @yieldparam [optional, Hash] batch_results results of all tests in a batch
|
11
|
+
# @yieldparam [optional, String] batch_id batch id
|
12
|
+
# @yieldparam [optional, Hash] batch_env env of batch
|
13
|
+
def after_batch(&proc)
|
14
|
+
Hooks.register_after_batch(proc)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,85 +1,176 @@
|
|
1
1
|
module ParallelCucumber
|
2
2
|
module Helper
|
3
3
|
module Command
|
4
|
+
class TimedOutError < RuntimeError; end
|
4
5
|
class << self
|
5
6
|
def wrap_block(log_decoration, block_name, logger)
|
7
|
+
[$stdout, $stderr].each(&:flush)
|
6
8
|
logger << format(log_decoration['start'] + "\n", block_name) if log_decoration['start']
|
9
|
+
[$stdout, $stderr].each(&:flush)
|
7
10
|
yield
|
8
11
|
ensure
|
12
|
+
[$stdout, $stderr].each(&:flush)
|
9
13
|
logger << format(log_decoration['end'] + "\n", block_name) if log_decoration['end']
|
14
|
+
[$stdout, $stderr].each(&:flush)
|
10
15
|
end
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
ONE_SECOND = 1
|
18
|
+
|
19
|
+
def exec_command(env, desc, script, logger, log_decoration = {}, timeout: 30, capture: false, return_script_error: false) # rubocop:disable Metrics/ParameterLists, Metrics/LineLength
|
20
|
+
block_name = ''
|
21
|
+
if log_decoration['worker_block']
|
22
|
+
if log_decoration['start'] || log_decoration['end']
|
23
|
+
block_name = "#{"#{env['TEST_USER']}-w#{env['WORKER_INDEX']}>"} #{desc}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
logger << format(log_decoration['start'] + "\n", block_name) if log_decoration['start']
|
28
|
+
full_script = "#{script} 2>&1"
|
16
29
|
env_string = env.map { |k, v| "#{k}=#{v}" }.sort.join(' ')
|
17
|
-
|
18
|
-
Running command `#{full_script}` with environment variables: #{env_string}
|
19
|
-
LOG
|
20
|
-
logger.debug(message)
|
30
|
+
logger << "== Running command `#{full_script}` at #{Time.now}\n== with environment variables: #{env_string}\n"
|
21
31
|
pstat = nil
|
22
32
|
pout = nil
|
33
|
+
capture &&= [''] # Pass by reference
|
34
|
+
exception = nil
|
35
|
+
|
23
36
|
begin
|
24
|
-
completed =
|
37
|
+
completed = begin
|
25
38
|
pin, pout, pstat = Open3.popen2e(env, full_script)
|
26
|
-
logger
|
39
|
+
logger << "Command has pid #{pstat[:pid]}\n"
|
27
40
|
pin.close
|
28
|
-
|
29
|
-
|
30
|
-
pstat.value # N.B. Await process termination
|
31
|
-
"Command completed #{pstat.value} and expecting '#{out}' to be empty due to redirects"
|
32
|
-
end
|
33
|
-
logger.debug(completed)
|
34
|
-
# system("lsof #{log_file} >> #{log_file} 2>&1")
|
35
|
-
system("ps -axf | grep '#{pstat[:pid]}\\s' >> #{log_file}")
|
36
|
-
return pstat.value.success?
|
37
|
-
rescue Timeout::Error
|
38
|
-
pout.close
|
39
|
-
logger.debug("Timeout, so trying SIGINT #{pstat[:pid]}")
|
40
|
-
wait_sigint = 15
|
41
|
-
logger.error("Timeout #{timeout}s was reached. Sending SIGINT(2), then waiting up to #{wait_sigint}s")
|
42
|
-
tree = Helper::Processes.ps_tree
|
43
|
-
begin
|
44
|
-
Helper::Processes.kill_tree('SIGINT', pstat[:pid].to_s, tree)
|
45
|
-
timed_out = wait_sigint.times do |t|
|
46
|
-
break if Helper::Processes.all_pids_dead?(pstat[:pid].to_s, nil, tree)
|
47
|
-
logger.info("Wait dead #{t}")
|
48
|
-
sleep 1
|
41
|
+
out_reader = Thread.new do
|
42
|
+
output_reader(pout, pstat, logger, capture)
|
49
43
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
44
|
+
|
45
|
+
unless out_reader.join(timeout)
|
46
|
+
raise TimedOutError
|
53
47
|
end
|
54
|
-
|
55
|
-
pstat
|
56
|
-
|
57
|
-
#
|
58
|
-
|
48
|
+
|
49
|
+
graceful_process_shutdown(out_reader, pstat, pout, logger)
|
50
|
+
|
51
|
+
pstat.value # reap already-terminated child.
|
52
|
+
"Command completed #{pstat.value} at #{Time.now}"
|
59
53
|
end
|
54
|
+
|
55
|
+
logger << "#{completed}\n"
|
56
|
+
|
57
|
+
raise "Script returned #{pstat.value.exitstatus}" unless pstat.value.success? || return_script_error
|
58
|
+
|
59
|
+
capture_or_empty = capture ? capture.first : '' # Even '' is truthy
|
60
|
+
return pstat.value.success? ? capture_or_empty : nil
|
61
|
+
rescue TimedOutError => e
|
62
|
+
force_kill_process_with_tree(out_reader, pstat, pout, full_script, logger, timeout)
|
63
|
+
|
64
|
+
exception = e
|
60
65
|
rescue => e
|
61
66
|
logger.debug("Exception #{pstat ? pstat[:pid] : "pstat=#{pstat}=nil"}")
|
62
67
|
trace = e.backtrace.join("\n\t").sub("\n\t", ": #{$ERROR_INFO}#{e.class ? " (#{e.class})" : ''}\n\t")
|
63
|
-
logger.error("Threw
|
68
|
+
logger.error("Threw for #{full_script}, caused #{trace}")
|
69
|
+
|
70
|
+
exception = e
|
64
71
|
ensure
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
72
|
+
logger << format(log_decoration['end'] + "\n", block_name) if log_decoration['end']
|
73
|
+
end
|
74
|
+
logger.error("*** UNUSUAL TERMINATION FOR: #{script}")
|
75
|
+
|
76
|
+
raise exception
|
77
|
+
end
|
78
|
+
|
79
|
+
def log_until_incomplete_line(logger, out_string)
|
80
|
+
loop do
|
81
|
+
line, out_string = out_string.split(/\n/, 2)
|
82
|
+
return line || '' unless out_string
|
83
|
+
logger << line
|
84
|
+
logger << "\n"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def output_reader(pout, pstat, logger, capture)
|
91
|
+
out_string = ''
|
92
|
+
|
93
|
+
loop do
|
94
|
+
io_select = IO.select([pout], [], [], ONE_SECOND)
|
95
|
+
unless io_select || pstat.alive?
|
96
|
+
logger << "\n== Terminating because io_select=#{io_select} when pstat.alive?=#{pstat.alive?}\n"
|
97
|
+
break
|
79
98
|
end
|
99
|
+
next unless io_select
|
100
|
+
# Windows doesn't support read_nonblock!
|
101
|
+
partial = pout.readpartial(8192)
|
102
|
+
capture[0] += partial if capture
|
103
|
+
out_string = log_until_incomplete_line(logger, out_string + partial)
|
104
|
+
end
|
105
|
+
rescue EOFError
|
106
|
+
logger << "\n== EOF is normal exit, #{pstat.inspect}\n"
|
107
|
+
rescue => e
|
108
|
+
logger << "\n== Exception in out_reader due to #{e.inspect} #{e.backtrace}\n"
|
109
|
+
ensure
|
110
|
+
logger << out_string
|
111
|
+
logger << ["\n== Left out_reader at #{Time.now}; ",
|
112
|
+
"pipe=#{pstat.status}+#{pstat.status ? '≤no value≥' : pstat.value}\n"].join
|
113
|
+
end
|
114
|
+
|
115
|
+
def graceful_process_shutdown(out_reader, pstat, pout, logger)
|
116
|
+
out_reader.value # Should terminate with pstat
|
117
|
+
pout.close
|
118
|
+
if pstat.status
|
119
|
+
logger << "== Thread #{pstat.inspect} is not dead"
|
120
|
+
|
121
|
+
if pstat.join(3)
|
122
|
+
logger << "== Thread #{pstat.inspect} joined late"
|
123
|
+
else
|
124
|
+
pstat.terminate # Just in case
|
125
|
+
logger << "== Thread #{pstat.inspect} terminated"
|
126
|
+
end # Make an effort to reap
|
127
|
+
end
|
128
|
+
|
129
|
+
pstat.value # reap already-terminated child.
|
130
|
+
"Command completed #{pstat.value} at #{Time.now}"
|
131
|
+
end
|
132
|
+
|
133
|
+
def force_kill_process_with_tree(out_reader, pstat, pout, full_script, logger, timeout) # rubocop:disable Metrics/ParameterLists, Metrics/LineLength
|
134
|
+
out_reader.exit
|
135
|
+
tree = Helper::Processes.ps_tree
|
136
|
+
pid = pstat[:pid].to_s
|
137
|
+
unless Helper::Processes.ms_windows?
|
138
|
+
logger << "Timeout, so trying SIGUSR1 to trigger watchdog stacktrace #{pstat[:pid]}=#{full_script}"
|
139
|
+
Helper::Processes.kill_tree('SIGUSR1', pid, logger, tree)
|
140
|
+
logger << %x(ps -ax)
|
141
|
+
sleep 2
|
142
|
+
end
|
143
|
+
|
144
|
+
logger << "Timeout, so trying SIGINT at #{pstat[:pid]}=#{full_script}"
|
145
|
+
|
146
|
+
log_copy = Thread.new do
|
147
|
+
pout.each_line { |l| logger << l }
|
148
|
+
end
|
149
|
+
log_copy.exit unless log_copy.join(2)
|
150
|
+
|
151
|
+
pout.close
|
152
|
+
|
153
|
+
wait_sigint = 15
|
154
|
+
logger << "Timeout #{timeout}s was reached. Sending SIGINT(2), SIGKILL after #{wait_sigint}s."
|
155
|
+
begin
|
156
|
+
Helper::Processes.kill_tree('SIGINT', pid, logger, tree)
|
157
|
+
|
158
|
+
timed_out = wait_sigint.times do |t|
|
159
|
+
break if Helper::Processes.all_pids_dead?(pid, logger, nil, tree)
|
160
|
+
logger << "Wait dead #{t} pid #{pid}"
|
161
|
+
sleep 1
|
162
|
+
end
|
163
|
+
|
164
|
+
if timed_out
|
165
|
+
logger << "Process #{pid} lasted #{wait_sigint}s after SIGINT(2), so SIGKILL(9)! Fatality!"
|
166
|
+
Helper::Processes.kill_tree('SIGKILL', pid, logger, nil, tree)
|
167
|
+
logger << "Tried SIGKILL #{pid}!"
|
168
|
+
end
|
169
|
+
|
170
|
+
logger << "About to reap root #{pid}"
|
171
|
+
pstat.value # reap root - everything else should be reaped by init.
|
172
|
+
logger << "Reaped root #{pid}"
|
80
173
|
end
|
81
|
-
logger.debug("Unusual termination for command: #{script}")
|
82
|
-
nil
|
83
174
|
end
|
84
175
|
end
|
85
176
|
end
|