diff --git a/lib/ansible/runner.rb b/lib/ansible/runner.rb index 19835d8d80f..8c77a8ab344 100644 --- a/lib/ansible/runner.rb +++ b/lib/ansible/runner.rb @@ -263,7 +263,7 @@ def credentials_info(credentials, base_dir) env_vars.merge!(cred.env_vars) extra_vars.merge!(cred.extra_vars) - cred.write_password_file + cred.write_config_files end [command_line, env_vars, extra_vars] diff --git a/lib/ansible/runner/credential.rb b/lib/ansible/runner/credential.rb index 45f4b5bb83c..30813a97f88 100644 --- a/lib/ansible/runner/credential.rb +++ b/lib/ansible/runner/credential.rb @@ -31,7 +31,7 @@ def extra_vars {} end - def write_password_file + def write_config_files end private diff --git a/lib/ansible/runner/credential/amazon_credential.rb b/lib/ansible/runner/credential/amazon_credential.rb new file mode 100644 index 00000000000..ade7042658e --- /dev/null +++ b/lib/ansible/runner/credential/amazon_credential.rb @@ -0,0 +1,21 @@ +module Ansible + class Runner + class AmazonCredential < Credential + def self.auth_type + "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::AmazonCredential" + end + + # Modeled off of aws injectors for awx: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/injectors.py#L11-L15 + # + def env_vars + { + "AWS_ACCESS_KEY_ID" => auth.userid || "", + "AWS_SECRET_ACCESS_KEY" => auth.password || "", + "AWS_SECURITY_TOKEN" => auth.auth_key + }.delete_nils + end + end + end +end diff --git a/lib/ansible/runner/credential/azure_credential.rb b/lib/ansible/runner/credential/azure_credential.rb new file mode 100644 index 00000000000..3c90f41f0e6 --- /dev/null +++ b/lib/ansible/runner/credential/azure_credential.rb @@ -0,0 +1,33 @@ +module Ansible + class Runner + class AzureCredential < Credential + def self.auth_type + "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::AzureCredential" + end + + # Modeled off of azure injectors for awx: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/injectors.py#L45-L60 + # + # NOTE: We don't currently support the AZURE_CLOUD_ENVIRONMENT variable + # as a configurable option. + # + def env_vars + if auth.options && auth.options[:client].present? && auth.options[:tenant].present? + { + "AZURE_CLIENT_ID" => (auth.options || {})[:client], + "AZURE_TENANT" => (auth.options || {})[:tenant], + "AZURE_SECRET" => auth.auth_key || "", + "AZURE_SUBSCRIPTION_ID" => (auth.options || {})[:subscription] || "" + } + else + { + "AZURE_AD_USER" => auth.userid || "", + "AZURE_PASSWORD" => auth.password || "", + "AZURE_SUBSCRIPTION_ID" => (auth.options || {})[:subscription] || "" + } + end + end + end + end +end diff --git a/lib/ansible/runner/credential/google_credential.rb b/lib/ansible/runner/credential/google_credential.rb new file mode 100644 index 00000000000..a815938237c --- /dev/null +++ b/lib/ansible/runner/credential/google_credential.rb @@ -0,0 +1,43 @@ +module Ansible + class Runner + class GoogleCredential < Credential + def self.auth_type + "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::GoogleCredential" + end + + # Modeled off of gce injectors for awx: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/injectors.py#L18-L42 + # + def env_vars + { + "GCE_EMAIL" => auth.userid || "", + "GCE_PROJECT" => auth.project || "", + "GCE_CREDENTIALS_FILE_PATH" => gce_credentials_file + } + end + + def write_config_files + write_gce_credentials_file + end + + private + + def write_gce_credentials_file + json_data = { + :type => "service_account", + :private_key => auth.auth_key || "", + :client_email => auth.userid || "", + :project_id => auth.project || "" + } + + File.write(gce_credentials_file, json_data.to_json) + File.chmod(0o0600, gce_credentials_file) + end + + def gce_credentials_file + File.join(base_dir, "gce_credentials") + end + end + end +end diff --git a/lib/ansible/runner/credential/machine_credential.rb b/lib/ansible/runner/credential/machine_credential.rb index c4958e5f824..35d36948332 100644 --- a/lib/ansible/runner/credential/machine_credential.rb +++ b/lib/ansible/runner/credential/machine_credential.rb @@ -12,16 +12,9 @@ def command_line end end - def write_password_file - password_hash = { - "^SSH [pP]assword:" => auth.password, - "^BECOME [pP]assword:" => auth.become_password, - "^Enter passphrase for [a-zA-Z0-9\-\/]+\/ssh_key_data:" => auth.ssh_key_unlock - }.delete_blanks - - File.write(password_file, password_hash.to_yaml) if password_hash.present? - - write_ssh_key if auth.auth_key.present? + def write_config_files + write_password_file + write_ssh_key_file end private @@ -36,8 +29,18 @@ def become_args } end - def write_ssh_key - File.write(ssh_key_file, auth.auth_key) + def write_password_file + password_hash = { + "^SSH [pP]assword:" => auth.password, + "^BECOME [pP]assword:" => auth.become_password, + "^Enter passphrase for [a-zA-Z0-9\-\/]+\/ssh_key_data:" => auth.ssh_key_unlock + }.delete_blanks + + File.write(password_file, password_hash.to_yaml) if password_hash.present? + end + + def write_ssh_key_file + File.write(ssh_key_file, auth.auth_key) if auth.auth_key.present? end end end diff --git a/lib/ansible/runner/credential/openstack_credential.rb b/lib/ansible/runner/credential/openstack_credential.rb new file mode 100644 index 00000000000..44f334580b8 --- /dev/null +++ b/lib/ansible/runner/credential/openstack_credential.rb @@ -0,0 +1,47 @@ +module Ansible + class Runner + class OpenstackCredential < Credential + def self.auth_type + "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::OpenstackCredential" + end + + # Modeled off of openstack injectors for awx: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/injectors.py#L70-L96 + # + def env_vars + { "OS_CLIENT_CONFIG_FILE" => os_credentials_file } + end + + def write_config_files + write_os_credentials_file + end + + private + + def write_os_credentials_file + openstack_data = { + "clouds" => { + "devstack" => { + "verify" => false, # NOTE: We don't have a way of configuring this currently + "auth" => { + "auth_url" => auth.host || "", + "username" => auth.userid || "", + "password" => auth.password || "", + "project_name" => auth.project || "", + "domain_name" => auth.domain + }.delete_nils + } + } + } + + File.write(os_credentials_file, openstack_data.to_yaml) + File.chmod(0o0600, os_credentials_file) + end + + def os_credentials_file + File.join(base_dir, "os_credentials") + end + end + end +end diff --git a/lib/ansible/runner/credential/rhv_credential.rb b/lib/ansible/runner/credential/rhv_credential.rb new file mode 100644 index 00000000000..7cfbf889450 --- /dev/null +++ b/lib/ansible/runner/credential/rhv_credential.rb @@ -0,0 +1,55 @@ +module Ansible + class Runner + class RhvCredential < Credential + def self.auth_type + "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::RhvCredential" + end + + # Modeled off of rhv injectors for awx: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/__init__.py#L1035-L1054 + # + def env_vars + { + "OVIRT_INI_PATH" => ovirt_ini_file, + "OVIRT_URL" => auth.host || "", + "OVIRT_USERNAME" => auth.userid || "", + "OVIRT_PASSWORD" => auth.password || "", + } + end + + def write_config_files + write_ovirt_ini_file + end + + private + + def write_ovirt_ini_file + ovirt_data = %W[ + [ovirt] + ovirt_url=#{auth.host} + ovirt_username=#{auth.userid} + ovirt_password=#{auth.password} + ] + + # NOTE: We currently DO NOT support ca_file support as is in `awx`. + # + # ansible/awx ref: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/__init__.py#L1046 + # + # To add, we need to update the GoogleCredential::API_OPTIONS in + # app/models and add the following line here: + # + # ovirt_data << "ovirt_ca_file=#{auth.auth_key}" if auth.auth_key + + File.write(ovirt_ini_file, ovirt_data.join("\n")) + File.chmod(0o0600, ovirt_ini_file) + end + + def ovirt_ini_file + File.join(base_dir, "ovirt.ini") + end + end + end +end diff --git a/lib/ansible/runner/credential/vmware_credential.rb b/lib/ansible/runner/credential/vmware_credential.rb new file mode 100644 index 00000000000..06529a456ee --- /dev/null +++ b/lib/ansible/runner/credential/vmware_credential.rb @@ -0,0 +1,23 @@ +module Ansible + class Runner + class VmwareCredential < Credential + def self.auth_type + "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::VmwareCredential" + end + + # Modeled off of vmware injectors for awx: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/injectors.py#L63-L67 + # + # NOTE: The VMWARE_VALIDATE_CERTS is currently not supported. + # + def env_vars + { + "VMWARE_USER" => auth.userid || "", + "VMWARE_PASSWORD" => auth.password || "", + "VMWARE_HOST" => auth.host || "" + } + end + end + end +end diff --git a/spec/factories/authentication.rb b/spec/factories/authentication.rb index db6a181d164..17ed0508a55 100644 --- a/spec/factories/authentication.rb +++ b/spec/factories/authentication.rb @@ -83,6 +83,14 @@ :parent => :embedded_ansible_credential, :class => "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::AmazonCredential" + factory :embedded_ansible_azure_credential, + :parent => :embedded_ansible_credential, + :class => "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::AzureCredential" + + factory :embedded_ansible_google_credential, + :parent => :embedded_ansible_credential, + :class => "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::GoogleCredential" + factory :embedded_ansible_machine_credential, :parent => :embedded_ansible_credential, :class => "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::MachineCredential" @@ -95,10 +103,22 @@ :parent => :embedded_ansible_credential, :class => "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::CloudCredential" + factory :embedded_ansible_openstack_credential, + :parent => :embedded_ansible_credential, + :class => "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::OpenstackCredential" + + factory :embedded_ansible_rhv_credential, + :parent => :embedded_ansible_credential, + :class => "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::RhvCredential" + factory :embedded_ansible_scm_credential, :parent => :embedded_ansible_credential, :class => "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ScmCredential" + factory :embedded_ansible_vmware_credential, + :parent => :embedded_ansible_credential, + :class => "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::VmwareCredential" + factory :auth_key_pair_cloud, :class => "ManageIQ::Providers::CloudManager::AuthKeyPair" factory :auth_key_pair_amazon, :class => "ManageIQ::Providers::Amazon::CloudManager::AuthKeyPair" factory :auth_key_pair_openstack, :class => "ManageIQ::Providers::Openstack::CloudManager::AuthKeyPair" diff --git a/spec/lib/ansible/runner/credential/amazon_credential_spec.rb b/spec/lib/ansible/runner/credential/amazon_credential_spec.rb new file mode 100644 index 00000000000..867fea23723 --- /dev/null +++ b/spec/lib/ansible/runner/credential/amazon_credential_spec.rb @@ -0,0 +1,79 @@ +require 'ansible/runner' +require 'ansible/runner/credential' + +RSpec.describe Ansible::Runner::AmazonCredential do + it ".auth_type is the correct Authentication sub-class" do + expect(described_class.auth_type).to eq("ManageIQ::Providers::EmbeddedAnsible::AutomationManager::AmazonCredential") + end + + context "with a credential object" do + around do |example| + Dir.mktmpdir("ansible-runner-credential-test") do |dir| + @base_dir = dir + example.run + end + end + + let(:auth) { FactoryBot.create(:embedded_ansible_amazon_credential, auth_attributes) } + let(:auth_attributes) do + { + :userid => "manageiq-aws", + :password => "aws_secret", + :auth_key => "key_data" + } + end + + let(:cred) { described_class.new(auth.id, @base_dir) } + + describe "#command_line" do + it "returns an empty hash" do + expect(cred.command_line).to eq({}) + end + end + + # Modeled off of aws injectors for awx: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/injectors.py#L11-L15 + # + describe "#env_vars" do + it "sets AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY" do + auth.update!(:auth_key => nil) + expected = { + "AWS_ACCESS_KEY_ID" => "manageiq-aws", + "AWS_SECRET_ACCESS_KEY" => "aws_secret" + } + expect(cred.env_vars).to eq(expected) + end + + it "defaults AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to ''" do + auth.update!(:userid => nil, :password => nil, :auth_key => nil) + expected = { + "AWS_ACCESS_KEY_ID" => "", + "AWS_SECRET_ACCESS_KEY" => "" + } + expect(cred.env_vars).to eq(expected) + end + + it "adds AWS_SECURITY_TOKEN if present" do + expected = { + "AWS_ACCESS_KEY_ID" => "manageiq-aws", + "AWS_SECRET_ACCESS_KEY" => "aws_secret", + "AWS_SECURITY_TOKEN" => "key_data" + } + expect(cred.env_vars).to eq(expected) + end + end + + describe "#extra_vars" do + it "returns an empty hash" do + expect(cred.extra_vars).to eq({}) + end + end + + describe "#write_config_files" do + it "no-ops" do + expect(cred.write_config_files).to be_nil + end + end + end +end diff --git a/spec/lib/ansible/runner/credential/azure_credential_spec.rb b/spec/lib/ansible/runner/credential/azure_credential_spec.rb new file mode 100644 index 00000000000..535e1362d4a --- /dev/null +++ b/spec/lib/ansible/runner/credential/azure_credential_spec.rb @@ -0,0 +1,139 @@ +require 'ansible/runner' +require 'ansible/runner/credential' + +RSpec.describe Ansible::Runner::AzureCredential do + it ".auth_type is the correct Authentication sub-class" do + expect(described_class.auth_type).to eq("ManageIQ::Providers::EmbeddedAnsible::AutomationManager::AzureCredential") + end + + context "with a credential object" do + around do |example| + Dir.mktmpdir("ansible-runner-credential-test") do |dir| + @base_dir = dir + example.run + end + end + + let(:auth) { FactoryBot.create(:embedded_ansible_azure_credential, auth_attributes) } + let(:auth_attributes) do + { + :userid => "manageiq-azure", + :password => "azure_password", + :auth_key => "client_secret", + :options => { + :client => "client_id", + :tenant => "tenant_id", + :subscription => "subscription_id" + } + } + end + + let(:cred) { described_class.new(auth.id, @base_dir) } + + describe "#command_line" do + it "returns an empty hash" do + expect(cred.command_line).to eq({}) + end + end + + # Modeled off of azure injectors for awx: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/injectors.py#L45-L60 + # + describe "#env_vars" do + context "client_id and tenant_id present" do + let(:auth_attributes) do + { + :auth_key => "client_secret", + :options => { + :client => "client_id", + :tenant => "tenant_id" + } + } + end + + it "sets AZURE_CLIENT_ID, AZURE_TENANT, and AZURE_SECRET" do + expected = { + "AZURE_CLIENT_ID" => "client_id", + "AZURE_TENANT" => "tenant_id", + "AZURE_SECRET" => "client_secret", + "AZURE_SUBSCRIPTION_ID" => "" + } + expect(cred.env_vars).to eq(expected) + end + + it "defaults AZURE_SECRET to '' if missing" do + auth.update!(:auth_key => nil) + expected = { + "AZURE_CLIENT_ID" => "client_id", + "AZURE_TENANT" => "tenant_id", + "AZURE_SECRET" => "", + "AZURE_SUBSCRIPTION_ID" => "" + } + expect(cred.env_vars).to eq(expected) + end + + it "adds AZURE_SUBSCRIPTION_ID if present" do + auth.update!(:options => auth.options.merge(:subscription => "subscription_id")) + expected = { + "AZURE_CLIENT_ID" => "client_id", + "AZURE_TENANT" => "tenant_id", + "AZURE_SECRET" => "client_secret", + "AZURE_SUBSCRIPTION_ID" => "subscription_id" + } + expect(cred.env_vars).to eq(expected) + end + end + + context "client_id and tenant_id missing" do + let(:auth_attributes) do + { + :userid => "manageiq-azure", + :password => "azure_password" + } + end + + it "sets AZURE_AD_USER and AZURE_PASSWORD" do + expected = { + "AZURE_AD_USER" => "manageiq-azure", + "AZURE_PASSWORD" => "azure_password", + "AZURE_SUBSCRIPTION_ID" => "" + } + expect(cred.env_vars).to eq(expected) + end + + it "defaults AZURE_AD_USER and AZURE_PASSWORD to ''" do + auth.update!(:userid => nil, :password => nil) + expected = { + "AZURE_AD_USER" => "", + "AZURE_PASSWORD" => "", + "AZURE_SUBSCRIPTION_ID" => "" + } + expect(cred.env_vars).to eq(expected) + end + + it "adds AWS_SECURITY_TOKEN if present" do + auth.update!(:options => { :subscription => "subscription_id" }) + expected = { + "AZURE_AD_USER" => "manageiq-azure", + "AZURE_PASSWORD" => "azure_password", + "AZURE_SUBSCRIPTION_ID" => "subscription_id" + } + expect(cred.env_vars).to eq(expected) + end + end + end + + describe "#extra_vars" do + it "returns an empty hash" do + expect(cred.extra_vars).to eq({}) + end + end + + describe "#write_config_files" do + it "no-ops" do + expect(cred.write_config_files).to be_nil + end + end + end +end diff --git a/spec/lib/ansible/runner/credential/generic_credential_spec.rb b/spec/lib/ansible/runner/credential/generic_credential_spec.rb index 16e28d12e0f..cc5de93bd27 100644 --- a/spec/lib/ansible/runner/credential/generic_credential_spec.rb +++ b/spec/lib/ansible/runner/credential/generic_credential_spec.rb @@ -31,9 +31,9 @@ expect(cred.extra_vars).to eq({}) end - it "#write_password_file does not write a file" do + it "#write_config_files does not write a file" do password_file = File.join(@base_dir, "env", "passwords") - cred.write_password_file + cred.write_config_files expect(File.exist?(password_file)).to be_falsey end end diff --git a/spec/lib/ansible/runner/credential/google_credential_spec.rb b/spec/lib/ansible/runner/credential/google_credential_spec.rb new file mode 100644 index 00000000000..44101c50959 --- /dev/null +++ b/spec/lib/ansible/runner/credential/google_credential_spec.rb @@ -0,0 +1,114 @@ +require 'ansible/runner' +require 'ansible/runner/credential' + +RSpec.describe Ansible::Runner::GoogleCredential do + it ".auth_type is the correct Authentication sub-class" do + expect(described_class.auth_type).to eq("ManageIQ::Providers::EmbeddedAnsible::AutomationManager::GoogleCredential") + end + + context "with a credential object" do + around do |example| + Dir.mktmpdir("ansible-runner-credential-test") do |dir| + @base_dir = dir + example.run + end + end + + let(:auth) { FactoryBot.create(:embedded_ansible_google_credential, auth_attributes) } + let(:auth_attributes) do + { + :userid => "manageiq@gmail.com", + :auth_key => "key_data", + :options => { :project => "google_project" } + } + end + + let(:cred) { described_class.new(auth.id, @base_dir) } + + describe "#command_line" do + it "returns an empty hash" do + expect(cred.command_line).to eq({}) + end + end + + # Modeled off of gce injectors for awx: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/injectors.py#L11-L15 + # + describe "#env_vars" do + it "sets GCE_EMAIL, GCE_PROJECT, and GCE_CREDENTIALS_FILE_PATH" do + filename = File.join(@base_dir, "gce_credentials") + expected = { + "GCE_EMAIL" => "manageiq@gmail.com", + "GCE_PROJECT" => "google_project", + "GCE_CREDENTIALS_FILE_PATH" => filename + } + expect(cred.env_vars).to eq(expected) + end + + it "defaults GCE_EMAIL and GCE_PROJECT to '' if missing" do + filename = File.join(@base_dir, "gce_credentials") + auth.update!(:userid => nil, :options => nil) + + expected = { + "GCE_EMAIL" => "", + "GCE_PROJECT" => "", + "GCE_CREDENTIALS_FILE_PATH" => filename + } + expect(cred.env_vars).to eq(expected) + end + end + + describe "#extra_vars" do + it "returns an empty hash" do + expect(cred.extra_vars).to eq({}) + end + end + + describe "#write_config_files" do + it "writes the the env/gce_credentials to a file" do + cred.write_config_files + + actual_data = JSON.parse(File.read(File.join(@base_dir, "gce_credentials"))) + expected_data = { + "type" => "service_account", + "private_key" => "key_data", + "client_email" => "manageiq@gmail.com", + "project_id" => "google_project", + } + + expect(expected_data).to eq(actual_data) + end + + it "files in empty data with emtpy strings (matching awx implementation)" do + auth.update!(:auth_key => nil, :userid => nil, :options => nil) + cred.write_config_files + + actual_data = JSON.parse(File.read(File.join(@base_dir, "gce_credentials"))) + expected_data = { + "type" => "service_account", + "private_key" => "", + "client_email" => "", + "project_id" => "", + } + + expect(expected_data).to eq(actual_data) + end + + it "handles empty options hash" do + auth.update!(:options => {}) + cred.write_config_files + + actual_data = JSON.parse(File.read(File.join(@base_dir, "gce_credentials"))) + expected_data = { + "type" => "service_account", + "private_key" => "key_data", + "client_email" => "manageiq@gmail.com", + "project_id" => "", + } + + expect(expected_data).to eq(actual_data) + end + end + end +end diff --git a/spec/lib/ansible/runner/credential/machine_credential_spec.rb b/spec/lib/ansible/runner/credential/machine_credential_spec.rb index 4517f6b7930..22be280497f 100644 --- a/spec/lib/ansible/runner/credential/machine_credential_spec.rb +++ b/spec/lib/ansible/runner/credential/machine_credential_spec.rb @@ -67,7 +67,7 @@ expect(cred.extra_vars).to eq({}) end - describe "#write_password_file" do + describe "#write_config_files" do let(:password_file) { File.join(@base_dir, "env", "passwords") } let(:key_file) { File.join(@base_dir, "env", "ssh_key") } @@ -76,7 +76,7 @@ def password_hash end it "writes out both the passwords file and the key file" do - cred.write_password_file + cred.write_config_files expect(password_hash).to eq( "^SSH [pP]assword:" => "secret", @@ -89,7 +89,7 @@ def password_hash it "doesn't create the password file if there are no passwords" do auth.update!(:password => nil, :become_password => nil, :auth_key_password => nil) - cred.write_password_file + cred.write_config_files expect(File.exist?(password_file)).to be_falsey end @@ -97,7 +97,7 @@ def password_hash password = '!compli-cat"ed&pass,"wor;d' auth.update!(:password => password) - cred.write_password_file + cred.write_config_files expect(password_hash["^SSH [pP]assword:"]).to eq(password) end diff --git a/spec/lib/ansible/runner/credential/openstack_credential_spec.rb b/spec/lib/ansible/runner/credential/openstack_credential_spec.rb new file mode 100644 index 00000000000..32046c01693 --- /dev/null +++ b/spec/lib/ansible/runner/credential/openstack_credential_spec.rb @@ -0,0 +1,123 @@ +require 'ansible/runner' +require 'ansible/runner/credential' + +RSpec.describe Ansible::Runner::OpenstackCredential do + it ".auth_type is the correct Authentication sub-class" do + expect(described_class.auth_type).to eq("ManageIQ::Providers::EmbeddedAnsible::AutomationManager::OpenstackCredential") + end + + context "with a credential object" do + around do |example| + Dir.mktmpdir("ansible-runner-credential-test") do |dir| + @base_dir = dir + example.run + end + end + + let(:auth) { FactoryBot.create(:embedded_ansible_openstack_credential, auth_attributes) } + let(:auth_attributes) do + { + :userid => "manageiq-openstack", + :password => "openstack_password", + :options => { + :host => "http://fat.openstacks.example.com", + :project => "project" + } + } + end + + let(:cred) { described_class.new(auth.id, @base_dir) } + + describe "#command_line" do + it "returns an empty hash" do + expect(cred.command_line).to eq({}) + end + end + + # Modeled off of openstack injectors for awx: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/injectors.py#L70-L96 + # + describe "#env_vars" do + it "sets OS_CLIENT_CONFIG_FILE" do + filename = File.join(@base_dir, "os_credentials") + expected = { "OS_CLIENT_CONFIG_FILE" => filename } + expect(cred.env_vars).to eq(expected) + end + end + + describe "#extra_vars" do + it "returns an empty hash" do + expect(cred.extra_vars).to eq({}) + end + end + + describe "#write_config_files" do + it "writes the YAML data to a file" do + cred.write_config_files + + actual_data = YAML.load_file(File.join(@base_dir, "os_credentials")) + expected_data = { + "clouds" => { + "devstack" => { + "verify" => false, + "auth" => { + "auth_url" => "http://fat.openstacks.example.com", + "username" => "manageiq-openstack", + "password" => "openstack_password", + "project_name" => "project" + } + } + } + } + + expect(expected_data).to eq(actual_data) + end + + it "files in empty data with emtpy strings (matching awx implementation)" do + auth.update!(:userid => nil, :password => nil, :options => nil) + cred.write_config_files + + actual_data = YAML.load_file(File.join(@base_dir, "os_credentials")) + expected_data = { + "clouds" => { + "devstack" => { + "verify" => false, + "auth" => { + "auth_url" => "", + "username" => "", + "password" => "", + "project_name" => "" + } + } + } + } + + expect(expected_data).to eq(actual_data) + end + + it "handles empty options hash" do + auth.update!(:options => auth.options.merge(:domain => "domain")) + cred.write_config_files + + actual_data = YAML.load_file(File.join(@base_dir, "os_credentials")) + expected_data = { + "clouds" => { + "devstack" => { + "verify" => false, + "auth" => { + "auth_url" => "http://fat.openstacks.example.com", + "username" => "manageiq-openstack", + "password" => "openstack_password", + "project_name" => "project", + "domain_name" => "domain" + } + } + } + } + + expect(expected_data).to eq(actual_data) + end + end + end +end diff --git a/spec/lib/ansible/runner/credential/rhv_credential_spec.rb b/spec/lib/ansible/runner/credential/rhv_credential_spec.rb new file mode 100644 index 00000000000..ce25e042d3f --- /dev/null +++ b/spec/lib/ansible/runner/credential/rhv_credential_spec.rb @@ -0,0 +1,118 @@ +require 'ansible/runner' +require 'ansible/runner/credential' + +RSpec.describe Ansible::Runner::RhvCredential do + it ".auth_type is the correct Authentication sub-class" do + expect(described_class.auth_type).to eq("ManageIQ::Providers::EmbeddedAnsible::AutomationManager::RhvCredential") + end + + context "with a credential object" do + around do |example| + Dir.mktmpdir("ansible-runner-credential-test") do |dir| + @base_dir = dir + example.run + end + end + + let(:auth) { FactoryBot.create(:embedded_ansible_rhv_credential, auth_attributes) } + let(:auth_attributes) do + { + :userid => "manageiq-rhv", + :password => "rhv_password", + :options => { :host => "rhv_host" } + } + end + + let(:cred) { described_class.new(auth.id, @base_dir) } + + describe "#command_line" do + it "returns an empty hash" do + expect(cred.command_line).to eq({}) + end + end + + # Modeled off of rhv injectors for awx: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/__init__.py#L1035-L1054 + # + describe "#env_vars" do + it "sets OVIRT_INI_PATH, OVIRT_URL, OVIRT_USERNAME, and OVIRT_PASSWORD" do + filename = File.join(@base_dir, "ovirt.ini") + expected = { + "OVIRT_INI_PATH" => filename, + "OVIRT_URL" => "rhv_host", + "OVIRT_USERNAME" => "manageiq-rhv", + "OVIRT_PASSWORD" => "rhv_password" + } + + expect(cred.env_vars).to eq(expected) + end + + it "defaults OVIRT_URL, OVIRT_USERNAME, and OVIRT_PASSWORD to ''" do + auth.update!(:userid => nil, :password => nil, :options => nil) + + filename = File.join(@base_dir, "ovirt.ini") + expected = { + "OVIRT_INI_PATH" => filename, + "OVIRT_URL" => "", + "OVIRT_USERNAME" => "", + "OVIRT_PASSWORD" => "" + } + + expect(cred.env_vars).to eq(expected) + end + end + + describe "#extra_vars" do + it "returns an empty hash" do + expect(cred.extra_vars).to eq({}) + end + end + + describe "#write_config_files" do + it "writes the ini file" do + cred.write_config_files + + actual_data = File.read(File.join(@base_dir, "ovirt.ini")) + expected_data = <<~OVIRT_INI.strip + [ovirt] + ovirt_url=rhv_host + ovirt_username=manageiq-rhv + ovirt_password=rhv_password + OVIRT_INI + + expect(expected_data).to eq(actual_data) + end + + it "fills in empty data (matching awx implementation)" do + auth.update!(:userid => nil, :password => nil, :options => nil) + cred.write_config_files + + actual_data = File.read(File.join(@base_dir, "ovirt.ini")) + expected_data = <<~OVIRT_INI.strip + [ovirt] + ovirt_url= + ovirt_username= + ovirt_password= + OVIRT_INI + + expect(expected_data).to eq(actual_data) + end + + it "handles empty options hash" do + auth.update!(:options => {}) + cred.write_config_files + + actual_data = File.read(File.join(@base_dir, "ovirt.ini")) + expected_data = <<~OVIRT_INI.strip + [ovirt] + ovirt_url= + ovirt_username=manageiq-rhv + ovirt_password=rhv_password + OVIRT_INI + + expect(expected_data).to eq(actual_data) + end + end + end +end diff --git a/spec/lib/ansible/runner/credential/vmware_credential_spec.rb b/spec/lib/ansible/runner/credential/vmware_credential_spec.rb new file mode 100644 index 00000000000..89df69749d5 --- /dev/null +++ b/spec/lib/ansible/runner/credential/vmware_credential_spec.rb @@ -0,0 +1,83 @@ +require 'ansible/runner' +require 'ansible/runner/credential' + +RSpec.describe Ansible::Runner::VmwareCredential do + it ".auth_type is the correct Authentication sub-class" do + expect(described_class.auth_type).to eq("ManageIQ::Providers::EmbeddedAnsible::AutomationManager::VmwareCredential") + end + + context "with a credential object" do + around do |example| + Dir.mktmpdir("ansible-runner-credential-test") do |dir| + @base_dir = dir + example.run + end + end + + let(:auth) { FactoryBot.create(:embedded_ansible_vmware_credential, auth_attributes) } + let(:auth_attributes) do + { + :userid => "manageiq-vmware", + :password => "vmware_secret", + :options => { + :host => "vmware_host" + } + } + end + + let(:cred) { described_class.new(auth.id, @base_dir) } + + describe "#command_line" do + it "returns an empty hash" do + expect(cred.command_line).to eq({}) + end + end + + # Modeled off of vmware injectors for awx: + # + # https://github.com/ansible/awx/blob/1242ee2b/awx/main/models/credential/injectors.py#L63-L67 + # + describe "#env_vars" do + it "sets VMWARE_USER, VMWARE_PASSWORD, and VMWARE_HOST" do + expected = { + "VMWARE_USER" => "manageiq-vmware", + "VMWARE_PASSWORD" => "vmware_secret", + "VMWARE_HOST" => "vmware_host" + } + expect(cred.env_vars).to eq(expected) + end + + it "defaults VMWARE_USER, VMWARE_PASSWORD, and VMWARE_HOST to '' if missing" do + auth.update!(:userid => nil, :password => nil, :options => nil) + expected = { + "VMWARE_USER" => "", + "VMWARE_PASSWORD" => "", + "VMWARE_HOST" => "" + } + expect(cred.env_vars).to eq(expected) + end + + it "handles empty options hash" do + auth.update!(:options => {}) + expected = { + "VMWARE_USER" => "manageiq-vmware", + "VMWARE_PASSWORD" => "vmware_secret", + "VMWARE_HOST" => "" + } + expect(cred.env_vars).to eq(expected) + end + end + + describe "#extra_vars" do + it "returns an empty hash" do + expect(cred.extra_vars).to eq({}) + end + end + + describe "#write_config_files" do + it "no-ops" do + expect(cred.write_config_files).to be_nil + end + end + end +end