mixlib-shellout 3.2.6 → 3.3.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ecadd2e4dc88a253a01b40a4791a2fa7940e2528569849aa3ed3127203c36a61
4
- data.tar.gz: 42ad87d950f91197a4ee90adb05a31eeb17d298b3c5cf997b6d90f1f8a431f1c
3
+ metadata.gz: 0a9ab082811bde7ebba65cb6d326ff71d9f55e6a9a8d966a9294a173c56cf35b
4
+ data.tar.gz: 8eb25e4d17a30d1d1fc1e8a3f9e6fa0434dc6c692f4e461980d7cd670cbceb2e
5
5
  SHA512:
6
- metadata.gz: c3afd05d78583036b4fa39420a305222dacbb9dcf2b32f3c4121c91677cde7cbfdbbc429cc02e3862c1140456c51c9d263321b279286def56cb9f6a8b74d8d19
7
- data.tar.gz: ce2ca5cd28c792552dbf8f312a9061da532995e9470ef6e21095e39a1eee5dba59acb0b80758cd7d3d66dbcf32c60ea0f5540a5d98a418e443871eca641db08f
6
+ metadata.gz: d0ae915100dd53a40c78f3c6dc7439d99de2684b9a057cdbbc724cf8c41997c5a279912af76f4e3b00b9b732c207f98ca9d2e3f5225b30e5aea3941c21c65646
7
+ data.tar.gz: 9a4d490d87c8f9d2297c84ef7e81e844769d581b59b1bca36c8ed2a50a8a41e7909d5c788e36207b8f224002ecc7a1ad263d09b272598fce6ae16a45fe47d52f
@@ -141,9 +141,43 @@ module Mixlib
141
141
  args.flatten.compact.map(&:to_s)
142
142
  end
143
143
 
144
+ # Join arguments into a string.
145
+ #
146
+ # Strips leading/trailing spaces from each argument. If an argument contains
147
+ # a space, it is quoted. Join into a single string with spaces between each argument.
148
+ #
149
+ # @param args [String] variable number of string arguments
150
+ # @return [String] merged string
151
+ #
152
+ def __join_whitespace(*args, quote: false)
153
+ args.map do |arg|
154
+ if arg.is_a?(Array)
155
+ __join_whitespace(*arg, quote: arg.count > 1)
156
+ else
157
+ arg = arg.include?(" ") ? sprintf('"%s"', arg) : arg if quote
158
+ arg.strip
159
+ end
160
+ end.join(" ")
161
+ end
162
+
144
163
  def __shell_out_command(*args, **options)
145
164
  if __transport_connection
146
- FakeShellOut.new(args, options, __transport_connection.run_command(args.join(" "))) # FIXME: train should accept run_command(*args)
165
+ command = __join_whitespace(args)
166
+ unless ChefUtils.windows?
167
+ if options[:cwd]
168
+ # as `timeout` is used, commands need to be executed in a subshell
169
+ command = "sh -c 'cd #{options[:cwd]}; #{command}'"
170
+ end
171
+
172
+ if options[:input]
173
+ command.concat "<<'COMMANDINPUT'\n"
174
+ command.concat __join_whitespace(options[:input])
175
+ command.concat "\nCOMMANDINPUT\n"
176
+ end
177
+ end
178
+
179
+ # FIXME: train should accept run_command(*args)
180
+ FakeShellOut.new(args, options, __transport_connection.run_command(command, options))
147
181
  else
148
182
  cmd = if options.empty?
149
183
  Mixlib::ShellOut.new(*args)
@@ -181,15 +215,16 @@ module Mixlib
181
215
  @stdout = result.stdout
182
216
  @stderr = result.stderr
183
217
  @exitstatus = result.exit_status
184
- @status = OpenStruct.new(success?: ( exitstatus == 0 ))
218
+ @valid_exit_codes = Array(options[:returns] || 0)
219
+ @status = OpenStruct.new(success?: (@valid_exit_codes.include? exitstatus))
185
220
  end
186
221
 
187
222
  def error?
188
- exitstatus != 0
223
+ @valid_exit_codes.none?(exitstatus)
189
224
  end
190
225
 
