mixlib-shellout 3.0.9 → 3.1.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f397fa01d53b269d311c32ad263372d115735935d637385ac9fc735b504216bd
4
- data.tar.gz: c871d5ef88067456dc0b11d2fc3eae6cacaf3a2ccffd90a1b14e15555389301e
3
+ metadata.gz: 26f12f230e23d828bfaaa639a1655b0ba9bac5e8189f62477d921c872bf6238a
4
+ data.tar.gz: 0b86d014984d7f34ed2eb87db3d7baddbb9d06f6e4d9168052e30023523bb290
5
5
  SHA512:
6
- metadata.gz: 00e5a3bd7d562a937d9719f61a8043181a5ab4e6b40ce698dffd1f1fc7ce5cc4376739cadeb259749cce448d84ba559134caf5474e9cede4f4f120fa96b3701f
7
- data.tar.gz: 76f80cba51a657c26a978668ecc04d9d96c2b8f6242968b55b022b97642963d7d5f1c2c482357508e46aae1b68dc43317d86ba3e38566b232c29c2d25b635046
6
+ metadata.gz: 2b13c61d4b9a2a054773159a895d3a9948dfc45a2b8b1e55f471069111129ad0dc45ffd6c93fffe0dbb56bd6559db1adb8206e0919645cd5c864ddb80fb177fe
7
+ data.tar.gz: 8e3e7eb05d3d9cd875019f049b6482645edc4e19d50bbdb11659ca946b7048278884aa9517d254bcaac273fcfbf7a857e1675e8d77a043a029e3f5ce2a772066
@@ -16,8 +16,8 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require "etc"
20
- require "tmpdir"
19
+ require "etc" unless defined?(Etc)
20
+ require "tmpdir" unless defined?(Dir.mktmpdir)
21
21
  require "fcntl"
22
22
  require_relative "shellout/exceptions"
23
23
 
@@ -65,7 +65,7 @@ module Mixlib
65
65
  # as the subprocess is running.
66
66
  attr_accessor :live_stderr
67
67
 
68
- # ShellOut will push data from :input down the stdin of the subprocss.
68
+ # ShellOut will push data from :input down the stdin of the subprocess.
69
69
  # Normally set via options passed to new.
70
70
  # Default: nil
71
71
  attr_accessor :input
@@ -122,7 +122,7 @@ module Mixlib
122
122
  # === Options:
123
123
  # If the last argument is a Hash, it is removed from the list of args passed
124
124
  # to exec and used as an options hash. The following options are available:
125
- # * +user+: the user the commmand should run as. if an integer is given, it is
125
+ # * +user+: the user the command should run as. if an integer is given, it is
126
126
  # used as a uid. A string is treated as a username and resolved to a uid
127
127
  # with Etc.getpwnam
128
128
  # * +group+: the group the command should run as. works similarly to +user+
@@ -248,7 +248,7 @@ module Mixlib
248
248
  # running or died without setting an exit status (e.g., terminated by
249
249
  # `kill -9`).
250
250
  def exitstatus
251
- @status && @status.exitstatus
251
+ @status&.exitstatus
252
252
  end
253
253
 
254
254
  # Run the command, writing the command's standard out and standard error
