app/lib/actions/proxy_action.rb in foreman-tasks-0.13.0 vs app/lib/actions/proxy_action.rb in foreman-tasks-0.13.1
- old
+ new
@@ -1,31 +1,41 @@
module Actions
class ProxyAction < Base
include ::Dynflow::Action::Cancellable
- include ::Dynflow::Action::Timeouts
middleware.use ::Actions::Middleware::HideSecrets
+ execution_plan_hooks.use :clean_remote_task, :on => :stopped
+ execution_plan_hooks.use :wipe_secrets!, :on => :stopped
+
class CallbackData
attr_reader :data
def initialize(data)
@data = data
end
end
+ class ProxyActionMissing < RuntimeError
+ def backtrace
+ []
+ end
+ end
+
+ class ProxyActionStopped; end
+
def plan(proxy, klass, options)
options[:connection_options] ||= {}
default_connection_options.each { |key, value| options[:connection_options][key] ||= value }
plan_self(options.merge(:proxy_url => proxy.url, :proxy_action_name => klass.to_s))
end
def run(event = nil)
with_connection_error_handling(event) do |event|
case event
when nil
- if output[:proxy_task_id]
+ if remote_task
on_resume
else
trigger_proxy_task
end
suspend
@@ -35,42 +45,45 @@
cancel_proxy_task
when ::Dynflow::Action::Cancellable::Abort
abort_proxy_task
when CallbackData
on_data(event.data)
- when ::Dynflow::Action::Timeouts::Timeout
- check_task_status
+ when ProxyActionMissing
+ on_proxy_action_missing
+ when ProxyActionStopped
+ on_proxy_action_stopped
else
raise "Unexpected event #{event.inspect}"
end
end
end
+ def remote_task
+ @remote_task ||= ForemanTasks::RemoteTask.find_by(:execution_plan_id => execution_plan_id, :step_id => run_step_id)
+ end
+
def trigger_proxy_task
suspend do |_suspended_action|
- set_timeout! unless timeout_set?
response = proxy.trigger_task(proxy_action_name,
input.merge(:callback => { :task_id => task.id,
:step_id => run_step_id }))
+ ::ForemanTasks::RemoteTask.new(:remote_task_id => response['task_id'], :execution_plan_id => execution_plan_id,
+ :state => 'triggered', :proxy_url => input[:proxy_url], :step_id => run_step_id).save!
output[:proxy_task_id] = response['task_id']
end
end
def check_task_status
- if output[:proxy_task_id]
- response = proxy.status_of_task(output[:proxy_task_id])
- if %w[stopped paused].include? response['state']
- if response['result'] == 'error'
- raise ::Foreman::Exception, _('The smart proxy task %s failed.') % output[:proxy_task_id]
- else
- on_data(response['actions'].find { |block_action| block_action['class'] == proxy_action_name }['output'])
- end
+ response = proxy.status_of_task(output[:proxy_task_id])
+ if %w[stopped paused].include? response['state']
+ if response['result'] == 'error'
+ raise ::Foreman::Exception, _('The smart proxy task %s failed.') % output[:proxy_task_id]
else
- suspend
+ on_data(response['actions'].find { |block_action| block_action['class'] == proxy_action_name }['output'])
end
else
- process_timeout
+ suspend
end
end
def cancel_proxy_task
if output[:cancel_sent]
@@ -93,30 +106,39 @@
end
# @override to put custom logic on event handling
def on_data(data)
output[:proxy_output] = data
- wipe_secrets!
end
- def wipe_secrets!
+ # Removes the :secrets key from the action's input and output and saves the action
+ def wipe_secrets!(_execution_plan)
input.delete(:secrets)
output.delete(:secrets)
+ world.persistence.save_action(execution_plan_id, self)
end
+ def on_proxy_action_missing
+ error! ProxyActionMissing.new(_('Proxy task gone missing from the smart proxy'))
+ end
+
+ def on_proxy_action_stopped
+ check_task_status
+ end
+
# @override String name of an action to be triggered on server
def proxy_action_name
input[:proxy_action_name]
end
def proxy
ProxyAPI::ForemanDynflow::DynflowProxy.new(:url => input[:proxy_url])
end
def proxy_output(live = false)
- if output.key?(:proxy_output)
- output.fetch(:proxy_output) || {}
+ if output.key?(:proxy_output) || state == :error
+ output.fetch(:proxy_output, {})
elsif live && output[:proxy_task_id]
proxy_data = proxy.status_of_task(output[:proxy_task_id])['actions'].detect { |action| action['class'] == proxy_action_name }
proxy_data.fetch('output', {})
else
{}
@@ -144,37 +166,30 @@
def metadata=(thing)
output[:metadata] ||= {}
output[:metadata] = thing
end
- def timeout_set?
- !metadata[:timeout].nil?
- end
-
- def set_timeout!
- time = Time.zone.now + input[:connection_options][:timeout]
- schedule_timeout(time)
- metadata[:timeout] = time.to_s
- end
-
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,
- :timeout => Setting['foreman_tasks_proxy_action_start_timeout'] || 60 }
+ :retry_count => Setting['foreman_tasks_proxy_action_retry_count'] || 4 }
end
+ def clean_remote_task(*_args)
+ remote_task.destroy! if remote_task
+ end
+
private
def failed_proxy_tasks
metadata[:failed_proxy_tasks] ||= []
end
def with_connection_error_handling(event = nil)
yield event
rescue ::RestClient::Exception, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ETIMEDOUT => e
- if event.class == CallbackData || event == ::Dynflow::Action::Timeouts::Timeout
+ if event.class == CallbackData
raise e
else
handle_connection_exception(e, event)
end
end