foreman_ansible 2.0.2 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/helpers/foreman_ansible/ansible_plugin_helper.rb +5 -1
- data/app/models/foreman_ansible/ansible_provider.rb +8 -4
- data/app/models/setting/ansible.rb +1 -34
- data/app/services/foreman_ansible/inventory_creator.rb +53 -6
- data/app/views/ansible_roles/index.html.erb +2 -1
- data/lib/foreman_ansible/version.rb +1 -1
- data/test/unit/services/inventory_creator_test.rb +115 -7
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 275aa3a16113e61a5eaf4fdaf6dddb0db8ebd40f
|
4
|
+
data.tar.gz: 2926a08f5acb7551b2945bb1662bddd57151daeb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd4524fc8bb4059abe867a4beea2c62e51e07560792fccbaa5e653ff4f09750451b36a3d5ab314ec873ab55853917302331e18a9c5bc8272653d1cfea3f69eb7
|
7
|
+
data.tar.gz: 746ea0fc1a44d761d323ab2077b0743169e88f00f4f338e50d8efeb4882ce3b7d9f1d6a76d0a9d913d325a26f0f64935031d3b6833419360f98e7cdcb3395b4c
|
@@ -1,8 +1,12 @@
|
|
1
|
+
require "#{ForemanAnsible::Engine.root}/lib/foreman_ansible/version"
|
2
|
+
|
1
3
|
module ForemanAnsible
|
2
4
|
# General helper for foreman_ansible
|
3
5
|
module AnsiblePluginHelper
|
4
6
|
def ansible_doc_url
|
5
|
-
'
|
7
|
+
major_version = ::ForemanAnsible::VERSION.split('.')[0]
|
8
|
+
'https://theforeman.org/plugins/foreman_ansible/'\
|
9
|
+
"#{major_version}.x/index.html"
|
6
10
|
end
|
7
11
|
end
|
8
12
|
end
|
@@ -4,12 +4,16 @@ if defined? ForemanRemoteExecution
|
|
4
4
|
# Read the source of other RemoteExecution providers for more.
|
5
5
|
class AnsibleProvider < RemoteExecutionProvider
|
6
6
|
class << self
|
7
|
-
def
|
8
|
-
|
7
|
+
def ssh_password(host)
|
8
|
+
host_setting(host, :remote_execution_ssh_password)
|
9
|
+
end
|
10
|
+
|
11
|
+
def ssh_key_passphrase(host)
|
12
|
+
host_setting(host, :remote_execution_ssh_key_passphrase)
|
9
13
|
end
|
10
14
|
|
11
|
-
def
|
12
|
-
|
15
|
+
def humanized_name
|
16
|
+
'Ansible'
|
13
17
|
end
|
14
18
|
|
15
19
|
def proxy_command_options(template_invocation, host)
|
@@ -10,41 +10,9 @@ class Setting
|
|
10
10
|
# rubocop:disable BlockLength
|
11
11
|
def load_defaults
|
12
12
|
return unless super
|
13
|
+
Setting::BLANK_ATTRS.push('ansible_ssh_private_key_file')
|
13
14
|
transaction do
|
14
15
|
[
|
15
|
-
set(
|
16
|
-
'ansible_port',
|
17
|
-
N_('Use this port to connect to hosts '\
|
18
|
-
'and run Ansible. You can override this on hosts '\
|
19
|
-
'by adding a parameter "ansible_port"'),
|
20
|
-
22,
|
21
|
-
N_('Port')
|
22
|
-
),
|
23
|
-
set(
|
24
|
-
'ansible_user',
|
25
|
-
N_('Foreman will try to connect to hosts as this user by '\
|
26
|
-
'default when running Ansible playbooks. You can '\
|
27
|
-
'override this on hosts by adding a parameter '\
|
28
|
-
'"ansible_user"'),
|
29
|
-
'root',
|
30
|
-
N_('User')
|
31
|
-
),
|
32
|
-
set(
|
33
|
-
'ansible_become',
|
34
|
-
N_('Foreman will use the sudo command to run roles on hosts '\
|
35
|
-
'You can override this on hosts by adding a parameter '\
|
36
|
-
'"ansible_become"'),
|
37
|
-
true,
|
38
|
-
N_('Become')
|
39
|
-
),
|
40
|
-
set(
|
41
|
-
'ansible_ssh_pass',
|
42
|
-
N_('Use this password by default when running Ansible '\
|
43
|
-
'playbooks. You can override this on hosts '\
|
44
|
-
'by adding a parameter "ansible_ssh_pass"'),
|
45
|
-
'ansible',
|
46
|
-
N_('Password')
|
47
|
-
),
|
48
16
|
set(
|
49
17
|
'ansible_ssh_private_key_file',
|
50
18
|
N_('Use this to supply a path to an SSH Private Key '\
|
@@ -100,7 +68,6 @@ class Setting
|
|
100
68
|
create(s.update(:category => 'Setting::Ansible'))
|
101
69
|
end
|
102
70
|
end
|
103
|
-
Setting::BLANK_ATTRS.push('ansible_ssh_private_key_file')
|
104
71
|
true
|
105
72
|
end
|
106
73
|
# rubocop:enable AbcSize
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'securerandom'
|
2
4
|
module ForemanAnsible
|
3
5
|
# Service to list an inventory to be passed to the ansible-playbook binary
|
@@ -40,10 +42,16 @@ module ForemanAnsible
|
|
40
42
|
end
|
41
43
|
|
42
44
|
def connection_params(host)
|
43
|
-
|
44
|
-
#
|
45
|
-
|
46
|
-
|
45
|
+
# Preference order is:
|
46
|
+
# 1st option: host parameters.
|
47
|
+
# - If they're set to 'ansible_whatever' we use that over anything else
|
48
|
+
# 2nd option: REX options.
|
49
|
+
# - both settings, ssh password, effective_user can be used
|
50
|
+
# 3rd option:
|
51
|
+
# - other settings
|
52
|
+
params = ansible_settings.
|
53
|
+
merge(remote_execution_options(host)).
|
54
|
+
merge(ansible_extra_options(host))
|
47
55
|
params
|
48
56
|
end
|
49
57
|
|
@@ -61,8 +69,7 @@ module ForemanAnsible
|
|
61
69
|
|
62
70
|
def ansible_settings
|
63
71
|
Hash[
|
64
|
-
%w[
|
65
|
-
ssh_private_key_file become
|
72
|
+
%w[connection ssh_private_key_file
|
66
73
|
winrm_server_cert_validation].map do |setting|
|
67
74
|
["ansible_#{setting}", Setting["ansible_#{setting}"]]
|
68
75
|
end
|
@@ -75,10 +82,50 @@ module ForemanAnsible
|
|
75
82
|
end
|
76
83
|
end
|
77
84
|
|
85
|
+
def remote_execution_options(host)
|
86
|
+
params = {
|
87
|
+
'ansible_become' => @template_invocation.effective_user,
|
88
|
+
'ansible_user' => host_setting(host, 'remote_execution_ssh_user'),
|
89
|
+
'ansible_ssh_pass' => rex_ssh_password(host),
|
90
|
+
'ansible_ssh_private_key_file' => ansible_or_rex_ssh_private_key(host),
|
91
|
+
'ansible_port' => host_setting(host, 'remote_execution_ssh_port')
|
92
|
+
}
|
93
|
+
# Backward compatibility for Ansible 1.x
|
94
|
+
params['ansible_ssh_port'] = params['ansible_port']
|
95
|
+
params['ansible_ssh_user'] = params['ansible_user']
|
96
|
+
params
|
97
|
+
end
|
98
|
+
|
99
|
+
def template_inputs(template_invocation)
|
100
|
+
input_values = template_invocation.input_values
|
101
|
+
result = input_values.each_with_object({}) do |input, vars_hash|
|
102
|
+
vars_hash[input.template_input.name] = input.value
|
103
|
+
end
|
104
|
+
result
|
105
|
+
end
|
106
|
+
|
107
|
+
def rex_ssh_password(host)
|
108
|
+
@template_invocation.job_invocation.password ||
|
109
|
+
host_setting(host, 'remote_execution_ssh_password')
|
110
|
+
end
|
111
|
+
|
112
|
+
def ansible_or_rex_ssh_private_key(host)
|
113
|
+
ansible_private_file = host_setting(host, 'ansible_ssh_private_key_file')
|
114
|
+
if !ansible_private_file.empty?
|
115
|
+
ansible_private_file
|
116
|
+
else
|
117
|
+
ForemanRemoteExecutionCore.settings[:ssh_identity_key_file]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
78
121
|
private
|
79
122
|
|
80
123
|
def render_rabl(host, template)
|
81
124
|
Rabl.render(host, template, :format => 'hash')
|
82
125
|
end
|
126
|
+
|
127
|
+
def host_setting(host, setting)
|
128
|
+
host.params[setting.to_s] || Setting[setting]
|
129
|
+
end
|
83
130
|
end
|
84
131
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
<% title _("Ansible Roles") %>
|
2
2
|
|
3
|
-
<% title_actions ansible_proxy_import(hash_for_import_ansible_roles_path)
|
3
|
+
<% title_actions ansible_proxy_import(hash_for_import_ansible_roles_path),
|
4
|
+
documentation_button('#4.1ImportingRoles', :root_url => ansible_doc_url) %>
|
4
5
|
|
5
6
|
<table class="<%= table_css_classes 'table-fixed' %>">
|
6
7
|
<thead>
|
@@ -1,11 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_plugin_helper'
|
2
4
|
|
3
5
|
module ForemanAnsible
|
4
6
|
# Test how the inventory creator service transforms host params into
|
5
7
|
# inventory variables and connection options
|
8
|
+
# rubocop:disable ClassLength
|
6
9
|
class InventoryCreatorTest < ActiveSupport::TestCase
|
7
10
|
setup do
|
8
11
|
@host = FactoryBot.build(:host)
|
12
|
+
@template_invocation = OpenStruct.new(
|
13
|
+
:job_invocation => OpenStruct.new(:password => 'foobar'),
|
14
|
+
:effective_user => 'foobar'
|
15
|
+
)
|
9
16
|
end
|
10
17
|
|
11
18
|
test 'ansible_ parameters get turned into host variables' do
|
@@ -16,34 +23,135 @@ module ForemanAnsible
|
|
16
23
|
'ansible_user' => 'someone'
|
17
24
|
}
|
18
25
|
@host.expects(:host_params).returns(extra_options).at_least_once
|
19
|
-
inventory = ForemanAnsible::InventoryCreator.new(@host
|
26
|
+
inventory = ForemanAnsible::InventoryCreator.new(@host,
|
27
|
+
@template_invocation)
|
20
28
|
|
21
29
|
assert_empty extra_options.to_a - inventory.connection_params(@host).to_a
|
22
30
|
end
|
23
31
|
|
24
32
|
test 'settings are respected if param cannot be found' do
|
25
33
|
extra_options = { 'ansible_user' => 'someone', 'ansible_port' => 2000 }
|
26
|
-
Setting.expects(:[]).with('
|
34
|
+
Setting.expects(:[]).with('Enable_Smart_Variables_in_ENC').
|
35
|
+
returns(nil).at_least_once
|
27
36
|
Setting.expects(:[]).with('ansible_ssh_private_key_file').
|
28
37
|
returns(nil).at_least_once
|
29
|
-
Setting.expects(:[]).with('
|
30
|
-
|
31
|
-
Setting.expects(:[]).with('
|
38
|
+
Setting.expects(:[]).with('remote_execution_ssh_port').
|
39
|
+
returns(2222).at_least_once
|
40
|
+
Setting.expects(:[]).with('remote_execution_ssh_user').
|
41
|
+
returns('root').at_least_once
|
42
|
+
Setting.expects(:[]).with('remote_execution_ssh_password').
|
32
43
|
returns('asafepassword').at_least_once
|
33
44
|
Setting.expects(:[]).with('ansible_winrm_server_cert_validation').
|
34
45
|
returns(true).at_least_once
|
35
46
|
Setting.expects(:[]).with('ansible_connection').
|
36
47
|
returns('ssh').at_least_once
|
37
48
|
@host.expects(:host_params).returns(extra_options).at_least_once
|
38
|
-
|
49
|
+
@template_invocation.job_invocation.expects(:password).
|
50
|
+
returns(nil).at_least_once
|
51
|
+
inventory = ForemanAnsible::InventoryCreator.new(@host,
|
52
|
+
@template_invocation)
|
39
53
|
connection_params = inventory.connection_params(@host)
|
40
54
|
assert_empty extra_options.to_a - inventory.connection_params(@host).to_a
|
55
|
+
assert_equal @template_invocation.effective_user,
|
56
|
+
connection_params['ansible_become']
|
41
57
|
assert_equal Setting['ansible_connection'],
|
42
58
|
connection_params['ansible_connection']
|
43
|
-
|
59
|
+
refute_equal Setting['remote_execution_ssh_user'],
|
60
|
+
connection_params['ansible_user']
|
61
|
+
assert_equal extra_options['ansible_user'],
|
62
|
+
connection_params['ansible_user']
|
63
|
+
refute_equal Setting['remote_execution_ssh_port'],
|
64
|
+
connection_params['ansible_port']
|
65
|
+
assert_equal ForemanRemoteExecutionCore.settings[:ssh_identity_key_file],
|
66
|
+
connection_params['ansible_ssh_private_key_file']
|
67
|
+
assert_equal extra_options['ansible_port'],
|
68
|
+
connection_params['ansible_port']
|
69
|
+
assert_equal Setting['remote_execution_ssh_password'],
|
44
70
|
connection_params['ansible_ssh_pass']
|
45
71
|
assert_equal Setting['ansible_winrm_server_cert_validation'],
|
46
72
|
connection_params['ansible_winrm_server_cert_validation']
|
47
73
|
end
|
74
|
+
|
75
|
+
test 'job invocation ssh password is passed when available' do
|
76
|
+
inventory = ForemanAnsible::InventoryCreator.new(@host,
|
77
|
+
@template_invocation)
|
78
|
+
assert_equal(@template_invocation.job_invocation.password,
|
79
|
+
inventory.rex_ssh_password(@host))
|
80
|
+
end
|
81
|
+
|
82
|
+
test 'ssh private key is passed when available' do
|
83
|
+
host = FactoryBot.build(:host)
|
84
|
+
path_to_key = '/path/to/private/key'
|
85
|
+
inventory = ForemanAnsible::InventoryCreator.new(host,
|
86
|
+
@template_invocation)
|
87
|
+
host.params.expects(:[]).with('ansible_ssh_private_key_file')
|
88
|
+
.returns(path_to_key)
|
89
|
+
host.params.expects(:[]).with('remote_execution_ssh_user')
|
90
|
+
.returns('root')
|
91
|
+
host.params.expects(:[]).with('remote_execution_ssh_port')
|
92
|
+
.returns('2222')
|
93
|
+
connection_params = inventory.connection_params(host)
|
94
|
+
assert_equal path_to_key,
|
95
|
+
connection_params['ansible_ssh_private_key_file']
|
96
|
+
end
|
97
|
+
|
98
|
+
test 'template invocation inputs are sent as Ansible variables' do
|
99
|
+
job_template = FactoryBot.build(
|
100
|
+
:job_template,
|
101
|
+
:template => 'service restart {{service_name}}'
|
102
|
+
)
|
103
|
+
job_invocation = FactoryBot.create(:job_invocation)
|
104
|
+
job_template.template_inputs << FactoryBot.build(:template_input,
|
105
|
+
:name => 'service_name',
|
106
|
+
:input_type => 'user',
|
107
|
+
:required => true)
|
108
|
+
template_invocation = FactoryBot.build(:template_invocation,
|
109
|
+
:template => job_template,
|
110
|
+
:job_invocation => job_invocation)
|
111
|
+
job_invocation.expects(:password).returns(nil).at_least_once
|
112
|
+
input_value = FactoryBot.create(
|
113
|
+
:template_invocation_input_value,
|
114
|
+
:template_invocation => template_invocation,
|
115
|
+
:template_input => job_template.template_inputs.first,
|
116
|
+
:value => 'foreman'
|
117
|
+
)
|
118
|
+
template_invocation.input_values << input_value
|
119
|
+
inventory = ForemanAnsible::InventoryCreator.new([@host],
|
120
|
+
template_invocation)
|
121
|
+
assert_equal({ 'service_name' => 'foreman' },
|
122
|
+
inventory.to_hash['all']['vars'])
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'top-level parameters sent as variables' do
|
126
|
+
setup do
|
127
|
+
# Fetching the Host parameters requires this Setting, since
|
128
|
+
# this plugin does not provide fixtures
|
129
|
+
Setting.create(:name => 'top_level_ansible_vars',
|
130
|
+
:description => 'sample description',
|
131
|
+
:default => true)
|
132
|
+
@template_invocation = OpenStruct.new(
|
133
|
+
:job_invocation => OpenStruct.new(:password => 'foobar'),
|
134
|
+
:input_values => []
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
test 'parameters are passed as top-level "hostvars" by default' do
|
139
|
+
@host.expects(:host_params).returns('hello' => 'foreman').at_least_once
|
140
|
+
inventory = ForemanAnsible::InventoryCreator.new([@host],
|
141
|
+
@template_invocation)
|
142
|
+
hostvar = inventory.to_hash['_meta']['hostvars'][@host.name]['hello']
|
143
|
+
assert_equal 'foreman', hostvar
|
144
|
+
end
|
145
|
+
|
146
|
+
test 'parameters NOT passed as top-level "hostvars" if false' do
|
147
|
+
Setting['top_level_ansible_vars'] = false
|
148
|
+
@host.expects(:host_params).returns('hello' => 'foreman').at_least_once
|
149
|
+
inventory = ForemanAnsible::InventoryCreator.new([@host],
|
150
|
+
@template_invocation)
|
151
|
+
hostvar = inventory.to_hash['_meta']['hostvars'][@host.name]['hello']
|
152
|
+
refute_equal 'foreman', hostvar
|
153
|
+
end
|
154
|
+
end
|
48
155
|
end
|
156
|
+
# rubocop:enable ClassLength
|
49
157
|
end
|