Class: HybridPlatformsConductor::CmdRunner
- Inherits:
-
Object
- Object
- HybridPlatformsConductor::CmdRunner
- Includes:
- LoggerHelpers
- Defined in:
- lib/hybrid_platforms_conductor/cmd_runner.rb
Defined Under Namespace
Classes: TimeoutError, UnexpectedExitCodeError
Constant Summary
Constants included from LoggerHelpers
LoggerHelpers::LEVELS_MODIFIERS, LoggerHelpers::LEVELS_TO_STDERR
Instance Attribute Summary collapse
-
#dry_run ⇒ Object
Dry-run switch.
Class Method Summary collapse
-
.executables_prefix ⇒ Object
Return the executables prefix to use to execute commands.
Instance Method Summary collapse
-
#initialize(logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR)) ⇒ CmdRunner
constructor
Constructor.
-
#options_parse(options_parser, parallel: true) ⇒ Object
Complete an option parser with options meant to control this Actions Executor.
-
#root? ⇒ Boolean
Is the current user root? Look into the environment to decide.
-
#run_cmd(cmd, log_to_file: nil, log_to_stdout: true, log_stdout_to_io: nil, log_stderr_to_io: nil, expected_code: 0, timeout: nil, no_exception: false, force_bash: false) ⇒ Object
Run an external command.
-
#whoami ⇒ Object
Who is the local user? Keep a cache of it.
Methods included from LoggerHelpers
#err, #init_loggers, #log_component=, #log_debug?, #log_level=, #out, #section, #set_loggers_format, #stderr_device, #stderr_device=, #stderr_displayed?, #stdout_device, #stdout_device=, #stdout_displayed?, #stdouts_to_s, #with_progress_bar
Constructor Details
#initialize(logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR)) ⇒ CmdRunner
Constructor
- Parameters
-
logger (Logger): Logger to be used [default = Logger.new(STDOUT)]
-
logger_stderr (Logger): Logger to be used for stderr [default = Logger.new(STDERR)]
36 37 38 39 |
# File 'lib/hybrid_platforms_conductor/cmd_runner.rb', line 36 def initialize(logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR)) init_loggers(logger, logger_stderr) @dry_run = false end |
Instance Attribute Details
#dry_run ⇒ Object
Dry-run switch. When true, then commands are just printed out without being executed.
Boolean
29 30 31 |
# File 'lib/hybrid_platforms_conductor/cmd_runner.rb', line 29 def dry_run @dry_run end |
Class Method Details
.executables_prefix ⇒ Object
Return the executables prefix to use to execute commands
- Result
-
String: The executable prefix
23 24 25 |
# File 'lib/hybrid_platforms_conductor/cmd_runner.rb', line 23 def self.executables_prefix $0.include?('/') ? "#{File.dirname($0)}/" : '' end |
Instance Method Details
#options_parse(options_parser, parallel: true) ⇒ Object
Complete an option parser with options meant to control this Actions Executor
- Parameters
-
options_parser (OptionParser): The option parser to complete
-
parallel (Boolean): Do we activate options regarding parallel execution? [default = true]
46 47 48 49 50 51 52 |
# File 'lib/hybrid_platforms_conductor/cmd_runner.rb', line 46 def (, parallel: true) .separator '' .separator 'Command runner options:' .on('-s', '--show-commands', 'Display the commands that would be run instead of running them') do @dry_run = true end end |
#root? ⇒ Boolean
Is the current user root? Look into the environment to decide.
- Result
-
Boolean: Is the current user root?
181 182 183 |
# File 'lib/hybrid_platforms_conductor/cmd_runner.rb', line 181 def root? whoami == 'root' end |
#run_cmd(cmd, log_to_file: nil, log_to_stdout: true, log_stdout_to_io: nil, log_stderr_to_io: nil, expected_code: 0, timeout: nil, no_exception: false, force_bash: false) ⇒ Object
Run an external command. Handle dry-run mode, timeout, and check for an expected return code. Raise an exception if the exit status is not the expected one.
- Parameters
-
cmd (String): Command to be run
-
log_to_file (String or nil): Log file capturing stdout or stderr (or nil for none). [default: nil]
-
log_to_stdout (Boolean): Do we send the output to stdout? [default: true]
-
log_stdout_to_io (IO or nil): IO to send command’s stdout to, or nil for none. [default: nil]
-
log_stderr_to_io (IO or nil): IO to send command’s stderr to, or nil for none. [default: nil]
-
expected_code (Integer, Symbol or Array<Integer or Symbol>): Return codes (or single one) that is expected [default: 0] Symbol error codes can be used:
-
command_error: The command could not be executed
-
timeout: The command ended in timeout
-
-
timeout (Integer or nil): Timeout to apply for the command to be run, or nil for no timeout [default: nil]
-
no_exception (Boolean): If true, don’t throw exception in case of error [default: false]
-
force_bash (Boolean): If true, then make sure command is invoked with bash instead of sh [default: false]
- Result
-
Integer or Symbol: Exit status of the command, or Symbol in case of error. In case of dry-run mode the expected code is returned without executing anything.
-
String: Standard output of the command
-
String: Standard error output of the command (can be a descriptive message of the error in case of error)
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/hybrid_platforms_conductor/cmd_runner.rb', line 75 def run_cmd( cmd, log_to_file: nil, log_to_stdout: true, log_stdout_to_io: nil, log_stderr_to_io: nil, expected_code: 0, timeout: nil, no_exception: false, force_bash: false ) expected_code = [expected_code] unless expected_code.is_a?(Array) if @dry_run out cmd return expected_code.first, '', '' else log_debug "#{timeout.nil? ? '' : "[ Timeout #{timeout} ] - "}#{cmd.light_cyan.bold}" exit_status = nil cmd_stdout = nil cmd_stderr = nil file_output = if log_to_file if File.exist?(log_to_file) File.open(log_to_file, 'a') else FileUtils.mkdir_p(File.dirname(log_to_file)) File.open(log_to_file, 'w') end else nil end start_time = Time.now if log_debug? bash_file = nil if force_bash bash_file = Tempfile.new('hpc_bash') bash_file.write(cmd) bash_file.chmod 0700 bash_file.close cmd = "/bin/bash -c #{bash_file.path}" end begin # Make sure we keep a trace of stdout and stderr, even if it was not asked, just to use it in case of exceptions raised cmd_result_stdout = '' cmd_result_stderr = '' # Route IOs stdout_queue = Queue.new stderr_queue = Queue.new IoRouter.with_io_router( stdout_queue => [cmd_result_stdout] + (log_stdout_to_io ? [log_stdout_to_io] : []) + (log_to_stdout ? [@logger] : []) + (file_output.nil? ? [] : [file_output]), stderr_queue => [cmd_result_stderr] + (log_stderr_to_io ? [log_stderr_to_io] : []) + (log_to_stdout ? [@logger_stderr] : []) + (file_output.nil? ? [] : [file_output]) ) do cmd_result = TTY::Command.new( printer: :null, pty: true, timeout: timeout, uuid: false ).run!(cmd) do |stdout, stderr| stdout_queue << stdout if stdout stderr_queue << stderr if stderr end exit_status = cmd_result.exit_status cmd_stdout = cmd_result.out cmd_stderr = cmd_result.err end rescue TTY::Command::TimeoutExceeded exit_status = :timeout cmd_stdout = cmd_result_stdout cmd_stderr = "#{cmd_result_stderr.empty? ? '' : "#{cmd_result_stderr}\n"}Timeout of #{timeout} triggered" rescue exit_status = :command_error cmd_stdout = cmd_result_stdout cmd_stderr = "#{cmd_result_stderr.empty? ? '' : "#{cmd_result_stderr}\n"}#{$!}\n#{$!.backtrace.join("\n")}" ensure file_output.close unless file_output.nil? bash_file.unlink unless bash_file.nil? end if log_debug? elapsed = Time.now - start_time log_debug "Finished in #{elapsed} seconds with exit status #{exit_status} (#{(expected_code.include?(exit_status) ? 'success'.light_green : 'failure'.light_red).bold})" end unless expected_code.include?(exit_status) error_title = "Command '#{cmd.split("\n").first}' returned error code #{exit_status} (expected #{expected_code.join(', ')})." if no_exception # We consider the caller is responsible for logging what he wants about the details of the error (stdout and stderr) log_error error_title else # The exception won't contain stdout and stderr details (unless output to stdout was on), so dump them now log_error "#{error_title}#{log_to_stdout ? '' : "\n----- Command STDOUT:\n#{cmd_stdout}\n----- Command STDERR:\n#{cmd_stderr}"}" raise exit_status == :timeout ? TimeoutError : UnexpectedExitCodeError, error_title end end return exit_status, cmd_stdout, cmd_stderr end end |
#whoami ⇒ Object
Who is the local user? Keep a cache of it.
- Result
-
String: Name of the local user
190 191 192 193 194 195 196 |
# File 'lib/hybrid_platforms_conductor/cmd_runner.rb', line 190 def whoami unless defined?(@whoami) _exit_status, stdout, _stderr = run_cmd 'whoami', log_to_stdout: log_debug? @whoami = stdout.strip end @whoami end |