Skip to content

Commit

Permalink
Merge pull request #75 from putmanoj/create-dialog-for-parsed-templat…
Browse files Browse the repository at this point in the history
…e-input-vars

Parse terraform template & create new service dialog with parsed input vars
  • Loading branch information
agrare committed Oct 1, 2024
2 parents 5a1a762 + 9719425 commit 6fd1b36
Show file tree
Hide file tree
Showing 8 changed files with 348 additions and 55 deletions.
85 changes: 85 additions & 0 deletions app/models/dialog/terraform_template_service_dialog.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
class Dialog
class TerraformTemplateServiceDialog
def self.create_dialog(label, terraform_template, extra_vars)
new.create_dialog(label, terraform_template, extra_vars)
end

# This dialog is to be used by a terraform template service item
def create_dialog(label, terraform_template, extra_vars)
Dialog.new(:label => label, :buttons => "submit,cancel").tap do |dialog|
tab = dialog.dialog_tabs.build(:display => "edit", :label => "Basic Information", :position => 0)
position = 0
if terraform_template.present?
add_template_variables_group(tab, position, terraform_template)
position += 1
end
if extra_vars.present?
add_variables_group(tab, position, extra_vars)
end
dialog.save!
end
end

private

def add_template_variables_group(tab, position, terraform_template)
require "json"
template_info = JSON.parse(terraform_template.payload)
input_vars = template_info["input_vars"]

return if input_vars.nil?

tab.dialog_groups.build(
:display => "edit",
:label => "Terraform Template Variables",
:position => position
).tap do |dialog_group|
input_vars.each_with_index do |(var_info), index|
key, value, required, readonly, hidden, label, description = var_info.values_at(
"name", "default", "required", "immutable", "hidden", "label", "description"
)
# TODO: use these when adding variable field
# type, secured = var_info.values_at("type", "secured")

next if hidden

add_variable_field(
key, value, dialog_group, index, label, description, required, readonly
)
end
end
end

def add_variables_group(tab, position, extra_vars)
tab.dialog_groups.build(
:display => "edit",
:label => "Extra Variables",
:position => position
).tap do |dialog_group|
extra_vars.transform_values { |val| val[:default] }.each_with_index do |(key, value), index|
add_variable_field(key, value, dialog_group, index, key, key, false, false)
end
end
end

def add_variable_field(key, value, group, position, label, description, required, read_only)
value = value.to_json if [Hash, Array].include?(value.class)
description = key if description.blank?

group.dialog_fields.build(
:type => "DialogFieldTextBox",
:name => key.to_s,
:data_type => "string",
:display => "edit",
:required => required,
:default_value => value,
:label => label,
:description => description,
:reconfigurable => true,
:position => position,
:dialog_group => group,
:read_only => read_only
)
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def sync
update!(:status => "successful", :last_updated_on => Time.zone.now, :last_update_error => nil)
rescue => error
update!(:status => "error", :last_updated_on => Time.zone.now, :last_update_error => error)
raise error
raise
end

# Return Template name, using relative_path's basename prefix,
Expand Down Expand Up @@ -75,8 +75,10 @@ def self.template_name_from_git_repo_url(git_repo_url, relative_path)
def find_templates_in_git_repo
template_dirs = {}

# checkout repo, for sending files to terraform-runner to parse for input/ouput vars.
git_checkout_tempdir = checkout_git_repo

# traverse through files in git-worktree
git_repository.update_repo
git_repository.with_worktree do |worktree|
worktree.ref = scm_branch

Expand All @@ -85,22 +87,53 @@ def find_templates_in_git_repo
.group_by { |file| File.dirname(file) }
.select { |_dir, files| files.any? { |f| f.end_with?(".tf", ".tf.json") } }
.transform_values! { |files| files.map { |f| File.basename(f) } }
.each do |parent_dir, files|
name = self.class.template_name_from_git_repo_url(git_repository.url, parent_dir)
.each do |relative_path, files|
name = self.class.template_name_from_git_repo_url(git_repository.url, relative_path)

template_full_path = File.join(git_checkout_tempdir, relative_path)

# TODO: add parsing for input/output vars
input_vars = nil
output_vars = nil
input_vars, output_vars, terraform_version = parse_vars_in_template(template_full_path)

template_dirs[name] = {
:relative_path => parent_dir,
:files => files,
:input_vars => input_vars,
:output_vars => output_vars
:relative_path => relative_path,
:files => files,
:input_vars => input_vars,
:output_vars => output_vars,
:terraform_version => terraform_version,
}
end
end

template_dirs
rescue => error
_log.error("Failing scaning for terraform templates in the git repo: #{error}")
raise
ensure
cleanup_git_repo(git_checkout_tempdir)
end