@@ -0,0 +1,197 @@
1
+ #--
2
+ # Author:: Daniel DeLeo (<dan@chef.io>)
3
+ # Copyright:: Copyright (c) Chef Software Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require_relative "../shellout"
19
+ require "chef-utils" unless defined?(ChefUtils)
20
+ require "chef-utils/dsl/default_paths"
21
+ require "chef-utils/internal"
22
+
23
+ module Mixlib
24
+ class ShellOut
25
+ module Helper
26
+ include ChefUtils::Internal
27
+ include ChefUtils::DSL::DefaultPaths
28
+
29
+ #
30
+ # These APIs are considered public for use in ohai and chef (by cookbooks and plugins, etc)
31
+ # but are considered private/experimental for now for the direct users of mixlib-shellout.
32
+ #
33
+ # You can see an example of how to handle the "dependency injection" in the rspec unit test.
34
+ # That backend API is left deliberately undocumented for now and may not follow SemVer and may
35
+ # break at any time (at least for the rest of 2020).
36
+ #
37
+
38
+ def shell_out(*args, **options)
39
+ options = options.dup
40
+ options = __maybe_add_timeout(self, options)
41
+ if options.empty?
42
+ shell_out_compacted(*__clean_array(*args))
43
+ else
44
+ shell_out_compacted(*__clean_array(*args), **options)
45
+ end
46
+ end
47
+
48
+ def shell_out!(*args, **options)
49
+ options = options.dup
50
+ options = __maybe_add_timeout(self, options)
51
+ if options.empty?
52
+ shell_out_compacted!(*__clean_array(*args))
53
+ else
54
+ shell_out_compacted!(*__clean_array(*args), **options)
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ # helper sugar for resources that support passing timeouts to shell_out
61
+ #
62
+ # module method to not pollute namespaces, but that means we need self injected as an arg
63
+ # @api private
64
+ def __maybe_add_timeout(obj, options)
65
+ options = options.dup
66
+ # historically resources have not properly declared defaults on their timeouts, so a default default of 900s was enforced here
67
+ default_val = 900
68
+ return options if options.key?(:timeout)
69
+
70
+ # FIXME: need to nuke descendent tracker out of Chef::Provider so we can just define that class here without requiring the
71
+ # world, and then just use symbol lookup
72
+ if obj.class.ancestors.map(&:name).include?("Chef::Provider") && obj.respond_to?(:new_resource) && obj.new_resource.respond_to?(:timeout) && !options.key?(:timeout)
73
+ options[:timeout] = obj.new_resource.timeout ? obj.new_resource.timeout.to_f : default_val
74
+ end
75
+ options
76
+ end
77
+
78
+ # helper function to mangle options when `default_env` is true
79
+ #
80
+ # @api private
81
+ def __apply_default_env(options)
82
+ options = options.dup
83
+ default_env = options.delete(:default_env)
84
+ default_env = true if default_env.nil?
85
+ if default_env
86
+ env_key = options.key?(:env) ? :env : :environment
87
+ options[env_key] = {
88
+ "LC_ALL" => __config[:internal_locale],
89
+ "LANGUAGE" => __config[:internal_locale],
90
+ "LANG" => __config[:internal_locale],
91
+ __env_path_name => default_paths,
92
+ }.update(options[env_key] || {})
93
+ end
94
+ options
95
+ end
96
+
97
+ # The shell_out_compacted/shell_out_compacted! APIs are private but are intended for use
98
+ # in rspec tests. They should always be used in rspec tests instead of shell_out to allow
99
+ # for less brittle rspec tests.
100
+ #
101
+ # This expectation:
102
+ #
103
+ # allow(provider).to receive(:shell_out_compacted!).with("foo", "bar", "baz")
104
+ #
105
+ # Is met by many different possible calling conventions that mean the same thing:
106
+ #
107
+ # provider.shell_out!("foo", [ "bar", nil, "baz"])
108
+ # provider.shell_out!(["foo", nil, "bar" ], ["baz"])
109
+ #
110
+ # Note that when setting `default_env: false` that you should just setup an expectation on
111
+ # :shell_out_compacted for `default_env: false`, rather than the expanded env settings so
112
+ # that the default_env implementation can change without breaking unit tests.
113
+ #
114
+ def shell_out_compacted(*args, **options)
115
+ options = __apply_default_env(options)
116
+ if options.empty?
117
+ __shell_out_command(*args)
118
+ else
119
+ __shell_out_command(*args, **options)
120
+ end
121
+ end
122
+
123
+ def shell_out_compacted!(*args, **options)
124
+ options = __apply_default_env(options)
125
+ cmd = if options.empty?
126
+ __shell_out_command(*args)
127
+ else
128
+ __shell_out_command(*args, **options)
129
+ end
130
+ cmd.error!
131
+ cmd
132
+ end
133
+
134
+ # Helper for subclasses to reject nil out of an array. It allows using the array form of
135
+ # shell_out (which avoids the need to surround arguments with quote marks to deal with shells).
136
+ #
137
+ # @param args [String] variable number of string arguments
138
+ # @return [Array] array of strings with nil and null string rejection
139
+ #
140
+ def __clean_array(*args)
141
+ args.flatten.compact.map(&:to_s)
142
+ end
143
+
144
+ def __shell_out_command(*args, **options)
145
+ if __transport_connection
146
+ FakeShellOut.new(args, options, __transport_connection.run_command(args.join(" "))) # FIXME: train should accept run_command(*args)
147
+ else
148
+ cmd = if options.empty?
149
+ Mixlib::ShellOut.new(*args)
150
+ else
151
+ Mixlib::ShellOut.new(*args, **options)
152
+ end
153
+ cmd.live_stream ||= __io_for_live_stream
154
+ cmd.run_command
155
+ cmd
156
+ end
157
+ end
158
+
159
+ def __io_for_live_stream
160
+ if !STDOUT.closed? && __log.trace?
161
+ STDOUT
162
+ else
163
+ nil
164
+ end
165
+ end
166
+
167
+ def __env_path_name
168
+ if ChefUtils.windows?
169
+ "Path"
170
+ else
171
+ "PATH"
172
+ end
173
+ end
174
+
175
+ class FakeShellOut
176
+ attr_reader :stdout, :stderr, :exitstatus, :status
177
+
178
+ def initialize(args, options, result)
179
+ @args = args
180
+ @options = options
181
+ @stdout = result.stdout
182
+ @stderr = result.stderr
183
+ @exitstatus = result.exit_status
184
+ @status = OpenStruct.new(success?: ( exitstatus == 0 ))
185
+ end
186
+
187
+ def error?
188
+ exitstatus != 0
189
+ end
190
+
191
+ def error!
192
+ raise Mixlib::ShellOut::ShellCommandFailed, "Unexpected exit status of #{exitstatus} running #{@args}" if error?
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -370,11 +370,11 @@ module Mixlib
370
370
  return if attempt_reap
