Skip to content

Commit

Permalink
Add overall task state for done method
Browse files Browse the repository at this point in the history
  • Loading branch information
chris1984 committed Sep 12, 2023
1 parent 46c31ef commit 5be74bb
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,17 @@ def rescue_strategy_for_self
end

def done?(current_status = invocation_status)
job_invocation.finished? || current_status.map { |_host_id, task_status| task_status['report_done'] }.all?
ActiveModel::Type::Boolean.new.cast(current_status[:task_state][:task_done_reported])
end

def job_finished?
job_invocation.finished?
end

def all_hosts_finished?(current_status)
current_status[:hosts_state].values.all? do |status|
ActiveModel::Type::Boolean.new.cast(status['report_done'] == true)
end
end

# noop, we don't want to do anything when the polling task starts
Expand Down Expand Up @@ -95,7 +105,11 @@ def correlation_id
end

def host_status(host)
external_task&.dig('invocation_status', host)
external_task&.dig('invocation_status', :hosts_state, host)
end

def task_done_state
ActiveModel::Type::Boolean.new.cast(external_task&.dig('invocation_status', :task_state, :task_done_reported))
end

def sequence(host)
Expand All @@ -111,13 +125,15 @@ def report_url
end

def invocation_status
Hash[job_invocation.targeting.hosts.map do |host|
hosts_state = Hash[job_invocation.targeting.hosts.map do |host|
next unless host.insights&.uuid
[
host.insights.uuid,
task_status(job_invocation.sub_task_for_host(host), host.insights.uuid),
]
end.compact]

{task_state: {task_done_reported: task_done_state}, hosts_state: hosts_state}
end

def task_status(host_task, host_name)
Expand All @@ -138,9 +154,9 @@ def report_job_progress(invocation_status)
generator = InsightsCloud::Generators::PlaybookProgressGenerator.new(correlation_id)
all_hosts_success = true

invocation_status.each do |host_name, status|
invocation_status[:hosts_state].each do |host_name, status|
# skip host if the host already reported that it's finished
next if status['report_done']
next if ActiveModel::Type::Boolean.new.cast(status['report_done'])

unless status['state'] == 'unknown'
sequence = status['sequence']
Expand All @@ -154,7 +170,11 @@ def report_job_progress(invocation_status)
all_hosts_success &&= status['exit_status'] == 0
end
end
generator.job_finished_message(all_hosts_success) if done?(invocation_status)

if (job_finished? || all_hosts_finished?(invocation_status))
generator.job_finished_message(all_hosts_success)
invocation_status[:task_state][:task_done_reported] = true
end

send_report(generator.generate)
end
Expand Down
141 changes: 105 additions & 36 deletions test/jobs/connector_playbook_execution_reporter_task_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ def send_report(report)
end

test 'It reports finish playbook messages' do
TestConnectorPlaybookExecutionReporterTask.any_instance.stubs(:done?).returns(true)
host1_task = @job_invocation.sub_task_for_host(Host.where(name: 'host1').first)
host1_task.state = 'stopped'
host1_task.save!

TestConnectorPlaybookExecutionReporterTask.any_instance.stubs(:job_finished?).returns(true)

actual = ForemanTasks.sync_task(TestConnectorPlaybookExecutionReporterTask, @job_invocation)

Expand All @@ -39,6 +43,9 @@ def send_report(report)
assert_not_nil actual_report
actual_jsonl = read_jsonl(actual_report)

assert_equal true, @job_invocation.finished?
assert_equal 'stopped', @job_invocation.sub_task_for_host(Host.where(name: 'host1').first)['state']

assert_not_nil actual_report_finished = actual_jsonl.find { |l| l['type'] == 'playbook_run_completed' }
assert_equal 'TEST_CORRELATION', actual_report_finished['correlation_id']
assert_equal 'success', actual_report_finished['status']
Expand All @@ -49,57 +56,117 @@ def send_report(report)
end

test 'It reports single progress message for done host' do
TestConnectorPlaybookExecutionReporterTask.any_instance.stubs(:done?).returns(false, true)
class ArrangeTestHost < InsightsCloud::Async::ConnectorPlaybookExecutionReporterTask
def send_report(report)
host1_task = @job_invocation.sub_task_for_host(Host.where(name: 'host1').first)
host1_task.state = 'stopped'
host1_task.save!

output[:saved_reports] = (output[:saved_reports] || []) << report
end
end

actual = ForemanTasks.sync_task(TestConnectorPlaybookExecutionReporterTask, @job_invocation)
ArrangeTestHost.instance_variable_set(:@connector_feature_id, nil)
host1_task = @job_invocation.sub_task_for_host(Host.where(name: 'host1').first)
host1_task.state = 'running'
host1_task.save!

actual_report = actual.output[:saved_reports].first.to_s
ArrangeTestHost.any_instance.stubs(:job_finished?).returns(false, true)

assert_equal 1, actual.output[:saved_reports].size
assert_not_nil actual_report
actual_jsonl = read_jsonl(actual_report)
actual = ForemanTasks.sync_task(ArrangeTestHost, @job_invocation)

actual_report1 = actual.output[:saved_reports].first.to_s
actual_report2 = actual.output[:saved_reports].second.to_s

assert_equal 2, actual.output[:saved_reports].size
assert_not_nil actual_report1
assert_not_nil actual_report2

actual_json1 = read_jsonl(actual_report1)
actual_json2 = read_jsonl(actual_report2)

assert_equal 'stopped', @job_invocation.sub_task_for_host(Host.where(name: 'host1').first)['state']

actual_host_updates = actual_jsonl
.select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host1.insights.uuid }
assert_equal 1, actual_host_updates.size
assert_equal 0, actual_host_updates.first['sequence']
assert_not_nil actual_report_updated = actual_json1.find { |l| l['type'] == 'playbook_run_update' && l['host'] == 'TEST_UUID1' }
assert_equal 'TEST_CORRELATION', actual_report_updated['correlation_id']
assert_equal 'TEST_UUID1', actual_report_updated['host']
assert_equal 0, actual_report_updated['sequence']

