From 0dd941a8b17d3b5971ab053f38a2288d8901d9d4 Mon Sep 17 00:00:00 2001 From: Bill Wei Date: Tue, 21 Feb 2017 17:55:56 -0500 Subject: [PATCH 1/2] Select Demo Inventory as the default --- app/models/service_template_ansible_playbook.rb | 2 +- spec/models/service_template_ansible_playbook_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/service_template_ansible_playbook.rb b/app/models/service_template_ansible_playbook.rb index 06f47070b31..37f0468d037 100644 --- a/app/models/service_template_ansible_playbook.rb +++ b/app/models/service_template_ansible_playbook.rb @@ -89,7 +89,7 @@ def self.build_parameter_list(name, description, info) :description => description || '', :project => playbook.configuration_script_source.manager_ref, :playbook => playbook.name, - :inventory => tower.inventory_root_groups.first.ems_ref, + :inventory => tower.inventory_root_groups.find_by!(:name => 'Demo Inventory').ems_ref, :ask_variables_on_launch => true, :ask_limit_on_launch => true, :ask_inventory_on_launch => true, diff --git a/spec/models/service_template_ansible_playbook_spec.rb b/spec/models/service_template_ansible_playbook_spec.rb index 89ad90115c8..5829b7bf596 100644 --- a/spec/models/service_template_ansible_playbook_spec.rb +++ b/spec/models/service_template_ansible_playbook_spec.rb @@ -6,7 +6,7 @@ let(:config_script) { FactoryGirl.create(:configuration_script) } let(:script_source) { FactoryGirl.create(:configuration_script_source, :manager => ems) } - let(:inventory_root_group) { FactoryGirl.create(:inventory_root_group) } + let(:inventory_root_group) { FactoryGirl.create(:inventory_root_group, :name => 'Demo Inventory') } let(:service_template_catalog) { FactoryGirl.create(:service_template_catalog) } let(:ems) do FactoryGirl.create(:automation_manager_ansible_tower, :inventory_root_groups => [inventory_root_group]) From c76637f2859200027b70873a1cd421a36089386b Mon Sep 17 00:00:00 2001 From: Bill Wei Date: Tue, 21 Feb 2017 17:56:38 -0500 Subject: [PATCH 2/2] Create temporary inventory when execute a playbook --- app/models/service_ansible_playbook.rb | 64 +++++++++++-- spec/models/service_ansible_playbook_spec.rb | 96 ++++++++++++++++---- 2 files changed, 134 insertions(+), 26 deletions(-) diff --git a/app/models/service_ansible_playbook.rb b/app/models/service_ansible_playbook.rb index a931fc615c4..349c3b33e74 100644 --- a/app/models/service_ansible_playbook.rb +++ b/app/models/service_ansible_playbook.rb @@ -3,6 +3,7 @@ class ServiceAnsiblePlaybook < ServiceGeneric # A chance for taking options from automate script to override options from a service dialog def preprocess(action, add_options = {}) + _log.info("Override with new options: #{add_options}") unless add_options.blank? save_job_options(action, add_options) end @@ -38,30 +39,46 @@ def job(action) service_resources.find_by!(:name => action, :resource_type => 'OrchestrationStack').try(:resource) end + def postprocess(action) + hosts = options.fetch_path(job_option_key(action), :inventory) + delete_inventory(action) unless use_default_inventory?(hosts) + end + private def get_job_options(action) - job_opts = options["#{action.downcase}_job_options".to_sym].deep_dup - credential_id = job_opts.delete(:credential_id) - job_opts[:credential] = Authentication.find(credential_id).manager_ref unless credential_id.blank? + job_opts = options[job_option_key(action)].deep_dup + # TODO: decryption may be needed job_opts end def save_job_options(action, overrides) - job_options = parse_dialog_options - job_options[:extra_vars] = (job_options[:extra_vars] || {}).merge(overrides[:extra_vars]) if overrides[:extra_vars] - job_options.merge!(overrides.except(:extra_vars)) + job_options = options.fetch_path(:config_info, action.downcase.to_sym).slice(:hosts, :extra_vars) + job_options.deep_merge!(parse_dialog_options) + job_options.deep_merge!(overrides) + + credential_id = job_options.delete(:credential_id) + job_options[:credential] = Authentication.find(credential_id).manager_ref unless credential_id.blank? - options["#{action.downcase}_job_options".to_sym] = job_options + hosts = job_options.delete(:hosts) + job_options[:inventory] = create_inventory_with_hosts(action, hosts).id unless use_default_inventory?(hosts) + + # TODO: encryption my be needed + options[job_option_key(action)] = job_options save! end + def job_option_key(action) + "#{action.downcase}_job_options".to_sym + end + def parse_dialog_options dialog_options = options[:dialog] || {} + { - :credential_id => dialog_options['dialog_credential_id'], - :hosts => dialog_options['dialog_hosts'] + :credential_id => dialog_options['dialog_credential'], + :hosts => dialog_options['dialog_hosts'].to_s.strip.presence }.compact.merge(extra_vars_from_dialog) end @@ -73,4 +90,33 @@ def extra_vars_from_dialog params.blank? ? {} : {:extra_vars => params} end + + def create_inventory_with_hosts(action, hosts) + manager(action).with_provider_connection do |connection| + connection.api.inventories.create!(:name => inventory_name(action), :organization => 1).tap do |inventory| + hosts.split(',').each do |host| + connection.api.hosts.create!(:name => host, :inventory => inventory.id) + end + end + end + end + + def delete_inventory(action) + manager(action).with_provider_connection do |connection| + inventory_id = options.fetch_path(job_option_key(action), :inventory) + connection.api.inventories.find(inventory_id).destroy! + end + end + + def manager(action) + job_template(action).manager + end + + def inventory_name(action) + "#{job_template(action).name}_#{id}" + end + + def use_default_inventory?(hosts) + hosts.blank? || hosts == 'localhost' + end end diff --git a/spec/models/service_ansible_playbook_spec.rb b/spec/models/service_ansible_playbook_spec.rb index 7bf15baa3f4..ff51419e6d2 100644 --- a/spec/models/service_ansible_playbook_spec.rb +++ b/spec/models/service_ansible_playbook_spec.rb @@ -1,8 +1,11 @@ describe(ServiceAnsiblePlaybook) do let(:tower_job) { FactoryGirl.create(:ansible_tower_job) } let(:tower_job_temp) { FactoryGirl.create(:ansible_configuration_script) } - let(:basic_service) { FactoryGirl.create(:service_ansible_playbook, :options => dialog_options) } + let(:basic_service) { FactoryGirl.create(:service_ansible_playbook, :options => config_info_options) } + let(:service) { FactoryGirl.create(:service_ansible_playbook, :options => config_info_options.merge(dialog_options)) } let(:action) { ResourceAction::PROVISION } + let(:credential_1) { FactoryGirl.create(:authentication, :manager_ref => 'a') } + let(:credential_2) { FactoryGirl.create(:authentication, :manager_ref => 'b') } let(:loaded_service) do service_template = FactoryGirl.create(:service_template_ansible_playbook) @@ -12,7 +15,7 @@ end let(:executed_service) do - basic_service.tap do |service| + FactoryGirl.create(:service_ansible_playbook, :options => provision_options).tap do |service| allow(service).to receive(:job).with(action).and_return(tower_job) end end @@ -20,32 +23,84 @@ let(:dialog_options) do { :dialog => { - 'dialog_hosts' => 'host1,host2', - 'dialog_credential_id' => 1, - 'dialog_param_var1' => 'value1', - 'dialog_param_var2' => 'value2' + 'dialog_hosts' => 'host1,host2', + 'dialog_credential' => credential_1.id, + 'dialog_param_var1' => 'value1', + 'dialog_param_var2' => 'value2' + } + } + end + + let(:config_info_options) do + { + :config_info => { + :provision => { + :hosts => "default_host1,default_host2", + :extra_vars => { + "var1" => "default_val1", + "var2" => "default_val2", + "var3" => "default_val3" + }, + } } } end let(:override_options) do { - :hosts => 'host3', - :extra_vars => { 'var1' => 'new_val1' } + :credential_id => credential_2.id, + :hosts => 'host3', + :extra_vars => { 'var1' => 'new_val1' } } end - let(:provision_options) { {:provision_job_options => override_options} } + let(:provision_options) do + { + :provision_job_options => { + :credential => 1, + :inventory => 2, + :extra_vars => {'var1' => 'value1', 'var2' => 'value2'} + } + } + end describe '#preprocess' do - it 'prepares options for action' do - basic_service.preprocess(action, override_options) - basic_service.reload - expect(basic_service.options[:provision_job_options]).to have_attributes( - :hosts => 'host3', - :credential_id => 1, - :extra_vars => {'var1' => 'new_val1', 'var2' => 'value2'} - ) + context 'basic service' do + it 'prepares job options from service template' do + hosts = config_info_options.fetch_path(:config_info, :provision, :hosts) + expect(basic_service).to receive(:create_inventory_with_hosts).with(action, hosts).and_return(double(:id => 10)) + basic_service.preprocess(action) + service.reload + expect(basic_service.options[:provision_job_options]).to have_attributes(:inventory => 10) + end + end + + context 'with dialog overrides' do + it 'prepares job options combines from service template and dialog' do + hosts = dialog_options[:dialog]['dialog_hosts'] + expect(service).to receive(:create_inventory_with_hosts).with(action, hosts).and_return(double(:id => 20)) + service.preprocess(action) + service.reload + expect(service.options[:provision_job_options]).to have_attributes( + :inventory => 20, + :credential => credential_1.manager_ref, + :extra_vars => {'var1' => 'value1', 'var2' => 'value2', 'var3' => 'default_val3'} + ) + end + end + + context 'with runtime overrides' do + it 'prepares job options combined from service template, dialog, and overrides' do + hosts = override_options[:hosts] + expect(service).to receive(:create_inventory_with_hosts).with(action, hosts).and_return(double(:id => 30)) + service.preprocess(action, override_options) + service.reload + expect(service.options[:provision_job_options]).to have_attributes( + :inventory => 30, + :credential => credential_2.manager_ref, + :extra_vars => {'var1' => 'new_val1', 'var2' => 'value2', 'var3' => 'default_val3'} + ) + end end end @@ -90,4 +145,11 @@ describe '#check_refreshed' do it { expect(executed_service.check_refreshed(action)).to eq([true, nil]) } end + + describe '#postprocess' do + it 'deletes inventory' do + expect(executed_service).to receive(:delete_inventory) + executed_service.postprocess(action) + end + end end