371
371
 
372
372
  @terminate_reason = "Command exceeded allowed execution time, process terminated"
373
- logger.error("Command exceeded allowed execution time, sending TERM") if logger
373
+ logger&.error("Command exceeded allowed execution time, sending TERM")
374
374
  Process.kill(:TERM, child_pgid)
375
375
  sleep 3
376
376
  attempt_reap
377
- logger.error("Command exceeded allowed execution time, sending KILL") if logger
377
+ logger&.error("Command exceeded allowed execution time, sending KILL")
378
378
  Process.kill(:KILL, child_pgid)
379
379
  reap
380
380
 
@@ -1,5 +1,5 @@
1
1
  module Mixlib
2
2
  class ShellOut
3
- VERSION = "3.0.9".freeze
3
+ VERSION = "3.1.6".freeze
4
4
  end
5
5
  end
@@ -89,7 +89,7 @@ module Mixlib
89
89
  # Start the process
90
90
  #
91
91
  process, profile, token = Process.create3(create_process_args)
92
- logger.debug(format_process(process, app_name, command_line, timeout)) if logger
92
+ logger&.debug(format_process(process, app_name, command_line, timeout))
93
93
  begin
94
94
  # Start pushing data into input
95
95
  stdin_write << input if input
@@ -124,7 +124,7 @@ module Mixlib
124
124
  kill_process_tree(process.process_id, wmi, logger)
125
125
  Process.kill(:KILL, process.process_id)
126
126
  rescue SystemCallError
127
- logger.warn("Failed to kill timed out process #{process.process_id}") if logger
127
+ logger&.warn("Failed to kill timed out process #{process.process_id}")
128
128
  end
