class ForemanMaintain::Utils::CommandRunner

Wrapper around running a command

Attributes

command[R]
logger[R]

Public Class Methods

new(logger, command, options) click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 10
def initialize(logger, command, options)
  options.validate_options!(:stdin, :hidden_patterns, :interactive, :valid_exit_statuses)
  options[:valid_exit_statuses] ||= [0]
  @logger = logger
  @command = command
  @stdin = options[:stdin]
  @hidden_patterns = Array(options[:hidden_patterns])
  @interactive = options[:interactive]
  @options = options
  @valid_exit_statuses = options[:valid_exit_statuses]
  raise ArgumentError, 'Can not pass stdin for interactive command' if @interactive && @stdin
end

Public Instance Methods

execution_error() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 53
def execution_error
  raise Error::ExecutionError.new(hide_strings(@command),
                                  exit_status,
                                  hide_strings(@stdin),
                                  @interactive ? nil : hide_strings(@output))
end
exit_status() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 44
def exit_status
  raise 'Command not yet executed' unless defined? @exit_status
  @exit_status
end
interactive?() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 35
def interactive?
  @interactive
end
output() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 39
def output
  raise 'Command not yet executed' unless defined? @output
  @output
end
run() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 23
def run
  if logger
    logger.debug(hide_strings("Running command #{@command} with stdin #{@stdin.inspect}"))
  end
  if @interactive
    run_interactively
  else
    run_non_interactively
  end
  logger.debug("output of the command:\n #{hide_strings(output)}") if logger
end
success?() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 49
def success?
  @valid_exit_statuses.include? exit_status
end

Private Instance Methods

full_command() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 93
def full_command
  "#{@command} 2>&1"
end
hide_strings(string) click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 97
def hide_strings(string)
  return unless string
  @hidden_patterns.reduce(string) do |result, hidden_pattern|
    result.gsub(hidden_pattern, '[FILTERED]')
  end
end
run_interactively() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 62
def run_interactively
  # use tmp files to capture output and exit status of the command when
  # running interactively
  log_file = Tempfile.open('captured-output')
  exit_file = Tempfile.open('captured-exit-code')
  Kernel.system("script -qc '#{full_command}; echo $? > #{exit_file.path}' #{log_file.path}")
  File.open(log_file.path) { |f| @output = f.read }
  File.open(exit_file.path) do |f|
    exit_status = f.read.strip
    @exit_status = if exit_status.empty?
                     256
                   else
                     exit_status.to_i
                   end
  end
ensure
  log_file.close
  exit_file.close
end
run_non_interactively() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 82
def run_non_interactively
  IO.popen(full_command, 'r+') do |f|
    if @stdin
      f.puts(@stdin)
      f.close_write
    end
    @output = f.read.strip
  end
  @exit_status = $CHILD_STATUS.exitstatus
end