assert_not_nil actual_report_updated = actual_json2.find { |l| l['type'] == 'playbook_run_update' && l['host'] == 'TEST_UUID1' }
assert_equal 'TEST_CORRELATION', actual_report_updated['correlation_id']
assert_equal 'TEST_UUID1', actual_report_updated['host']
assert_equal 1, actual_report_updated['sequence']

assert_not_nil actual_host_finished = actual_json2.find { |l| l['type'] == 'playbook_run_finished' && l['host'] == 'TEST_UUID1' }
assert_equal 'TEST_CORRELATION', actual_host_finished['correlation_id']
assert_equal 'TEST_UUID1', actual_host_finished['host']
assert_equal 'success', actual_host_finished['status']

assert_not_nil actual_report_finished = actual_json2.find { |l| l['type'] == 'playbook_run_completed' }
assert_equal 'TEST_CORRELATION', actual_report_finished['correlation_id']
assert_equal 'success', actual_report_finished['status']
end

test 'It reports two progress messages for in progress host' do
TestConnectorPlaybookExecutionReporterTask.any_instance.stubs(:done?).returns(false, false, true)
class ArrangeTestHostTwo < InsightsCloud::Async::ConnectorPlaybookExecutionReporterTask
def send_report(report)
iteration_number = output[:iteration_number].to_i

if iteration_number == 1
host1_task = job_invocation.sub_task_for_host(Host.where(name: 'host1').first)
host1_task.state = 'stopped'
host1_task.save!
end

output[:iteration_number] = iteration_number + 1
output[:saved_reports] = (output[:saved_reports] || []) << report
end
end

host1_task = @job_invocation.template_invocations.joins(:host).where(hosts: {name: @host1.name}).first.run_host_job_task
ArrangeTestHostTwo.instance_variable_set(:@connector_feature_id, nil)
host1_task = @job_invocation.sub_task_for_host(Host.where(name: 'host1').first)
host1_task.state = 'running'
host1_task.save!