# Parse template and return input-vars, output-vars & terraform-version
def parse_vars_in_template(template_path)
response = Terraform::Runner.parse_template_variables(template_path)
return response['template_input_params'], response['template_output_params'], response['terraform_version']
end

# checkout git repo to temp dir
def checkout_git_repo
git_checkout_tempdir = Dir.mktmpdir("embedded-terraform-runner-git")

_log.debug("Checking out git repository to #{git_checkout_tempdir}...")
checkout_git_repository(git_checkout_tempdir)
git_checkout_tempdir
end

# clean temp dir
def cleanup_git_repo(git_checkout_tempdir)
return if git_checkout_tempdir.nil?

_log.debug("Cleaning up git repository checked out at #{git_checkout_tempdir}...")
FileUtils.rm_rf(git_checkout_tempdir)
rescue Errno::ENOENT
nil
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ class ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Template < Mana
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) || {}
env_vars = vars.delete(:env) || {}
credentials = vars.delete(:credentials)

self.class.module_parent::Job.create_job(self, env_vars, vars, credentials).tap(&:signal_start)
Expand Down
52 changes: 52 additions & 0 deletions app/models/service_template_terraform_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,28 @@ def self.create_catalog_item(options, _auth_user)

transaction do
create_from_options(options).tap do |service_template|
dialog_ids = service_template.send(:create_dialogs, config_info)
config_info.deep_merge!(dialog_ids)
service_template.options[:config_info] = config_info
service_template.create_resource_actions(config_info)
end
end
end

def update_catalog_item(options, auth_user = nil)
config_info = validate_update_config_info(options)
unless config_info
update!(options)
return reload
end

config_info.deep_merge!(create_dialogs(config_info))

options[:config_info] = config_info

super
end

def self.validate_config_info(info)
info[:provision][:fqname] ||= default_provisioning_entry_point(SERVICE_TYPE_ATOMIC) if info.key?(:provision)
info[:reconfigure][:fqname] ||= default_reconfiguration_entry_point if info.key?(:reconfigure)
Expand All @@ -41,4 +58,39 @@ def terraform_template(action)

ManageIQ::Providers::EmbeddedTerraform::AutomationManager::Template.find(template_id)
end

def create_dialogs(config_info)
dialog_hash = {}

info = config_info[:provision]
if info
# create new dialog, if required for :provision action
if info.key?(:new_dialog_name) && !info.key?(:dialog_id)
provision_dialog_id = create_new_dialog(info[:new_dialog_name], terraform_template(:provision), info[:extra_vars]).id
dialog_hash[:provision] = {:dialog_id => provision_dialog_id}
else
provision_dialog_id = info[:dialog_id]
end

# For :retirement & :reconfigure, we use the same dialog as in :provision action
dialog_hash = [:retirement, :reconfigure].each_with_object(dialog_hash) do |action, hash|
hash[action] = {:dialog_id => provision_dialog_id}
end
end

dialog_hash
end

private

def create_new_dialog(dialog_name, terraform_template, extra_vars)
Dialog::TerraformTemplateServiceDialog.create_dialog(dialog_name, terraform_template, extra_vars)
end

def validate_update_config_info(options)
opts = super
return unless options.key?(:config_info)

self.class.send(:validate_config_info, opts)
end
end
28 changes: 28 additions & 0 deletions lib/terraform/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ def fetch_result_by_stack_id(stack_id)
retrieve_stack_job(stack_id)
end

# Parse Terraform Template input/output variables
# @param template_path [String] Path to the template we will want to parse for input/output variables
# @return Response(body) object of terraform-runner api/template/variables,
# - the response object had template_input_params, template_output_params and terraform_version
def parse_template_variables(template_path)
template_variables(template_path)
end

# =================================================
# TerraformRunner Stack-API interaction methods
# =================================================
Expand Down Expand Up @@ -179,6 +187,26 @@ def encoded_zip_from_directory(template_path)
end
end

# Parse Variables in Terraform Template
def template_variables(
template_path
)
_log.debug("prase template: #{template_path}")
encoded_zip_file = encoded_zip_from_directory(template_path)

payload = {
:templateZipFile => encoded_zip_file,
}

http_response = terraform_runner_client.post(
"api/template/variables",
*json_post_arguments(payload)
)

_log.debug("==== http_response.body: \n #{http_response.body}")
JSON.parse(http_response.body)
end

def jwt_token
require "jwt"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"template_input_params": [
{
"name": "name",
"label": "name",
"type": "string",
"description": "",
"required": true,
"secured": false,
"hidden": false,
"immutable": false,
"default": "World"
}
],
"template_output_params": [
{
"name": "greeting",
"label": "greeting",
"description": "",
"secured": false,
"hidden": false
}
],
"terraform_version": ">= 1.1.0"
}
Loading

0 comments on commit 6fd1b36

Please sign in to comment.