Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #26355 - background template rendering #414

Merged
merged 4 commits into from
Apr 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions lib/hammer_cli_foreman/report_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,85 @@ def request_params
build_options
end

class ScheduleCommand < HammerCLIForeman::CreateCommand
command_name "schedule"
action :schedule_report

option '--inputs', 'INPUTS', N_('Specify inputs'),
:format => HammerCLI::Options::Normalizers::KeyValueList.new
option '--wait', :flag, _('Turns a command to be active, wait for the result and download it right away')
option '--path', "PATH", _("Path to directory where downloaded content will be saved. Only usable if wait is specified"),
:attribute_name => :option_path

def request_params
params = super
params['input_values'] = option_inputs || {}
params
end

def execute
data = send_request
if option_wait?
poll_for_report(data)
else
print_message(_('The report has been scheduled. Job ID: %{job_id}') % { job_id: data['job_id'] })
end
HammerCLI::EX_OK
end

build_options

private

def poll_for_report(schedule_data)
report_data_args = build_report_data_args(schedule_data)
report_command = ReportDataCommand.new(invocation_path, context)
report_command.parse(report_data_args)
begin
response = report_command.send_request
end while response && response.code == 204
report_command.handle_success(response)
end

def build_report_data_args(schedule_data)
[
'--id', option_id,
'--job-id', schedule_data['job_id'],
'--path', option_path
]
end
end

class ReportDataCommand < HammerCLIForeman::DownloadCommand
command_name "report-data"
action :report_data

def default_filename
"Report-#{Time.new.strftime("%Y-%m-%d")}.txt"
end

build_options

def execute
response = send_request
if response.code == 204
print_message(_('The report is not ready yet.'))
Copy link
Member Author

@ezr-ondrej ezr-ondrej Apr 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some Exit code to suggest that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I could suggest HammerCLI::EX_RETRY but I'm sure it will flood the terminal with that message in some cases, which is not quite good. HammerCLI::EX_OK is good enough for it. If you meant something else, ping me on irc (ofedoren) so we can merge it today.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EX_RETRY will lead in re-executing the command again which in the end will make hammer loop here till the report is delivered. If this is not what we want I'd suggest to look at https://github.com/theforeman/hammer-cli/blob/master/lib/hammer_cli/exit_codes.rb
EX_TEMPFAIL may be a good candidate. From certain perspective (expected result, no error) the EX_OK is fine too if no better option is found.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I have rolled out with EX_TEMPFAIL 👍 Thank you both for advises!

HammerCLI::EX_TEMPFAIL
else
handle_success(response)
HammerCLI::EX_OK
end
end

def handle_success(response)
if option_path
filepath = store_response(response)
print_message(_('The response has been saved to %{path}.'), {:path => filepath})
else
puts response.body
end
end
end

class CreateCommand < HammerCLIForeman::CreateCommand
option ['--interactive', '-i'], :flag, _('Open empty template in an $EDITOR. Upload the result')
Expand Down
114 changes: 107 additions & 7 deletions test/functional/report_template_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -219,17 +219,25 @@
end
end

let(:generated_report_response) do
response = mock('ReportResponse')
response.stubs(:code).returns(200)
response.stubs(:body).returns('Report')
response.stubs(:headers).returns({:content_disposition => "filename=\"#{File.basename(tempfile.path)}\""})
response
end
let(:report_not_ready_response) do
mock('NoDataResponse', code: 204)
end

describe 'generate' do
let(:cmd) { %w(report-template generate) }
let(:tempfile) { Tempfile.new('template', '/tmp') }

it 'generates the report to the file' do
params = ['--id=3', '--path=/tmp', '--inputs=Host filter=filter']
response = mock()
response.stubs(:body).returns('Report')
response.stubs(:headers).returns({:content_disposition => "filename=\"#{File.basename(tempfile.path)}\""})
api_expects(:report_templates, :generate, 'Generate').with_params(
'id' => '3', "input_values" => {"Host filter" => "filter"}).returns(response)
'id' => '3', "input_values" => {"Host filter" => "filter"}).returns(generated_report_response)

output = OutputMatcher.new("The response has been saved to #{tempfile.path}")
expected_result = success_result(output)
Expand All @@ -240,15 +248,107 @@

