module Mixlib::ShellOut::Helper

Public Instance Methods

shell_out(*args, **options) click to toggle source

These APIs are considered public for use in ohai and chef (by cookbooks and plugins, etc) but are considered private/experimental for now for the direct users of mixlib-shellout.

You can see an example of how to handle the “dependency injection” in the rspec unit test. That backend API is left deliberately undocumented for now and may not follow SemVer and may break at any time (at least for the rest of 2020).

# File lib/mixlib/shellout/helper.rb, line 38
def shell_out(*args, **options)
  options = options.dup
  options = __maybe_add_timeout(self, options)
  if options.empty?
    shell_out_compacted(*__clean_array(*args))
  else
    shell_out_compacted(*__clean_array(*args), **options)
  end
end
shell_out!(*args, **options) click to toggle source
# File lib/mixlib/shellout/helper.rb, line 48
def shell_out!(*args, **options)
  options = options.dup
  options = __maybe_add_timeout(self, options)
  if options.empty?
    shell_out_compacted!(*__clean_array(*args))
  else
    shell_out_compacted!(*__clean_array(*args), **options)
  end
end

Private Instance Methods

__apply_default_env(options) click to toggle source

helper function to mangle options when `default_env` is true

@api private

# File lib/mixlib/shellout/helper.rb, line 81
def __apply_default_env(options)
  options = options.dup
  default_env = options.delete(:default_env)
  default_env = true if default_env.nil?
  if default_env
    env_key = options.key?(:env) ? :env : :environment
    options[env_key] = {
      "LC_ALL" => __config[:internal_locale],
      "LANGUAGE" => __config[:internal_locale],
      "LANG" => __config[:internal_locale],
      __env_path_name => default_paths,
    }.update(options[env_key] || {})
  end
  options
end
__clean_array(*args) click to toggle source

Helper for subclasses to reject nil out of an array. It allows using the array form of shell_out (which avoids the need to surround arguments with quote marks to deal with shells).

@param args [String] variable number of string arguments @return [Array] array of strings with nil and null string rejection

# File lib/mixlib/shellout/helper.rb, line 140
def __clean_array(*args)
  args.flatten.compact.map(&:to_s)
end
__env_path_name() click to toggle source
# File lib/mixlib/shellout/helper.rb, line 167
def __env_path_name
  if ChefUtils.windows?
    "Path"
  else
    "PATH"
  end
end
__io_for_live_stream() click to toggle source
# File lib/mixlib/shellout/helper.rb, line 159
def __io_for_live_stream
  if !STDOUT.closed? && __log.trace?
    STDOUT
  else
    nil
  end
end
__maybe_add_timeout(obj, options) click to toggle source

helper sugar for resources that support passing timeouts to shell_out

module method to not pollute namespaces, but that means we need self injected as an arg @api private

# File lib/mixlib/shellout/helper.rb, line 64
def __maybe_add_timeout(obj, options)
  options = options.dup
  # historically resources have not properly declared defaults on their timeouts, so a default default of 900s was enforced here
  default_val = 900
  return options if options.key?(:timeout)

  # FIXME: need to nuke descendent tracker out of Chef::Provider so we can just define that class here without requiring the
  # world, and then just use symbol lookup
  if obj.class.ancestors.map(&:name).include?("Chef::Provider") && obj.respond_to?(:new_resource) && obj.new_resource.respond_to?(:timeout) && !options.key?(:timeout)
    options[:timeout] = obj.new_resource.timeout ? obj.new_resource.timeout.to_f : default_val
  end
  options
end
__shell_out_command(*args, **options) click to toggle source
# File lib/mixlib/shellout/helper.rb, line 144
def __shell_out_command(*args, **options)
  if __transport_connection
    FakeShellOut.new(args, options, __transport_connection.run_command(args.join(" "))) # FIXME: train should accept run_command(*args)
  else
    cmd = if options.empty?
            Mixlib::ShellOut.new(*args)
          else
            Mixlib::ShellOut.new(*args, **options)
          end
    cmd.live_stream ||= __io_for_live_stream
    cmd.run_command
    cmd
  end
end
shell_out_compacted(*args, **options) click to toggle source

The shell_out_compacted/shell_out_compacted! APIs are private but are intended for use in rspec tests. They should always be used in rspec tests instead of shell_out to allow for less brittle rspec tests.

This expectation:

allow(provider).to receive(:shell_out_compacted!).with(“foo”, “bar”, “baz”)

Is met by many different possible calling conventions that mean the same thing:

provider.shell_out!(“foo”, [ “bar”, nil, “baz”]) provider.shell_out!([“foo”, nil, “bar” ], [“baz”])

Note that when setting `default_env: false` that you should just setup an expectation on :shell_out_compacted for `default_env: false`, rather than the expanded env settings so that the default_env implementation can change without breaking unit tests.

# File lib/mixlib/shellout/helper.rb, line 114
def shell_out_compacted(*args, **options)
  options = __apply_default_env(options)
  if options.empty?
    __shell_out_command(*args)
  else
    __shell_out_command(*args, **options)
  end
end
shell_out_compacted!(*args, **options) click to toggle source
# File lib/mixlib/shellout/helper.rb, line 123
def shell_out_compacted!(*args, **options)
  options = __apply_default_env(options)
  cmd = if options.empty?
          __shell_out_command(*args)
        else
          __shell_out_command(*args, **options)
        end
  cmd.error!
  cmd
end