From eb6be29c20dc4dfe4bd061ba6fbc0d296faf973f Mon Sep 17 00:00:00 2001 From: James Wong Date: Thu, 16 Mar 2017 18:04:33 -0400 Subject: [PATCH] Share specs between EmbeddedAnsible and AnsibleTower --- .../configuration_script_source_spec.rb | 119 +---------- .../configuration_script_spec.rb | 176 +--------------- .../automation_manager/credential_spec.rb | 116 +--------- .../event_catcher/stream_spec.rb | 71 +------ .../automation_manager/job/status_spec.rb | 42 +--- .../automation_manager/job_spec.rb | 156 +------------- .../automation_manager/refresher_spec.rb | 198 +----------------- .../automation_manager/refresher_v2_spec.rb | 155 +------------- .../ansible_tower/automation_manager_spec.rb | 12 +- .../providers/ansible_tower/provider_spec.rb | 68 +----- .../configuration_script_source_spec.rb | 5 + .../configuration_script_spec.rb | 161 +------------- .../automation_manager/credential_spec.rb | 5 + .../event_catcher/stream_spec.rb | 6 + .../automation_manager/job/status_spec.rb | 42 +--- .../automation_manager/job_spec.rb | 155 +------------- .../automation_manager/refresher_spec.rb | 184 +--------------- .../automation_manager/refresher_v2_spec.rb | 155 +------------- .../automation_manager_spec.rb | 12 +- .../embedded_ansible/provider_spec.rb | 68 +----- .../ansible_shared/automation_manager.rb | 11 + .../configuration_script.rb | 176 ++++++++++++++++ .../configuration_script_source.rb | 120 +++++++++++ .../automation_manager/credential.rb | 117 +++++++++++ .../event_catcher/stream.rb | 69 ++++++ .../ansible_shared/automation_manager/job.rb | 156 ++++++++++++++ .../automation_manager/job/status.rb | 42 ++++ .../automation_manager/refresher.rb | 194 +++++++++++++++++ .../automation_manager/refresher_v2.rb | 151 +++++++++++++ spec/support/ansible_shared/provider.rb | 70 +++++++ 30 files changed, 1186 insertions(+), 1826 deletions(-) create mode 100644 spec/models/manageiq/providers/embedded_ansible/automation_manager/configuration_script_source_spec.rb create mode 100644 spec/models/manageiq/providers/embedded_ansible/automation_manager/credential_spec.rb create mode 100644 spec/models/manageiq/providers/embedded_ansible/automation_manager/event_catcher/stream_spec.rb create mode 100644 spec/support/ansible_shared/automation_manager.rb create mode 100644 spec/support/ansible_shared/automation_manager/configuration_script.rb create mode 100644 spec/support/ansible_shared/automation_manager/configuration_script_source.rb create mode 100644 spec/support/ansible_shared/automation_manager/credential.rb create mode 100644 spec/support/ansible_shared/automation_manager/event_catcher/stream.rb create mode 100644 spec/support/ansible_shared/automation_manager/job.rb create mode 100644 spec/support/ansible_shared/automation_manager/job/status.rb create mode 100644 spec/support/ansible_shared/automation_manager/refresher.rb create mode 100644 spec/support/ansible_shared/automation_manager/refresher_v2.rb create mode 100644 spec/support/ansible_shared/provider.rb diff --git a/spec/models/manageiq/providers/ansible_tower/automation_manager/configuration_script_source_spec.rb b/spec/models/manageiq/providers/ansible_tower/automation_manager/configuration_script_source_spec.rb index 3e0848d3551..18caad8912d 100644 --- a/spec/models/manageiq/providers/ansible_tower/automation_manager/configuration_script_source_spec.rb +++ b/spec/models/manageiq/providers/ansible_tower/automation_manager/configuration_script_source_spec.rb @@ -1,120 +1,5 @@ -require 'ansible_tower_client' +require 'support/ansible_shared/automation_manager/configuration_script_source' describe ManageIQ::Providers::AnsibleTower::AutomationManager::ConfigurationScriptSource do - let(:finished_task) { FactoryGirl.create(:miq_task, :state => "Finished") } - let(:manager) { FactoryGirl.create(:provider_ansible_tower, :with_authentication).managers.first } - let(:atc) { double("AnsibleTowerClient::Connection", :api => api) } - let(:api) { double("AnsibleTowerClient::Api", :projects => projects) } - - context "create through API" do - let(:projects) { double("AnsibleTowerClient::Collection", :create! => project) } - let(:project) { AnsibleTowerClient::Project.new(nil, project_json) } - - let(:project_json) do - params.merge( - :id => 10, - "scm_type" => "git", - "scm_url" => "https://github.com/ansible/ansible-tower-samples" - ).stringify_keys.to_json - end - - let(:params) do - { - :description => "Description", - :name => "My Project", - :related => {} - } - end - - it ".create_in_provider" do - expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) - store_new_project(project, manager) - expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) - expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) - - expect(described_class.create_in_provider(manager.id, params)).to be_a(described_class) - end - - it "not found during refresh" do - expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) - expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) - expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) - - expect { described_class.create_in_provider(manager.id, params) }.to raise_error(ActiveRecord::RecordNotFound) - end - - it ".create_in_provider_queue" do - EvmSpecHelper.local_miq_server - task_id = described_class.create_in_provider_queue(manager.id, params) - expect(MiqTask.find(task_id)).to have_attributes(:name => "Creating #{described_class.name} with name=#{params[:name]}") - expect(MiqQueue.first).to have_attributes( - :args => [manager.id, params], - :class_name => described_class.name, - :method_name => "create_in_provider", - :priority => MiqQueue::HIGH_PRIORITY, - :role => "ems_operations", - :zone => manager.my_zone - ) - end - - def store_new_project(project, manager) - described_class.create!( - :manager => manager, - :manager_ref => project.id.to_s, - :name => project.name, - ) - end - end - - context "Delete through API" do - let(:projects) { double("AnsibleTowerClient::Collection", :find => tower_project) } - let(:tower_project) { double("AnsibleTowerClient::Project", :destroy! => nil, :id => 1) } - let(:project) { described_class.create!(:manager => manager, :manager_ref => tower_project.id) } - - it "#delete_in_provider" do - expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) - expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) - project.delete_in_provider - end - - it "#delete_in_provider_queue" do - task_id = project.delete_in_provider_queue - expect(MiqTask.find(task_id)).to have_attributes(:name => "Deleting #{described_class.name} with manager_ref=#{project.manager_ref}") - expect(MiqQueue.first).to have_attributes( - :instance_id => project.id, - :args => [], - :class_name => described_class.name, - :method_name => "delete_in_provider", - :priority => MiqQueue::HIGH_PRIORITY, - :role => "ems_operations", - :zone => manager.my_zone - ) - end - end - - context "Update through API" do - let(:projects) { double("AnsibleTowerClient::Collection", :find => tower_project) } - let(:tower_project) { double("AnsibleTowerClient::Project", :update_attributes! => {}, :id => 1) } - let(:project) { described_class.create!(:manager => manager, :manager_ref => tower_project.id) } - - it "#update_in_provider" do - expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) - expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) - expect(project.update_in_provider({})).to be_a(described_class) - end - - it "#update_in_provider_queue" do - task_id = project.update_in_provider_queue({}) - expect(MiqTask.find(task_id)).to have_attributes(:name => "Updating #{described_class.name} with manager_ref=#{project.manager_ref}") - expect(MiqQueue.first).to have_attributes( - :instance_id => project.id, - :args => [{:task_id => task_id}], - :class_name => described_class.name, - :method_name => "update_in_provider", - :priority => MiqQueue::HIGH_PRIORITY, - :role => "ems_operations", - :zone => manager.my_zone - ) - end - end + it_behaves_like 'ansible configuration_script_source' end diff --git a/spec/models/manageiq/providers/ansible_tower/automation_manager/configuration_script_spec.rb b/spec/models/manageiq/providers/ansible_tower/automation_manager/configuration_script_spec.rb index c1d5511875b..c8590298aa6 100644 --- a/spec/models/manageiq/providers/ansible_tower/automation_manager/configuration_script_spec.rb +++ b/spec/models/manageiq/providers/ansible_tower/automation_manager/configuration_script_spec.rb @@ -1,175 +1,5 @@ -require 'ansible_tower_client' -require 'faraday' -describe ManageIQ::Providers::AnsibleTower::AutomationManager::ConfigurationScript do - let(:api) { double(:api, :job_templates => double(:job_templates)) } - let(:connection) { double(:connection, :api => api) } - let(:job) { AnsibleTowerClient::Job.new(connection.api, "id" => 1) } - let(:job_template) { AnsibleTowerClient::JobTemplate.new(connection.api, "limit" => "", "id" => 1, "url" => "api/job_templates/1/", "name" => "template", "description" => "description", "extra_vars" => {:instance_ids => ['i-3434']}) } - let(:manager) { FactoryGirl.create(:automation_manager_ansible_tower, :provider, :configuration_script) } - - it "belongs_to the Ansible Tower manager" do - expect(manager.configuration_scripts.size).to eq 1 - expect(manager.configuration_scripts.first.variables).to eq :instance_ids => ['i-3434'] - expect(manager.configuration_scripts.first).to be_a ConfigurationScript - end - - context "relates to playbook" do - let(:configuration_script_source) { FactoryGirl.create(:configuration_script_source, :manager => manager) } - let!(:payload) { FactoryGirl.create(:configuration_script_payload) } - let(:configuration_scripts_without_payload) { FactoryGirl.create(:configuration_script) } - let(:configuration_scripts) do - [FactoryGirl.create(:configuration_script, :parent => payload), - FactoryGirl.create(:configuration_script, :parent => payload)] - end - - it "can refer to a payload" do - expect(configuration_scripts[0].parent).to eq(payload) - expect(configuration_scripts[1].parent).to eq(payload) - expect(payload.children).to match_array(configuration_scripts) - end - - it "can be without payload" do - expect(configuration_scripts_without_payload.parent).to be_nil - end - end - - context "#run" do - before do - allow_any_instance_of(Provider).to receive_messages(:connect => connection) - allow(api.job_templates).to receive(:find) { job_template } - end - - it "launches the referenced ansible job template" do - expect(job_template).to receive(:launch).with(:extra_vars => "{\"instance_ids\":[\"i-3434\"]}").and_return(job) - expect(manager.configuration_scripts.first.run).to be_a AnsibleTowerClient::Job - end - - it "accepts different variables to launch a job template against" do - added_extras = {:extra_vars => {:some_key => :some_value}} - expect(job_template).to receive(:launch).with(:extra_vars=>"{\"instance_ids\":[\"i-3434\"],\"some_key\":\"some_value\"}").and_return(job) - expect(manager.configuration_scripts.first.run(added_extras)).to be_a AnsibleTowerClient::Job - end - end - - context "#merge_extra_vars" do - it "merges internal and external hashes to send out to the tower gem" do - config_script = manager.configuration_scripts.first - external = {:some_key => :some_value} - internal = config_script.variables - expect(internal).to be_a Hash - expect(config_script.merge_extra_vars(external)).to eq(:extra_vars => "{\"instance_ids\":[\"i-3434\"],\"some_key\":\"some_value\"}") - end - - it "merges an internal hash and an empty hash to send out to the tower gem" do - config_script = manager.configuration_scripts.first - external = nil - expect(config_script.merge_extra_vars(external)).to eq(:extra_vars => "{\"instance_ids\":[\"i-3434\"]}") - end - - it "merges an empty internal hash and a hash to send out to the tower gem" do - external = {:some_key => :some_value} - internal = {} - config_script = manager.configuration_scripts.first - config_script.variables = internal - expect(config_script.merge_extra_vars(external)).to eq(:extra_vars => "{\"some_key\":\"some_value\"}") - end - - it "merges all empty arguments to send out to the tower gem" do - external = nil - internal = {} - config_script = manager.configuration_scripts.first - config_script.variables = internal - expect(config_script.merge_extra_vars(external)).to eq(:extra_vars => "{}") - end - end - - context "creates via the API" do - let(:provider) { FactoryGirl.create(:provider_ansible_tower, :with_authentication) } - let(:manager) { provider.managers.first } - let(:atc) { double("AnsibleTowerClient::Connection", :api => api) } - let(:api) { double("AnsibleTowerClient::Api", :job_templates => job_templates) } - let(:job_templates) { double("AnsibleTowerClient::Collection", :create! => job_template) } - let(:job_template) { AnsibleTowerClient::JobTemplate.new(nil, job_template_json) } +require 'support/ansible_shared/automation_manager/configuration_script' - let(:job_template_json) do - params.merge( - :id => 10, - :inventory => 1, - :related => {"inventory" => "blah/1"} - ).except(:inventory_id).stringify_keys.to_json - end - - let(:params) do - { - :description => "Description", - :extra_vars => {}.to_json, - :inventory_id => 1, - :name => "My Job Template", - :related => {} - } - end - - context ".create_in_provider" do - let(:finished_task) { FactoryGirl.create(:miq_task, :state => "Finished") } - - it "successfully created in provider" do - expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) - - store_new_job_template(job_template, manager) - - expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) - expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) - - expect(described_class.create_in_provider(manager.id, params)).to be_a(described_class) - end - - it "not found during refresh" do - expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) - expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) - expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) - - expect { described_class.create_in_provider(manager.id, params) }.to raise_error(ActiveRecord::RecordNotFound) - end - end - - it ".create_in_provider_queue" do - EvmSpecHelper.local_miq_server - task_id = described_class.create_in_provider_queue(manager.id, params) - expect(MiqTask.find(task_id)).to have_attributes(:name => "Creating Ansible Tower Job Template") - expect(MiqQueue.first).to have_attributes( - :args => [manager.id, params], - :class_name => "ManageIQ::Providers::AnsibleTower::AutomationManager::ConfigurationScript", - :method_name => "create_in_provider", - :priority => MiqQueue::HIGH_PRIORITY, - :role => "ems_operations", - :zone => manager.zone.name - ) - end - - it ".update_in_provider_queue" do - EvmSpecHelper.local_miq_server - params[:manager_ref] = 10 - task_id = described_class.update_in_provider_queue(manager.id, params) - expect(MiqTask.find(task_id)).to have_attributes(:name => "Updating Ansible Tower Job Template") - expect(MiqQueue.first).to have_attributes( - :args => [manager.id, params], - :class_name => "ManageIQ::Providers::AnsibleTower::AutomationManager::ConfigurationScript", - :method_name => "update_in_provider", - :priority => MiqQueue::HIGH_PRIORITY, - :role => "ems_operations", - :zone => manager.zone.name - ) - end - - def store_new_job_template(job_template, manager) - described_class.create!( - :manager => manager, - :manager_ref => job_template.id.to_s, - :name => job_template.name, - :survey_spec => job_template.survey_spec_hash, - :variables => job_template.extra_vars_hash, - ) - end - - end +describe ManageIQ::Providers::AnsibleTower::AutomationManager::ConfigurationScript do + it_behaves_like 'ansible configuration_script' end diff --git a/spec/models/manageiq/providers/ansible_tower/automation_manager/credential_spec.rb b/spec/models/manageiq/providers/ansible_tower/automation_manager/credential_spec.rb index 0dee8a5de6f..ac67cdd2b42 100644 --- a/spec/models/manageiq/providers/ansible_tower/automation_manager/credential_spec.rb +++ b/spec/models/manageiq/providers/ansible_tower/automation_manager/credential_spec.rb @@ -1,117 +1,5 @@ -require 'ansible_tower_client' +require 'support/ansible_shared/automation_manager/credential' describe ManageIQ::Providers::AnsibleTower::AutomationManager::Credential do - let(:finished_task) { FactoryGirl.create(:miq_task, :state => "Finished") } - let(:manager) { FactoryGirl.create(:provider_ansible_tower, :with_authentication).managers.first } - let(:atc) { double("AnsibleTowerClient::Connection", :api => api) } - let(:api) { double("AnsibleTowerClient::Api", :credentials => credentials) } - - context "Create through API" do - let(:credentials) { double("AnsibleTowerClient::Collection", :create! => credential) } - let(:credential) { AnsibleTowerClient::Credential.new(nil, credential_json) } - - let(:credential_json) do - params.merge( - :id => 10, - ).stringify_keys.to_json - end - - let(:params) do - { - :description => "Description", - :name => "My Credential", - :related => {} - } - end - - it ".create_in_provider" do - expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) - store_new_credential(credential, manager) - expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) - expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) - - expect(described_class.create_in_provider(manager.id, params)).to be_a(described_class) - end - - it "not found during refresh" do - expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) - expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) - expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) - - expect { described_class.create_in_provider(manager.id, params) }.to raise_error(ActiveRecord::RecordNotFound) - end - - it ".create_in_provider_queue" do - task_id = described_class.create_in_provider_queue(manager.id, params) - expect(MiqTask.find(task_id)).to have_attributes(:name => "Creating #{described_class.name} with name=#{params[:name]}") - expect(MiqQueue.first).to have_attributes( - :args => [manager.id, params], - :class_name => described_class.name, - :method_name => "create_in_provider", - :priority => MiqQueue::HIGH_PRIORITY, - :role => "ems_operations", - :zone => manager.my_zone - ) - end - - def store_new_credential(credential, manager) - described_class.create!( - :resource => manager, - :manager_ref => credential.id.to_s, - :name => credential.name, - ) - end - end - - context "Delete through API" do - let(:credentials) { double("AnsibleTowerClient::Collection", :find => credential) } - let(:credential) { double("AnsibleTowerClient::Credential", :destroy! => nil, :id => 1) } - let(:ansible_cred) { described_class.create!(:resource => manager, :manager_ref => credential.id) } - - it "#delete_in_provider" do - expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) - expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) - ansible_cred.delete_in_provider - end - - it "#delete_in_provider_queue" do - task_id = ansible_cred.delete_in_provider_queue - expect(MiqTask.find(task_id)).to have_attributes(:name => "Deleting #{described_class.name} with manager_ref=#{ansible_cred.manager_ref}") - expect(MiqQueue.first).to have_attributes( - :instance_id => ansible_cred.id, - :args => [], - :class_name => described_class.name, - :method_name => "delete_in_provider", - :priority => MiqQueue::HIGH_PRIORITY, - :role => "ems_operations", - :zone => manager.my_zone - ) - end - end - - context "Update through API" do - let(:credentials) { double("AnsibleTowerClient::Collection", :find => credential) } - let(:credential) { double("AnsibleTowerClient::Credential", :update_attributes! => {}, :id => 1) } - let(:ansible_cred) { described_class.create!(:resource => manager, :manager_ref => credential.id) } - - it "#update_in_provider" do - expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) - expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) - expect(ansible_cred.update_in_provider({})).to be_a(described_class) - end - - it "#update_in_provider_queue" do - task_id = ansible_cred.update_in_provider_queue({}) - expect(MiqTask.find(task_id)).to have_attributes(:name => "Updating #{described_class.name} with manager_ref=#{ansible_cred.manager_ref}") - expect(MiqQueue.first).to have_attributes( - :instance_id => ansible_cred.id, - :args => [{:task_id => task_id}], - :class_name => described_class.name, - :method_name => "update_in_provider", - :priority => MiqQueue::HIGH_PRIORITY, - :role => "ems_operations", - :zone => manager.my_zone - ) - end - end + it_behaves_like 'ansible credential' end diff --git a/spec/models/manageiq/providers/ansible_tower/automation_manager/event_catcher/stream_spec.rb b/spec/models/manageiq/providers/ansible_tower/automation_manager/event_catcher/stream_spec.rb index f08de41e3ce..560c3e2cc11 100644 --- a/spec/models/manageiq/providers/ansible_tower/automation_manager/event_catcher/stream_spec.rb +++ b/spec/models/manageiq/providers/ansible_tower/automation_manager/event_catcher/stream_spec.rb @@ -1,69 +1,6 @@ -describe ManageIQ::Providers::AnsibleTower::AutomationManager::EventCatcher::Stream do - let(:tower_url) { ENV['TOWER_URL'] || "https://dev-ansible-tower3.example.com/api/v1/" } - let(:auth_userid) { ENV['TOWER_USER'] || 'testuser' } - let(:auth_password) { ENV['TOWER_PASSWORD'] || 'secret' } - - let(:auth) { FactoryGirl.create(:authentication, :userid => auth_userid, :password => auth_password) } - let(:automation_manager) { provider.automation_manager } - let(:provider) do - FactoryGirl.create(:provider_ansible_tower, - :url => tower_url, - :verify_ssl => false,).tap { |provider| provider.authentications << auth } - end - - subject do - described_class.new(automation_manager) - end +require 'support/ansible_shared/automation_manager/event_catcher/stream' - context "#poll" do - it "yields valid events" do - subject.instance_variable_set(:@last_activity, OpenStruct.new(:timestamp => '2016-01-01 01:00:00')) - subject.instance_variable_set(:@poll_sleep, 0) - expected_event = { - "id" => 1, - "type" => "activity_stream", - "url" => "/api/v1/activity_stream/1/", - "related" => { - "user" => [ - "/api/v1/users/1/" - ] - }, - "summary_fields" => { - "user" => [ - { - "username" => "admin", - "first_name" => "", - "last_name" => "", - "id" => 1 - } - ] - }, - "timestamp" => "2016-08-02T17:56:37.212874Z", - "operation" => "create", - "changes" => { - "username" => "admin", - "first_name" => "", - "last_name" => "", - "is_active" => true, - "id" => 1, - "is_superuser" => true, - "is_staff" => true, - "password" => "hidden", - "email" => "admin@example.com", - "date_joined" => "2016-08-02 17:56:37.162225+00:00" - }, - "object1" => "user", - "object2" => "", - "object_association" => "" - } - - VCR.use_cassette(described_class.name.underscore.to_s) do - subject.poll do |event| - polled_event = event - expect(polled_event.to_h).to eq(expected_event) - subject.stop - end - end - end - end +describe ManageIQ::Providers::AnsibleTower::AutomationManager::EventCatcher::Stream do + it_behaves_like 'ansible event_catcher stream', + described_class.name.underscore.to_s end diff --git a/spec/models/manageiq/providers/ansible_tower/automation_manager/job/status_spec.rb b/spec/models/manageiq/providers/ansible_tower/automation_manager/job/status_spec.rb index da6137d9de3..15dccc9c1ff 100644 --- a/spec/models/manageiq/providers/ansible_tower/automation_manager/job/status_spec.rb +++ b/spec/models/manageiq/providers/ansible_tower/automation_manager/job/status_spec.rb @@ -1,41 +1,5 @@ -describe ManageIQ::Providers::AnsibleTower::AutomationManager::Job::Status do - it 'parses Succeeded' do - status = described_class.new('Successful', '') - expect(status.completed?).to be_truthy - expect(status.succeeded?).to be_truthy - expect(status.failed?).to be_falsey - expect(status.deleted?).to be_falsey - expect(status.rolled_back?).to be_falsey - expect(status.normalized_status).to eq(['create_complete', '']) - end - - it 'parses Failed' do - status = described_class.new('Failed', nil) - expect(status.completed?).to be_truthy - expect(status.succeeded?).to be_falsey - expect(status.failed?).to be_truthy - expect(status.deleted?).to be_falsey - expect(status.rolled_back?).to be_falsey - expect(status.normalized_status).to eq(['failed', 'Job launching failed']) - end +require 'support/ansible_shared/automation_manager/job/status' - it 'parses Canceled' do - status = described_class.new('Canceled', nil) - expect(status.completed?).to be_truthy - expect(status.succeeded?).to be_falsey - expect(status.canceled?).to be_truthy - expect(status.deleted?).to be_falsey - expect(status.rolled_back?).to be_falsey - expect(status.normalized_status).to eq(['create_canceled', 'Job launching was canceled']) - end - - it 'parses transient status' do - status = described_class.new('Running', nil) - expect(status.completed?).to be_falsey - expect(status.succeeded?).to be_falsey - expect(status.failed?).to be_falsey - expect(status.deleted?).to be_falsey - expect(status.rolled_back?).to be_falsey - expect(status.normalized_status).to eq(%w(transient Running)) - end +describe ManageIQ::Providers::AnsibleTower::AutomationManager::Job::Status do + it_behaves_like 'ansible job status' end diff --git a/spec/models/manageiq/providers/ansible_tower/automation_manager/job_spec.rb b/spec/models/manageiq/providers/ansible_tower/automation_manager/job_spec.rb index c6ebec36790..03fd8f26fb5 100644 --- a/spec/models/manageiq/providers/ansible_tower/automation_manager/job_spec.rb +++ b/spec/models/manageiq/providers/ansible_tower/automation_manager/job_spec.rb @@ -1,155 +1,5 @@ -require 'ansible_tower_client' -require 'faraday' -describe ManageIQ::Providers::AnsibleTower::AutomationManager::Job do - let(:faraday_connection) { instance_double("Faraday::Connection", :post => post, :get => get) } - let(:post) { instance_double("Faraday::Result", :body => {}.to_json) } - let(:get) { instance_double("Faraday::Result", :body => {'id' => 1}.to_json) } - - let(:connection) { double(:connection, :api => double(:api, :jobs => double(:jobs, :find => the_raw_job))) } - - let(:manager) { FactoryGirl.create(:automation_manager_ansible_tower, :provider) } - let(:mock_api) { AnsibleTowerClient::Api.new(faraday_connection) } - - let(:machine_credential) { FactoryGirl.create(:ansible_machine_credential, :manager_ref => '1', :resource => manager) } - let(:cloud_credential) { FactoryGirl.create(:ansible_cloud_credential, :manager_ref => '2', :resource => manager) } - let(:network_credential) { FactoryGirl.create(:ansible_network_credential, :manager_ref => '3', :resource => manager) } - - let(:the_raw_job) do - AnsibleTowerClient::Job.new( - mock_api, - 'id' => '1', - 'name' => template.name, - 'status' => 'Successful', - 'extra_vars' => {'param1' => 'val1'}.to_json, - 'verbosity' => 3, - 'started' => Time.current, - 'finished' => Time.current, - 'credential_id' => machine_credential.manager_ref, - 'cloud_credential_id' => cloud_credential.manager_ref, - 'network_credential_id' => network_credential.manager_ref - ).tap do |rjob| - allow(rjob).to receive(:stdout).and_return('job stdout') - allow(rjob).to receive(:job_plays).and_return(the_raw_plays) - end - end - - let(:the_raw_plays) do - [ - double('play1', :play => 'play1', :started => Time.current, :failed => false, :id => 1), - double('play2', :play => 'play2', :started => Time.current + 1, :failed => true, :id => 2) - ] - end - - let(:template) { FactoryGirl.create(:configuration_script, :manager => manager) } - subject { FactoryGirl.create(:ansible_tower_job, :job_template => template, :ext_management_system => manager) } - - describe 'job operations' do - context ".create_job" do - it 'creates a job' do - expect(template).to receive(:run).and_return(the_raw_job) - - job = ManageIQ::Providers::AnsibleTower::AutomationManager::Job.create_job(template, {}) - expect(job.class).to eq(described_class) - expect(job.name).to eq(template.name) - expect(job.ems_ref).to eq(the_raw_job.id) - expect(job.job_template).to eq(template) - expect(job.status).to eq(the_raw_job.status) - expect(job.ext_management_system).to eq(manager) - end - - it 'catches errors from provider' do - expect(template).to receive(:run).and_raise('bad request') - - expect do - ManageIQ::Providers::AnsibleTower::AutomationManager::Job.create_job(template, {}) - end.to raise_error(MiqException::MiqOrchestrationProvisionError) - end - end - - context "#refres_ems" do - before do - allow_any_instance_of(Provider).to receive_messages(:connect => connection) - end - - it 'syncs the job with the provider' do - subject.refresh_ems - expect(subject).to have_attributes( - :ems_ref => the_raw_job.id, - :status => the_raw_job.status, - :start_time => the_raw_job.started, - :finish_time => the_raw_job.finished, - :verbosity => the_raw_job.verbosity - ) - subject.reload - expect(subject.ems_ref).to eq(the_raw_job.id) - expect(subject.status).to eq(the_raw_job.status) - expect(subject.parameters.first).to have_attributes(:name => 'param1', :value => 'val1') - expect(subject.authentications).to match_array([machine_credential, cloud_credential, network_credential]) +require 'support/ansible_shared/automation_manager/job' - expect(subject.job_plays.first).to have_attributes( - :start_time => the_raw_plays.first.started, - :finish_time => the_raw_plays.last.started, - :resource_status => 'successful', - :resource_category => 'job_play', - :name => 'play1' - ) - expect(subject.job_plays.last).to have_attributes( - :start_time => the_raw_plays.last.started, - :finish_time => the_raw_job.finished, - :resource_status => 'failed', - :resource_category => 'job_play', - :name => 'play2' - ) - end - - it 'catches errors from provider' do - expect(connection.api.jobs).to receive(:find).and_raise('bad request') - expect { subject.refresh_ems }.to raise_error(MiqException::MiqOrchestrationUpdateError) - end - end - end - - describe 'job status' do - before do - allow_any_instance_of(Provider).to receive_messages(:connect => connection) - end - - context '#raw_status and #raw_exists' do - it 'gets the stack status' do - rstatus = subject.raw_status - expect(rstatus).to have_attributes(:status => 'Successful', :reason => nil) - - expect(subject.raw_exists?).to be_truthy - end - - it 'detects job not exist' do - expect(connection.api.jobs).to receive(:find).twice.and_raise(AnsibleTowerClient::ResourceNotFoundError.new(nil)) - expect { subject.raw_status }.to raise_error(MiqException::MiqOrchestrationStackNotExistError) - - expect(subject.raw_exists?).to be_falsey - end - - it 'catches errors from provider' do - expect(connection.api.jobs).to receive(:find).twice.and_raise("bad happened") - expect { subject.raw_status }.to raise_error(MiqException::MiqOrchestrationStatusError) - - expect { subject.raw_exists? }.to raise_error(MiqException::MiqOrchestrationStatusError) - end - end - end - - describe '#raw_stdout' do - before do - allow_any_instance_of(Provider).to receive_messages(:connect => connection) - end - - it 'gets the standard output of the job' do - expect(subject.raw_stdout).to eq('job stdout') - end - - it 'catches errors from provider' do - expect(connection.api.jobs).to receive(:find).and_raise("bad happened") - expect { subject.raw_stdout }.to raise_error(MiqException::MiqOrchestrationStatusError) - end - end +describe ManageIQ::Providers::AnsibleTower::AutomationManager::Job do + it_behaves_like 'ansible job' end diff --git a/spec/models/manageiq/providers/ansible_tower/automation_manager/refresher_spec.rb b/spec/models/manageiq/providers/ansible_tower/automation_manager/refresher_spec.rb index 6c3f499bc00..1b98b0a8bbb 100644 --- a/spec/models/manageiq/providers/ansible_tower/automation_manager/refresher_spec.rb +++ b/spec/models/manageiq/providers/ansible_tower/automation_manager/refresher_spec.rb @@ -1,193 +1,9 @@ -describe ManageIQ::Providers::AnsibleTower::AutomationManager::Refresher do - # To re-record cassettes or to add cassettes you can add another inner `VCR.use_cassette` block to the - # 'will perform a full refresh' example. When running specs, new requests are recorded to the innermost cassette and - # can be played back from any level of nesting (it tries the innermost cassette first, then searches up the parent - # chain) - http://stackoverflow.com/a/13425826 - # - # To add a new cassette - # * add another block (innermost) with an empty cassette - # * change existing cassettes to use your working credentials - # * run the specs to create a new cassette - # * change new and existing cassettes to use default credentials - # - # To re-record a cassette - # * temporarily make the cassette the innermost one (see above about recording) - # * rm cassette ; run specs - # * change back the order of cassettes - # - # To change credentials in cassettes: - # replace with defaults - before committing - # ruby -pi -e 'gsub /yourdomain.com/, "example.com"; gsub /admin:smartvm/, "testuser:secret"' spec/vcr_cassettes/manageiq/providers/ansible_tower/automation_manager/*.yml - # replace with your working credentials - # ruby -pi -e 'gsub /example.com/, "yourdomain.com"; gsub /testuser:secret/, "admin:smartvm"' spec/vcr_cassettes/manageiq/providers/ansible_tower/automation_manager/*.yml - - let(:tower_url) { ENV['TOWER_URL'] || "https://dev-ansible-tower3.example.com/api/v1/" } - let(:auth_userid) { ENV['TOWER_USER'] || 'testuser' } - let(:auth_password) { ENV['TOWER_PASSWORD'] || 'secret' } - - let(:auth) { FactoryGirl.create(:authentication, :userid => auth_userid, :password => auth_password) } - let(:automation_manager) { provider.automation_manager } - let(:expected_counterpart_vm) { FactoryGirl.create(:vm, :uid_ems => "4233080d-7467-de61-76c9-c8307b6e4830") } - let(:provider) do - _guid, _server, zone = EvmSpecHelper.create_guid_miq_server_zone - FactoryGirl.create(:provider_ansible_tower, - :zone => zone, - :url => tower_url, - :verify_ssl => false,).tap { |provider| provider.authentications << auth } - end - - it ".ems_type" do - expect(described_class.ems_type).to eq(:ansible_tower_automation) - end - - it "will perform a full refresh" do - expected_counterpart_vm - - 2.times do - # to re-record cassettes see comment at the beginning of this file - VCR.use_cassette(described_class.name.underscore) do - VCR.use_cassette(described_class.name.underscore + '_configuration_script_sources') do - VCR.use_cassette(described_class.name.underscore + '_credentials') do - EmsRefresh.refresh(automation_manager) - expect(automation_manager.reload.last_refresh_error).to be_nil - end - end - end - - assert_counts - assert_configured_system - assert_configuration_script_with_nil_survey_spec - assert_configuration_script_with_survey_spec - assert_inventory_root_group - assert_configuration_script_sources - assert_playbooks - assert_credentials - end - end - - def assert_counts - expect(Provider.count).to eq(1) - expect(automation_manager).to have_attributes(:api_version => "3.0.1") - expect(automation_manager.configured_systems.count).to eq(84) - expect(automation_manager.configuration_scripts.count).to eq(11) - expect(automation_manager.inventory_groups.count).to eq(6) - expect(automation_manager.configuration_script_sources.count).to eq(6) - expect(automation_manager.configuration_script_payloads.count).to eq(438) - expect(automation_manager.credentials.count).to eq(8) - end - - def assert_credentials - expect(expected_configuration_script.authentications.count).to eq(3) - machine_credential = expected_configuration_script.authentications.find_by( - :type => ManageIQ::Providers::AnsibleTower::AutomationManager::MachineCredential - ) - expect(machine_credential).to have_attributes( - :name => "Demo Credential", - :userid => "admin", - ) - expect(machine_credential.options.keys).to match_array(machine_credential.class::EXTRA_ATTRIBUTES.keys) - expect(machine_credential.options[:become_method]).to eq('su') - expect(machine_credential.options[:become_username]).to eq('root') - - network_credential = expected_configuration_script.authentications.find_by( - :type => ManageIQ::Providers::AnsibleTower::AutomationManager::NetworkCredential - ) - expect(network_credential).to have_attributes( - :name => "Demo Creds 2", - :userid => "awdd", - ) - expect(network_credential.options.keys).to match_array(network_credential.class::EXTRA_ATTRIBUTES.keys) - - cloud_credential = expected_configuration_script.authentications.find_by( - :type => ManageIQ::Providers::AnsibleTower::AutomationManager::VmwareCredential - ) - expect(cloud_credential).to have_attributes( - :name => "dev-vc60", - :userid => "MiqAnsibleUser@vsphere.local", - ) - expect(cloud_credential.options.keys).to match_array(cloud_credential.class::EXTRA_ATTRIBUTES.keys) - end +require 'support/ansible_shared/automation_manager/refresher' - def assert_playbooks - expect(expected_configuration_script_source.configuration_script_payloads.first).to be_an_instance_of(ManageIQ::Providers::AnsibleTower::AutomationManager::Playbook) - expect(expected_configuration_script_source.configuration_script_payloads.count).to eq(8) - expect(expected_configuration_script_source.configuration_script_payloads.map(&:name)).to include('start_ec2.yml') - end - - def assert_configuration_script_sources - expect(automation_manager.configuration_script_sources.count).to eq(6) - expect(expected_configuration_script_source).to be_an_instance_of(ManageIQ::Providers::AnsibleTower::AutomationManager::ConfigurationScriptSource) - expect(expected_configuration_script_source).to have_attributes( - :name => 'DB_Github', - :description => 'DB Playbooks', - :scm_type => 'git', - :scm_url => 'https://github.com/syncrou/playbooks', - :scm_branch => 'master', - :scm_clean => false, - :scm_delete_on_update => false, - :scm_update_on_launch => true - ) - expect(expected_configuration_script_source.authentication.name).to eq('db-github') - end - - def assert_configured_system - expect(expected_configured_system).to have_attributes( - :type => "ManageIQ::Providers::AnsibleTower::AutomationManager::ConfiguredSystem", - :hostname => "Ansible-Host", - :manager_ref => "3", - :virtual_instance_ref => "4233080d-7467-de61-76c9-c8307b6e4830", - ) - expect(expected_configured_system.counterpart).to eq(expected_counterpart_vm) - expect(expected_configured_system.inventory_root_group).to eq(expected_inventory_root_group) - end - - def assert_configuration_script_with_nil_survey_spec - expect(expected_configuration_script).to have_attributes( - :description => "Ansible-JobTemplate-Description", - :manager_ref => "80", - :name => "Ansible-JobTemplate", - :survey_spec => {}, - :variables => {'abc' => 123}, - ) - expect(expected_configuration_script.inventory_root_group).to have_attributes(:ems_ref => "2") - end - - def assert_configuration_script_with_survey_spec - system = automation_manager.configuration_scripts.where(:name => "Ansible-JobTemplate-Survey").first - expect(system).to have_attributes( - :name => "Ansible-JobTemplate-Survey", - :description => "Ansible-JobTemplate-Description", - :manager_ref => "81", - :variables => {'abc' => 123} - ) - survey = system.survey_spec - expect(survey).to be_a Hash - expect(survey['spec'].first['question_name']).to eq('Survey') - end - - def assert_inventory_root_group - expect(expected_inventory_root_group).to have_attributes( - :name => "Dev-VC60", - :ems_ref => "2", - :type => "ManageIQ::Providers::AutomationManager::InventoryRootGroup", - ) - end - - private - - def expected_configured_system - @expected_configured_system ||= automation_manager.configured_systems.where(:hostname => "Ansible-Host").first - end - - def expected_configuration_script - @expected_configuration_script ||= automation_manager.configuration_scripts.where(:name => "Ansible-JobTemplate").first - end - - def expected_inventory_root_group - @expected_inventory_root_group ||= automation_manager.inventory_groups.where(:name => "Dev-VC60").first - end - - def expected_configuration_script_source - @expected_configuration_script_source ||= automation_manager.configuration_script_sources.find_by(:name => 'DB_Github') - end +describe ManageIQ::Providers::AnsibleTower::AutomationManager::Refresher do + it_behaves_like 'ansible refresher', + :provider_ansible_tower, + described_class.parent, + :ansible_tower_automation, + described_class.name.underscore end diff --git a/spec/models/manageiq/providers/ansible_tower/automation_manager/refresher_v2_spec.rb b/spec/models/manageiq/providers/ansible_tower/automation_manager/refresher_v2_spec.rb index e70df79c417..1b98b0a8bbb 100644 --- a/spec/models/manageiq/providers/ansible_tower/automation_manager/refresher_v2_spec.rb +++ b/spec/models/manageiq/providers/ansible_tower/automation_manager/refresher_v2_spec.rb @@ -1,150 +1,9 @@ -describe ManageIQ::Providers::AnsibleTower::AutomationManager::Refresher do - # To change credentials in cassettes: - # replace with defaults - before committing - # ruby -pi -e 'gsub /yourdomain.com/, "example.com"; gsub /admin:smartvm/, "testuser:secret"' spec/vcr_cassettes/manageiq/providers/ansible_tower/automation_manager/refresher_v2.yml - # replace with your working credentials - # ruby -pi -e 'gsub /example.com/, "yourdomain.com"; gsub /testuser:secret/, "admin:smartvm"' spec/vcr_cassettes/manageiq/providers/ansible_tower/automation_manager/refresher_v2.yml - let(:tower_url) { ENV['TOWER_URL'] || "https://dev-ansible-tower2.example.com/api/v1/" } - let(:auth_userid) { ENV['TOWER_USER'] || 'testuser' } - let(:auth_password) { ENV['TOWER_PASSWORD'] || 'secret' } - - let(:auth) { FactoryGirl.create(:authentication, :userid => auth_userid, :password => auth_password) } - let(:automation_manager) { provider.automation_manager } - let(:expected_counterpart_vm) { FactoryGirl.create(:vm, :uid_ems => "4233080d-7467-de61-76c9-c8307b6e4830") } - let(:provider) do - _guid, _server, zone = EvmSpecHelper.create_guid_miq_server_zone - FactoryGirl.create(:provider_ansible_tower, - :zone => zone, - :url => tower_url, - :verify_ssl => false,).tap { |provider| provider.authentications << auth } - end - - it ".ems_type" do - expect(described_class.ems_type).to eq(:ansible_tower_automation) - end - - it "will perform a full refresh" do - expected_counterpart_vm - - 2.times do - VCR.use_cassette(described_class.name.underscore + '_v2') do - EmsRefresh.refresh(automation_manager) - expect(automation_manager.reload.last_refresh_error).to be_nil - end - - assert_counts - assert_configured_system - assert_configuration_script_with_nil_survey_spec - assert_configuration_script_with_survey_spec - assert_inventory_root_group - assert_configuration_script_sources - assert_playbooks - assert_credentials - end - end - - def assert_counts - expect(Provider.count).to eq(1) - expect(automation_manager).to have_attributes(:api_version => "2.4.2") - expect(automation_manager.configured_systems.count).to eq(168) - expect(automation_manager.configuration_scripts.count).to eq(14) - expect(automation_manager.inventory_groups.count).to eq(8) - expect(automation_manager.configuration_script_sources.count).to eq(6) - expect(automation_manager.configuration_script_payloads.count).to eq(77) - expect(automation_manager.credentials.count).to eq(11) - end - - def assert_credentials - expect(expected_configuration_script.authentications.count).to eq(2) - machine_credential = expected_configuration_script.authentications.find_by( - :type => ManageIQ::Providers::AnsibleTower::AutomationManager::MachineCredential - ) - expect(machine_credential).to have_attributes( - :name => "appliance", - :userid => "root", - ) - cloud_credential = expected_configuration_script.authentications.find_by( - :type => ManageIQ::Providers::AnsibleTower::AutomationManager::AmazonCredential - ) - expect(cloud_credential).to have_attributes( - :name => "AWS", - :userid => "065ZMGNV5WNKPMX4FF82", - ) - end - - def assert_playbooks - expect(expected_configuration_script_source.configuration_script_payloads.first).to be_an_instance_of(ManageIQ::Providers::AnsibleTower::AutomationManager::Playbook) - expect(expected_configuration_script_source.configuration_script_payloads.count).to eq(7) - expect(expected_configuration_script_source.configuration_script_payloads.map(&:name)).to include('create_ec2.yml') - end - - def assert_configuration_script_sources - expect(automation_manager.configuration_script_sources.count).to eq(6) - expect(expected_configuration_script_source).to be_an_instance_of(ManageIQ::Providers::AnsibleTower::AutomationManager::ConfigurationScriptSource) - expect(expected_configuration_script_source).to have_attributes( - :name => 'db-projects', - :description => 'projects', - ) - end +require 'support/ansible_shared/automation_manager/refresher' - def assert_configured_system - expect(expected_configured_system).to have_attributes( - :type => "ManageIQ::Providers::AnsibleTower::AutomationManager::ConfiguredSystem", - :hostname => "Ansible-Host", - :manager_ref => "145", - :virtual_instance_ref => "4233080d-7467-de61-76c9-c8307b6e4830", - ) - expect(expected_configured_system.counterpart).to eq(expected_counterpart_vm) - expect(expected_configured_system.inventory_root_group).to eq(expected_inventory_root_group) - end - - def assert_configuration_script_with_nil_survey_spec - expect(expected_configuration_script).to have_attributes( - :description => "Ansible-JobTemplate-Description", - :manager_ref => "149", - :name => "Ansible-JobTemplate", - :survey_spec => {}, - :variables => {'abc' => 123}, - ) - expect(expected_configuration_script.inventory_root_group).to have_attributes(:ems_ref => "2") - end - - def assert_configuration_script_with_survey_spec - system = automation_manager.configuration_scripts.where(:name => "Ansible-JobTemplate-Survey").first - expect(system).to have_attributes( - :name => "Ansible-JobTemplate-Survey", - :description => "Ansible-JobTemplate-Description", - :manager_ref => "155", - :variables => {'abc' => 123} - ) - survey = system.survey_spec - expect(survey).to be_a Hash - expect(survey['spec'].first['index']).to eq 0 - end - - def assert_inventory_root_group - expect(expected_inventory_root_group).to have_attributes( - :name => "Dev VC60", - :ems_ref => "17", - :type => "ManageIQ::Providers::AutomationManager::InventoryRootGroup", - ) - end - - private - - def expected_configured_system - @expected_configured_system ||= automation_manager.configured_systems.where(:hostname => "Ansible-Host").first - end - - def expected_configuration_script - @expected_configuration_script ||= automation_manager.configuration_scripts.where(:name => "Ansible-JobTemplate").first - end - - def expected_inventory_root_group - @expected_inventory_root_group ||= automation_manager.inventory_groups.where(:name => "Dev VC60").first - end - - def expected_configuration_script_source - @expected_configuration_script_source ||= automation_manager.configuration_script_sources.find_by(:name => 'db-projects') - end +describe ManageIQ::Providers::AnsibleTower::AutomationManager::Refresher do + it_behaves_like 'ansible refresher', + :provider_ansible_tower, + described_class.parent, + :ansible_tower_automation, + described_class.name.underscore end diff --git a/spec/models/manageiq/providers/ansible_tower/automation_manager_spec.rb b/spec/models/manageiq/providers/ansible_tower/automation_manager_spec.rb index 5b31d1f84cd..0ac650d51c7 100644 --- a/spec/models/manageiq/providers/ansible_tower/automation_manager_spec.rb +++ b/spec/models/manageiq/providers/ansible_tower/automation_manager_spec.rb @@ -1,11 +1,5 @@ -describe ManageIQ::Providers::AnsibleTower::AutomationManager do - let(:provider) { FactoryGirl.build(:provider) } - let(:ansible_automation_manager) { FactoryGirl.build(:automation_manager_ansible_tower, :provider => provider) } +require 'support/ansible_shared/automation_manager' - describe "#connect" do - it "delegates to the provider" do - expect(provider).to receive(:connect) - ansible_automation_manager.connect - end - end +describe ManageIQ::Providers::AnsibleTower::AutomationManager do + it_behaves_like 'ansible automation_manager' end diff --git a/spec/models/manageiq/providers/ansible_tower/provider_spec.rb b/spec/models/manageiq/providers/ansible_tower/provider_spec.rb index 176b107a581..e8f0429500a 100644 --- a/spec/models/manageiq/providers/ansible_tower/provider_spec.rb +++ b/spec/models/manageiq/providers/ansible_tower/provider_spec.rb @@ -1,69 +1,5 @@ -require "ansible_tower_client" +require 'support/ansible_shared/provider' describe ManageIQ::Providers::AnsibleTower::Provider do - subject { FactoryGirl.build(:provider_ansible_tower) } - - describe "#connect" do - let(:attrs) { {:username => "admin", :password => "smartvm", :verify_ssl => OpenSSL::SSL::VERIFY_PEER} } - - it "with no port" do - url = "example.com" - - expect(AnsibleTowerClient::Connection).to receive(:new).with(attrs.merge(:base_url => url)) - subject.connect(attrs.merge(:url => url)) - end - - it "with a port" do - url = "example.com:555" - - expect(AnsibleTowerClient::Connection).to receive(:new).with(attrs.merge(:base_url => url)) - subject.connect(attrs.merge(:url => url)) - end - end - - describe "#destroy" do - it "will remove all child objects" do - provider = FactoryGirl.create(:provider_ansible_tower, :zone => FactoryGirl.create(:zone)) - - provider.automation_manager.configured_systems = [ - FactoryGirl.create(:configured_system, :computer_system => - FactoryGirl.create(:computer_system, - :operating_system => FactoryGirl.create(:operating_system), - :hardware => FactoryGirl.create(:hardware), - ) - ) - ] - - provider.destroy - - expect(Provider.count).to eq(0) - expect(ConfiguredSystem.count).to eq(0) - expect(ComputerSystem.count).to eq(0) - expect(OperatingSystem.count).to eq(0) - expect(Hardware.count).to eq(0) - end - end - - context "#url=" do - it "with full URL" do - subject.url = "https://server.example.com:1234/api/v1" - expect(subject.url).to eq("https://server.example.com:1234/api/v1") - end - - it "missing scheme" do - subject.url = "server.example.com:1234/api/v1" - expect(subject.url).to eq("https://server.example.com:1234/api/v1") - end - - it "works with #update_attributes" do - subject.update_attributes(:url => "server.example.com") - subject.update_attributes(:url => "server2.example.com") - expect(Endpoint.find(subject.default_endpoint.id).url).to eq("https://server2.example.com/api/v1") - end - end - - it "with only hostname" do - subject.url = "server.example.com" - expect(subject.url).to eq("https://server.example.com/api/v1") - end + it_behaves_like 'ansible provider' end diff --git a/spec/models/manageiq/providers/embedded_ansible/automation_manager/configuration_script_source_spec.rb b/spec/models/manageiq/providers/embedded_ansible/automation_manager/configuration_script_source_spec.rb new file mode 100644 index 00000000000..acc106c9cc5 --- /dev/null +++ b/spec/models/manageiq/providers/embedded_ansible/automation_manager/configuration_script_source_spec.rb @@ -0,0 +1,5 @@ +require 'support/ansible_shared/automation_manager/configuration_script_source' + +describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfigurationScriptSource do + it_behaves_like 'ansible configuration_script_source' +end diff --git a/spec/models/manageiq/providers/embedded_ansible/automation_manager/configuration_script_spec.rb b/spec/models/manageiq/providers/embedded_ansible/automation_manager/configuration_script_spec.rb index 6440c6611f9..caee70ffd02 100644 --- a/spec/models/manageiq/providers/embedded_ansible/automation_manager/configuration_script_spec.rb +++ b/spec/models/manageiq/providers/embedded_ansible/automation_manager/configuration_script_spec.rb @@ -1,160 +1,5 @@ -require 'ansible_tower_client' -require 'faraday' -describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfigurationScript do - let(:api) { double(:api, :job_templates => double(:job_templates)) } - let(:connection) { double(:connection, :api => api) } - let(:job) { AnsibleTowerClient::Job.new(connection.api, "id" => 1) } - let(:job_template) { AnsibleTowerClient::JobTemplate.new(connection.api, "limit" => "", "id" => 1, "url" => "api/job_templates/1/", "name" => "template", "description" => "description", "extra_vars" => {:instance_ids => ['i-3434']}) } - let(:manager) { FactoryGirl.create(:embedded_automation_manager_ansible, :provider, :configuration_script) } - - it "belongs_to the Ansible Tower manager" do - expect(manager.configuration_scripts.size).to eq 1 - expect(manager.configuration_scripts.first.variables).to eq :instance_ids => ['i-3434'] - expect(manager.configuration_scripts.first).to be_a ConfigurationScript - end - - context "relates to playbook" do - let(:configuration_script_source) { FactoryGirl.create(:configuration_script_source, :manager => manager) } - let!(:payload) { FactoryGirl.create(:configuration_script_payload) } - let(:configuration_scripts_without_payload) { FactoryGirl.create(:configuration_script) } - let(:configuration_scripts) do - [FactoryGirl.create(:configuration_script, :parent => payload), - FactoryGirl.create(:configuration_script, :parent => payload)] - end - - it "can refer to a payload" do - expect(configuration_scripts[0].parent).to eq(payload) - expect(configuration_scripts[1].parent).to eq(payload) - expect(payload.children).to match_array(configuration_scripts) - end - - it "can be without payload" do - expect(configuration_scripts_without_payload.parent).to be_nil - end - end - - context "#run" do - before do - allow_any_instance_of(Provider).to receive_messages(:connect => connection) - allow(api.job_templates).to receive(:find) { job_template } - end - - it "launches the referenced ansible job template" do - expect(job_template).to receive(:launch).with(:extra_vars => "{\"instance_ids\":[\"i-3434\"]}").and_return(job) - expect(manager.configuration_scripts.first.run).to be_a AnsibleTowerClient::Job - end - - it "accepts different variables to launch a job template against" do - added_extras = {:extra_vars => {:some_key => :some_value}} - expect(job_template).to receive(:launch).with(:extra_vars=>"{\"instance_ids\":[\"i-3434\"],\"some_key\":\"some_value\"}").and_return(job) - expect(manager.configuration_scripts.first.run(added_extras)).to be_a AnsibleTowerClient::Job - end - end - - context "#merge_extra_vars" do - it "merges internal and external hashes to send out to the tower gem" do - config_script = manager.configuration_scripts.first - external = {:some_key => :some_value} - internal = config_script.variables - expect(internal).to be_a Hash - expect(config_script.merge_extra_vars(external)).to eq(:extra_vars => "{\"instance_ids\":[\"i-3434\"],\"some_key\":\"some_value\"}") - end - - it "merges an internal hash and an empty hash to send out to the tower gem" do - config_script = manager.configuration_scripts.first - external = nil - expect(config_script.merge_extra_vars(external)).to eq(:extra_vars => "{\"instance_ids\":[\"i-3434\"]}") - end - - it "merges an empty internal hash and a hash to send out to the tower gem" do - external = {:some_key => :some_value} - internal = {} - config_script = manager.configuration_scripts.first - config_script.variables = internal - expect(config_script.merge_extra_vars(external)).to eq(:extra_vars => "{\"some_key\":\"some_value\"}") - end - - it "merges all empty arguments to send out to the tower gem" do - external = nil - internal = {} - config_script = manager.configuration_scripts.first - config_script.variables = internal - expect(config_script.merge_extra_vars(external)).to eq(:extra_vars => "{}") - end - end - - context "creates via the API" do - let(:provider) { FactoryGirl.create(:provider_embedded_ansible, :with_authentication) } - let(:manager) { provider.managers.first } - let(:atc) { double("AnsibleTowerClient::Connection", :api => api) } - let(:api) { double("AnsibleTowerClient::Api", :job_templates => job_templates) } - let(:job_templates) { double("AnsibleTowerClient::Collection", :create! => job_template) } - let(:job_template) { AnsibleTowerClient::JobTemplate.new(nil, job_template_json) } +require 'support/ansible_shared/automation_manager/configuration_script' - let(:job_template_json) do - params.merge( - :id => 10, - :inventory => 1, - :related => {"inventory" => "blah/1"} - ).except(:inventory_id).stringify_keys.to_json - end - - let(:params) do - { - :description => "Description", - :extra_vars => {}.to_json, - :inventory_id => 1, - :name => "My Job Template", - :related => {} - } - end - - context ".create_in_provider" do - let(:finished_task) { FactoryGirl.create(:miq_task, :state => "Finished") } - - it "successfully created in provider" do - expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) - - store_new_job_template(job_template, manager) - - expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) - expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) - - expect(described_class.create_in_provider(manager.id, params)).to be_a(described_class) - end - - it "not found during refresh" do - expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) - expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) - expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) - - expect { described_class.create_in_provider(manager.id, params) }.to raise_error(ActiveRecord::RecordNotFound) - end - end - - it ".create_in_provider_queue" do - EvmSpecHelper.local_miq_server - task_id = described_class.create_in_provider_queue(manager.id, params) - expect(MiqTask.find(task_id)).to have_attributes(:name => "Creating Ansible Tower Job Template") - expect(MiqQueue.first).to have_attributes( - :args => [manager.id, params], - :class_name => "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfigurationScript", - :method_name => "create_in_provider", - :priority => MiqQueue::HIGH_PRIORITY, - :role => "ems_operations", - :zone => manager.zone.name - ) - end - - def store_new_job_template(job_template, manager) - described_class.create!( - :manager => manager, - :manager_ref => job_template.id.to_s, - :name => job_template.name, - :survey_spec => job_template.survey_spec_hash, - :variables => job_template.extra_vars_hash, - ) - end - - end +describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfigurationScript do + it_behaves_like 'ansible configuration_script' end diff --git a/spec/models/manageiq/providers/embedded_ansible/automation_manager/credential_spec.rb b/spec/models/manageiq/providers/embedded_ansible/automation_manager/credential_spec.rb new file mode 100644 index 00000000000..6196dda14d6 --- /dev/null +++ b/spec/models/manageiq/providers/embedded_ansible/automation_manager/credential_spec.rb @@ -0,0 +1,5 @@ +require 'support/ansible_shared/automation_manager/credential' + +describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Credential do + it_behaves_like 'ansible credential' +end diff --git a/spec/models/manageiq/providers/embedded_ansible/automation_manager/event_catcher/stream_spec.rb b/spec/models/manageiq/providers/embedded_ansible/automation_manager/event_catcher/stream_spec.rb new file mode 100644 index 00000000000..c8fcbdcf0f3 --- /dev/null +++ b/spec/models/manageiq/providers/embedded_ansible/automation_manager/event_catcher/stream_spec.rb @@ -0,0 +1,6 @@ +require 'support/ansible_shared/automation_manager/event_catcher/stream' + +describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::EventCatcher::Stream do + it_behaves_like 'ansible event_catcher stream', + ManageIQ::Providers::AnsibleTower::AutomationManager::EventCatcher::Stream.name.underscore.to_s +end diff --git a/spec/models/manageiq/providers/embedded_ansible/automation_manager/job/status_spec.rb b/spec/models/manageiq/providers/embedded_ansible/automation_manager/job/status_spec.rb index 6235ae0e5c7..8fd8d995cd1 100644 --- a/spec/models/manageiq/providers/embedded_ansible/automation_manager/job/status_spec.rb +++ b/spec/models/manageiq/providers/embedded_ansible/automation_manager/job/status_spec.rb @@ -1,41 +1,5 @@ -describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Job::Status do - it 'parses Succeeded' do - status = described_class.new('Successful', '') - expect(status.completed?).to be_truthy - expect(status.succeeded?).to be_truthy - expect(status.failed?).to be_falsey - expect(status.deleted?).to be_falsey - expect(status.rolled_back?).to be_falsey - expect(status.normalized_status).to eq(['create_complete', '']) - end - - it 'parses Failed' do - status = described_class.new('Failed', nil) - expect(status.completed?).to be_truthy - expect(status.succeeded?).to be_falsey - expect(status.failed?).to be_truthy - expect(status.deleted?).to be_falsey - expect(status.rolled_back?).to be_falsey - expect(status.normalized_status).to eq(['failed', 'Job launching failed']) - end +require 'support/ansible_shared/automation_manager/job/status' - it 'parses Canceled' do - status = described_class.new('Canceled', nil) - expect(status.completed?).to be_truthy - expect(status.succeeded?).to be_falsey - expect(status.canceled?).to be_truthy - expect(status.deleted?).to be_falsey - expect(status.rolled_back?).to be_falsey - expect(status.normalized_status).to eq(['create_canceled', 'Job launching was canceled']) - end - - it 'parses transient status' do - status = described_class.new('Running', nil) - expect(status.completed?).to be_falsey - expect(status.succeeded?).to be_falsey - expect(status.failed?).to be_falsey - expect(status.deleted?).to be_falsey - expect(status.rolled_back?).to be_falsey - expect(status.normalized_status).to eq(%w(transient Running)) - end +describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Job::Status do + it_behaves_like 'ansible job status' end diff --git a/spec/models/manageiq/providers/embedded_ansible/automation_manager/job_spec.rb b/spec/models/manageiq/providers/embedded_ansible/automation_manager/job_spec.rb index abbaf3f84ca..e6bccb4c98f 100644 --- a/spec/models/manageiq/providers/embedded_ansible/automation_manager/job_spec.rb +++ b/spec/models/manageiq/providers/embedded_ansible/automation_manager/job_spec.rb @@ -1,154 +1,5 @@ -require 'ansible_tower_client' -require 'faraday' -describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Job do - let(:faraday_connection) { instance_double("Faraday::Connection", :post => post, :get => get) } - let(:post) { instance_double("Faraday::Result", :body => {}.to_json) } - let(:get) { instance_double("Faraday::Result", :body => {'id' => 1}.to_json) } - - let(:connection) { double(:connection, :api => double(:api, :jobs => double(:jobs, :find => the_raw_job))) } - - let(:manager) { FactoryGirl.create(:embedded_automation_manager_ansible, :provider) } - let(:mock_api) { AnsibleTowerClient::Api.new(faraday_connection) } - - let(:machine_credential) { FactoryGirl.create(:ansible_machine_credential, :manager_ref => '1', :resource => manager) } - let(:cloud_credential) { FactoryGirl.create(:ansible_cloud_credential, :manager_ref => '2', :resource => manager) } - let(:network_credential) { FactoryGirl.create(:ansible_network_credential, :manager_ref => '3', :resource => manager) } - - let(:the_raw_job) do - AnsibleTowerClient::Job.new( - mock_api, - 'id' => '1', - 'name' => template.name, - 'status' => 'Successful', - 'extra_vars' => {'param1' => 'val1'}.to_json, - 'verbosity' => 3, - 'started' => Time.current, - 'finished' => Time.current, - 'credential_id' => machine_credential.manager_ref, - 'cloud_credential_id' => cloud_credential.manager_ref, - 'network_credential_id' => network_credential.manager_ref - ).tap do |rjob| - allow(rjob).to receive(:stdout).and_return('job stdout') - allow(rjob).to receive(:job_plays).and_return(the_raw_plays) - end - end - - let(:the_raw_plays) do - [ - double('play1', :play => 'play1', :started => Time.current, :failed => false, :id => 1), - double('play2', :play => 'play2', :started => Time.current + 1, :failed => true, :id => 2) - ] - end - - let(:template) { FactoryGirl.create(:configuration_script, :manager => manager) } - subject { FactoryGirl.create(:embedded_ansible_job, :job_template => template, :ext_management_system => manager) } - - describe 'job operations' do - context ".create_job" do - it 'creates a job' do - expect(template).to receive(:run).and_return(the_raw_job) - - job = ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Job.create_job(template, {}) - expect(job.class).to eq(described_class) - expect(job.name).to eq(template.name) - expect(job.ems_ref).to eq(the_raw_job.id) - expect(job.job_template).to eq(template) - expect(job.status).to eq(the_raw_job.status) - expect(job.ext_management_system).to eq(manager) - end - - it 'catches errors from provider' do - expect(template).to receive(:run).and_raise('bad request') - - expect do - ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Job.create_job(template, {}) - end.to raise_error(MiqException::MiqOrchestrationProvisionError) - end - end - - context "#refres_ems" do - before do - allow_any_instance_of(Provider).to receive_messages(:connect => connection) - end - - it 'syncs the job with the provider' do - subject.refresh_ems - expect(subject).to have_attributes( - :ems_ref => the_raw_job.id, - :status => the_raw_job.status, - :start_time => the_raw_job.started, - :finish_time => the_raw_job.finished, - :verbosity => the_raw_job.verbosity - ) - expect(subject.ems_ref).to eq(the_raw_job.id) - expect(subject.status).to eq(the_raw_job.status) - expect(subject.parameters.first).to have_attributes(:name => 'param1', :value => 'val1') - expect(subject.authentications).to match_array([machine_credential, cloud_credential, network_credential]) +require 'support/ansible_shared/automation_manager/job' - expect(subject.job_plays.first).to have_attributes( - :start_time => the_raw_plays.first.started, - :finish_time => the_raw_plays.last.started, - :resource_status => 'successful', - :resource_category => 'job_play', - :name => 'play1' - ) - expect(subject.job_plays.last).to have_attributes( - :start_time => the_raw_plays.last.started, - :finish_time => the_raw_job.finished, - :resource_status => 'failed', - :resource_category => 'job_play', - :name => 'play2' - ) - end - - it 'catches errors from provider' do - expect(connection.api.jobs).to receive(:find).and_raise('bad request') - expect { subject.refresh_ems }.to raise_error(MiqException::MiqOrchestrationUpdateError) - end - end - end - - describe 'job status' do - before do - allow_any_instance_of(Provider).to receive_messages(:connect => connection) - end - - context '#raw_status and #raw_exists' do - it 'gets the stack status' do - rstatus = subject.raw_status - expect(rstatus).to have_attributes(:status => 'Successful', :reason => nil) - - expect(subject.raw_exists?).to be_truthy - end - - it 'detects job not exist' do - expect(connection.api.jobs).to receive(:find).twice.and_raise(AnsibleTowerClient::ResourceNotFoundError.new(nil)) - expect { subject.raw_status }.to raise_error(MiqException::MiqOrchestrationStackNotExistError) - - expect(subject.raw_exists?).to be_falsey - end - - it 'catches errors from provider' do - expect(connection.api.jobs).to receive(:find).twice.and_raise("bad happened") - expect { subject.raw_status }.to raise_error(MiqException::MiqOrchestrationStatusError) - - expect { subject.raw_exists? }.to raise_error(MiqException::MiqOrchestrationStatusError) - end - end - end - - describe '#raw_stdout' do - before do - allow_any_instance_of(Provider).to receive_messages(:connect => connection) - end - - it 'gets the standard output of the job' do - expect(subject.raw_stdout).to eq('job stdout') - end - - it 'catches errors from provider' do - expect(connection.api.jobs).to receive(:find).and_raise("bad happened") - expect { subject.raw_stdout }.to raise_error(MiqException::MiqOrchestrationStatusError) - end - end +describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Job do + it_behaves_like 'ansible job' end diff --git a/spec/models/manageiq/providers/embedded_ansible/automation_manager/refresher_spec.rb b/spec/models/manageiq/providers/embedded_ansible/automation_manager/refresher_spec.rb index d7a41be4615..897329eaa2c 100644 --- a/spec/models/manageiq/providers/embedded_ansible/automation_manager/refresher_spec.rb +++ b/spec/models/manageiq/providers/embedded_ansible/automation_manager/refresher_spec.rb @@ -1,179 +1,9 @@ -describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Refresher do - # To re-record cassettes or to add cassettes you can add another inner `VCR.use_cassette` block to the - # 'will perform a full refresh' example. When running specs, new requests are recorded to the innermost cassette and - # can be played back from any level of nesting (it tries the innermost cassette first, then searches up the parent - # chain) - http://stackoverflow.com/a/13425826 - # - # To add a new cassette - # * add another block (innermost) with an empty cassette - # * change existing cassettes to use your working credentials - # * run the specs to create a new cassette - # * change new and existing cassettes to use default credentials - # - # To re-record a cassette - # * temporarily make the cassette the innermost one (see above about recording) - # * rm cassette ; run specs - # * change back the order of cassettes - # - # To change credentials in cassettes: - # replace with defaults - before committing - # ruby -pi -e 'gsub /yourdomain.com/, "example.com"; gsub /admin:smartvm/, "testuser:secret"' spec/vcr_cassettes/manageiq/providers/embedded_ansible/automation_manager/*.yml - # replace with your working credentials - # ruby -pi -e 'gsub /example.com/, "yourdomain.com"; gsub /testuser:secret/, "admin:smartvm"' spec/vcr_cassettes/manageiq/providers/embedded_ansible/automation_manager/*.yml - - let(:tower_url) { ENV['TOWER_URL'] || "https://dev-ansible-tower3.example.com/api/v1/" } - let(:auth_userid) { ENV['TOWER_USER'] || 'testuser' } - let(:auth_password) { ENV['TOWER_PASSWORD'] || 'secret' } - - let(:auth) { FactoryGirl.create(:authentication, :userid => auth_userid, :password => auth_password) } - let(:automation_manager) { provider.automation_manager } - let(:expected_counterpart_vm) { FactoryGirl.create(:vm, :uid_ems => "4233080d-7467-de61-76c9-c8307b6e4830") } - let(:provider) do - _guid, _server, zone = EvmSpecHelper.create_guid_miq_server_zone - FactoryGirl.create(:provider_embedded_ansible, - :zone => zone, - :url => tower_url, - :verify_ssl => false,).tap { |provider| provider.authentications << auth } - end - - it ".ems_type" do - expect(described_class.ems_type).to eq(:embedded_ansible_automation) - end - - it "will perform a full refresh" do - expected_counterpart_vm - - 2.times do - # to re-record cassettes see comment at the beginning of this file - VCR.use_cassette(described_class.name.underscore) do - VCR.use_cassette(described_class.name.underscore + '_configuration_script_sources') do - VCR.use_cassette(described_class.name.underscore + '_credentials') do - EmsRefresh.refresh(automation_manager) - expect(automation_manager.reload.last_refresh_error).to be_nil - end - end - end - - assert_counts - assert_configured_system - assert_configuration_script_with_nil_survey_spec - assert_configuration_script_with_survey_spec - assert_inventory_root_group - assert_configuration_script_sources - assert_playbooks - assert_credentials - end - end - - def assert_counts - expect(Provider.count).to eq(1) - expect(automation_manager).to have_attributes(:api_version => "3.0.1") - expect(automation_manager.configured_systems.count).to eq(84) - expect(automation_manager.configuration_scripts.count).to eq(11) - expect(automation_manager.inventory_groups.count).to eq(6) - expect(automation_manager.configuration_script_sources.count).to eq(6) - expect(automation_manager.configuration_script_payloads.count).to eq(438) - expect(automation_manager.credentials.count).to eq(8) - end - - def assert_credentials - expect(expected_configuration_script.authentications.count).to eq(3) - machine_credential = expected_configuration_script.authentications.find_by( - :type => ManageIQ::Providers::EmbeddedAnsible::AutomationManager::MachineCredential - ) - expect(machine_credential).to have_attributes( - :name => "Demo Credential", - :userid => "admin", - ) - network_credential = expected_configuration_script.authentications.find_by( - :type => ManageIQ::Providers::EmbeddedAnsible::AutomationManager::NetworkCredential - ) - expect(network_credential).to have_attributes( - :name => "Demo Creds 2", - :userid => "awdd", - ) - cloud_credential = expected_configuration_script.authentications.find_by( - :type => ManageIQ::Providers::EmbeddedAnsible::AutomationManager::VmwareCredential - ) - expect(cloud_credential).to have_attributes( - :name => "dev-vc60", - :userid => "MiqAnsibleUser@vsphere.local", - ) - end - - def assert_playbooks - expect(expected_configuration_script_source.configuration_script_payloads.first).to be_an_instance_of(ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Playbook) - expect(expected_configuration_script_source.configuration_script_payloads.count).to eq(1) - expect(expected_configuration_script_source.configuration_script_payloads.map(&:name)).to include('hello_world.yml') - end +require 'support/ansible_shared/automation_manager/refresher' - def assert_configuration_script_sources - expect(automation_manager.configuration_script_sources.count).to eq(6) - expect(expected_configuration_script_source).to be_an_instance_of(ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfigurationScriptSource) - expect(expected_configuration_script_source).to have_attributes( - :name => 'Demo Project', - :description => 'A great demo', - ) - end - - def assert_configured_system - expect(expected_configured_system).to have_attributes( - :type => "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfiguredSystem", - :hostname => "Ansible-Host", - :manager_ref => "3", - :virtual_instance_ref => "4233080d-7467-de61-76c9-c8307b6e4830", - ) - expect(expected_configured_system.counterpart).to eq(expected_counterpart_vm) - expect(expected_configured_system.inventory_root_group).to eq(expected_inventory_root_group) - end - - def assert_configuration_script_with_nil_survey_spec - expect(expected_configuration_script).to have_attributes( - :description => "Ansible-JobTemplate-Description", - :manager_ref => "80", - :name => "Ansible-JobTemplate", - :survey_spec => {}, - :variables => {'abc' => 123}, - ) - expect(expected_configuration_script.inventory_root_group).to have_attributes(:ems_ref => "2") - end - - def assert_configuration_script_with_survey_spec - system = automation_manager.configuration_scripts.where(:name => "Ansible-JobTemplate-Survey").first - expect(system).to have_attributes( - :name => "Ansible-JobTemplate-Survey", - :description => "Ansible-JobTemplate-Description", - :manager_ref => "81", - :variables => {'abc' => 123} - ) - survey = system.survey_spec - expect(survey).to be_a Hash - expect(survey['spec'].first['question_name']).to eq('Survey') - end - - def assert_inventory_root_group - expect(expected_inventory_root_group).to have_attributes( - :name => "Dev-VC60", - :ems_ref => "2", - :type => "ManageIQ::Providers::AutomationManager::InventoryRootGroup", - ) - end - - private - - def expected_configured_system - @expected_configured_system ||= automation_manager.configured_systems.where(:hostname => "Ansible-Host").first - end - - def expected_configuration_script - @expected_configuration_script ||= automation_manager.configuration_scripts.where(:name => "Ansible-JobTemplate").first - end - - def expected_inventory_root_group - @expected_inventory_root_group ||= automation_manager.inventory_groups.where(:name => "Dev-VC60").first - end - - def expected_configuration_script_source - @expected_configuration_script_source ||= automation_manager.configuration_script_sources.find_by(:name => 'Demo Project') - end +describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Refresher do + it_behaves_like 'ansible refresher', + :provider_embedded_ansible, + described_class.parent, + :embedded_ansible_automation, + ManageIQ::Providers::AnsibleTower::AutomationManager::Refresher.name.underscore end diff --git a/spec/models/manageiq/providers/embedded_ansible/automation_manager/refresher_v2_spec.rb b/spec/models/manageiq/providers/embedded_ansible/automation_manager/refresher_v2_spec.rb index 2e4acce7f9d..1afc5e316ea 100644 --- a/spec/models/manageiq/providers/embedded_ansible/automation_manager/refresher_v2_spec.rb +++ b/spec/models/manageiq/providers/embedded_ansible/automation_manager/refresher_v2_spec.rb @@ -1,150 +1,9 @@ -describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Refresher do - # To change credentials in cassettes: - # replace with defaults - before committing - # ruby -pi -e 'gsub /yourdomain.com/, "example.com"; gsub /admin:smartvm/, "testuser:secret"' spec/vcr_cassettes/manageiq/providers/embedded_ansible/automation_manager/refresher_v2.yml - # replace with your working credentials - # ruby -pi -e 'gsub /example.com/, "yourdomain.com"; gsub /testuser:secret/, "admin:smartvm"' spec/vcr_cassettes/manageiq/providers/embedded_ansible/automation_manager/refresher_v2.yml - let(:tower_url) { ENV['TOWER_URL'] || "https://dev-ansible-tower2.example.com/api/v1/" } - let(:auth_userid) { ENV['TOWER_USER'] || 'testuser' } - let(:auth_password) { ENV['TOWER_PASSWORD'] || 'secret' } - - let(:auth) { FactoryGirl.create(:authentication, :userid => auth_userid, :password => auth_password) } - let(:automation_manager) { provider.automation_manager } - let(:expected_counterpart_vm) { FactoryGirl.create(:vm, :uid_ems => "4233080d-7467-de61-76c9-c8307b6e4830") } - let(:provider) do - _guid, _server, zone = EvmSpecHelper.create_guid_miq_server_zone - FactoryGirl.create(:provider_embedded_ansible, - :zone => zone, - :url => tower_url, - :verify_ssl => false,).tap { |provider| provider.authentications << auth } - end - - it ".ems_type" do - expect(described_class.ems_type).to eq(:embedded_ansible_automation) - end - - it "will perform a full refresh" do - expected_counterpart_vm - - 2.times do - VCR.use_cassette(described_class.name.underscore + '_v2') do - EmsRefresh.refresh(automation_manager) - expect(automation_manager.reload.last_refresh_error).to be_nil - end - - assert_counts - assert_configured_system - assert_configuration_script_with_nil_survey_spec - assert_configuration_script_with_survey_spec - assert_inventory_root_group - assert_configuration_script_sources - assert_playbooks - assert_credentials - end - end - - def assert_counts - expect(Provider.count).to eq(1) - expect(automation_manager).to have_attributes(:api_version => "2.4.2") - expect(automation_manager.configured_systems.count).to eq(168) - expect(automation_manager.configuration_scripts.count).to eq(14) - expect(automation_manager.inventory_groups.count).to eq(8) - expect(automation_manager.configuration_script_sources.count).to eq(6) - expect(automation_manager.configuration_script_payloads.count).to eq(77) - expect(automation_manager.credentials.count).to eq(11) - end - - def assert_credentials - expect(expected_configuration_script.authentications.count).to eq(2) - machine_credential = expected_configuration_script.authentications.find_by( - :type => ManageIQ::Providers::EmbeddedAnsible::AutomationManager::MachineCredential - ) - expect(machine_credential).to have_attributes( - :name => "appliance", - :userid => "root", - ) - cloud_credential = expected_configuration_script.authentications.find_by( - :type => ManageIQ::Providers::EmbeddedAnsible::AutomationManager::AmazonCredential - ) - expect(cloud_credential).to have_attributes( - :name => "AWS", - :userid => "065ZMGNV5WNKPMX4FF82", - ) - end - - def assert_playbooks - expect(expected_configuration_script_source.configuration_script_payloads.first).to be_an_instance_of(ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Playbook) - expect(expected_configuration_script_source.configuration_script_payloads.count).to eq(7) - expect(expected_configuration_script_source.configuration_script_payloads.map(&:name)).to include('create_ec2.yml') - end - - def assert_configuration_script_sources - expect(automation_manager.configuration_script_sources.count).to eq(6) - expect(expected_configuration_script_source).to be_an_instance_of(ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfigurationScriptSource) - expect(expected_configuration_script_source).to have_attributes( - :name => 'db-projects', - :description => 'projects', - ) - end +require 'support/ansible_shared/automation_manager/refresher' - def assert_configured_system - expect(expected_configured_system).to have_attributes( - :type => "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfiguredSystem", - :hostname => "Ansible-Host", - :manager_ref => "145", - :virtual_instance_ref => "4233080d-7467-de61-76c9-c8307b6e4830", - ) - expect(expected_configured_system.counterpart).to eq(expected_counterpart_vm) - expect(expected_configured_system.inventory_root_group).to eq(expected_inventory_root_group) - end - - def assert_configuration_script_with_nil_survey_spec - expect(expected_configuration_script).to have_attributes( - :description => "Ansible-JobTemplate-Description", - :manager_ref => "149", - :name => "Ansible-JobTemplate", - :survey_spec => {}, - :variables => {'abc' => 123}, - ) - expect(expected_configuration_script.inventory_root_group).to have_attributes(:ems_ref => "2") - end - - def assert_configuration_script_with_survey_spec - system = automation_manager.configuration_scripts.where(:name => "Ansible-JobTemplate-Survey").first - expect(system).to have_attributes( - :name => "Ansible-JobTemplate-Survey", - :description => "Ansible-JobTemplate-Description", - :manager_ref => "155", - :variables => {'abc' => 123} - ) - survey = system.survey_spec - expect(survey).to be_a Hash - expect(survey['spec'].first['index']).to eq 0 - end - - def assert_inventory_root_group - expect(expected_inventory_root_group).to have_attributes( - :name => "Dev VC60", - :ems_ref => "17", - :type => "ManageIQ::Providers::AutomationManager::InventoryRootGroup", - ) - end - - private - - def expected_configured_system - @expected_configured_system ||= automation_manager.configured_systems.where(:hostname => "Ansible-Host").first - end - - def expected_configuration_script - @expected_configuration_script ||= automation_manager.configuration_scripts.where(:name => "Ansible-JobTemplate").first - end - - def expected_inventory_root_group - @expected_inventory_root_group ||= automation_manager.inventory_groups.where(:name => "Dev VC60").first - end - - def expected_configuration_script_source - @expected_configuration_script_source ||= automation_manager.configuration_script_sources.find_by(:name => 'db-projects') - end +describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Refresher do + it_behaves_like 'ansible refresher_v2', + :provider_embedded_ansible, + described_class.parent, + :embedded_ansible_automation, + ManageIQ::Providers::AnsibleTower::AutomationManager::Refresher.name.underscore end diff --git a/spec/models/manageiq/providers/embedded_ansible/automation_manager_spec.rb b/spec/models/manageiq/providers/embedded_ansible/automation_manager_spec.rb index 04335ee917b..64bd361ecc0 100644 --- a/spec/models/manageiq/providers/embedded_ansible/automation_manager_spec.rb +++ b/spec/models/manageiq/providers/embedded_ansible/automation_manager_spec.rb @@ -1,11 +1,5 @@ -describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager do - let(:provider) { FactoryGirl.build(:provider) } - let(:ansible_automation_manager) { FactoryGirl.build(:embedded_automation_manager_ansible, :provider => provider) } +require 'support/ansible_shared/automation_manager' - describe "#connect" do - it "delegates to the provider" do - expect(provider).to receive(:connect) - ansible_automation_manager.connect - end - end +describe ManageIQ::Providers::EmbeddedAnsible::AutomationManager do + it_behaves_like 'ansible automation_manager' end diff --git a/spec/models/manageiq/providers/embedded_ansible/provider_spec.rb b/spec/models/manageiq/providers/embedded_ansible/provider_spec.rb index 8f3a77a68aa..c399fa76485 100644 --- a/spec/models/manageiq/providers/embedded_ansible/provider_spec.rb +++ b/spec/models/manageiq/providers/embedded_ansible/provider_spec.rb @@ -1,69 +1,5 @@ -require "ansible_tower_client" +require 'support/ansible_shared/provider' describe ManageIQ::Providers::EmbeddedAnsible::Provider do - subject { FactoryGirl.build(:provider_embedded_ansible) } - - describe "#connect" do - let(:attrs) { {:username => "admin", :password => "smartvm", :verify_ssl => OpenSSL::SSL::VERIFY_PEER} } - - it "with no port" do - url = "example.com" - - expect(AnsibleTowerClient::Connection).to receive(:new).with(attrs.merge(:base_url => url)) - subject.connect(attrs.merge(:url => url)) - end - - it "with a port" do - url = "example.com:555" - - expect(AnsibleTowerClient::Connection).to receive(:new).with(attrs.merge(:base_url => url)) - subject.connect(attrs.merge(:url => url)) - end - end - - describe "#destroy" do - it "will remove all child objects" do - provider = FactoryGirl.create(:provider_embedded_ansible, :zone => FactoryGirl.create(:zone)) - - provider.automation_manager.configured_systems = [ - FactoryGirl.create(:configured_system, :computer_system => - FactoryGirl.create(:computer_system, - :operating_system => FactoryGirl.create(:operating_system), - :hardware => FactoryGirl.create(:hardware), - ) - ) - ] - - provider.destroy - - expect(Provider.count).to eq(0) - expect(ConfiguredSystem.count).to eq(0) - expect(ComputerSystem.count).to eq(0) - expect(OperatingSystem.count).to eq(0) - expect(Hardware.count).to eq(0) - end - end - - context "#url=" do - it "with full URL" do - subject.url = "https://server.example.com:1234/api/v1" - expect(subject.url).to eq("https://server.example.com:1234/api/v1") - end - - it "missing scheme" do - subject.url = "server.example.com:1234/api/v1" - expect(subject.url).to eq("https://server.example.com:1234/api/v1") - end - - it "works with #update_attributes" do - subject.update_attributes(:url => "server.example.com") - subject.update_attributes(:url => "server2.example.com") - expect(Endpoint.find(subject.default_endpoint.id).url).to eq("https://server2.example.com/api/v1") - end - end - - it "with only hostname" do - subject.url = "server.example.com" - expect(subject.url).to eq("https://server.example.com/api/v1") - end + it_behaves_like 'ansible provider' end diff --git a/spec/support/ansible_shared/automation_manager.rb b/spec/support/ansible_shared/automation_manager.rb new file mode 100644 index 00000000000..57e49ff3ad0 --- /dev/null +++ b/spec/support/ansible_shared/automation_manager.rb @@ -0,0 +1,11 @@ +shared_examples_for "ansible automation_manager" do + let(:provider) { FactoryGirl.build(:provider) } + let(:ansible_automation_manager) { FactoryGirl.build(:automation_manager_ansible_tower, :provider => provider) } + + describe "#connect" do + it "delegates to the provider" do + expect(provider).to receive(:connect) + ansible_automation_manager.connect + end + end +end diff --git a/spec/support/ansible_shared/automation_manager/configuration_script.rb b/spec/support/ansible_shared/automation_manager/configuration_script.rb new file mode 100644 index 00000000000..996d9a6c901 --- /dev/null +++ b/spec/support/ansible_shared/automation_manager/configuration_script.rb @@ -0,0 +1,176 @@ +require 'ansible_tower_client' +require 'faraday' + +shared_examples_for "ansible configuration_script" do + let(:api) { double(:api, :job_templates => double(:job_templates)) } + let(:connection) { double(:connection, :api => api) } + let(:job) { AnsibleTowerClient::Job.new(connection.api, "id" => 1) } + let(:job_template) { AnsibleTowerClient::JobTemplate.new(connection.api, "limit" => "", "id" => 1, "url" => "api/job_templates/1/", "name" => "template", "description" => "description", "extra_vars" => {:instance_ids => ['i-3434']}) } + let(:manager) { FactoryGirl.create(:automation_manager_ansible_tower, :provider, :configuration_script) } + + it "belongs_to the Ansible Tower manager" do + expect(manager.configuration_scripts.size).to eq 1 + expect(manager.configuration_scripts.first.variables).to eq :instance_ids => ['i-3434'] + expect(manager.configuration_scripts.first).to be_a ConfigurationScript + end + + context "relates to playbook" do + let(:configuration_script_source) { FactoryGirl.create(:configuration_script_source, :manager => manager) } + let!(:payload) { FactoryGirl.create(:configuration_script_payload) } + let(:configuration_scripts_without_payload) { FactoryGirl.create(:configuration_script) } + let(:configuration_scripts) do + [FactoryGirl.create(:configuration_script, :parent => payload), + FactoryGirl.create(:configuration_script, :parent => payload)] + end + + it "can refer to a payload" do + expect(configuration_scripts[0].parent).to eq(payload) + expect(configuration_scripts[1].parent).to eq(payload) + expect(payload.children).to match_array(configuration_scripts) + end + + it "can be without payload" do + expect(configuration_scripts_without_payload.parent).to be_nil + end + end + + context "#run" do + before do + allow_any_instance_of(Provider).to receive_messages(:connect => connection) + allow(api.job_templates).to receive(:find) { job_template } + end + + it "launches the referenced ansible job template" do + expect(job_template).to receive(:launch).with(:extra_vars => "{\"instance_ids\":[\"i-3434\"]}").and_return(job) + expect(manager.configuration_scripts.first.run).to be_a AnsibleTowerClient::Job + end + + it "accepts different variables to launch a job template against" do + added_extras = {:extra_vars => {:some_key => :some_value}} + expect(job_template).to receive(:launch).with(:extra_vars=>"{\"instance_ids\":[\"i-3434\"],\"some_key\":\"some_value\"}").and_return(job) + expect(manager.configuration_scripts.first.run(added_extras)).to be_a AnsibleTowerClient::Job + end + end + + context "#merge_extra_vars" do + it "merges internal and external hashes to send out to the tower gem" do + config_script = manager.configuration_scripts.first + external = {:some_key => :some_value} + internal = config_script.variables + expect(internal).to be_a Hash + expect(config_script.merge_extra_vars(external)).to eq(:extra_vars => "{\"instance_ids\":[\"i-3434\"],\"some_key\":\"some_value\"}") + end + + it "merges an internal hash and an empty hash to send out to the tower gem" do + config_script = manager.configuration_scripts.first + external = nil + expect(config_script.merge_extra_vars(external)).to eq(:extra_vars => "{\"instance_ids\":[\"i-3434\"]}") + end + + it "merges an empty internal hash and a hash to send out to the tower gem" do + external = {:some_key => :some_value} + internal = {} + config_script = manager.configuration_scripts.first + config_script.variables = internal + expect(config_script.merge_extra_vars(external)).to eq(:extra_vars => "{\"some_key\":\"some_value\"}") + end + + it "merges all empty arguments to send out to the tower gem" do + external = nil + internal = {} + config_script = manager.configuration_scripts.first + config_script.variables = internal + expect(config_script.merge_extra_vars(external)).to eq(:extra_vars => "{}") + end + end + + context "creates via the API" do + let(:provider) { FactoryGirl.create(:provider_ansible_tower, :with_authentication) } + let(:manager) { provider.managers.first } + let(:atc) { double("AnsibleTowerClient::Connection", :api => api) } + let(:api) { double("AnsibleTowerClient::Api", :job_templates => job_templates) } + let(:job_templates) { double("AnsibleTowerClient::Collection", :create! => job_template) } + let(:job_template) { AnsibleTowerClient::JobTemplate.new(nil, job_template_json) } + + let(:job_template_json) do + params.merge( + :id => 10, + :inventory => 1, + :related => {"inventory" => "blah/1"} + ).except(:inventory_id).stringify_keys.to_json + end + + let(:params) do + { + :description => "Description", + :extra_vars => {}.to_json, + :inventory_id => 1, + :name => "My Job Template", + :related => {} + } + end + + context ".create_in_provider" do + let(:finished_task) { FactoryGirl.create(:miq_task, :state => "Finished") } + + it "successfully created in provider" do + expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) + + store_new_job_template(job_template, manager) + + expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) + expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) + + expect(described_class.create_in_provider(manager.id, params)).to be_a(described_class) + end + + it "not found during refresh" do + expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) + expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) + expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) + + expect { described_class.create_in_provider(manager.id, params) }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + it ".create_in_provider_queue" do + EvmSpecHelper.local_miq_server + task_id = described_class.create_in_provider_queue(manager.id, params) + expect(MiqTask.find(task_id)).to have_attributes(:name => "Creating Ansible Tower Job Template") + expect(MiqQueue.first).to have_attributes( + :args => [manager.id, params], + :class_name => described_class.name, + :method_name => "create_in_provider", + :priority => MiqQueue::HIGH_PRIORITY, + :role => "ems_operations", + :zone => manager.zone.name + ) + end + + it ".update_in_provider_queue" do + EvmSpecHelper.local_miq_server + params[:manager_ref] = 10 + task_id = described_class.update_in_provider_queue(manager.id, params) + expect(MiqTask.find(task_id)).to have_attributes(:name => "Updating Ansible Tower Job Template") + expect(MiqQueue.first).to have_attributes( + :args => [manager.id, params], + :class_name => described_class.name, + :method_name => "update_in_provider", + :priority => MiqQueue::HIGH_PRIORITY, + :role => "ems_operations", + :zone => manager.zone.name + ) + end + + def store_new_job_template(job_template, manager) + described_class.create!( + :manager => manager, + :manager_ref => job_template.id.to_s, + :name => job_template.name, + :survey_spec => job_template.survey_spec_hash, + :variables => job_template.extra_vars_hash, + ) + end + + end +end diff --git a/spec/support/ansible_shared/automation_manager/configuration_script_source.rb b/spec/support/ansible_shared/automation_manager/configuration_script_source.rb new file mode 100644 index 00000000000..86c2e9c7e93 --- /dev/null +++ b/spec/support/ansible_shared/automation_manager/configuration_script_source.rb @@ -0,0 +1,120 @@ +require 'ansible_tower_client' + +shared_examples_for "ansible configuration_script_source" do + let(:finished_task) { FactoryGirl.create(:miq_task, :state => "Finished") } + let(:manager) { FactoryGirl.create(:provider_ansible_tower, :with_authentication).managers.first } + let(:atc) { double("AnsibleTowerClient::Connection", :api => api) } + let(:api) { double("AnsibleTowerClient::Api", :projects => projects) } + + context "create through API" do + let(:projects) { double("AnsibleTowerClient::Collection", :create! => project) } + let(:project) { AnsibleTowerClient::Project.new(nil, project_json) } + + let(:project_json) do + params.merge( + :id => 10, + "scm_type" => "git", + "scm_url" => "https://github.com/ansible/ansible-tower-samples" + ).stringify_keys.to_json + end + + let(:params) do + { + :description => "Description", + :name => "My Project", + :related => {} + } + end + + it ".create_in_provider" do + expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) + store_new_project(project, manager) + expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) + expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) + + expect(described_class.create_in_provider(manager.id, params)).to be_a(described_class) + end + + it "not found during refresh" do + expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) + expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) + expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) + + expect { described_class.create_in_provider(manager.id, params) }.to raise_error(ActiveRecord::RecordNotFound) + end + + it ".create_in_provider_queue" do + EvmSpecHelper.local_miq_server + task_id = described_class.create_in_provider_queue(manager.id, params) + expect(MiqTask.find(task_id)).to have_attributes(:name => "Creating #{described_class.name} with name=#{params[:name]}") + expect(MiqQueue.first).to have_attributes( + :args => [manager.id, params], + :class_name => described_class.name, + :method_name => "create_in_provider", + :priority => MiqQueue::HIGH_PRIORITY, + :role => "ems_operations", + :zone => manager.my_zone + ) + end + + def store_new_project(project, manager) + described_class.create!( + :manager => manager, + :manager_ref => project.id.to_s, + :name => project.name, + ) + end + end + + context "Delete through API" do + let(:projects) { double("AnsibleTowerClient::Collection", :find => tower_project) } + let(:tower_project) { double("AnsibleTowerClient::Project", :destroy! => nil, :id => 1) } + let(:project) { described_class.create!(:manager => manager, :manager_ref => tower_project.id) } + + it "#delete_in_provider" do + expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) + expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) + project.delete_in_provider + end + + it "#delete_in_provider_queue" do + task_id = project.delete_in_provider_queue + expect(MiqTask.find(task_id)).to have_attributes(:name => "Deleting #{described_class.name} with manager_ref=#{project.manager_ref}") + expect(MiqQueue.first).to have_attributes( + :instance_id => project.id, + :args => [], + :class_name => described_class.name, + :method_name => "delete_in_provider", + :priority => MiqQueue::HIGH_PRIORITY, + :role => "ems_operations", + :zone => manager.my_zone + ) + end + end + + context "Update through API" do + let(:projects) { double("AnsibleTowerClient::Collection", :find => tower_project) } + let(:tower_project) { double("AnsibleTowerClient::Project", :update_attributes! => {}, :id => 1) } + let(:project) { described_class.create!(:manager => manager, :manager_ref => tower_project.id) } + + it "#update_in_provider" do + expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) + expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) + expect(project.update_in_provider({})).to be_a(described_class) + end + + it "#update_in_provider_queue" do + task_id = project.update_in_provider_queue({}) + expect(MiqTask.find(task_id)).to have_attributes(:name => "Updating #{described_class.name} with manager_ref=#{project.manager_ref}") + expect(MiqQueue.first).to have_attributes( + :instance_id => project.id, + :args => [{:task_id => task_id}], + :class_name => described_class.name, + :method_name => "update_in_provider", + :priority => MiqQueue::HIGH_PRIORITY, + :role => "ems_operations", + :zone => manager.my_zone + ) + end + end +end diff --git a/spec/support/ansible_shared/automation_manager/credential.rb b/spec/support/ansible_shared/automation_manager/credential.rb new file mode 100644 index 00000000000..e35028c42a8 --- /dev/null +++ b/spec/support/ansible_shared/automation_manager/credential.rb @@ -0,0 +1,117 @@ +require 'ansible_tower_client' + +shared_examples_for "ansible credential" do + let(:finished_task) { FactoryGirl.create(:miq_task, :state => "Finished") } + let(:manager) { FactoryGirl.create(:provider_ansible_tower, :with_authentication).managers.first } + let(:atc) { double("AnsibleTowerClient::Connection", :api => api) } + let(:api) { double("AnsibleTowerClient::Api", :credentials => credentials) } + + context "Create through API" do + let(:credentials) { double("AnsibleTowerClient::Collection", :create! => credential) } + let(:credential) { AnsibleTowerClient::Credential.new(nil, credential_json) } + + let(:credential_json) do + params.merge( + :id => 10, + ).stringify_keys.to_json + end + + let(:params) do + { + :description => "Description", + :name => "My Credential", + :related => {} + } + end + + it ".create_in_provider" do + expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) + store_new_credential(credential, manager) + expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) + expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) + + expect(described_class.create_in_provider(manager.id, params)).to be_a(described_class) + end + + it "not found during refresh" do + expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) + expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) + expect(ExtManagementSystem).to receive(:find).with(manager.id).and_return(manager) + + expect { described_class.create_in_provider(manager.id, params) }.to raise_error(ActiveRecord::RecordNotFound) + end + + it ".create_in_provider_queue" do + task_id = described_class.create_in_provider_queue(manager.id, params) + expect(MiqTask.find(task_id)).to have_attributes(:name => "Creating #{described_class.name} with name=#{params[:name]}") + expect(MiqQueue.first).to have_attributes( + :args => [manager.id, params], + :class_name => described_class.name, + :method_name => "create_in_provider", + :priority => MiqQueue::HIGH_PRIORITY, + :role => "ems_operations", + :zone => manager.my_zone + ) + end + + def store_new_credential(credential, manager) + described_class.create!( + :resource => manager, + :manager_ref => credential.id.to_s, + :name => credential.name, + ) + end + end + + context "Delete through API" do + let(:credentials) { double("AnsibleTowerClient::Collection", :find => credential) } + let(:credential) { double("AnsibleTowerClient::Credential", :destroy! => nil, :id => 1) } + let(:ansible_cred) { described_class.create!(:resource => manager, :manager_ref => credential.id) } + + it "#delete_in_provider" do + expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) + expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) + ansible_cred.delete_in_provider + end + + it "#delete_in_provider_queue" do + task_id = ansible_cred.delete_in_provider_queue + expect(MiqTask.find(task_id)).to have_attributes(:name => "Deleting #{described_class.name} with manager_ref=#{ansible_cred.manager_ref}") + expect(MiqQueue.first).to have_attributes( + :instance_id => ansible_cred.id, + :args => [], + :class_name => described_class.name, + :method_name => "delete_in_provider", + :priority => MiqQueue::HIGH_PRIORITY, + :role => "ems_operations", + :zone => manager.my_zone + ) + end + end + + context "Update through API" do + let(:credentials) { double("AnsibleTowerClient::Collection", :find => credential) } + let(:credential) { double("AnsibleTowerClient::Credential", :update_attributes! => {}, :id => 1) } + let(:ansible_cred) { described_class.create!(:resource => manager, :manager_ref => credential.id) } + + it "#update_in_provider" do + expect(AnsibleTowerClient::Connection).to receive(:new).and_return(atc) + expect(EmsRefresh).to receive(:queue_refresh_task).and_return([finished_task]) + expect(ansible_cred.update_in_provider({})).to be_a(described_class) + end + + it "#update_in_provider_queue" do + task_id = ansible_cred.update_in_provider_queue({}) + expect(MiqTask.find(task_id)).to have_attributes(:name => "Updating #{described_class.name} with manager_ref=#{ansible_cred.manager_ref}") + expect(MiqQueue.first).to have_attributes( + :instance_id => ansible_cred.id, + :args => [{:task_id => task_id}], + :class_name => described_class.name, + :method_name => "update_in_provider", + :priority => MiqQueue::HIGH_PRIORITY, + :role => "ems_operations", + :zone => manager.my_zone + ) + end + end +end diff --git a/spec/support/ansible_shared/automation_manager/event_catcher/stream.rb b/spec/support/ansible_shared/automation_manager/event_catcher/stream.rb new file mode 100644 index 00000000000..ca26b273dfe --- /dev/null +++ b/spec/support/ansible_shared/automation_manager/event_catcher/stream.rb @@ -0,0 +1,69 @@ +shared_examples_for "ansible event_catcher stream" do |cassette_file| + let(:tower_url) { ENV['TOWER_URL'] || "https://dev-ansible-tower3.example.com/api/v1/" } + let(:auth_userid) { ENV['TOWER_USER'] || 'testuser' } + let(:auth_password) { ENV['TOWER_PASSWORD'] || 'secret' } + + let(:auth) { FactoryGirl.create(:authentication, :userid => auth_userid, :password => auth_password) } + let(:automation_manager) { provider.automation_manager } + let(:provider) do + FactoryGirl.create(:provider_ansible_tower, + :url => tower_url, + :verify_ssl => false,).tap { |provider| provider.authentications << auth } + end + + subject do + described_class.new(automation_manager) + end + + context "#poll" do + it "yields valid events" do + subject.instance_variable_set(:@last_activity, OpenStruct.new(:timestamp => '2016-01-01 01:00:00')) + subject.instance_variable_set(:@poll_sleep, 0) + expected_event = { + "id" => 1, + "type" => "activity_stream", + "url" => "/api/v1/activity_stream/1/", + "related" => { + "user" => [ + "/api/v1/users/1/" + ] + }, + "summary_fields" => { + "user" => [ + { + "username" => "admin", + "first_name" => "", + "last_name" => "", + "id" => 1 + } + ] + }, + "timestamp" => "2016-08-02T17:56:37.212874Z", + "operation" => "create", + "changes" => { + "username" => "admin", + "first_name" => "", + "last_name" => "", + "is_active" => true, + "id" => 1, + "is_superuser" => true, + "is_staff" => true, + "password" => "hidden", + "email" => "admin@example.com", + "date_joined" => "2016-08-02 17:56:37.162225+00:00" + }, + "object1" => "user", + "object2" => "", + "object_association" => "" + } + + VCR.use_cassette(cassette_file) do + subject.poll do |event| + polled_event = event + expect(polled_event.to_h).to eq(expected_event) + subject.stop + end + end + end + end +end diff --git a/spec/support/ansible_shared/automation_manager/job.rb b/spec/support/ansible_shared/automation_manager/job.rb new file mode 100644 index 00000000000..155f98caf59 --- /dev/null +++ b/spec/support/ansible_shared/automation_manager/job.rb @@ -0,0 +1,156 @@ +require 'ansible_tower_client' +require 'faraday' + +shared_examples_for "ansible job" do + let(:faraday_connection) { instance_double("Faraday::Connection", :post => post, :get => get) } + let(:post) { instance_double("Faraday::Result", :body => {}.to_json) } + let(:get) { instance_double("Faraday::Result", :body => {'id' => 1}.to_json) } + + let(:connection) { double(:connection, :api => double(:api, :jobs => double(:jobs, :find => the_raw_job))) } + + let(:manager) { FactoryGirl.create(:automation_manager_ansible_tower, :provider) } + let(:mock_api) { AnsibleTowerClient::Api.new(faraday_connection) } + + let(:machine_credential) { FactoryGirl.create(:ansible_machine_credential, :manager_ref => '1', :resource => manager) } + let(:cloud_credential) { FactoryGirl.create(:ansible_cloud_credential, :manager_ref => '2', :resource => manager) } + let(:network_credential) { FactoryGirl.create(:ansible_network_credential, :manager_ref => '3', :resource => manager) } + + let(:the_raw_job) do + AnsibleTowerClient::Job.new( + mock_api, + 'id' => '1', + 'name' => template.name, + 'status' => 'Successful', + 'extra_vars' => {'param1' => 'val1'}.to_json, + 'verbosity' => 3, + 'started' => Time.current, + 'finished' => Time.current, + 'credential_id' => machine_credential.manager_ref, + 'cloud_credential_id' => cloud_credential.manager_ref, + 'network_credential_id' => network_credential.manager_ref + ).tap do |rjob| + allow(rjob).to receive(:stdout).and_return('job stdout') + allow(rjob).to receive(:job_plays).and_return(the_raw_plays) + end + end + + let(:the_raw_plays) do + [ + double('play1', :play => 'play1', :started => Time.current, :failed => false, :id => 1), + double('play2', :play => 'play2', :started => Time.current + 1, :failed => true, :id => 2) + ] + end + + let(:template) { FactoryGirl.create(:configuration_script, :manager => manager) } + subject { FactoryGirl.create(:ansible_tower_job, :job_template => template, :ext_management_system => manager) } + + describe 'job operations' do + context ".create_job" do + it 'creates a job' do + expect(template).to receive(:run).and_return(the_raw_job) + + job = described_class.create_job(template, {}) + expect(job.class).to eq(described_class) + expect(job.name).to eq(template.name) + expect(job.ems_ref).to eq(the_raw_job.id) + expect(job.job_template).to eq(template) + expect(job.status).to eq(the_raw_job.status) + expect(job.ext_management_system).to eq(manager) + end + + it 'catches errors from provider' do + expect(template).to receive(:run).and_raise('bad request') + + expect do + described_class.create_job(template, {}) + end.to raise_error(MiqException::MiqOrchestrationProvisionError) + end + end + + context "#refres_ems" do + before do + allow_any_instance_of(Provider).to receive_messages(:connect => connection) + end + + it 'syncs the job with the provider' do + subject.refresh_ems + expect(subject).to have_attributes( + :ems_ref => the_raw_job.id, + :status => the_raw_job.status, + :start_time => the_raw_job.started, + :finish_time => the_raw_job.finished, + :verbosity => the_raw_job.verbosity + ) + subject.reload + expect(subject.ems_ref).to eq(the_raw_job.id) + expect(subject.status).to eq(the_raw_job.status) + expect(subject.parameters.first).to have_attributes(:name => 'param1', :value => 'val1') + expect(subject.authentications).to match_array([machine_credential, cloud_credential, network_credential]) + + expect(subject.job_plays.first).to have_attributes( + :start_time => the_raw_plays.first.started, + :finish_time => the_raw_plays.last.started, + :resource_status => 'successful', + :resource_category => 'job_play', + :name => 'play1' + ) + expect(subject.job_plays.last).to have_attributes( + :start_time => the_raw_plays.last.started, + :finish_time => the_raw_job.finished, + :resource_status => 'failed', + :resource_category => 'job_play', + :name => 'play2' + ) + end + + it 'catches errors from provider' do + expect(connection.api.jobs).to receive(:find).and_raise('bad request') + expect { subject.refresh_ems }.to raise_error(MiqException::MiqOrchestrationUpdateError) + end + end + end + + describe 'job status' do + before do + allow_any_instance_of(Provider).to receive_messages(:connect => connection) + end + + context '#raw_status and #raw_exists' do + it 'gets the stack status' do + rstatus = subject.raw_status + expect(rstatus).to have_attributes(:status => 'Successful', :reason => nil) + + expect(subject.raw_exists?).to be_truthy + end + + it 'detects job not exist' do + expect(connection.api.jobs).to receive(:find).twice.and_raise(AnsibleTowerClient::ResourceNotFoundError.new(nil)) + expect { subject.raw_status }.to raise_error(MiqException::MiqOrchestrationStackNotExistError) + + expect(subject.raw_exists?).to be_falsey + end + + it 'catches errors from provider' do + expect(connection.api.jobs).to receive(:find).twice.and_raise("bad happened") + expect { subject.raw_status }.to raise_error(MiqException::MiqOrchestrationStatusError) + + expect { subject.raw_exists? }.to raise_error(MiqException::MiqOrchestrationStatusError) + end + end + end + + describe '#raw_stdout' do + before do + allow_any_instance_of(Provider).to receive_messages(:connect => connection) + end + + it 'gets the standard output of the job' do + expect(subject.raw_stdout).to eq('job stdout') + end + + it 'catches errors from provider' do + expect(connection.api.jobs).to receive(:find).and_raise("bad happened") + expect { subject.raw_stdout }.to raise_error(MiqException::MiqOrchestrationStatusError) + end + end +end diff --git a/spec/support/ansible_shared/automation_manager/job/status.rb b/spec/support/ansible_shared/automation_manager/job/status.rb new file mode 100644 index 00000000000..90fbebc778b --- /dev/null +++ b/spec/support/ansible_shared/automation_manager/job/status.rb @@ -0,0 +1,42 @@ +shared_examples_for "ansible job status" do + + it 'parses Succeeded' do + status = described_class.new('Successful', '') + expect(status.completed?).to be_truthy + expect(status.succeeded?).to be_truthy + expect(status.failed?).to be_falsey + expect(status.deleted?).to be_falsey + expect(status.rolled_back?).to be_falsey + expect(status.normalized_status).to eq(['create_complete', '']) + end + + it 'parses Failed' do + status = described_class.new('Failed', nil) + expect(status.completed?).to be_truthy + expect(status.succeeded?).to be_falsey + expect(status.failed?).to be_truthy + expect(status.deleted?).to be_falsey + expect(status.rolled_back?).to be_falsey + expect(status.normalized_status).to eq(['failed', 'Job launching failed']) + end + + it 'parses Canceled' do + status = described_class.new('Canceled', nil) + expect(status.completed?).to be_truthy + expect(status.succeeded?).to be_falsey + expect(status.canceled?).to be_truthy + expect(status.deleted?).to be_falsey + expect(status.rolled_back?).to be_falsey + expect(status.normalized_status).to eq(['create_canceled', 'Job launching was canceled']) + end + + it 'parses transient status' do + status = described_class.new('Running', nil) + expect(status.completed?).to be_falsey + expect(status.succeeded?).to be_falsey + expect(status.failed?).to be_falsey + expect(status.deleted?).to be_falsey + expect(status.rolled_back?).to be_falsey + expect(status.normalized_status).to eq(%w(transient Running)) + end +end diff --git a/spec/support/ansible_shared/automation_manager/refresher.rb b/spec/support/ansible_shared/automation_manager/refresher.rb new file mode 100644 index 00000000000..24ff8d65f56 --- /dev/null +++ b/spec/support/ansible_shared/automation_manager/refresher.rb @@ -0,0 +1,194 @@ + +shared_examples_for "ansible refresher" do |ansible_provider, manager_class, ems_type, cassette_path| + # To re-record cassettes or to add cassettes you can add another inner `VCR.use_cassette` block to the + # 'will perform a full refresh' example. When running specs, new requests are recorded to the innermost cassette and + # can be played back from any level of nesting (it tries the innermost cassette first, then searches up the parent + # chain) - http://stackoverflow.com/a/13425826 + # + # To add a new cassette + # * add another block (innermost) with an empty cassette + # * change existing cassettes to use your working credentials + # * run the specs to create a new cassette + # * change new and existing cassettes to use default credentials + # + # To re-record a cassette + # * temporarily make the cassette the innermost one (see above about recording) + # * rm cassette ; run specs + # * change back the order of cassettes + # + # To change credentials in cassettes: + # replace with defaults - before committing + # ruby -pi -e 'gsub /yourdomain.com/, "example.com"; gsub /admin:smartvm/, "testuser:secret"' spec/vcr_cassettes/manageiq/providers/ansible_tower/automation_manager/*.yml + # replace with your working credentials + # ruby -pi -e 'gsub /example.com/, "yourdomain.com"; gsub /testuser:secret/, "admin:smartvm"' spec/vcr_cassettes/manageiq/providers/ansible_tower/automation_manager/*.yml + + let(:tower_url) { ENV['TOWER_URL'] || "https://dev-ansible-tower3.example.com/api/v1/" } + let(:auth_userid) { ENV['TOWER_USER'] || 'testuser' } + let(:auth_password) { ENV['TOWER_PASSWORD'] || 'secret' } + + let(:auth) { FactoryGirl.create(:authentication, :userid => auth_userid, :password => auth_password) } + let(:automation_manager) { provider.automation_manager } + let(:expected_counterpart_vm) { FactoryGirl.create(:vm, :uid_ems => "4233080d-7467-de61-76c9-c8307b6e4830") } + let(:provider) do + _guid, _server, zone = EvmSpecHelper.create_guid_miq_server_zone + FactoryGirl.create(ansible_provider, + :zone => zone, + :url => tower_url, + :verify_ssl => false,).tap { |provider| provider.authentications << auth } + end + let(:manager_class) { manager_class } + + it ".ems_type" do + expect(described_class.ems_type).to eq(ems_type) + end + + it "will perform a full refresh" do + expected_counterpart_vm + + 2.times do + # to re-record cassettes see comment at the beginning of this file + VCR.use_cassette(cassette_path) do + VCR.use_cassette("#{cassette_path}_configuration_script_sources") do + VCR.use_cassette("#{cassette_path}_credentials") do + EmsRefresh.refresh(automation_manager) + expect(automation_manager.reload.last_refresh_error).to be_nil + end + end + end + assert_counts + assert_configured_system + assert_configuration_script_with_nil_survey_spec + assert_configuration_script_with_survey_spec + assert_inventory_root_group + assert_configuration_script_sources + assert_playbooks + assert_credentials + end + end + + def assert_counts + expect(Provider.count).to eq(1) + expect(automation_manager).to have_attributes(:api_version => "3.0.1") + expect(automation_manager.configured_systems.count).to eq(84) + expect(automation_manager.configuration_scripts.count).to eq(11) + expect(automation_manager.inventory_groups.count).to eq(6) + expect(automation_manager.configuration_script_sources.count).to eq(6) + expect(automation_manager.configuration_script_payloads.count).to eq(438) + expect(automation_manager.credentials.count).to eq(8) + end + + def assert_credentials + expect(expected_configuration_script.authentications.count).to eq(3) + machine_credential = expected_configuration_script.authentications.find_by( + :type => manager_class::MachineCredential + ) + expect(machine_credential).to have_attributes( + :name => "Demo Credential", + :userid => "admin", + ) + expect(machine_credential.options.keys).to match_array(machine_credential.class::EXTRA_ATTRIBUTES.keys) + expect(machine_credential.options[:become_method]).to eq('su') + expect(machine_credential.options[:become_username]).to eq('root') + + network_credential = expected_configuration_script.authentications.find_by( + :type => manager_class::NetworkCredential + ) + expect(network_credential).to have_attributes( + :name => "Demo Creds 2", + :userid => "awdd", + ) + expect(network_credential.options.keys).to match_array(network_credential.class::EXTRA_ATTRIBUTES.keys) + + cloud_credential = expected_configuration_script.authentications.find_by( + :type => manager_class::VmwareCredential + ) + expect(cloud_credential).to have_attributes( + :name => "dev-vc60", + :userid => "MiqAnsibleUser@vsphere.local", + ) + expect(cloud_credential.options.keys).to match_array(cloud_credential.class::EXTRA_ATTRIBUTES.keys) + end + + def assert_playbooks + expect(expected_configuration_script_source.configuration_script_payloads.first).to be_an_instance_of(manager_class::Playbook) + expect(expected_configuration_script_source.configuration_script_payloads.count).to eq(8) + expect(expected_configuration_script_source.configuration_script_payloads.map(&:name)).to include('start_ec2.yml') + end + + def assert_configuration_script_sources + expect(automation_manager.configuration_script_sources.count).to eq(6) + expect(expected_configuration_script_source).to be_an_instance_of(manager_class::ConfigurationScriptSource) + expect(expected_configuration_script_source).to have_attributes( + :name => 'DB_Github', + :description => 'DB Playbooks', + :scm_type => 'git', + :scm_url => 'https://github.com/syncrou/playbooks', + :scm_branch => 'master', + :scm_clean => false, + :scm_delete_on_update => false, + :scm_update_on_launch => true + ) + expect(expected_configuration_script_source.authentication.name).to eq('db-github') + end + + def assert_configured_system + expect(expected_configured_system).to have_attributes( + :type => manager_class::ConfiguredSystem.name, + :hostname => "Ansible-Host", + :manager_ref => "3", + :virtual_instance_ref => "4233080d-7467-de61-76c9-c8307b6e4830", + ) + expect(expected_configured_system.counterpart).to eq(expected_counterpart_vm) + expect(expected_configured_system.inventory_root_group).to eq(expected_inventory_root_group) + end + + def assert_configuration_script_with_nil_survey_spec + expect(expected_configuration_script).to have_attributes( + :description => "Ansible-JobTemplate-Description", + :manager_ref => "80", + :name => "Ansible-JobTemplate", + :survey_spec => {}, + :variables => {'abc' => 123}, + ) + expect(expected_configuration_script.inventory_root_group).to have_attributes(:ems_ref => "2") + end + + def assert_configuration_script_with_survey_spec + system = automation_manager.configuration_scripts.where(:name => "Ansible-JobTemplate-Survey").first + expect(system).to have_attributes( + :name => "Ansible-JobTemplate-Survey", + :description => "Ansible-JobTemplate-Description", + :manager_ref => "81", + :variables => {'abc' => 123} + ) + survey = system.survey_spec + expect(survey).to be_a Hash + expect(survey['spec'].first['question_name']).to eq('Survey') + end + + def assert_inventory_root_group + expect(expected_inventory_root_group).to have_attributes( + :name => "Dev-VC60", + :ems_ref => "2", + :type => "ManageIQ::Providers::AutomationManager::InventoryRootGroup", + ) + end + + private + + def expected_configured_system + @expected_configured_system ||= automation_manager.configured_systems.where(:hostname => "Ansible-Host").first + end + + def expected_configuration_script + @expected_configuration_script ||= automation_manager.configuration_scripts.where(:name => "Ansible-JobTemplate").first + end + + def expected_inventory_root_group + @expected_inventory_root_group ||= automation_manager.inventory_groups.where(:name => "Dev-VC60").first + end + + def expected_configuration_script_source + @expected_configuration_script_source ||= automation_manager.configuration_script_sources.find_by(:name => 'DB_Github') + end +end diff --git a/spec/support/ansible_shared/automation_manager/refresher_v2.rb b/spec/support/ansible_shared/automation_manager/refresher_v2.rb new file mode 100644 index 00000000000..3cf5703e5dc --- /dev/null +++ b/spec/support/ansible_shared/automation_manager/refresher_v2.rb @@ -0,0 +1,151 @@ +shared_examples_for "ansible refresher_v2" do |ansible_provider, manager_class, ems_type, cassette_path| + # To change credentials in cassettes: + # replace with defaults - before committing + # ruby -pi -e 'gsub /yourdomain.com/, "example.com"; gsub /admin:smartvm/, "testuser:secret"' spec/vcr_cassettes/manageiq/providers/ansible_tower/automation_manager/refresher_v2.yml + # replace with your working credentials + # ruby -pi -e 'gsub /example.com/, "yourdomain.com"; gsub /testuser:secret/, "admin:smartvm"' spec/vcr_cassettes/manageiq/providers/ansible_tower/automation_manager/refresher_v2.yml + let(:tower_url) { ENV['TOWER_URL'] || "https://dev-ansible-tower2.example.com/api/v1/" } + let(:auth_userid) { ENV['TOWER_USER'] || 'testuser' } + let(:auth_password) { ENV['TOWER_PASSWORD'] || 'secret' } + + let(:auth) { FactoryGirl.create(:authentication, :userid => auth_userid, :password => auth_password) } + let(:automation_manager) { provider.automation_manager } + let(:expected_counterpart_vm) { FactoryGirl.create(:vm, :uid_ems => "4233080d-7467-de61-76c9-c8307b6e4830") } + let(:provider) do + _guid, _server, zone = EvmSpecHelper.create_guid_miq_server_zone + FactoryGirl.create(ansible_provider, + :zone => zone, + :url => tower_url, + :verify_ssl => false,).tap { |provider| provider.authentications << auth } + end + let(:manager_class) { manager_class } + + it ".ems_type" do + expect(described_class.ems_type).to eq(ems_type) + end + + it "will perform a full refresh" do + expected_counterpart_vm + + 2.times do + VCR.use_cassette("#{cassette_path}_v2") do + EmsRefresh.refresh(automation_manager) + expect(automation_manager.reload.last_refresh_error).to be_nil + end + + assert_counts + assert_configured_system + assert_configuration_script_with_nil_survey_spec + assert_configuration_script_with_survey_spec + assert_inventory_root_group + assert_configuration_script_sources + assert_playbooks + assert_credentials + end + end + + def assert_counts + expect(Provider.count).to eq(1) + expect(automation_manager).to have_attributes(:api_version => "2.4.2") + expect(automation_manager.configured_systems.count).to eq(168) + expect(automation_manager.configuration_scripts.count).to eq(14) + expect(automation_manager.inventory_groups.count).to eq(8) + expect(automation_manager.configuration_script_sources.count).to eq(6) + expect(automation_manager.configuration_script_payloads.count).to eq(77) + expect(automation_manager.credentials.count).to eq(11) + end + + def assert_credentials + expect(expected_configuration_script.authentications.count).to eq(2) + machine_credential = expected_configuration_script.authentications.find_by( + :type => manager_class::MachineCredential + ) + expect(machine_credential).to have_attributes( + :name => "appliance", + :userid => "root", + ) + cloud_credential = expected_configuration_script.authentications.find_by( + :type => manager_class::AmazonCredential + ) + expect(cloud_credential).to have_attributes( + :name => "AWS", + :userid => "065ZMGNV5WNKPMX4FF82", + ) + end + + def assert_playbooks + expect(expected_configuration_script_source.configuration_script_payloads.first).to be_an_instance_of(manager_class::Playbook) + expect(expected_configuration_script_source.configuration_script_payloads.count).to eq(7) + expect(expected_configuration_script_source.configuration_script_payloads.map(&:name)).to include('create_ec2.yml') + end + + def assert_configuration_script_sources + expect(automation_manager.configuration_script_sources.count).to eq(6) + expect(expected_configuration_script_source).to be_an_instance_of(manager_class::ConfigurationScriptSource) + expect(expected_configuration_script_source).to have_attributes( + :name => 'db-projects', + :description => 'projects', + ) + end + + def assert_configured_system + expect(expected_configured_system).to have_attributes( + :type => manager_class::ConfiguredSystem.name, + :hostname => "Ansible-Host", + :manager_ref => "145", + :virtual_instance_ref => "4233080d-7467-de61-76c9-c8307b6e4830", + ) + expect(expected_configured_system.counterpart).to eq(expected_counterpart_vm) + expect(expected_configured_system.inventory_root_group).to eq(expected_inventory_root_group) + end + + def assert_configuration_script_with_nil_survey_spec + expect(expected_configuration_script).to have_attributes( + :description => "Ansible-JobTemplate-Description", + :manager_ref => "149", + :name => "Ansible-JobTemplate", + :survey_spec => {}, + :variables => {'abc' => 123}, + ) + expect(expected_configuration_script.inventory_root_group).to have_attributes(:ems_ref => "2") + end + + def assert_configuration_script_with_survey_spec + system = automation_manager.configuration_scripts.where(:name => "Ansible-JobTemplate-Survey").first + expect(system).to have_attributes( + :name => "Ansible-JobTemplate-Survey", + :description => "Ansible-JobTemplate-Description", + :manager_ref => "155", + :variables => {'abc' => 123} + ) + survey = system.survey_spec + expect(survey).to be_a Hash + expect(survey['spec'].first['index']).to eq 0 + end + + def assert_inventory_root_group + expect(expected_inventory_root_group).to have_attributes( + :name => "Dev VC60", + :ems_ref => "17", + :type => "ManageIQ::Providers::AutomationManager::InventoryRootGroup", + ) + end + + private + + def expected_configured_system + @expected_configured_system ||= automation_manager.configured_systems.where(:hostname => "Ansible-Host").first + end + + def expected_configuration_script + @expected_configuration_script ||= automation_manager.configuration_scripts.where(:name => "Ansible-JobTemplate").first + end + + def expected_inventory_root_group + @expected_inventory_root_group ||= automation_manager.inventory_groups.where(:name => "Dev VC60").first + end + + def expected_configuration_script_source + @expected_configuration_script_source ||= automation_manager.configuration_script_sources.find_by(:name => 'db-projects') + end +end diff --git a/spec/support/ansible_shared/provider.rb b/spec/support/ansible_shared/provider.rb new file mode 100644 index 00000000000..5d573202a01 --- /dev/null +++ b/spec/support/ansible_shared/provider.rb @@ -0,0 +1,70 @@ +require "ansible_tower_client" + +shared_examples_for "ansible provider" do + + subject { FactoryGirl.build(:provider_ansible_tower) } + + describe "#connect" do + let(:attrs) { {:username => "admin", :password => "smartvm", :verify_ssl => OpenSSL::SSL::VERIFY_PEER} } + + it "with no port" do + url = "example.com" + + expect(AnsibleTowerClient::Connection).to receive(:new).with(attrs.merge(:base_url => url)) + subject.connect(attrs.merge(:url => url)) + end + + it "with a port" do + url = "example.com:555" + + expect(AnsibleTowerClient::Connection).to receive(:new).with(attrs.merge(:base_url => url)) + subject.connect(attrs.merge(:url => url)) + end + end + + describe "#destroy" do + it "will remove all child objects" do + provider = FactoryGirl.create(:provider_ansible_tower, :zone => FactoryGirl.create(:zone)) + + provider.automation_manager.configured_systems = [ + FactoryGirl.create(:configured_system, :computer_system => + FactoryGirl.create(:computer_system, + :operating_system => FactoryGirl.create(:operating_system), + :hardware => FactoryGirl.create(:hardware), + ) + ) + ] + + provider.destroy + + expect(Provider.count).to eq(0) + expect(ConfiguredSystem.count).to eq(0) + expect(ComputerSystem.count).to eq(0) + expect(OperatingSystem.count).to eq(0) + expect(Hardware.count).to eq(0) + end + end + + context "#url=" do + it "with full URL" do + subject.url = "https://server.example.com:1234/api/v1" + expect(subject.url).to eq("https://server.example.com:1234/api/v1") + end + + it "missing scheme" do + subject.url = "server.example.com:1234/api/v1" + expect(subject.url).to eq("https://server.example.com:1234/api/v1") + end + + it "works with #update_attributes" do + subject.update_attributes(:url => "server.example.com") + subject.update_attributes(:url => "server2.example.com") + expect(Endpoint.find(subject.default_endpoint.id).url).to eq("https://server2.example.com/api/v1") + end + end + + it "with only hostname" do + subject.url = "server.example.com" + expect(subject.url).to eq("https://server.example.com/api/v1") + end +end