Class: Proxy::RemoteExecution::Ssh::Runners::PollingScriptRunner

Inherits:
ScriptRunner
  • Object
show all
Defined in:
lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb

Constant Summary collapse

DEFAULT_REFRESH_INTERVAL =
60
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

load_script('control.sh')
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

load_script('retrieve.sh')

Constants inherited from ScriptRunner

ScriptRunner::EXPECTED_POWER_ACTION_MESSAGES, ScriptRunner::UNSHARE_PREFIX

Instance Attribute Summary

Attributes inherited from ScriptRunner

#execution_timeout_interval

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ScriptRunner

build, #close_session, #preflight_checks, #publish_data, #start, #timeout, #timeout_interval

Methods included from CommandLogging

#log_command, #set_pm_debug_logging

Constructor Details

#initialize(options, user_method, suspended_action: nil) ⇒ PollingScriptRunner

Returns a new instance of PollingScriptRunner.



22
23
24
25
26
27
28
# 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

Class Method Details

.load_script(name) ⇒ Object



7
8
9
10
# 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

Instance Method Details

#closeObject



96
97
98
99
# 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_scriptObject

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



111
112
113
114
115
116
117
118
# 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) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
# 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_scriptObject



36
37
38
39
40
41
42
43
44
45
46
# 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

#killObject



64
65
66
67
68
# 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_startObject



30
31
32
33
34
# 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) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
# 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

#refreshObject



52
53
54
55
56
57
58
59
60
61
62
# 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) ⇒ Object



48
49
50
# File 'lib/smart_proxy_remote_execution_ssh/runners/polling_script_runner.rb', line 48

def trigger(*args)
  run_sync(*args)
end

#upload_control_scriptsObject



101
102
103
104
105
106
107
108
# 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