mixlib-shellout 1.2.0 → 1.3.0.rc.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Y2I1ZDQ4Zjk4OThmNzU0M2ZjYWQ3ODQyNThiOTg2YTkxNGViYTg5Nw==
5
+ data.tar.gz: !binary |-
6
+ YmU5Y2E4NDgyMDQxZjIzMzRkMmNhNDFiZTVhNmI1MzIwZTM1OTQxMQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ ZGQ0MmIwNjZmNWM2NjJiZmZmZDU0YWY4NWFiZDI2YTU0ZDA5MGY1NmZkYjBj
10
+ MDRmYzhkNGNiZjg1MjU5ZjQ4NzI4MTgzYWE3M2QyNDFlYzI4YzEzNjcyMjFh
11
+ ZmEyZjc0YmU0ZWY2MzA1OGVlNjVjNDIxMTc2MDRlNzcwZGY0Yzc=
12
+ data.tar.gz: !binary |-
13
+ ZWVkYTc1M2E1YjM5NWVlOTgzMDQyM2Y5YmY0NGMwMWM2MDFhNGNkNjY2YjU0
14
+ NzQ0MTRjNTViOTA4OGZhZGViMjhmNDhjNTI0ODQyMDkxOTNmNDRkODE4MjA3
15
+ ZDRkOWY5NmMwNDI0ODQwZjUyZWIzNzE5YWIzMDJhNDdkODcyYjQ=
@@ -154,6 +154,7 @@ module Mixlib
154
154
  @environment = DEFAULT_ENVIRONMENT
155
155
  @cwd = nil
156
156
  @valid_exit_codes = [0]
157
+ @terminate_reason = nil
157
158
 
158
159
  if command_args.last.is_a?(Hash)
159
160
  parse_options(command_args.pop)
@@ -191,6 +192,7 @@ module Mixlib
191
192
  # results when the command exited with an unexpected status.
192
193
  def format_for_exception
193
194
  msg = ""
195
+ msg << "#{@terminate_reason}\n" if @terminate_reason
194
196
  msg << "---- Begin output of #{command} ----\n"
195
197
  msg << "STDOUT: #{stdout.strip}\n"
196
198
  msg << "STDERR: #{stderr.strip}\n"
@@ -38,6 +38,7 @@ module Mixlib
38
38
  # within +timeout+ seconds (default: 600s)
39
39
  def run_command
40
40
  @child_pid = fork_subprocess
41
+ @reaped = false
41
42
 
42
43
  configure_parent_process_file_descriptors
43
44
 
@@ -57,36 +58,33 @@ module Mixlib
57
58
  write_to_child_stdin
58
59
 
59
60
  until @status
60
- ready = IO.select(open_pipes, nil, nil, READ_WAIT_TIME)
61
- unless ready
61
+ ready_buffers = attempt_buffer_read
62
+ unless ready_buffers
62
63
  @execution_time += READ_WAIT_TIME
63
64
  if @execution_time >= timeout && !@result
64
- raise CommandTimeout, "command timed out:\n#{format_for_exception}"
65
+ # kill the bad proccess
66
+ reap_errant_child
67
+ # read anything it wrote when we killed it
68
+ attempt_buffer_read
69
+ # raise
70
+ raise CommandTimeout, "Command timed out after #{@execution_time.to_i}s:\n#{format_for_exception}"
65
71
  end
66
72
  end
67
73
 
68
- if ready && ready.first.include?(child_stdout)
69
- read_stdout_to_buffer
70
- end
71
- if ready && ready.first.include?(child_stderr)
72
- read_stderr_to_buffer
73
- end
74
-
75
- unless @status
76
- # make one more pass to get the last of the output after the
77
- # child process dies
78
- if results = Process.waitpid2(@child_pid, Process::WNOHANG)
79
- @status = results.last
80
- redo
81
- end
82
- end
74
+ attempt_reap
83
75
  end
76
+
84
77
  self
85
- rescue Exception
86
- # do our best to kill zombies
87
- Process.waitpid2(@child_pid, Process::WNOHANG) rescue nil
78
+ rescue Errno::ENOENT
79
+ # When ENOENT happens, we can be reasonably sure that the child process
80
+ # is going to exit quickly, so we use the blocking variant of waitpid2
81
+ reap
88
82
  raise
89
83
  ensure
84
+ reap_errant_child if should_reap?
85
+ # make one more pass to get the last of the output after the
86
+ # child process dies
87
+ attempt_buffer_read
90
88
  # no matter what happens, turn the GC back on, and hope whatever busted
