foreman_ansible 2.0.2 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|