Class: Actions::ProxyAction

Inherits:
Base
  • Object
show all
Includes:
Dynflow::Action::Cancellable
Defined in:
app/lib/actions/proxy_action.rb

Defined Under Namespace

Classes: CallbackData, ProxyActionMissing, ProxyActionStopped

Constant Summary collapse

ProxyActionStoppedEvent =
::Algebrick.type do
  fields! exception: type { variants NilClass, Exception }
end

Instance Method Summary collapse

Methods inherited from Base

#already_running?, #humanized_errors, #humanized_input, #humanized_name, #humanized_output, #notify_paused, #serializer_class, #task, #task_input, #task_output

Methods included from TaskSynchronization

included, #sync_execution_plan_to_task

Methods included from Helpers::LifecycleLogging

included, #log_task_state_change

Instance Method Details

#abort_proxy_taskObject



119
120
121
122
# File 'app/lib/actions/proxy_action.rb', line 119

def abort_proxy_task
  proxy.cancel_task(proxy_task_id)
  error! ForemanTasks::Task::TaskCancelledException.new(_('Task aborted: the task might be still running on the proxy'))
end

#cancel_proxy_taskObject



109
110
111
112
113
114
115
116
117
# File 'app/lib/actions/proxy_action.rb', line 109

def cancel_proxy_task
  if output[:cancel_sent]
    error! ForemanTasks::Task::TaskCancelledException.new(_('Cancel enforced: the task might be still running on the proxy'))
  else
    proxy.cancel_task(proxy_task_id)
    output[:cancel_sent] = true
    suspend
  end
end

#check_task_statusObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'app/lib/actions/proxy_action.rb', line 94

def check_task_status
  response = proxy.status_of_task(proxy_task_id)
  if %w[stopped paused].include? response['state']
    if response['result'] == 'error'
      raise ::Foreman::Exception, _('The smart proxy task %s failed.') % proxy_task_id
    else
      on_data(get_proxy_data(response))
    end
  else
    suspend
  end
rescue RestClient::NotFound
  on_proxy_action_missing
end

#clean_remote_task(*_args) ⇒ Object



210
211
212
# File 'app/lib/actions/proxy_action.rb', line 210

def clean_remote_task(*_args)
  remote_task.destroy! if remote_task
end

#default_connection_optionsObject



202
203
204
205
206
207
208
# File 'app/lib/actions/proxy_action.rb', line 202

def default_connection_options
  # Fails if the plan is not finished within 60 seconds from the first task trigger attempt on the smart proxy
  # If the triggering fails, it retries 3 more times with 15 second delays
  { :retry_interval => Setting['foreman_tasks_proxy_action_retry_interval'] || 15,
    :retry_count    => Setting['foreman_tasks_proxy_action_retry_count'] || 4,
    :proxy_batch_triggering => Setting['foreman_tasks_proxy_batch_trigger'] || false }
end

#fill_continuous_output(continuous_output) ⇒ Object

The proxy action is able to contribute to continuous output



180
181
182
183
184
185
186
# File 'app/lib/actions/proxy_action.rb', line 180

def fill_continuous_output(continuous_output)
  failed_proxy_tasks.each do |failure_data|
    message = _('Initialization error: %s') %
              "#{failure_data[:exception_class]} - #{failure_data[:exception_message]}"
    continuous_output.add_output(message, 'debug', failure_data[:timestamp])
  end
end

#metadataObject



192
193
194
195
# File 'app/lib/actions/proxy_action.rb', line 192

def 
  output[:metadata] ||= {}
  output[:metadata]
end

#metadata=(thing) ⇒ Object



197
198
199
200
# File 'app/lib/actions/proxy_action.rb', line 197

def metadata=(thing)
  output[:metadata] ||= {}
  output[:metadata] = thing
end

#on_data(data, meta = {}) ⇒ Object



130
131
132
133
# File 'app/lib/actions/proxy_action.rb', line 130

def on_data(data, meta = {})
  action_logger.info(_('Event delivered by request %{request_id}') % { :request_id => meta[:request_id] }) if meta[:request_id].present?
  output[:proxy_output] = data
end

#on_proxy_action_missingObject



142
143
144
# File 'app/lib/actions/proxy_action.rb', line 142

def on_proxy_action_missing
  error! ProxyActionMissing.new(_('Proxy task gone missing from the smart proxy'))
end

#on_proxy_action_stopped(event) ⇒ Object



146
147
148
149
150
151
152
# File 'app/lib/actions/proxy_action.rb', line 146