191
226
  def error!
192
- raise Mixlib::ShellOut::ShellCommandFailed, "Unexpected exit status of #{exitstatus} running #{@args}" if error?
227
+ raise Mixlib::ShellOut::ShellCommandFailed, "Unexpected exit status of #{exitstatus} running #{@args}: #{stderr}" if error?
193
228
  end
194
229
  end
195
230
  end
@@ -16,15 +16,12 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
+ require "fileutils" unless defined?(FileUtils)
20
+
19
21
  module Mixlib
20
22
  class ShellOut
21
23
  module Unix
22
24
 
23
- # "1.8.7" as a frozen string. We use this with a hack that disables GC to
24
- # avoid segfaults on Ruby 1.8.7, so we need to allocate the fewest
25
- # objects we possibly can.
26
- ONE_DOT_EIGHT_DOT_SEVEN = "1.8.7".freeze
27
-
28
25
  # Option validation that is unix specific
29
26
  def validate_options(opts)
30
27
  if opts[:elevated]
@@ -99,12 +96,6 @@ module Mixlib
99
96
 
100
97
  configure_parent_process_file_descriptors
101
98
 
102
- # Ruby 1.8.7 and 1.8.6 from mid 2009 try to allocate objects during GC
103
- # when calling IO.select and IO#read. Disabling GC works around the
104
- # segfault, but obviously it's a bad workaround. We no longer support
105
- # 1.8.6 so we only need this hack for 1.8.7.
106
- GC.disable if RUBY_VERSION == ONE_DOT_EIGHT_DOT_SEVEN
107
-
108
99
  # CHEF-3390: Marshall.load on Ruby < 1.8.7p369 also has a GC bug related
109
100
  # to Marshall.load, so try disabling GC first.
110
101
  propagate_pre_exec_failure
@@ -142,7 +133,7 @@ module Mixlib
142
133
  reap_errant_child if should_reap?
143
134
  # make one more pass to get the last of the output after the
144
135
  # child process dies
145
- attempt_buffer_read
136
+ attempt_buffer_read(0)
146
137
  # no matter what happens, turn the GC back on, and hope whatever busted
147
138
  # version of ruby we're on doesn't allocate some objects during the next
148
139
  # GC run.
@@ -187,6 +178,16 @@ module Mixlib
187
178
  Dir.chdir(cwd) if cwd
188
179
  end
189
180
 
181
+ def set_cgroup
182
+ new_cgroup_path = "/sys/fs/cgroup/#{cgroup}"
183
+ # Create cgroup if missing
184
+ unless Dir.exist?(new_cgroup_path)
185
+ FileUtils.mkdir_p new_cgroup_path
186
+ end
187
+ # Migrate current process to newly cgroup, any subprocesses will run inside new cgroup
188
+ File.write("#{new_cgroup_path}/cgroup.procs", Process.pid.to_s)
189
+ end
190
+
190
191
  # Since we call setsid the child_pgid will be the child_pid, set to negative here
191
192
  # so it can be directly used in arguments to kill, wait, etc.
192
193
  def child_pgid
@@ -276,8 +277,8 @@ module Mixlib
276
277
  child_stdin.close # Kick things off
277
278
  end
278
279
 
279
- def attempt_buffer_read
280
- ready = IO.select(open_pipes, nil, nil, READ_WAIT_TIME)
280
+ def attempt_buffer_read(timeout = READ_WAIT_TIME)
281
+ ready = IO.select(open_pipes, nil, nil, timeout)
281
282
  if ready
282
283
  read_stdout_to_buffer if ready.first.include?(child_stdout)
283
284
  read_stderr_to_buffer if ready.first.include?(child_stderr)
@@ -315,6 +316,10 @@ module Mixlib
315
316
  open_pipes.delete(child_process_status)
316
317
  end
317
318
 
319
+ def cgroupv2_available?
320
+ File.read("/proc/mounts").match?(%r{^cgroup2 /sys/fs/cgroup})
321
+ end
322
+
318
323
  def fork_subprocess
319
324
  initialize_ipc
320
325
 
@@ -332,6 +337,10 @@ module Mixlib
332
337
 
