parallel_cucumber 0.2.0.pre.36 → 0.2.3
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/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
|