it 'generates the report to stdout' do
params = ['--id=3', '--inputs=Host filter=filter']
response = mock()
response.stubs(:body).returns('Report')
api_expects(:report_templates, :generate, 'Generate').with_params(
'id' => '3', "input_values" => {"Host filter" => "filter"}).returns(response)
'id' => '3', "input_values" => {"Host filter" => "filter"}).returns(generated_report_response)

output = OutputMatcher.new('Report')
expected_result = success_result(output)
result = run_cmd(cmd + params)
assert_cmd(expected_result, result)
end
end

describe 'schedule' do
let(:cmd) { %w(report-template schedule) }
let(:tempfile) { Tempfile.new('template', '/tmp') }

let(:schedule_response) do
job_id = 'JOB-UNIQUE-ID'
{ 'job_id' => job_id, 'data_url' => "/report_data/#{job_id}" }
end

context 'without --wait' do
it 'schedule report and prints out data for getting the result' do
params = ['--id=3', '--inputs=Host filter=filter']
api_expects(:report_templates, :schedule_report, 'Schedule').with_params(
'id' => '3', "input_values" => {"Host filter" => "filter"}).returns(schedule_response)

output = OutputMatcher.new(schedule_response['job_id'])
expected_result = success_result(output)
result = run_cmd(cmd + params)
assert_cmd(expected_result, result)
end
end

context 'with --wait' do
it 'generates the report to the file' do
params = ['--id=3', '--inputs=Host filter=filter', '--wait', '--path=/tmp']
api_expects(:report_templates, :schedule_report, 'Schedule').with_params(
'id' => '3', "input_values" => {"Host filter" => "filter"}).returns(schedule_response)
api_expects(:report_templates, :report_data, 'Download report').with_params(
'id' => '3', 'job_id' => 'JOB-UNIQUE-ID').returns(generated_report_response)

output = OutputMatcher.new("The response has been saved to #{tempfile.path}")
expected_result = success_result(output)
result = run_cmd(cmd + params)
assert_cmd(expected_result, result)
assert_equal('Report', tempfile.read)
end

it 'generates the report to stdout' do
params = ['--id=3', '--inputs=Host filter=filter', '--wait']
api_expects(:report_templates, :schedule_report, 'Schedule').with_params(
'id' => '3', "input_values" => {"Host filter" => "filter"}).returns(schedule_response)
api_expects(:report_templates, :report_data, 'Download report').with_params(
'id' => '3', 'job_id' => 'JOB-UNIQUE-ID').returns(generated_report_response)

output = OutputMatcher.new('Report')
expected_result = success_result(output)
result = run_cmd(cmd + params)
assert_cmd(expected_result, result)
end

it 'polls for report while not ready' do
params = ['--id=3', '--inputs=Host filter=filter', '--wait']
api_expects(:report_templates, :schedule_report, 'Schedule').with_params(
'id' => '3', "input_values" => {"Host filter" => "filter"}).returns(schedule_response)
api_expects(:report_templates, :report_data, 'No data first, download second call')
.twice.with_params('id' => '3', 'job_id' => 'JOB-UNIQUE-ID')
.returns(report_not_ready_response)
.then.returns(generated_report_response)

output = OutputMatcher.new('Report')
expected_result = success_result(output)
result = run_cmd(cmd + params)
assert_cmd(expected_result, result)
end
end
end

describe 'report-data' do
let(:cmd) { %w(report-template report-data) }
let(:tempfile) { Tempfile.new('template', '/tmp') }
let(:params) { ['--id=3', '--path=/tmp', '--job-id=JOB-UNIQUE-ID'] }

it 'download data if ready' do
api_expects(:report_templates, :report_data, 'Download report').with_params(
'id' => '3', 'job_id' => 'JOB-UNIQUE-ID').returns(generated_report_response)

output = OutputMatcher.new("The response has been saved to #{tempfile.path}")
expected_result = success_result(output)
result = run_cmd(cmd + params)
assert_cmd(expected_result, result)
assert_equal('Report', tempfile.read)
end

it 'let user know data are not ready' do
api_expects(:report_templates, :report_data, 'Download report').with_params(
'id' => '3', 'job_id' => 'JOB-UNIQUE-ID').returns(report_not_ready_response)

output = OutputMatcher.new("The report is not ready yet.")
expected_result = CommandExpectation.new(output, '', HammerCLI::EX_TEMPFAIL)
result = run_cmd(cmd + params)
assert_cmd(expected_result, result)
end
end
end