333
338
  configure_subprocess_file_descriptors
334
339
 
340
+ if cgroup && cgroupv2_available?
341
+ set_cgroup
342
+ end
343
+
335
344
  set_secondarygroups
336
345
  set_group
337
346
  set_user
@@ -1,5 +1,5 @@
1
1
  module Mixlib
2
2
  class ShellOut
3
- VERSION = "3.2.6".freeze
3
+ VERSION = "3.3.9".freeze
4
4
  end
5
5
  end
@@ -60,6 +60,7 @@ module Mixlib
60
60
  stderr_read, stderr_write = IO.pipe
61
61
  stdin_read, stdin_write = IO.pipe
62
62
  open_streams = [ stdout_read, stderr_read ]
63
+ @execution_time = 0
63
64
 
64
65
  begin
65
66
 
@@ -105,6 +106,8 @@ module Mixlib
105
106
  wait_status = WaitForSingleObject(process.process_handle, 0)
106
107
  case wait_status
107
108
  when WAIT_OBJECT_0
109
+ # Save the execution time
110
+ @execution_time = Time.now - start_wait
108
111
  # Get process exit code
109
112
  exit_code = [0].pack("l")
110
113
  unless GetExitCodeProcess(process.process_handle, exit_code)
@@ -127,6 +130,9 @@ module Mixlib
127
130
  logger&.warn("Failed to kill timed out process #{process.process_id}")
128
131
  end
129
132
 
133
+ # Save the execution time
134
+ @execution_time = Time.now - start_wait
135
+
130
136
  raise Mixlib::ShellOut::CommandTimeout, [
131
137
  "command timed out:",
132
138
  format_for_exception,
@@ -114,6 +114,9 @@ module Mixlib
114
114
 
115
115
  attr_accessor :sensitive
116
116
 
117
+ # Path to cgroupv2 that the process should run on
118
+ attr_accessor :cgroup
119
+
117
120
  # === Arguments:
118
121
  # Takes a single command, or a list of command fragments. These are used
119
122
  # as arguments to Kernel.exec. See the Kernel.exec documentation for more
@@ -179,6 +182,7 @@ module Mixlib
179
182
  @timeout = nil
180
183
  @elevated = false
181
184
  @sensitive = false
185
+ @cgroup = nil
182
186
 
183
187
  if command_args.last.is_a?(Hash)
184
188
  parse_options(command_args.pop)
@@ -303,7 +307,8 @@ module Mixlib
303
307
  def inspect
304
308
  "<#{self.class.name}##{object_id}: command: '#{@command}' process_status: #{@status.inspect} " +
305
309
  "stdout: '#{stdout.strip}' stderr: '#{stderr.strip}' child_pid: #{@child_pid.inspect} " +
306
- "environment: #{@environment.inspect} timeout: #{timeout} user: #{@user} group: #{@group} working_dir: #{@cwd} >"
310
+ "environment: #{@environment.inspect} timeout: #{timeout} user: #{@user} group: #{@group} working_dir: #{@cwd} " +
311
+ "cgroup: #{@cgroup} >"
307
312
  end
308
313
 
309
314
  private
@@ -354,6 +359,8 @@ module Mixlib
354
359
  self.elevated = setting
355
360
  when "sensitive"
356
361
  self.sensitive = setting
362
+ when "cgroup"
363
+ self.cgroup = setting
357
364
  else
358
365
  raise InvalidCommandOption, "option '#{option.inspect}' is not a valid option for #{self.class.name}"
359
366
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mixlib-shellout
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.6
4
+ version: 3.3.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef Software Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-29 00:00:00.000000000 Z
11
+ date: 2025-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef-utils
@@ -50,14 +50,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: '2.4'
53
+ version: '3.0'
54
54
  required_rubygems_version: !ruby/object:Gem::Requirement
55
55
  requirements:
56
56
  - - ">="
57
57
  - !ruby/object:Gem::Version
58
58
  version: '0'
59
59
  requirements: []
60
- rubygems_version: 3.1.4
60
+ rubygems_version: 3.2.3
61
61
  signing_key:
62
62
  specification_version: 4
63
63
  summary: Run external commands on Unix or Windows