class Proxy::RemoteExecution::Ssh::Runners::PollingScriptRunner

Constants

CONTROL_SCRIPT

The script that controls the flow of the job, able to initiate update or finish on the task, or take over the control over script lifecycle

DEFAULT_REFRESH_INTERVAL
RETRIEVE_SCRIPT

The script always outputs at least one line First line of the output either has to begin with “RUNNING” or “DONE $EXITCODE” The following lines are treated as regular output

Public Class Methods

load_script(name) click to toggle source
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 7
def self.load_script(name)
  script_dir = File.expand_path('../async_scripts', __dir__)
  File.read(File.join(script_dir, name))
end
new(options, user_method, suspended_action: nil) click to toggle source
Calls superclass method
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 22
def initialize(options, user_method, suspended_action: nil)
  super(options, user_method, suspended_action: suspended_action)
  @callback_host = options[:callback_host]
  @task_id = options[:uuid]
  @step_id = options[:step_id]
  @otp = Proxy::Dynflow::OtpManager.generate_otp(@task_id)
end

Public Instance Methods

close() click to toggle source
Calls superclass method
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 96
def close
  super
  Proxy::Dynflow::OtpManager.drop_otp(@task_id, @otp) if @otp
end
env_script() click to toggle source

Script setting the dynamic values to env variables: it's sourced from other control scripts

# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 111
    def env_script
      <<-SCRIPT.gsub(/^ +\| /, '')
      | CALLBACK_HOST="#{@callback_host}"
      | TASK_ID="#{@task_id}"
      | STEP_ID="#{@step_id}"
      | OTP="#{@otp}"
      SCRIPT
    end
external_event(event) click to toggle source
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 84
def external_event(event)
  data = event.data
  if data['manual_mode']
    load_event_updates(data)
  else
    # getting the update from automatic mode - reaching to the host to get the latest update
    return run_refresh
  end
ensure
  destroy_session
end
initialization_script() click to toggle source
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 36
    def initialization_script
      close_stdin = '</dev/null'
      close_fds = close_stdin + ' >/dev/null 2>/dev/null'
      main_script = "(#{@remote_script_wrapper} #{@remote_script} #{close_stdin} 2>&1; echo $?>#{@base_dir}/init_exit_code) >#{@base_dir}/output"
      control_script_finish = "#{@control_script_path} init-script-finish"
      <<-SCRIPT.gsub(/^ +\| /, '')
      | export CONTROL_SCRIPT="#{@control_script_path}"
      | #{"chown #{@user_method.effective_user} #{@base_dir}" if @user_method.cli_command_prefix}
      | #{@user_method.cli_command_prefix} sh -c '#{main_script}; #{control_script_finish}' #{close_fds} &
      SCRIPT
    end
kill() click to toggle source
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 64
def kill
  run_sync("pkill -P $(cat #{@pid_path})")
rescue StandardError => e
  publish_exception('Unexpected error', e, false)
end
prepare_start() click to toggle source
Calls superclass method
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 30
def prepare_start
  super
  @base_dir = File.dirname @remote_script
  upload_control_scripts
end
process_retrieved_data(output, err) click to toggle source
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 70
def process_retrieved_data(output, err)
  return if output.nil? || output.empty?

  lines = output.lines
  result = lines.shift.match(/^DONE (\d+)?/)
  publish_data(lines.join, 'stdout') unless lines.empty?
  publish_data(err, 'stderr') unless err.empty?
  if result
    exitcode = result[1] || 0
    publish_exit_status(exitcode.to_i)
    cleanup
  end
end
refresh() click to toggle source
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 52
def refresh
  @connection.establish! unless @connection.connected?
  begin
    pm = run_sync("#{@user_method.cli_command_prefix} #{@retrieval_script}")
    process_retrieved_data(pm.stdout.to_s.chomp, pm.stderr.to_s.chomp)
  rescue StandardError => e
    @logger.info("Error while connecting to the remote host on refresh: #{e.message}")
  end
ensure
  destroy_session
end
trigger(*args) click to toggle source
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 48
def trigger(*args)
  run_sync(*args)
end
upload_control_scripts() click to toggle source
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 101
def upload_control_scripts
  return if @control_scripts_uploaded

  cp_script_to_remote(env_script, 'env.sh')
  @control_script_path = cp_script_to_remote(CONTROL_SCRIPT, 'control.sh')
  @retrieval_script = cp_script_to_remote(RETRIEVE_SCRIPT, 'retrieve.sh')
  @control_scripts_uploaded = true
end

Private Instance Methods

cleanup() click to toggle source
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 133
def cleanup
  if @cleanup_working_dirs
    ensure_remote_command("rm -rf #{remote_command_dir}",
                          publish: true,
                          error: "Unable to remove working directory #{remote_command_dir} on remote system, exit code: %{exit_code}")
  end
end
destroy_session() click to toggle source
# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 141
def destroy_session
  if @connection.connected?
    @logger.debug("Closing session with #{@ssh_user}@#{@host}")
    close_session
  end
end
load_event_updates(event_data) click to toggle source

Generates updates based on the callback data from the manual mode

# File lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb, line 123
def load_event_updates(event_data)
  continuous_output = Proxy::Dynflow::ContinuousOutput.new
  if event_data.key?('output')
    lines = Base64.decode64(event_data['output']).sub(/\A(RUNNING|DONE).*\n/, '')
    continuous_output.add_output(lines, 'stdout')
  end
  cleanup if event_data['exit_code']
  new_update(continuous_output, event_data['exit_code'])
end