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, :merge_stderr)
  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]
  @merge_stderr = options.fetch(:merge_stderr, true)
  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 58
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 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 25
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
stderr() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 49
def stderr
  raise 'Command not yet executed' unless defined? @stderr
  @stderr
end
success?() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 54
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 96
def full_command
  if @merge_stderr
    "#{@command} 2>&1"
  else
    @command
  end
end
run_interactively() click to toggle source
# File lib/foreman_maintain/utils/command_runner.rb, line 67
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 90
def run_non_interactively
  @output, @stderr, status = Open3.capture3(@env, full_command, :stdin_data => @stdin)
  @output.strip!
  @exit_status = status.exitstatus
end