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

Create temporary inventory when execute a playbook #14008

Merged
merged 2 commits into from
Feb 23, 2017
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
64 changes: 55 additions & 9 deletions app/models/service_ansible_playbook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand All @@ -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
2 changes: 1 addition & 1 deletion app/models/service_template_ansible_playbook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Copy link
Member

Choose a reason for hiding this comment

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

The inventory name should probably be a constant on the manager. It can then be reused by the setup scripting that we discussed with @gtanzillo @carbonin and @gmcculloug.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure where should the constant be replaced. We can refactor once the solution is finalized.

Copy link
Member

Choose a reason for hiding this comment

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

@bzwei Agreed, please open a pivotal story to track this work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

:ask_variables_on_launch => true,
:ask_limit_on_launch => true,
:ask_inventory_on_launch => true,
Expand Down
96 changes: 79 additions & 17 deletions spec/models/service_ansible_playbook_spec.rb
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -12,40 +15,92 @@
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)
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this build the relation rather than stubbing it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We cannot stub method for tower_job later in the tests if build a relation here.

Copy link
Member

Choose a reason for hiding this comment

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

Then it sounds like it shouldn't be shared, but don't fix in this PR.

end
end

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

Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion spec/models/service_template_ansible_playbook_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down