# frozen_string_literal: true require 'childprocess' module NoradBeacon class Runner attr_reader :result_set, :exit_code # Class method to return results file def self.results_file @results_fn ||= "/tmp/norad.results.#{Time.now.strftime('%Y%m%d-%H%M%S')}" end # Initialize an instance of the Runner class # # @param prog [String] the program to execute # @param args [Array] list of arguments to pass to the program # @param timeout [Fixnum] optionally specify the timeout for the sub process # @return [NoradBeacon::Runner] an instance of the Runner class def initialize(prog, args, timeout = 600) raise ArgumentError, 'args must be an Array' unless args.is_a?(Array) @prog = prog @timeout = timeout @result_set = NoradBeacon::ResultSet.new @args = format_args(args) @exit_code = nil end # Execute the command def execute(stdout_results = false) process = ChildProcess.build(@prog, *@args) set_child_proc_io(process, stdout_results) process.start begin process.poll_for_exit(@timeout) rescue ChildProcess::TimeoutError puts 'Beacon Timeout reached. Stopping the process...' process.stop # tries increasingly harsher methods to kill the process. end @exit_code = process.exit_code end # Parse the results stored in the results file def parse_results raw_results = File.open(self.class.results_file, 'r') yield raw_results raw_results.close end private # Replaces the filename delimeter and any local options we need to override # # @param args [Array] list of arguments to format # @return [Array] formatted list of arguments def format_args(args) local_opts = NoradBeacon.container_options[(ENV['NORAD_SECURITY_CONTAINER_NAME'] || '').to_sym] || {} args.map do |arg| begin next format(arg, local_opts) if arg =~ /\A%\{[a-zA-Z_][a-zA-Z_0-9]*\}\z/ arg rescue KeyError, ArgumentError arg end end.compact end def set_child_proc_io(process, stdout_results) if stdout_results process.io.stdout = File.open(self.class.results_file, 'w') else process.io.inherit! end end end end