91
89
  # version of ruby we're on doesn't allocate some objects during the next
92
90
  # GC run.
@@ -231,6 +229,17 @@ module Mixlib
231
229
  child_stdin.close # Kick things off
232
230
  end
233
231
 
232
+ def attempt_buffer_read
233
+ ready = IO.select(open_pipes, nil, nil, READ_WAIT_TIME)
234
+ if ready && ready.first.include?(child_stdout)
235
+ read_stdout_to_buffer
236
+ end
237
+ if ready && ready.first.include?(child_stderr)
238
+ read_stderr_to_buffer
239
+ end
240
+ ready
241
+ end
242
+
234
243
  def read_stdout_to_buffer
235
244
  while chunk = child_stdout.read_nonblock(READ_SIZE)
236
245
  @stdout << chunk
@@ -291,6 +300,44 @@ module Mixlib
291
300
  end
292
301
  end
293
302
 
303
+ def reap_errant_child
304
+ return if attempt_reap
305
+ @terminate_reason = "Command execeded allowed execution time, killed by TERM signal."
306
+ logger.error("Command execeded allowed execution time, sending TERM") if logger
307
+ Process.kill(:TERM, @child_pid)
308
+ sleep 3
309
+ return if attempt_reap
310
+ @terminate_reason = "Command execeded allowed execution time, did not respond to TERM. Killed by KILL signal."
311
+ logger.error("Command did not exit from TERM, sending KILL") if logger
312
+ Process.kill(:KILL, @child_pid)
313
+ reap
314
+
315
+ # Should not hit this but it's possible if something is calling waitall
316
+ # in a separate thread.
317
+ rescue Errno::ESRCH
318
+ nil
319
+ end
320
+
321
+ def should_reap?
322
+ # if we fail to fork, no child pid so nothing to reap
323
+ @child_pid && !@reaped
324
+ end
325
+
326
+ def reap
327
+ results = Process.waitpid2(@child_pid)
328
+ @reaped = true
329
+ @status = results.last
330
+ end
331
+
332
+ def attempt_reap
333
+ if results = Process.waitpid2(@child_pid, Process::WNOHANG)
334
+ @reaped = true
335
+ @status = results.last
336
+ else
337
+ nil
338
+ end
339
+ end
340
+
294
341
  end
295
342
  end
296
343
  end
@@ -1,5 +1,5 @@
1
1
  module Mixlib
2
2
  class ShellOut
3
- VERSION = "1.2.0"
3
+ VERSION = "1.3.0.rc.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,48 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mixlib-shellout
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
5
- prerelease:
4
+ version: 1.3.0.rc.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Opscode
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-07-19 00:00:00.000000000 Z
11
+ date: 2013-11-19 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rspec
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - ~>
20
18
  - !ruby/object:Gem::Version
21
- version: '0'
19
+ version: '2.0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - ~>
28
25
  - !ruby/object:Gem::Version
29
- version: '0'
30
- - !ruby/object:Gem::Dependency
31
- name: ap
32
- requirement: !ruby/object:Gem::Requirement
33
- none: false
34
- requirements:
35
- - - ! '>='
36
- - !ruby/object:Gem::Version
37
- version: '0'
38
- type: :development
39
- prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ! '>='
44
- - !ruby/object:Gem::Version
45
- version: '0'
26
+ version: '2.0'
46
27
  description: Run external commands on Unix or Windows
47
28
  email: info@opscode.com
48
29
  executables: []
@@ -61,26 +42,25 @@ files:
61
42
  - lib/mixlib/shellout.rb
62
43
  homepage: http://wiki.opscode.com/
63
44
  licenses: []
45
+ metadata: {}
64
46
  post_install_message:
65
47
  rdoc_options: []
66
48
  require_paths:
67
49
  - lib
68
50
  required_ruby_version: !ruby/object:Gem::Requirement
69
- none: false
70
51
  requirements:
71
52
  - - ! '>='
72
53
  - !ruby/object:Gem::Version
73
54
  version: '0'
74
55
  required_rubygems_version: !ruby/object:Gem::Requirement
75
- none: false
76
56
  requirements:
77
- - - ! '>='
57
+ - - ! '>'
78
58
  - !ruby/object:Gem::Version
79
- version: '0'
59
+ version: 1.3.1
80
60
  requirements: []
81
61
  rubyforge_project:
82
- rubygems_version: 1.8.23
62
+ rubygems_version: 2.0.3
83
63
  signing_key:
84
- specification_version: 3
64
+ specification_version: 4
85
65
  summary: Run external commands on Unix or Windows
86
66
  test_files: []