diff --git a/lib/smart_proxy_dynflow/action/runner.rb b/lib/smart_proxy_dynflow/action/runner.rb index 70dc0d8..025b5b1 100644 --- a/lib/smart_proxy_dynflow/action/runner.rb +++ b/lib/smart_proxy_dynflow/action/runner.rb @@ -59,6 +59,7 @@ def kill_run def finish_run(update) output[:exit_status] = update.exit_status + output[:exit_status_timestamp] = update.exit_status_timestamp.to_f output[:result] = output_result drop_output_chunks! end diff --git a/lib/smart_proxy_dynflow/runner/base.rb b/lib/smart_proxy_dynflow/runner/base.rb index ac5341a..de105c7 100644 --- a/lib/smart_proxy_dynflow/runner/base.rb +++ b/lib/smart_proxy_dynflow/runner/base.rb @@ -71,6 +71,7 @@ def publish_exception(context, exception, fatal = true) def publish_exit_status(status) @exit_status = status + @exit_status_timestamp = Time.now.utc end def dispatch_exception(context, exception) @@ -90,7 +91,7 @@ def no_update end def new_update(data, exit_status) - { @suspended_action => Runner::Update.new(data, exit_status) } + { @suspended_action => Runner::Update.new(data, exit_status, exit_status_timestamp: @exit_status_timestamp) } end def initialize_continuous_outputs diff --git a/lib/smart_proxy_dynflow/runner/parent.rb b/lib/smart_proxy_dynflow/runner/parent.rb index fe81dd7..b311357 100644 --- a/lib/smart_proxy_dynflow/runner/parent.rb +++ b/lib/smart_proxy_dynflow/runner/parent.rb @@ -13,13 +13,13 @@ def initialize(targets = {}, suspended_action: nil, id: nil) def generate_updates base = {} - base[@suspended_action] = Runner::Update.new(Proxy::Dynflow::ContinuousOutput.new, @exit_status) if @exit_status + base[@suspended_action] = Runner::Update.new(Proxy::Dynflow::ContinuousOutput.new, @exit_status, exit_status_timestamp: @exit_status_timestamp) if @exit_status # Operate on all hosts if the main process ended or only on hosts for which we have updates @outputs.reject { |_, output| @exit_status.nil? && output.empty? } .reduce(base) do |acc, (identifier, output)| @outputs[identifier] = Proxy::Dynflow::ContinuousOutput.new # Create a new ContinuousOutput for next round of updates exit_status = @exit_statuses[identifier] || @exit_status if @exit_status - acc.merge(host_action(identifier) => Runner::Update.new(output, exit_status)) + acc.merge(host_action(identifier) => Runner::Update.new(output, exit_status, exit_status_timestamp: @exit_status_timestamp)) end end diff --git a/lib/smart_proxy_dynflow/runner/update.rb b/lib/smart_proxy_dynflow/runner/update.rb index 29ee60c..b3a0bcb 100644 --- a/lib/smart_proxy_dynflow/runner/update.rb +++ b/lib/smart_proxy_dynflow/runner/update.rb @@ -7,11 +7,12 @@ module Runner # Runner::Update represents chunk of data produced by runner that # can be consumed by other components, such as RunnerAction class Update - attr_reader :continuous_output, :exit_status + attr_reader :continuous_output, :exit_status, :exit_status_timestamp - def initialize(continuous_output, exit_status) + def initialize(continuous_output, exit_status, exit_status_timestamp: nil) @continuous_output = continuous_output @exit_status = exit_status + @exit_status_timestamp = exit_status_timestamp || Time.now.utc if @exit_status end def self.encode_exception(context, exception, fatal = true) diff --git a/test/runner_test.rb b/test/runner_test.rb index 3961021..637be33 100644 --- a/test/runner_test.rb +++ b/test/runner_test.rb @@ -39,6 +39,15 @@ class RunnerTest < Minitest::Spec _(update.continuous_output.raw_outputs.count).must_equal 1 end end + + describe '#publish_exit_status' do + it 'sets exit status and timestamp' do + runner.publish_exit_status(0) + updates = runner.generate_updates + assert_equal 0, updates[suspended_action].exit_status + assert_instance_of Time, updates[suspended_action].exit_status_timestamp + end + end end describe Parent do @@ -109,6 +118,36 @@ class RunnerTest < Minitest::Spec runner.publish_exception('general failure', exception, true) end end + + describe '#publish_exit_status' do + it 'sets exit status and timestamp' do + runner.publish_exit_status(0) + updates = runner.generate_updates + + # There are updates for all targets + assert_equal 3, updates.keys.count + + # They all share the same exit status and timestamp + assert_equal 1, updates.values.map(&:exit_status).uniq.count + assert_equal 1, updates.values.map(&:exit_status_timestamp).uniq.count + + assert_equal 0, updates[suspended_action].exit_status + assert_instance_of Time, updates[suspended_action].exit_status_timestamp + end + + it 'allows settings exit status per-host' do + runner.publish_exit_status_for('foo', 1) + runner.publish_exit_status(0) + updates = runner.generate_updates + assert_equal 3, updates.keys.count + + assert_equal(1, updates.values.count { |update| update.exit_status == 1 }) + assert_equal(2, updates.values.count { |update| update.exit_status.zero? }) + + # They all share the same timestamp + assert_equal 1, updates.values.map(&:exit_status_timestamp).uniq.count + end + end end end end