Class: Proxy::RemoteExecution::Ssh::Runners::ScriptRunner
- Inherits:
-
Dynflow::Runner::Base
- Object
- Dynflow::Runner::Base
- Proxy::RemoteExecution::Ssh::Runners::ScriptRunner
- Includes:
- Dynflow::Runner::ProcessManagerCommand, CommandLogging
- Defined in:
- lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb
Direct Known Subclasses
Constant Summary collapse
- EXPECTED_POWER_ACTION_MESSAGES =
['restart host', 'shutdown host'].freeze
- DEFAULT_REFRESH_INTERVAL =
1- UNSHARE_PREFIX =
'unshare --fork --kill-child'.freeze
Instance Attribute Summary collapse
-
#execution_timeout_interval ⇒ Object
readonly
Returns the value of attribute execution_timeout_interval.
Class Method Summary collapse
Instance Method Summary collapse
- #close ⇒ Object
- #close_session ⇒ Object
- #detect_capabilities ⇒ Object
-
#initialization_script ⇒ Object
the script that initiates the execution.
-
#initialize(options, user_method, suspended_action: nil) ⇒ ScriptRunner
constructor
A new instance of ScriptRunner.
- #kill ⇒ Object
- #preflight_checks ⇒ Object
- #prepare_start ⇒ Object
- #publish_data(data, type, pm = nil) ⇒ Object
- #refresh ⇒ Object
- #start ⇒ Object
- #timeout ⇒ Object
- #timeout_interval ⇒ Object
- #trigger(*args) ⇒ Object
Methods included from CommandLogging
#log_command, #set_pm_debug_logging
Constructor Details
#initialize(options, user_method, suspended_action: nil) ⇒ ScriptRunner
Returns a new instance of ScriptRunner.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 104 def initialize(, user_method, suspended_action: nil) super suspended_action: suspended_action @host = .fetch(:hostname) @script = .fetch(:script) @ssh_user = .fetch(:ssh_user, 'root') @ssh_port = .fetch(:ssh_port, 22) @host_public_key = .fetch(:host_public_key, nil) @execution_timeout_interval = .fetch(:execution_timeout_interval, nil) @client_private_key_file = settings.ssh_identity_key_file @local_working_dir = .fetch(:local_working_dir, settings.local_working_dir) @remote_working_dir = .fetch(:remote_working_dir, settings.remote_working_dir.shellescape) @socket_working_dir = .fetch(:socket_working_dir, settings.socket_working_dir) @cleanup_working_dirs = .fetch(:cleanup_working_dirs, settings.cleanup_working_dirs) @first_execution = .fetch(:first_execution, false) @user_method = user_method @options = @supports_unshare = false end |
Instance Attribute Details
#execution_timeout_interval ⇒ Object (readonly)
Returns the value of attribute execution_timeout_interval.
97 98 99 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 97 def execution_timeout_interval @execution_timeout_interval end |
Class Method Details
.build(options, suspended_action:) ⇒ Object
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 124 def self.build(, suspended_action:) effective_user = .fetch(:effective_user, nil) ssh_user = .fetch(:ssh_user, 'root') effective_user_method = .fetch(:effective_user_method, 'sudo') user_method = if effective_user.nil? || effective_user == ssh_user NoopUserMethod.new elsif effective_user_method == 'sudo' SudoUserMethod.new(effective_user, ssh_user, .fetch(:secrets, {}).fetch(:effective_user_password, nil)) elsif effective_user_method == 'dzdo' DzdoUserMethod.new(effective_user, ssh_user, .fetch(:secrets, {}).fetch(:effective_user_password, nil)) elsif effective_user_method == 'su' SuUserMethod.new(effective_user, ssh_user, .fetch(:secrets, {}).fetch(:effective_user_password, nil)) else raise "effective_user_method '#{effective_user_method}' not supported" end new(, user_method, suspended_action: suspended_action) end |
Instance Method Details
#close ⇒ Object
253 254 255 256 257 258 259 260 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 253 def close run_sync("rm -rf #{remote_command_dir}") if should_cleanup? rescue StandardError => e publish_exception('Error when removing remote working dir', e, false) ensure close_session if @process_manager FileUtils.rm_rf(local_command_dir) if Dir.exist?(local_command_dir) && @cleanup_working_dirs end |
#close_session ⇒ Object
247 248 249 250 251 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 247 def close_session raise 'Control socket file does not exist' unless File.exist?(socket_file) @logger.debug("Sending exit request for session #{@ssh_user}@#{@host}") @connection.disconnect! end |
#detect_capabilities ⇒ Object
167 168 169 170 171 172 173 174 175 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 167 def detect_capabilities script = cp_script_to_remote("#!/bin/sh\nexec #{UNSHARE_PREFIX} true") begin ensure_remote_command(script) @supports_unshare = true rescue; end # The path should already be escaped ensure_remote_command("rm #{script}") end |
#initialization_script ⇒ Object
the script that initiates the execution
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 205 def initialization_script su_method = @user_method.instance_of?(SuUserMethod) # pipe the output to tee while capturing the exit code in a file <<~SCRIPT sh <<EOF | /usr/bin/tee #{@output_path} #{@remote_script_wrapper} #{@user_method.cli_command_prefix}#{su_method ? "'#{@remote_script} < /dev/null '" : "#{@remote_script} < /dev/null"} echo \\$?>#{@exit_code_path} EOF if [ -f #{@exit_code_path} ] && [ $(wc -l < #{@exit_code_path}) -gt 0 ]; then exit $(cat #{@exit_code_path}) else exit 1 fi SCRIPT end |
#kill ⇒ Object
228 229 230 231 232 233 234 235 236 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 228 def kill if @process_manager&.started? run_sync("pkill -P $(cat #{@pid_path})") else logger.debug('connection closed') end rescue StandardError => e publish_exception('Unexpected error', e, false) end |
#preflight_checks ⇒ Object
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 177 def preflight_checks script = cp_script_to_remote("#!/bin/sh\nexec true") ensure_remote_command(script, error: 'Failed to execute script on remote machine, exit code: %{exit_code}.' ) unless @user_method.is_a? NoopUserMethod ensure_remote_command("#{@user_method.cli_command_prefix} #{script}", error: 'Failed to change to effective user, exit code: %{exit_code}', tty: true, user_method: @user_method, close_stdin: false) end # The path should already be escaped ensure_remote_command("rm #{script}") end |
#prepare_start ⇒ Object
193 194 195 196 197 198 199 200 201 202 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 193 def prepare_start @remote_script = cp_script_to_remote @output_path = File.join(File.dirname(@remote_script), 'output') @exit_code_path = File.join(File.dirname(@remote_script), 'exit_code') @pid_path = File.join(File.dirname(@remote_script), 'pid') @remote_script_wrapper = upload_data( "echo $$ > #{@pid_path}; exec #{@supports_unshare ? UNSHARE_PREFIX : ''} \"$@\";", File.join(File.dirname(@remote_script), 'script-wrapper'), 555) end |
#publish_data(data, type, pm = nil) ⇒ Object
262 263 264 265 266 267 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 262 def publish_data(data, type, pm = nil) pm ||= @process_manager data = data.dup if data.frozen? super(data.force_encoding('UTF-8'), type) unless @user_method.filter_password?(data) @user_method.on_data(data, pm.stdin) if pm end |
#refresh ⇒ Object
221 222 223 224 225 226 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 221 def refresh return if @process_manager.nil? super ensure check_expecting_disconnect end |
#start ⇒ Object
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 147 def start Proxy::RemoteExecution::Utils.prune_known_hosts!(@host, @ssh_port, logger) if @first_execution ensure_local_directory(@socket_working_dir) @connection = MultiplexedSSHConnection.new(@options.merge(:id => @id), logger: logger) @connection.establish! preflight_checks detect_capabilities prepare_start script = initialization_script logger.debug("executing script:\n#{indent_multiline(script)}") trigger(script) rescue StandardError, NotImplementedError => e logger.error("error while initializing command #{e.class} #{e.}:\n #{e.backtrace.join("\n")}") publish_exception('Error initializing command', e) end |
#timeout ⇒ Object
238 239 240 241 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 238 def timeout @logger.debug('job timed out') super end |
#timeout_interval ⇒ Object
243 244 245 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 243 def timeout_interval execution_timeout_interval end |
#trigger(*args) ⇒ Object
163 164 165 |
# File 'lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb', line 163 def trigger(*args) run_async(*args) end |