mixlib-shellout 1.2.0 → 1.3.0.rc.0

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 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: []