def on_proxy_action_stopped(event)
  if event.exception
    error! ProxyActionStopped.new(_('Failed to trigger task on the smart proxy: ') + event.exception.message)
  else
    check_task_status
  end
end

#on_resumeObject



124
125
126
127
# File 'app/lib/actions/proxy_action.rb', line 124

def on_resume
  # TODO: add logic to load the data from the external action
  suspend
end

#plan(proxy, klass, options) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
# File 'app/lib/actions/proxy_action.rb', line 35

def plan(proxy, klass, options)
  options[:connection_options] ||= {}
  default_connection_options.each do |key, value|
    options[:connection_options][key] = value unless options[:connection_options].key?(key)
  end
  plan_self(options.merge(:proxy_url => proxy.url, :proxy_action_name => klass.to_s))
  # Just saving the RemoteTask is enough when using batch triggering
  # It will be picked up by the ProxyBatchTriggering middleware
  if input[:use_batch_triggering] && input.dig(:connection_options, :proxy_batch_triggering)
    prepare_remote_task.save!
  end
end

#proxyObject



164
165
166
# File 'app/lib/actions/proxy_action.rb', line 164

def proxy
  ProxyAPI::ForemanDynflow::DynflowProxy.new(:url => input[:proxy_url])
end

#proxy_action_nameObject



155
156
157
# File 'app/lib/actions/proxy_action.rb', line 155

def proxy_action_name
  input[:proxy_action_name]
end

#proxy_input(task_id = task.id) ⇒ Object



89
90
91
92
# File 'app/lib/actions/proxy_action.rb', line 89

def proxy_input(task_id = task.id)
  input.merge(:callback => { :task_id => task_id,
                             :step_id => run_step_id })
end

#proxy_operation_nameObject



160
161
162
# File 'app/lib/actions/proxy_action.rb', line 160

def proxy_operation_name
  input[:proxy_operation_name]
end

#proxy_output(live = false) ⇒ Object



168
169
170
171
172
173
174
175
176
177
# File 'app/lib/actions/proxy_action.rb', line 168

def proxy_output(live = false)
  if output.key?(:proxy_output) || state == :error
    output.fetch(:proxy_output, {})
  elsif live && proxy_task_id
    response = proxy.status_of_task(proxy_task_id)
    get_proxy_data(response)
  else
    {}
  end
end

#proxy_output=(output) ⇒ Object



188
189
190
# File 'app/lib/actions/proxy_action.rb', line 188

def proxy_output=(output)
  output[:proxy_output] = output
end

#remote_taskObject



71
72
73
# File 'app/lib/actions/proxy_action.rb', line 71

def remote_task
  @remote_task ||= ForemanTasks::RemoteTask.find_by(:execution_plan_id => execution_plan_id, :step_id => run_step_id)
end

#run(event = nil) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'app/lib/actions/proxy_action.rb', line 48

def run(event = nil)
  with_connection_error_handling(event) do |event|
    case event
    when nil
      start_or_resume
    when ::Dynflow::Action::Skip
      # do nothing
    when ::Dynflow::Action::Cancellable::Cancel
      cancel_proxy_task
    when ::Dynflow::Action::Cancellable::Abort
      abort_proxy_task
    when CallbackData
      on_data(event.data, event.meta)
    when ProxyActionMissing
      on_proxy_action_missing
    when ProxyActionStoppedEvent
      on_proxy_action_stopped(event)
    else
      raise "Unexpected event #{event.inspect}"
    end
  end
end

#trigger_proxy_taskObject



75
76
77
78
79
80
81
# File 'app/lib/actions/proxy_action.rb', line 75

def trigger_proxy_task
  suspend do |_suspended_action|
    remote_task = prepare_remote_task
    ForemanTasks::RemoteTask.batch_trigger(remote_task.operation, [remote_task])
    output[:proxy_task_id] = remote_task.remote_task_id
  end
end

#trigger_remote_taskObject



83
84
85
86
87
# File 'app/lib/actions/proxy_action.rb', line 83

def trigger_remote_task
  suspend do |_suspended_action|
    ForemanTasks::RemoteTask.batch_trigger(remote_task.operation, [remote_task])
  end
end

#wipe_secrets!(_execution_plan) ⇒ Object

Removes the :secrets key from the action’s input and output and saves the action



136
137
138
139
140
# File 'app/lib/actions/proxy_action.rb', line 136

def wipe_secrets!(_execution_plan)
  input.delete(:secrets)
  output.delete(:secrets)
  world.persistence.save_action(execution_plan_id, self)
end