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