Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass credentials to ansible runner from services #18969

Merged
merged 5 commits into from
Jul 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions app/models/manageiq/providers/ansible_playbook_workflow.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class ManageIQ::Providers::AnsiblePlaybookWorkflow < ManageIQ::Providers::AnsibleRunnerWorkflow
def self.job_options(env_vars, extra_vars, playbook_options, timeout, poll_interval, hosts)
def self.job_options(env_vars, extra_vars, playbook_options, timeout, poll_interval, hosts, credentials)
{
:credentials => credentials,
:env_vars => env_vars,
:extra_vars => extra_vars,
:hosts => hosts,
Expand All @@ -16,9 +17,9 @@ def pre_playbook
end

def run_playbook
env_vars, extra_vars, hosts, playbook_path = options.values_at(:env_vars, :extra_vars, :hosts, :playbook_path)
credentials, env_vars, extra_vars, hosts, playbook_path = options.values_at(:credentials, :env_vars, :extra_vars, :hosts, :playbook_path)

response = Ansible::Runner.run_async(env_vars, extra_vars, playbook_path, :hosts => hosts)
response = Ansible::Runner.run_async(env_vars, extra_vars, playbook_path, :hosts => hosts, :credentials => credentials)
if response.nil?
queue_signal(:abort, "Failed to run ansible playbook", "error")
else
Expand Down
3 changes: 2 additions & 1 deletion app/models/manageiq/providers/ansible_role_workflow.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class ManageIQ::Providers::AnsibleRoleWorkflow < ManageIQ::Providers::AnsibleRunnerWorkflow
def self.job_options(env_vars, extra_vars, role_options, timeout, poll_interval, hosts)
def self.job_options(env_vars, extra_vars, role_options, timeout, poll_interval, hosts, credentials)
{
:credentials => credentials,
:env_vars => env_vars,
:extra_vars => extra_vars,
:hosts => hosts,
Expand Down
4 changes: 2 additions & 2 deletions app/models/manageiq/providers/ansible_runner_workflow.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class ManageIQ::Providers::AnsibleRunnerWorkflow < Job
def self.create_job(env_vars, extra_vars, role_or_playbook_options, hosts = "localhost", timeout: 1.hour, poll_interval: 1.second)
super(name, job_options(env_vars, extra_vars, role_or_playbook_options, timeout, poll_interval, hosts))
def self.create_job(env_vars, extra_vars, role_or_playbook_options, hosts = ["localhost"], credentials = [], timeout: 1.hour, poll_interval: 1.second)
super(name, job_options(env_vars, extra_vars, role_or_playbook_options, timeout, poll_interval, hosts, credentials))
end

def current_job_timeout(_timeout_adjustment = 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ def run(vars = {})
workflow = ManageIQ::Providers::AnsiblePlaybookWorkflow
extra_vars = merge_extra_vars(vars[:extra_vars])
playbook_vars = { :playbook_path => parent.path }
credentials = collect_credentials(vars)

workflow.create_job({}, extra_vars, playbook_vars, vars[:hosts]).tap do |job|
workflow.create_job({}, extra_vars, playbook_vars, vars[:hosts], credentials).tap do |job|
job.signal(:start)
end
end
Expand All @@ -48,4 +49,13 @@ def merge_extra_vars(external)

extra_vars
end

def collect_credentials(options)
options.values_at(
:credential,
:cloud_credential,
:network_credential,
:vault_credential
).compact
end
end
32 changes: 25 additions & 7 deletions app/models/service_ansible_playbook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,37 @@ def get_job_options(action)
options[job_option_key(action)].deep_dup
end

CONFIG_OPTIONS_WHITELIST = %i[
hosts
extra_vars
credential_id
vault_credential_id
network_credential_id
cloud_credential_id
].freeze

def config_options(action)
options.fetch_path(:config_info, action.downcase.to_sym).slice(*CONFIG_OPTIONS_WHITELIST).with_indifferent_access
end

def translate_credentials!(job_options)
%i[credential vault_credential network_credential cloud_credential].each do |cred|
bdunne marked this conversation as resolved.
Show resolved Hide resolved
cred_sym = "#{cred}_id".to_sym
credential_id = job_options.delete(cred_sym)
job_options[cred] = Authentication.find(credential_id).native_ref if credential_id.present?
end
end

def save_job_options(action, overrides)
job_options = options.fetch_path(:config_info, action.downcase.to_sym).slice(:hosts, :extra_vars).with_indifferent_access
job_options = config_options(action)

job_options[:extra_vars].try(:transform_values!) do |val|
val.kind_of?(String) ? val : val[:default] # TODO: support Hash only
end

job_options.deep_merge!(parse_dialog_options) unless action == ResourceAction::RETIREMENT
job_options.deep_merge!(overrides)

%i[credential vault_credential].each do |cred|
cred_sym = "#{cred}_id".to_sym
credential_id = job_options.delete(cred_sym)
job_options[cred] = Authentication.find(credential_id).native_ref if credential_id.present?
end
translate_credentials!(job_options)

options[job_option_key(action)] = job_options
save!
Expand Down
9 changes: 6 additions & 3 deletions lib/ansible/runner/credential/machine_credential.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ def self.auth_type
end

def command_line
{:user => auth.userid}.delete_blanks.merge(become_args)
{:user => auth.userid}.delete_blanks.merge(become_args).tap do |args|
# Add `--ask-pass` flag to ansible_playbook if we have a password to provide
args[:ask_pass] = nil if auth.password.present?
carbonin marked this conversation as resolved.
Show resolved Hide resolved
end
end

def write_password_file
password_hash = {
"^SSH [pP]assword:$" => auth.password,
"^BECOME [pP]assword:$" => auth.become_password
"^SSH [pP]assword:" => auth.password,
"^BECOME [pP]assword:" => auth.become_password
}.delete_blanks

File.write(password_file, password_hash.to_yaml) if password_hash.present?
Expand Down
10 changes: 6 additions & 4 deletions spec/lib/ansible/runner/credential/machine_credential_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
describe "#command_line" do
it "is correct with all attributes" do
expected = {
:ask_pass => nil,
:become => nil,
:become_user => "root",
:become_method => "su",
Expand All @@ -44,6 +45,7 @@
auth.update!(:userid => nil)

expected = {
:ask_pass => nil,
:become => nil,
:become_user => "root",
:become_method => "su"
Expand All @@ -53,7 +55,7 @@

it "doesn't send the become keys if :become_user is not set" do
auth.update!(:become_username => nil)
expect(cred.command_line).to eq(:user => "manageiq")
expect(cred.command_line).to eq(:ask_pass => nil, :user => "manageiq")
end
end

Expand All @@ -77,8 +79,8 @@ def password_hash
cred.write_password_file

expect(password_hash).to eq(
"^SSH [pP]assword:$" => "secret",
"^BECOME [pP]assword:$" => "othersecret"
"^SSH [pP]assword:" => "secret",
"^BECOME [pP]assword:" => "othersecret"
)

expect(File.read(key_file)).to eq("key_data")
Expand All @@ -96,7 +98,7 @@ def password_hash

cred.write_password_file

expect(password_hash["^SSH [pP]assword:$"]).to eq(password)
expect(password_hash["^SSH [pP]assword:"]).to eq(password)
end
end
end
Expand Down
33 changes: 24 additions & 9 deletions spec/models/service_ansible_playbook_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,18 +119,28 @@
it 'prepares job options from service template' do
basic_service.preprocess(action)
service.reload
expect(basic_service.options[:provision_job_options][:hosts]).to eq("default_host1,default_host2")
expect(basic_service.options[:provision_job_options]).to include(
:hosts => "default_host1,default_host2",
:credential => credential_0.native_ref,
:vault_credential => credential_3.native_ref
)
end
end

context 'with dialog overrides' do
it 'prepares job options combines from service template and dialog' do
service.preprocess(action)
service.reload
expect(service.options[:provision_job_options][:hosts]).to eq("host1,host2")
expect(service.options[:provision_job_options][:credential]).to eq(credential_1.native_ref)
expect(service.options[:provision_job_options][:extra_vars]).to eq(
'var1' => 'value1', 'var2' => 'value2', 'var3' => 'default_val3', 'pswd' => encrypted_val
expect(service.options[:provision_job_options]).to include(
:hosts => "host1,host2",
:credential => credential_1.native_ref,
:vault_credential => credential_3.native_ref,
:extra_vars => {
'var1' => 'value1',
'var2' => 'value2',
'var3' => 'default_val3',
'pswd' => encrypted_val
}
)
end

Expand All @@ -146,11 +156,16 @@
it 'ignores dialog options' do
service.preprocess(action)
service.reload
expect(service.options[:retirement_job_options][:hosts]).to eq("default_host1,default_host2")
expect(service.options[:retirement_job_options][:extra_vars]).to eq(
'var1' => 'default_val1', 'var2' => 'default_val2', 'var3' => 'default_val3'
expect(service.options[:retirement_job_options]).to include(
:hosts => "default_host1,default_host2",
:credential => credential_0.native_ref,
:vault_credential => credential_3.native_ref,
:extra_vars => {
'var1' => 'default_val1',
'var2' => 'default_val2',
'var3' => 'default_val3'
}
)
expect(service.options[:retirement_job_options]).not_to have_key(:credential)
end
end
end
Expand Down