-
Notifications
You must be signed in to change notification settings - Fork 12
Add EmbeddedTerraform Stack and Job #12
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
Merged
jrafanie
merged 3 commits into
ManageIQ:master
from
agrare:add_orchestration_stack_and_job
Apr 25, 2024
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
129 changes: 129 additions & 0 deletions
129
app/models/manageiq/providers/embedded_terraform/automation_manager/job.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| class ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Job < Job | ||
| def self.create_job(template, env_vars, input_vars, credentials, poll_interval: 1.minute) | ||
| super( | ||
| :template_id => template.id, | ||
| :env_vars => env_vars, | ||
| :input_vars => input_vars, | ||
| :credentials => credentials, | ||
| :poll_interval => poll_interval, | ||
| ) | ||
| end | ||
|
|
||
| def start | ||
| queue_signal(:pre_execute) | ||
| end | ||
|
|
||
| def pre_execute | ||
| checkout_git_repository | ||
| signal(:execute) | ||
| end | ||
|
|
||
| def execute | ||
| template_path = File.join(options[:git_checkout_tempdir], template_relative_path) | ||
|
|
||
| response = Terraform::Runner.run( | ||
| options[:input_vars], | ||
| template_path, | ||
| :credentials => options[:credentials], | ||
| :env_vars => options[:env_vars] | ||
| ) | ||
|
|
||
| options[:terraform_stack_id] = response.stack_id | ||
| save! | ||
|
|
||
| queue_poll_runner | ||
| end | ||
|
|
||
| def poll_runner | ||
| if running? | ||
| queue_poll_runner | ||
| else | ||
| signal(:post_execute) | ||
| end | ||
| end | ||
|
|
||
| def post_execute | ||
| cleanup_git_repository | ||
|
|
||
| success? ? queue_signal(:finish, message, status) : abort_job("Failed to run template", "error") | ||
| end | ||
|
|
||
| alias initializing dispatch_start | ||
| alias finish process_finished | ||
| alias abort_job process_abort | ||
| alias cancel process_cancel | ||
| alias error process_error | ||
|
|
||
| protected | ||
|
|
||
| def running? | ||
| stack_response&.running? | ||
| end | ||
|
|
||
| def success? | ||
| stack_response&.response&.status == "SUCCESS" | ||
| end | ||
|
|
||
| def load_transitions | ||
| self.state ||= 'initialize' | ||
|
|
||
| { | ||
| :initializing => {'initialize' => 'waiting_to_start'}, | ||
| :start => {'waiting_to_start' => 'pre_execute'}, | ||
| :pre_execute => {'pre_execute' => 'execute'}, | ||
| :execute => {'execute' => 'running'}, | ||
| :poll_runner => {'running' => 'running'}, | ||
| :post_execute => {'running' => 'post_execute'}, | ||
| :finish => {'*' => 'finished'}, | ||
| :abort_job => {'*' => 'aborting'}, | ||
| :cancel => {'*' => 'canceling'}, | ||
| :error => {'*' => '*'} | ||
| } | ||
| end | ||
|
|
||
| def poll_interval | ||
| options.fetch(:poll_interval, 1.minute).to_i | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def template | ||
| @template ||= self.class.module_parent::Template.find(options[:template_id]) | ||
| end | ||
|
|
||
| def template_relative_path | ||
| JSON.parse(template.payload)["relative_path"] | ||
| end | ||
|
|
||
| def stack_response | ||
| return if options[:terraform_stack_id].nil? | ||
|
|
||
| @stack_response ||= Terraform::Runner::ResponseAsync.new(options[:terraform_stack_id]) | ||
| end | ||
|
|
||
| def configuration_script_source | ||
| @configuration_script_source ||= template.configuration_script_source | ||
| end | ||
|
|
||
| def queue_poll_runner | ||
| queue_signal(:poll_runner, :deliver_on => Time.now.utc + poll_interval) | ||
| end | ||
|
|
||
| def checkout_git_repository | ||
| options[:git_checkout_tempdir] = Dir.mktmpdir("embedded-terraform-runner-git") | ||
| save! | ||
|
|
||
| _log.info("Checking out git repository to #{options[:git_checkout_tempdir].inspect}...") | ||
| configuration_script_source.checkout_git_repository(options[:git_checkout_tempdir]) | ||
| rescue MiqException::MiqUnreachableError => err | ||
| miq_task.job.timeout! | ||
| raise "Failed to connect with [#{err.class}: #{err}], job aborted" | ||
| end | ||
|
|
||
| def cleanup_git_repository | ||
| return unless options[:git_checkout_tempdir] | ||
|
|
||
| _log.info("Cleaning up git repository checkout at #{options[:git_checkout_tempdir].inspect}...") | ||
| FileUtils.rm_rf(options[:git_checkout_tempdir]) | ||
| end | ||
| end | ||
44 changes: 44 additions & 0 deletions
44
app/models/manageiq/providers/embedded_terraform/automation_manager/stack.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| class ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Stack < ManageIQ::Providers::EmbeddedAutomationManager::OrchestrationStack | ||
| belongs_to :ext_management_system, :foreign_key => :ems_id, :class_name => "ManageIQ::Providers::EmbeddedTerraform::AutomationManager", :inverse_of => false | ||
| belongs_to :configuration_script_payload, :foreign_key => :configuration_script_base_id, :class_name => "ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Template", :inverse_of => :stacks | ||
| belongs_to :miq_task, :foreign_key => :ems_ref, :inverse_of => false | ||
|
|
||
| class << self | ||
| alias create_job create_stack | ||
| alias raw_create_job raw_create_stack | ||
|
|
||
| def create_stack(terraform_template, options = {}) | ||
| authentications = collect_authentications(terraform_template.manager, options) | ||
|
|
||
| job = raw_create_stack(terraform_template, options) | ||
|
|
||
| miq_task = job&.miq_task | ||
|
|
||
| create!( | ||
| :name => terraform_template.name, | ||
| :ext_management_system => terraform_template.manager, | ||
| :verbosity => options[:verbosity].to_i, | ||
| :authentications => authentications, | ||
| :configuration_script_payload => terraform_template, | ||
| :miq_task => miq_task, | ||
| :status => miq_task&.state, | ||
| :start_time => miq_task&.started_on | ||
| ) | ||
| end | ||
|
|
||
| def raw_create_stack(terraform_template, options = {}) | ||
| terraform_template.run(options) | ||
| rescue => err | ||
| _log.error("Failed to create job from template(#{terraform_template.name}), error: #{err}") | ||
| raise MiqException::MiqOrchestrationProvisionError, err.to_s, err.backtrace | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def collect_authentications(manager, options) | ||
| credential_ids = options[:credential] || [] | ||
|
|
||
| manager.credentials.where(:id => credential_ids) | ||
| end | ||
| end | ||
| end |
6 changes: 5 additions & 1 deletion
6
app/models/manageiq/providers/embedded_terraform/automation_manager/template.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,10 @@ | ||
| class ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Template < ManageIQ::Providers::EmbeddedAutomationManager::ConfigurationScriptPayload | ||
| has_many :jobs, :class_name => 'OrchestrationStack', :foreign_key => :configuration_script_base_id | ||
| has_many :stacks, :class_name => "ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Stack", :foreign_key => :configuration_script_base_id, :inverse_of => :configuration_script_payload, :dependent => :nullify | ||
|
|
||
| def run(vars = {}, _userid = nil) | ||
| env_vars = vars.delete(:env) || {} | ||
| credentials = vars.delete(:credential) || [] | ||
|
|
||
| self.class.module_parent::Job.create_job(self, env_vars, vars, credentials).tap(&:signal_start) | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| FactoryBot.define do | ||
| factory :terraform_template, | ||
| :class => "ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Template", | ||
| :parent => :configuration_script_payload | ||
| end |
141 changes: 141 additions & 0 deletions
141
spec/models/manageiq/providers/embedded_terraform/automation_manager/job_spec.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| RSpec.describe ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Job do | ||
| let(:template) { FactoryBot.create(:terraform_template) } | ||
| let(:job) { described_class.create_job(template, env_vars, input_vars, credentials).tap { |job| job.state = state} } | ||
| let(:state) { "waiting_to_start" } | ||
| let(:env_vars) { {} } | ||
| let(:input_vars) { {} } | ||
| let(:credentials) { [] } | ||
|
|
||
| describe ".create_job" do | ||
| it "create a job" do | ||
| expect(described_class.create_job(template, env_vars, input_vars, credentials)).to have_attributes( | ||
| :type => "ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Job", | ||
| :options => { | ||
| :template_id => template.id, | ||
| :env_vars => env_vars, | ||
| :input_vars => input_vars, | ||
| :credentials => credentials, | ||
| :poll_interval => 60 | ||
| } | ||
| ) | ||
| end | ||
| end | ||
|
|
||
| describe "#signal" do | ||
| %w[start pre_execute execute poll_runner post_execute finish abort_job cancel error].each do |signal| | ||
| shared_examples_for "allows #{signal} signal" do | ||
| it signal.to_s do | ||
| expect(job).to receive(signal.to_sym) | ||
| job.signal(signal.to_sym) | ||
| end | ||
| end | ||
| end | ||
|
|
||
| %w[start pre_execute execute poll_runner post_execute finish abort_job cancel error].each do |signal| | ||
| shared_examples_for "doesn't allow #{signal} signal" do | ||
| it signal.to_s do | ||
| expect { job.signal(signal.to_sym) }.to raise_error(RuntimeError, /#{signal} is not permitted at state #{job.state}/) | ||
| end | ||
| end | ||
| end | ||
|
|
||
| context "waiting_to_start" do | ||
| let(:state) { "waiting_to_start" } | ||
|
|
||
| it_behaves_like "allows start signal" | ||
| it_behaves_like "doesn't allow pre_execute signal" | ||
| it_behaves_like "doesn't allow execute signal" | ||
| it_behaves_like "doesn't allow poll_runner signal" | ||
| it_behaves_like "doesn't allow post_execute signal" | ||
| it_behaves_like "allows finish signal" | ||
| it_behaves_like "allows abort_job signal" | ||
| it_behaves_like "allows cancel signal" | ||
| it_behaves_like "allows error signal" | ||
| end | ||
|
|
||
| context "pre_execute" do | ||
| let(:state) { "pre_execute" } | ||
|
|
||
| it_behaves_like "doesn't allow start signal" | ||
| it_behaves_like "allows pre_execute signal" | ||
| it_behaves_like "doesn't allow execute signal" | ||
| it_behaves_like "doesn't allow poll_runner signal" | ||
| it_behaves_like "doesn't allow post_execute signal" | ||
| it_behaves_like "allows finish signal" | ||
| it_behaves_like "allows abort_job signal" | ||
| it_behaves_like "allows cancel signal" | ||
| it_behaves_like "allows error signal" | ||
| end | ||
|
|
||
| context "running" do | ||
| let(:state) { "running" } | ||
|
|
||
| it_behaves_like "doesn't allow start signal" | ||
| it_behaves_like "doesn't allow pre_execute signal" | ||
| it_behaves_like "doesn't allow execute signal" | ||
| it_behaves_like "allows poll_runner signal" | ||
| it_behaves_like "allows post_execute signal" | ||
| it_behaves_like "allows finish signal" | ||
| it_behaves_like "allows abort_job signal" | ||
| it_behaves_like "allows cancel signal" | ||
| it_behaves_like "allows error signal" | ||
| end | ||
|
|
||
| context "post_execute" do | ||
| let(:state) { "post_execute" } | ||
|
|
||
| it_behaves_like "doesn't allow start signal" | ||
| it_behaves_like "doesn't allow pre_execute signal" | ||
| it_behaves_like "doesn't allow execute signal" | ||
| it_behaves_like "doesn't allow poll_runner signal" | ||
| it_behaves_like "doesn't allow post_execute signal" | ||
| it_behaves_like "allows finish signal" | ||
| it_behaves_like "allows abort_job signal" | ||
| it_behaves_like "allows cancel signal" | ||
| it_behaves_like "allows error signal" | ||
| end | ||
|
|
||
| context "finished" do | ||
| let(:state) { "finished" } | ||
|
|
||
| it_behaves_like "doesn't allow start signal" | ||
| it_behaves_like "doesn't allow pre_execute signal" | ||
| it_behaves_like "doesn't allow execute signal" | ||
| it_behaves_like "doesn't allow poll_runner signal" | ||
| it_behaves_like "doesn't allow post_execute signal" | ||
| it_behaves_like "allows finish signal" | ||
| it_behaves_like "allows abort_job signal" | ||
| it_behaves_like "allows cancel signal" | ||
| it_behaves_like "allows error signal" | ||
| end | ||
| end | ||
|
|
||
| describe "#start" do | ||
| it "moves to state pre_execute" do | ||
| job.signal(:start) | ||
| expect(job.reload.state).to eq("pre_execute") | ||
| end | ||
| end | ||
|
|
||
| describe "#poll_runner" do | ||
| let(:state) { "running" } | ||
|
|
||
| context "still running" do | ||
| before { expect(job).to receive(:running?).and_return(true) } | ||
|
|
||
| it "requeues poll_runner" do | ||
| job.signal(:poll_runner) | ||
| expect(job.reload.state).to eq("running") | ||
| end | ||
| end | ||
|
|
||
| context "completed" do | ||
| before { expect(job).to receive(:running?).and_return(false) } | ||
|
|
||
| it "moves to state finished" do | ||
| job.signal(:poll_runner) | ||
| expect(job.reload.state).to eq("finished") | ||
| end | ||
| end | ||
| end | ||
| end |
10 changes: 10 additions & 0 deletions
10
spec/models/manageiq/providers/embedded_terraform/automation_manager/template_spec.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| RSpec.describe ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Template do | ||
| let(:template) { FactoryBot.create(:terraform_template) } | ||
|
|
||
| describe "#run" do | ||
| it "creates a Job" do | ||
| job = template.run | ||
| expect(job).to be_a(ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Job) | ||
| end | ||
| end | ||
| end |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 minute seems like a long time. How long are we expecting short and long templates to take to run? Would 30 seconds be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably a good question for @putmanoj