129
129
 
130
130
  raise Mixlib::ShellOut::CommandTimeout, [
@@ -208,7 +208,7 @@ module Mixlib
208
208
  # 4. if the argument must be quoted by #1 and terminates in a sequence of backslashes then all the backlashes must themselves
209
209
  # be backslash excaped (double the backslashes).
210
210
  # 5. if an interior quote that must be escaped by #2 has a sequence of backslashes before it then all the backslashes must
211
- # themselves be backslash excaped along with the backslash ecape of the interior quote (double plus one backslashes).
211
+ # themselves be backslash excaped along with the backslash escape of the interior quote (double plus one backslashes).
212
212
  #
213
213
  # And to restate. We are constructing a string which will be parsed by the windows parser into arguments, and we want those
214
214
  # arguments to match the *args array we are passed here. So call the windows parser operation A then we need to apply A^-1 to
@@ -398,20 +398,16 @@ module Mixlib
398
398
 
399
399
  def kill_process(instance, logger)
400
400
  child_pid = instance.wmi_ole_object.processid
401
- if logger
402
- logger.debug([
401
+ logger&.debug([
403
402
  "killing child process #{child_pid}::",
404
403
  "#{instance.wmi_ole_object.Name} of parent #{pid}",
405
404
  ].join)
406
- end
407
405
  Process.kill(:KILL, instance.wmi_ole_object.processid)
408
406
  rescue SystemCallError
409
- if logger
410
- logger.debug([
407
+ logger&.debug([
411
408
  "Failed to kill child process #{child_pid}::",
412
409
  "#{instance.wmi_ole_object.Name} of parent #{pid}",
413
410
  ].join)
414
- end
415
411
  end
416
412
 
417
413
  def format_process(process, app_name, command_line, timeout)
@@ -21,7 +21,6 @@ require "win32/process"
21
21
 
22
22
  # Add new constants for Logon
23
23
  module Process::Constants
24
- private
25
24
 
26
25
  LOGON32_LOGON_INTERACTIVE = 0x00000002
27
26
  LOGON32_LOGON_BATCH = 0x00000004
@@ -148,15 +147,13 @@ module Process
148
147
  si_hash = {}
149
148
 
150
149
  # If the startup_info key is present, validate its subkeys
151
- if hash["startup_info"]
152
- hash["startup_info"].each do |key, val|
153
- key = key.to_s.downcase
154
- unless valid_si_keys.include?(key)
155
- raise ArgumentError, "invalid startup_info key '#{key}'"
156
- end
157
-
158
- si_hash[key] = val
150
+ hash["startup_info"]&.each do |key, val|
151
+ key = key.to_s.downcase
152
+ unless valid_si_keys.include?(key)
153
+ raise ArgumentError, "invalid startup_info key '#{key}'"
159
154
  end
155
+
156
+ si_hash[key] = val
160
157
  end
161
158
 
162
159
  # The +command_line+ key is mandatory unless the +app_name+ key
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mixlib-shellout
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.9
4
+ version: 3.1.6
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: 2019-12-30 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2020-09-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chef-utils
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: Run external commands on Unix or Windows
14
28
  email: info@chef.io
15
29
  executables: []
@@ -19,6 +33,7 @@ files:
19
33
  - LICENSE
20
34
  - lib/mixlib/shellout.rb
21
35
  - lib/mixlib/shellout/exceptions.rb
36
+ - lib/mixlib/shellout/helper.rb
22
37
  - lib/mixlib/shellout/unix.rb
23
38
  - lib/mixlib/shellout/version.rb
24
39
  - lib/mixlib/shellout/windows.rb
@@ -34,7 +49,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
34
49
  requirements:
35
50
  - - ">="
36
51
  - !ruby/object:Gem::Version
37
- version: '2.2'
52
+ version: '2.4'
38
53
  required_rubygems_version: !ruby/object:Gem::Requirement
39
54
  requirements:
40
55
  - - ">="