actual = ForemanTasks.sync_task(TestConnectorPlaybookExecutionReporterTask, @job_invocation)
ArrangeTestHostTwo.any_instance.stubs(:job_finished?).returns(false, false, true)

assert_equal 2, actual.output[:saved_reports].size
actual = ForemanTasks.sync_task(ArrangeTestHostTwo, @job_invocation)

first_report = actual.output[:saved_reports].first.to_s
actual_jsonl = read_jsonl(first_report)
actual_report1 = actual.output[:saved_reports].first.to_s
actual_report2 = actual.output[:saved_reports].second.to_s
actual_report3 = actual.output[:saved_reports].third.to_s

actual_host_updates = actual_jsonl
.select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host1.insights.uuid }
assert_equal 1, actual_host_updates.size
assert_equal 0, actual_host_updates.first['sequence']
assert_equal 3, actual.output[:saved_reports].size
assert_not_nil actual_report1
assert_not_nil actual_report2
assert_not_nil actual_report3

actual_host_updates = actual_jsonl
.select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host2.insights.uuid }
assert_equal 1, actual_host_updates.size
assert_equal 0, actual_host_updates.first['sequence']
actual_json1 = read_jsonl(actual_report1)
actual_json2 = read_jsonl(actual_report2)
actual_json3 = read_jsonl(actual_report3)

second_report = actual.output[:saved_reports].last.to_s
actual_jsonl = read_jsonl(second_report)
assert_not_nil actual_report_updated = actual_json1.find { |l| l['type'] == 'playbook_run_update' && l['host'] == 'TEST_UUID1' }
assert_equal 'TEST_CORRELATION', actual_report_updated['correlation_id']
assert_equal 'TEST_UUID1', actual_report_updated['host']
assert_equal 0, actual_report_updated['sequence']
assert_equal 6, actual_report_updated.size

actual_host_updates = actual_jsonl
.select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host1.insights.uuid }
assert_equal 1, actual_host_updates.size
assert_equal 1, actual_host_updates.first['sequence']
assert_not_nil actual_report_updated = actual_json2.find { |l| l['type'] == 'playbook_run_update' && l['host'] == 'TEST_UUID1' }
assert_equal 'TEST_CORRELATION', actual_report_updated['correlation_id']
assert_equal 'TEST_UUID1', actual_report_updated['host']
assert_equal 1, actual_report_updated['sequence']
assert_equal 6, actual_report_updated.size

actual_host_updates = actual_jsonl
.select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host2.insights.uuid }
assert_equal 0, actual_host_updates.size
assert_not_nil actual_host_finished = actual_json3.find { |l| l['type'] == 'playbook_run_finished' && l['host'] == 'TEST_UUID1' }
assert_equal 'TEST_CORRELATION', actual_host_finished['correlation_id']
assert_equal 'TEST_UUID1', actual_host_finished['host']
assert_equal 'success', actual_host_finished['status']
assert_equal 7, actual_host_finished.size

assert_not_nil actual_report_finished = actual_json3.find { |l| l['type'] == 'playbook_run_completed' }
assert_equal 'TEST_CORRELATION', actual_report_finished['correlation_id']
assert_equal 'success', actual_report_finished['status']
assert_equal 4, actual_report_finished.size
end

private
Expand Down Expand Up @@ -164,10 +231,12 @@ def generate_job_invocation
:value => '1'
)

@host1 = FactoryBot.create(:host, :with_insights_hits, name: 'host1')
@host1 = FactoryBot.create(:host, :with_insights_hits)
@host1.name = 'host1' # overriding name since there is an issue with Factorybot and setting the name correctly, same for 2nd host
@host1.insights.uuid = 'TEST_UUID1'
@host1.insights.save!
@host2 = FactoryBot.create(:host, :with_insights_hits, name: 'host2')
@host2 = FactoryBot.create(:host, :with_insights_hits)
@host2.name = 'host2'
@host2.insights.uuid = 'TEST_UUID2'
@host2.insights.save!

Expand Down

0 comments on commit 5be74bb

Please sign in to comment.