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, :interactive, :valid_exit_statuses, :env)
  options[:valid_exit_statuses] ||= [0]
  options[:env] ||= {}
  @logger = logger
  @command = command
  @stdin = options[:stdin]
  @interactive = options[:interactive]
  @options = options
  @valid_exit_statuses = options[:valid_exit_statuses]
  @env = options[:env]
  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 52
def execution_error
  raise Error::ExecutionError.new(@command,
    exit_status,
    @stdin,
    @interactive ? nil : @output)
end
exit_status() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 43
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 34
def interactive?
  @interactive
end
output() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 38
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 24
def run
  logger&.debug("Running command #{@command} with stdin #{@stdin.inspect}")
  if @interactive
    run_interactively
  else
    run_non_interactively
  end
  logger&.debug("output of the command:\n #{output}")
end
success?() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 48
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 95
def full_command
  "#{@command} 2>&1"
end
run_interactively() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 61
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(
    "stdbuf -oL -eL bash -c '#{full_command}; echo $? > #{exit_file.path}'"\
    "| tee -i #{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 84
def run_non_interactively
  IO.popen(@env, 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