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

Add methods to fetch terraform stdout for showing in UI. #78

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,61 @@ def refresh
def raw_status
Status.new(miq_task)
end

# Intend to be called by UI to display stdout. The stdout is stored in TerraformRunner(api/stack#message)
def raw_stdout_via_worker(userid, format = 'txt')
unless MiqRegion.my_region.role_active?("embedded_terraform")
msg = "Cannot get standard output of this terraform-template because the embedded terraform role is not enabled"
return MiqTask.create(
:name => 'terraform_stdout',
:userid => userid || 'system',
:state => MiqTask::STATE_FINISHED,
:status => MiqTask::STATUS_ERROR,
:message => msg
).id
end

options = {:userid => userid || 'system', :action => 'terraform_stdout'}
queue_options = {
:class_name => self.class,
:method_name => 'raw_stdout',
:instance_id => id,
:args => [format],
:priority => MiqQueue::HIGH_PRIORITY,
:role => nil
}

MiqTask.generic_action_with_callback(options, queue_options)
end

def raw_stdout(format = 'txt')
case format
when "html" then raw_stdout_html
else raw_stdout_txt
end
end

def raw_stdout_txt
data = terraform_runner_stack_data
data&.message
end

def raw_stdout_html
text = raw_stdout_txt
text = _("No output available") if text.blank?
TerminalToHtml.render(text)
end

private

def terraform_runner_stack_data
return if miq_task.nil? || miq_task.job.nil?

job = miq_task.job
terraform_stack_id = job.options[:terraform_stack_id]

return if terraform_stack_id.blank?

Terraform::Runner.fetch_result_by_stack_id(terraform_stack_id)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,161 @@
end
end
end

describe "#raw_stdout" do
let(:stack) { FactoryBot.create(:terraform_stack, :miq_task => miq_task) }
let(:template) { FactoryBot.create(:terraform_template) }

context "when miq_task.job present" do
let(:terraform_runner_url) { "https://1.2.3.4:7000" }
let(:hello_world_retrieve_response) do
require 'json'
JSON.parse(File.read(File.join(__dir__, "../../../../../lib/terraform/runner/data/responses/hello-world-retrieve-success.json")))
Copy link
Member

@agrare agrare Oct 21, 2024

Choose a reason for hiding this comment

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

We might want to consider moving this up to spec/data or something if it is going to be used by more than just the Terraform::Runner specs since this ../../../etc... makes it hard to read where this file lives. Without counting the parent dirs I thought this lived in the "production" lib/terraform directory not spec/lib/terraform

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes agree makes it harder to read,
could make it like
"../../../../../../spec/lib/terraform/runner/data/responses/hello-world-retrieve-success.json"

also maybe should move this common test data, to somewhere like <base-dir>/spec/data. then still look like
"../../../../../../spec/data/responses/hello-world-retrieve-success.json"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

don't know if there more easier way to refer to test data resources without using "../../../../../../"

Copy link
Member

Choose a reason for hiding this comment

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

We can use the ManageIQ::Providers::EmbeddedTerraform::Engine.root Pathname to build a path from the root of the plugin

end
let(:miq_task) { FactoryBot.create(:miq_task, :job => job) }

let(:job) do
ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Job.create_job(template, {}, {}, []).tap do |job|
job.state = "finished"
job.options = {
:terraform_stack_id => hello_world_retrieve_response['stack_id']
}
Comment on lines +47 to +51
Copy link
Member

Choose a reason for hiding this comment

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

I think a FactoryBot factory would be a better way to set up these Job objects

Copy link
Member

Choose a reason for hiding this comment

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

#80

end
end

let(:terraform_runner_stdout) { hello_world_retrieve_response['message'] }
let(:terraform_runner_stdout_html) { TerminalToHtml.render(terraform_runner_stdout) }

before do
stub_const("ENV", ENV.to_h.merge("TERRAFORM_RUNNER_URL" => terraform_runner_url))

stub_request(:post, "#{terraform_runner_url}/api/stack/retrieve")
.with(:body => hash_including({:stack_id => hello_world_retrieve_response['stack_id']}))
.to_return(
:status => 200,
:body => hello_world_retrieve_response.to_json
)
end

it "json" do
expect(stack.raw_stdout("json")).to eq terraform_runner_stdout
end

it "txt" do
expect(stack.raw_stdout("txt")).to eq terraform_runner_stdout
end

it "html" do
expect(stack.raw_stdout("html")).to eq terraform_runner_stdout_html
end

it "nil" do
expect(stack.raw_stdout).to eq terraform_runner_stdout
end
end

shared_examples_for "terraform runner stdout not available from miq_task" do
it "json" do
expect(stack.raw_stdout("json")).to be_nil
end

it "txt" do
expect(stack.raw_stdout("txt")).to be_nil
end

it "html" do
expect(stack.raw_stdout("html")).to include <<~EOHTML
<div class='term-container'>
No output available
</div>
EOHTML
end

it "nil" do
expect(stack.raw_stdout).to be_nil
end
end

context "when miq_task is missing" do
let(:miq_task) { nil }

it_behaves_like "terraform runner stdout not available from miq_task"
end

context "when miq_task present, but missing miq_task.job" do
let(:miq_task) { FactoryBot.create(:miq_task, :job => nil) }

it_behaves_like "terraform runner stdout not available from miq_task"
end

context "when miq_task.job.options present but missing terraform_stack_id" do
let(:job) do
ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Job.create_job(template, {}, {}, []).tap do |job|
job.state = "waiting_to_start"
job.options = {}
end
end

let(:miq_task) { FactoryBot.create(:miq_task, :job => job) }

it_behaves_like "terraform runner stdout not available from miq_task"
end
end

describe "#raw_stdout_via_worker" do
let(:stack) { FactoryBot.create(:terraform_stack) }

context "when embedded_terraform role is enabled" do
before do
EmbeddedTerraformEvmSpecHelper.assign_embedded_terraform_role

allow_any_instance_of(ManageIQ::Providers::EmbeddedTerraform::AutomationManager::ConfigurationScriptSource).to receive(:checkout_git_repository)
end

describe "#raw_stdout_via_worker with no errors" do
before do
EvmSpecHelper.local_miq_server
allow(described_class).to receive(:find).and_return(stack)

allow(MiqTask).to receive(:wait_for_taskid) do
request = MiqQueue.find_by(:class_name => described_class.name)
request.update(:state => MiqQueue::STATE_DEQUEUE)
request.deliver_and_process
end
end

it "gets stdout from the job" do
expect(stack).to receive(:raw_stdout).and_return("A stdout from the job")
taskid = stack.raw_stdout_via_worker("user")
MiqTask.wait_for_taskid(taskid)
expect(MiqTask.find(taskid)).to have_attributes(
:task_results => "A stdout from the job",
:status => "Ok"
)
end

it "returns the error message" do
expect(stack).to receive(:raw_stdout).and_throw("Failed to get stdout from the job")
taskid = stack.raw_stdout_via_worker("user")
MiqTask.wait_for_taskid(taskid)
expect(MiqTask.find(taskid).message).to include("Failed to get stdout from the job")
expect(MiqTask.find(taskid).status).to eq("Error")
end
end
end

context "when embedded_terraform role is disabled" do
describe "#raw_stdout_via_worker return error" do
let(:role_enabled) { false }

it "returns an error message" do
taskid = stack.raw_stdout_via_worker("user")
expect(MiqTask.find(taskid)).to have_attributes(
:message => "Cannot get standard output of this terraform-template because the embedded terraform role is not enabled",
:status => "Error"
)
end
end
end
end
end