foreman_ansible 6.4.1 → 7.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/ansible_inventories_controller.rb +1 -1
- data/app/graphql/mutations/ansible_variable_overrides/create.rb +26 -0
- data/app/graphql/mutations/ansible_variable_overrides/delete.rb +38 -0
- data/app/graphql/mutations/ansible_variable_overrides/update.rb +26 -0
- data/app/graphql/mutations/hosts/assign_ansible_roles.rb +37 -0
- data/app/graphql/presenters/ansible_role_presenter.rb +12 -0
- data/app/graphql/presenters/overriden_ansible_variable_presenter.rb +19 -0
- data/app/graphql/types/ansible_role.rb +9 -0
- data/app/graphql/types/ansible_variable.rb +23 -0
- data/app/graphql/types/ansible_variable_override.rb +9 -0
- data/app/graphql/types/inherited_ansible_role.rb +13 -0
- data/app/graphql/types/overriden_ansible_variable.rb +27 -0
- data/app/helpers/foreman_ansible/ansible_roles_data_preparations.rb +22 -22
- data/app/models/concerns/foreman_ansible/host_managed_extensions.rb +23 -4
- data/app/models/concerns/foreman_ansible/hostgroup_extensions.rb +2 -1
- data/app/models/foreman_ansible/ansible_provider.rb +7 -5
- data/app/services/foreman_ansible/ansible_report_importer.rb +2 -2
- data/app/services/foreman_ansible/inventory_creator.rb +1 -1
- data/app/services/foreman_ansible/override_resolver.rb +22 -0
- data/app/views/api/v2/ansible_override_values/index.json.rabl +3 -0
- data/app/views/api/v2/ansible_variables/show.json.rabl +1 -1
- data/app/views/foreman_ansible/ansible_roles/_hostgroup_ansible_roles_button.erb +3 -0
- data/app/views/foreman_ansible/job_templates/convert_to_rhel.erb +6 -2
- data/app/views/foreman_ansible/job_templates/run_openscap_scans_-_ansible_default.erb +20 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20210818083407_fix_ansible_setting_category_to_dsl.rb +5 -0
- data/lib/foreman_ansible/engine.rb +0 -17
- data/lib/foreman_ansible/register.rb +115 -4
- data/lib/foreman_ansible/version.rb +1 -1
- data/package.json +4 -2
- data/test/functional/api/v2/ansible_inventories_controller_test.rb +1 -2
- data/test/graphql/mutations/hosts/assign_ansible_roles_mutation_test.rb +96 -0
- data/test/graphql/queries/ansible_roles_query_test.rb +35 -0
- data/test/unit/ansible_provider_test.rb +3 -6
- data/test/unit/concerns/host_managed_extensions_test.rb +8 -0
- data/test/unit/concerns/hostgroup_extensions_test.rb +6 -0
- data/test/unit/hostgroup_ansible_role_test.rb +13 -0
- data/test/unit/services/override_resolver_test.rb +34 -0
- data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.js +51 -27
- data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.test.js +12 -6
- data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.js +22 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.scss +4 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.test.js +104 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/index.js +38 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverrides.scss +3 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTable.js +238 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTableHelper.js +111 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.js +161 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.scss +7 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableActionHelper.js +49 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValue.js +70 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValueHelper.js +35 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.fixtures.js +429 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.test.js +71 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesDelete.test.js +74 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesUpdate.test.js +188 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/index.js +58 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/JobsTabHelper.js +79 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobHelper.js +106 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.js +129 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.scss +7 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/PreviousJobsTable.js +103 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/RecurringJobsTable.js +96 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.fixtures.js +184 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.test.js +195 -0
- data/webpack/components/AnsibleHostDetail/components/JobsTab/index.js +88 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/AllRolesTable.js +89 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js +80 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesForm.js +90 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesModal.scss +3 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesModalHelper.js +40 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/index.js +82 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/RolesTable.js +129 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/EditRoles.test.js +85 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.fixtures.js +180 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.test.js +75 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/index.js +51 -0
- data/webpack/components/AnsibleHostDetail/components/SecondaryTabRoutes.js +60 -0
- data/webpack/components/AnsibleHostDetail/components/TabLayout.js +12 -0
- data/webpack/components/AnsibleHostDetail/constants.js +9 -0
- data/webpack/components/AnsibleHostDetail/helpers.js +4 -0
- data/webpack/components/AnsibleRolesAndVariables/__test__/AnsibleRolesAndVariablesImport.test.js +15 -10
- data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.js +29 -0
- data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.test.js +3 -0
- data/webpack/components/AnsibleRolesSwitcher/components/AvailableRolesList.js +2 -1
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsibleRole.test.js.snap +3 -3
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AvailableRolesList.test.js.snap +4 -0
- data/webpack/components/DualList/DualList.scss +3 -0
- data/webpack/components/DualList/ListControls.js +65 -0
- data/webpack/components/DualList/ListHeader.js +16 -0
- data/webpack/components/DualList/ListItem.js +69 -0
- data/webpack/components/DualList/ListPane.js +95 -0
- data/webpack/components/DualList/SelectedStatus.js +21 -0
- data/webpack/components/DualList/index.js +103 -0
- data/webpack/components/ErrorState.js +16 -0
- data/webpack/components/withLoading.js +135 -0
- data/webpack/components/withPagination.js +0 -0
- data/webpack/formHelper.js +131 -0
- data/webpack/globalIdHelper.js +13 -0
- data/webpack/global_index.js +7 -1
- data/webpack/graphql/mutations/assignAnsibleRoles.gql +17 -0
- data/webpack/graphql/mutations/cancelRecurringLogic.gql +12 -0
- data/webpack/graphql/mutations/createAnsibleVariableOverride.gql +28 -0
- data/webpack/graphql/mutations/createJobInvocation.gql +11 -0
- data/webpack/graphql/mutations/deleteAnsibleVariableOverride.gql +17 -0
- data/webpack/graphql/mutations/updateAnsibleVariableOverride.gql +29 -0
- data/webpack/graphql/queries/allAnsibleRoles.gql +13 -0
- data/webpack/graphql/queries/ansibleRoles.gql +13 -0
- data/webpack/graphql/queries/currentUserAttributes.gql +11 -0
- data/webpack/graphql/queries/hostAnsibleRoles.gql +17 -0
- data/webpack/graphql/queries/hostAvailableAnsibleRoles.gql +11 -0
- data/webpack/graphql/queries/hostVariableOverrides.gql +39 -0
- data/webpack/graphql/queries/recurringJobs.gql +28 -0
- data/webpack/helpers/pageParamsHelper.js +40 -0
- data/webpack/helpers/paginationHelper.js +9 -0
- data/webpack/permissionsHelper.js +58 -0
- data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.fixtures.js +63 -0
- data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.test.js +112 -0
- data/webpack/routes/HostgroupJobs/index.js +26 -0
- data/webpack/routes/routes.js +10 -0
- data/webpack/testHelper.js +165 -0
- data/webpack/toastHelper.js +4 -0
- metadata +127 -54
- data/app/assets/images/foreman_ansible/Ansible.png +0 -0
- data/app/models/foreman_ansible/fact_name.rb +0 -16
- data/app/models/setting/ansible.rb +0 -106
- data/app/services/foreman_ansible/fact_importer.rb +0 -99
- data/app/services/foreman_ansible/fact_parser.rb +0 -126
- data/app/services/foreman_ansible/fact_sparser.rb +0 -37
- data/app/services/foreman_ansible/operating_system_parser.rb +0 -102
- data/app/services/foreman_ansible/structured_fact_importer.rb +0 -25
- data/test/unit/services/fact_importer_test.rb +0 -52
- data/test/unit/services/fact_parser_test.rb +0 -281
- data/test/unit/services/fact_sparser_test.rb +0 -24
- data/test/unit/services/structured_fact_importer_test.rb +0 -30
- data/webpack/components/AnsibleRolesAndVariables/__test__/__snapshots__/AnsibleRolesAndVariablesImport.test.js.snap +0 -177
@@ -19,12 +19,6 @@ module ForemanAnsible
|
|
19
19
|
config.autoload_paths += Dir["#{config.root}/app/views"]
|
20
20
|
config.autoload_paths += Dir["#{config.root}/app/lib"]
|
21
21
|
|
22
|
-
initializer 'foreman_ansible.load_default_settings',
|
23
|
-
:before => :load_config_initializers do
|
24
|
-
require_dependency(File.join(ForemanAnsible::Engine.root,
|
25
|
-
'app/models/setting/ansible.rb'))
|
26
|
-
end
|
27
|
-
|
28
22
|
initializer 'foreman_ansible.register_gettext',
|
29
23
|
:after => :load_config_initializers do
|
30
24
|
locale_dir = File.join(File.expand_path('../..', __dir__), 'locale')
|
@@ -71,17 +65,6 @@ module ForemanAnsible
|
|
71
65
|
end
|
72
66
|
|
73
67
|
config.to_prepare do
|
74
|
-
foreman_version = ::Foreman::Version.new
|
75
|
-
if Rails.env.test? ||
|
76
|
-
foreman_version.major.to_i == 1 && foreman_version.minor.to_i < 13
|
77
|
-
::Foreman::Plugin.fact_importer_registry.register(:ansible, ForemanAnsible::FactImporter)
|
78
|
-
else
|
79
|
-
::Foreman::Plugin.fact_importer_registry.register(
|
80
|
-
:ansible,
|
81
|
-
ForemanAnsible::StructuredFactImporter
|
82
|
-
)
|
83
|
-
end
|
84
|
-
::FactParser.register_fact_parser(:ansible, ForemanAnsible::FactParser)
|
85
68
|
::Host::Managed.prepend ForemanAnsible::HostManagedExtensions
|
86
69
|
::Hostgroup.include ForemanAnsible::HostgroupExtensions
|
87
70
|
::HostsHelper.include ForemanAnsible::HostsHelperExtensions
|
@@ -1,7 +1,89 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Foreman::Plugin.register :foreman_ansible do
|
4
|
-
requires_foreman '>=
|
4
|
+
requires_foreman '>= 3.0'
|
5
|
+
|
6
|
+
settings do
|
7
|
+
category :ansible, N_('Ansible') do
|
8
|
+
setting 'ansible_ssh_private_key_file',
|
9
|
+
type: :string,
|
10
|
+
description: N_('Use this to supply a path to an SSH Private Key '\
|
11
|
+
'that Ansible will use in lieu of a password '\
|
12
|
+
'Override with "ansible_ssh_private_key_file" '\
|
13
|
+
'host parameter'),
|
14
|
+
default: '',
|
15
|
+
full_name: N_('Private Key Path')
|
16
|
+
setting 'ansible_connection',
|
17
|
+
type: :string,
|
18
|
+
description: N_('Use this connection type by default when running '\
|
19
|
+
'Ansible playbooks. You can override this on hosts by '\
|
20
|
+
'adding a parameter "ansible_connection"'),
|
21
|
+
default: 'ssh',
|
22
|
+
full_name: N_('Connection type')
|
23
|
+
setting 'ansible_winrm_server_cert_validation',
|
24
|
+
type: :string,
|
25
|
+
description: N_('Enable/disable WinRM server certificate '\
|
26
|
+
'validation when running Ansible playbooks. You can override '\
|
27
|
+
'this on hosts by adding a parameter '\
|
28
|
+
'"ansible_winrm_server_cert_validation"'),
|
29
|
+
default: 'validate',
|
30
|
+
full_name: N_('WinRM cert Validation')
|
31
|
+
setting 'ansible_verbosity',
|
32
|
+
type: :integer,
|
33
|
+
description: N_('Foreman will add this level of verbosity for '\
|
34
|
+
'additional debugging output when running Ansible playbooks.'),
|
35
|
+
default: '0',
|
36
|
+
full_name: N_('Default verbosity level'),
|
37
|
+
value: nil,
|
38
|
+
collection: proc {
|
39
|
+
{ '0' => N_('Disabled'),
|
40
|
+
'1' => N_('Level 1 (-v)'),
|
41
|
+
'2' => N_('Level 2 (-vv)'),
|
42
|
+
'3' => N_('Level 3 (-vvv)'),
|
43
|
+
'4' => N_('Level 4 (-vvvv)') }
|
44
|
+
}
|
45
|
+
setting 'ansible_post_provision_timeout',
|
46
|
+
type: :integer,
|
47
|
+
description: N_('Timeout (in seconds) to set when Foreman will trigger a '\
|
48
|
+
'play Ansible roles task after a host is fully provisioned. '\
|
49
|
+
'Set this to the maximum time you expect a host to take '\
|
50
|
+
'until it is ready after a reboot.'),
|
51
|
+
default: '360',
|
52
|
+
full_name: N_('Post-provision timeout')
|
53
|
+
setting 'ansible_interval',
|
54
|
+
type: :integer,
|
55
|
+
description: N_('Timeout (in minutes) when hosts should have reported.'),
|
56
|
+
default: '30',
|
57
|
+
full_name: N_('Ansible report timeout')
|
58
|
+
setting 'ansible_out_of_sync_disabled',
|
59
|
+
type: :boolean,
|
60
|
+
description: format(N_('Disable host configuration status turning to out of'\
|
61
|
+
' sync for %{cfgmgmt} after report does not arrive within'\
|
62
|
+
' configured interval'), :cfgmgmt => 'Ansible'),
|
63
|
+
default: false,
|
64
|
+
full_name: format(N_('%{cfgmgmt} out of sync disabled'), :cfgmgmt => 'Ansible')
|
65
|
+
setting 'ansible_inventory_template',
|
66
|
+
type: :string,
|
67
|
+
description: N_('Foreman will use this template to schedule the report '\
|
68
|
+
'with Ansible inventory'),
|
69
|
+
default: 'Ansible - Ansible Inventory',
|
70
|
+
full_name: N_('Default Ansible inventory report template')
|
71
|
+
setting 'ansible_roles_to_ignore',
|
72
|
+
type: :array,
|
73
|
+
description: N_('Those roles will be excluded when importing roles from smart proxy, '\
|
74
|
+
'The expected input is comma separated values and you can use * wildcard metacharacters'\
|
75
|
+
'For example: foo*, *b*,*bar'),
|
76
|
+
default: [],
|
77
|
+
full_name: N_('Ansible roles to ignore')
|
78
|
+
setting 'foreman_ansible_proxy_batch_size',
|
79
|
+
type: :integer,
|
80
|
+
description: N_('Number of tasks which should be sent to the smart proxy in one request, '\
|
81
|
+
'if foreman_tasks_proxy_batch_trigger is enabled. '\
|
82
|
+
'If set, overrides foreman_tasks_proxy_batch_size setting for Ansible jobs.'),
|
83
|
+
default: nil,
|
84
|
+
full_name: N_('Proxy tasks batch size for Ansible')
|
85
|
+
end
|
86
|
+
end
|
5
87
|
|
6
88
|
security_block :foreman_ansible do
|
7
89
|
permission :play_roles_on_host,
|
@@ -29,23 +111,27 @@ Foreman::Plugin.register :foreman_ansible do
|
|
29
111
|
:resource_type => 'AnsibleRole'
|
30
112
|
permission :view_ansible_variables,
|
31
113
|
{
|
114
|
+
:lookup_values => [:index],
|
32
115
|
:ansible_variables => [:index, :auto_complete_search],
|
33
116
|
:'api/v2/ansible_variables' => [:index, :show]
|
34
117
|
},
|
35
118
|
:resource_type => 'AnsibleVariable'
|
36
119
|
permission :edit_ansible_variables,
|
37
|
-
{ :
|
120
|
+
{ :lookup_values => [:update],
|
121
|
+
:ansible_variables => [:edit, :update],
|
38
122
|
:'api/v2/ansible_variables' => [:update],
|
39
123
|
:'api/v2/ansible_override_values' => [:create, :destroy] },
|
40
124
|
:resource_type => 'AnsibleVariable'
|
41
125
|
permission :destroy_ansible_variables,
|
42
126
|
{
|
127
|
+
:lookup_values => [:destroy],
|
43
128
|
:ansible_variables => [:destroy],
|
44
129
|
:'api/v2/ansible_variables' => [:destroy, :obsolete]
|
45
130
|
},
|
46
131
|
:resource_type => 'AnsibleVariable'
|
47
132
|
permission :create_ansible_variables,
|
48
133
|
{
|
134
|
+
:lookup_values => [:create],
|
49
135
|
:ansible_variables => [:new, :create],
|
50
136
|
:'api/v2/ansible_variables' => [:create]
|
51
137
|
},
|
@@ -76,9 +162,10 @@ Foreman::Plugin.register :foreman_ansible do
|
|
76
162
|
|
77
163
|
role 'Ansible Roles Manager',
|
78
164
|
[:play_roles_on_host, :play_roles_on_hostgroup,
|
165
|
+
:create_job_invocations, :view_job_templates, # to allow the play_roles
|
166
|
+
:create_template_invocations, :view_smart_proxies, # ...
|
79
167
|
:view_ansible_roles, :destroy_ansible_roles,
|
80
|
-
:import_ansible_roles,
|
81
|
-
:view_ansible_variables,
|
168
|
+
:import_ansible_roles, :view_ansible_variables,
|
82
169
|
:create_ansible_variables, :import_ansible_variables,
|
83
170
|
:edit_ansible_variables, :destroy_ansible_variables]
|
84
171
|
|
@@ -98,6 +185,30 @@ Foreman::Plugin.register :foreman_ansible do
|
|
98
185
|
|
99
186
|
register_global_js_file 'global'
|
100
187
|
|
188
|
+
extend_graphql_type :type => ::Types::Host do
|
189
|
+
field :all_ansible_roles, ::Types::InheritedAnsibleRole.connection_type, :null => true, :method => :present_all_ansible_roles
|
190
|
+
field :own_ansible_roles, ::Types::AnsibleRole.connection_type, :null => true
|
191
|
+
field :available_ansible_roles, ::Types::AnsibleRole.connection_type, :null => true
|
192
|
+
field :ansible_variables_with_overrides, Types::OverridenAnsibleVariable.connection_type, :null => false
|
193
|
+
|
194
|
+
def present_all_ansible_roles
|
195
|
+
inherited_ansible_roles = object.inherited_ansible_roles.map { |role| ::Presenters::AnsibleRolePresenter.new(role, true) }
|
196
|
+
ansible_roles = object.ansible_roles.map { |role| ::Presenters::AnsibleRolePresenter.new(role, false) }
|
197
|
+
(inherited_ansible_roles + ansible_roles).uniq
|
198
|
+
end
|
199
|
+
|
200
|
+
def ansible_variables_with_overrides
|
201
|
+
resolver = ::ForemanAnsible::OverrideResolver.new(object)
|
202
|
+
AnsibleVariable.where(:ansible_role_id => object.all_ansible_roles.pluck(:id), :override => true).map { |variable| ::Presenters::OverridenAnsibleVariablePresenter.new variable, resolver }
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
register_graphql_query_field :ansible_roles, '::Types::AnsibleRole', :collection_field
|
207
|
+
register_graphql_mutation_field :assign_ansible_roles, '::Mutations::Hosts::AssignAnsibleRoles'
|
208
|
+
register_graphql_mutation_field :delete_ansible_variable_override, ::Mutations::AnsibleVariableOverrides::Delete
|
209
|
+
register_graphql_mutation_field :update_ansible_variable_override, ::Mutations::AnsibleVariableOverrides::Update
|
210
|
+
register_graphql_mutation_field :create_ansible_variable_override, ::Mutations::AnsibleVariableOverrides::Create
|
211
|
+
|
101
212
|
divider :top_menu, :caption => N_('Ansible'), :parent => :configure_menu
|
102
213
|
menu :top_menu, :ansible_roles,
|
103
214
|
:caption => N_('Roles'),
|
data/package.json
CHANGED
@@ -7,19 +7,21 @@
|
|
7
7
|
"test": "test"
|
8
8
|
},
|
9
9
|
"peerDependencies": {
|
10
|
-
"@theforeman/vendor": ">=
|
10
|
+
"@theforeman/vendor": ">= 8.16.0"
|
11
11
|
},
|
12
12
|
"dependencies": {
|
13
13
|
"react-json-tree": "^0.11.0"
|
14
14
|
},
|
15
15
|
"devDependencies": {
|
16
16
|
"@babel/core": "^7.7.0",
|
17
|
+
"@testing-library/user-event": "^13.2.1",
|
17
18
|
"@theforeman/builder": "^8.4.1",
|
18
19
|
"@theforeman/eslint-plugin-foreman": "^8.4.1",
|
19
20
|
"@theforeman/find-foreman": "^8.4.1",
|
20
21
|
"@theforeman/stories": "^8.4.1",
|
21
|
-
"@theforeman/test": "^8.
|
22
|
+
"@theforeman/test": "^8.9.0",
|
22
23
|
"@theforeman/vendor-dev": "^8.4.1",
|
24
|
+
"@testing-library/user-event": "^13.2.1",
|
23
25
|
"babel-eslint": "^10.0.3",
|
24
26
|
"eslint": "^6.7.2",
|
25
27
|
"prettier": "^1.13.5"
|
@@ -34,8 +34,7 @@ module Api
|
|
34
34
|
|
35
35
|
test 'schedule inventory by user' do
|
36
36
|
report = FactoryBot.create(:report_template)
|
37
|
-
Setting
|
38
|
-
:default => report.name, :description => 'inventory'
|
37
|
+
Setting['ansible_inventory_template'] = report.name
|
39
38
|
user = FactoryBot.create(:user)
|
40
39
|
user.roles << Role.find_by(:name => 'Ansible Tower Inventory Reader')
|
41
40
|
post :schedule, { :session => set_session_user(user) }
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
module Mutations
|
4
|
+
module Hosts
|
5
|
+
class CreateMutationTest < GraphQLQueryTestCase
|
6
|
+
let(:tax_location) { FactoryBot.create(:location) }
|
7
|
+
let(:location_id) { Foreman::GlobalId.for(tax_location) }
|
8
|
+
let(:organization) { FactoryBot.create(:organization) }
|
9
|
+
let(:organization_id) { Foreman::GlobalId.for(organization) }
|
10
|
+
|
11
|
+
let(:role1) { FactoryBot.create(:ansible_role) }
|
12
|
+
let(:role2) { FactoryBot.create(:ansible_role) }
|
13
|
+
let(:role3) { FactoryBot.create(:ansible_role) }
|
14
|
+
let(:host) { FactoryBot.create(:host, :ansible_roles => [role1, role2, role3], :organization => organization, :location => tax_location) }
|
15
|
+
|
16
|
+
let(:variables) { { id: Foreman::GlobalId.for(host), ansibleRoleIds: [role3.id, role2.id, role1.id] } }
|
17
|
+
let(:query) do
|
18
|
+
<<-GRAPHQL
|
19
|
+
mutation AssignAnsibleRoles($id: ID!, $ansibleRoleIds: [Int!]!) {
|
20
|
+
assignAnsibleRoles(input: { id: $id, ansibleRoleIds: $ansibleRoleIds }) {
|
21
|
+
host {
|
22
|
+
id
|
23
|
+
ownAnsibleRoles {
|
24
|
+
nodes {
|
25
|
+
id
|
26
|
+
name
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
errors {
|
31
|
+
path
|
32
|
+
message
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
GRAPHQL
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'with admin permissions' do
|
40
|
+
let(:context_user) { FactoryBot.create(:user, :admin) }
|
41
|
+
let(:data) { result['data']['assignAnsibleRoles']['host'] }
|
42
|
+
|
43
|
+
it 'reorderes ansible roles' do
|
44
|
+
assert_empty result['errors']
|
45
|
+
assert_not_nil data
|
46
|
+
assert_empty result['data']['assignAnsibleRoles']['errors']
|
47
|
+
|
48
|
+
assert_equal([role3, role2, role1].map { |role| Foreman::GlobalId.for role }, data['ownAnsibleRoles']['nodes'].map { |node| node['id'] })
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'with edit permission' do
|
53
|
+
let(:context_user) do
|
54
|
+
setup_user('edit', 'hosts') do |user|
|
55
|
+
user.roles << Role.find_by(name: 'Viewer')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
let(:data) { result['data']['assignAnsibleRoles']['host'] }
|
59
|
+
let(:variables) { { id: Foreman::GlobalId.for(host), ansibleRoleIds: [role3.id, role2.id] } }
|
60
|
+
|
61
|
+
before do
|
62
|
+
Location.current = tax_location
|
63
|
+
Organization.current = organization
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'reorderes ansible roles' do
|
67
|
+
assert_empty result['errors']
|
68
|
+
assert_not_nil data
|
69
|
+
assert_empty result['data']['assignAnsibleRoles']['errors']
|
70
|
+
|
71
|
+
assert_equal([role3, role2].map { |role| Foreman::GlobalId.for role }, data['ownAnsibleRoles']['nodes'].map { |node| node['id'] })
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'with view only permissions' do
|
76
|
+
let(:context_user) do
|
77
|
+
setup_user('show', 'hosts') do |user|
|
78
|
+
user.roles << Role.find_by(name: 'Viewer')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
before do
|
83
|
+
Location.current = tax_location
|
84
|
+
Organization.current = organization
|
85
|
+
end
|
86
|
+
|
87
|
+
test 'cannot create a host' do
|
88
|
+
expected_error = 'Unauthorized. You do not have the required permission edit_hosts.'
|
89
|
+
|
90
|
+
assert_not_empty result['errors']
|
91
|
+
assert_includes result['errors'].map { |e| e['message'] }, expected_error
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
module Queries
|
4
|
+
class AnsibleRolesQueryTest < GraphQLQueryTestCase
|
5
|
+
let(:query) do
|
6
|
+
<<-GRAPHQL
|
7
|
+
query {
|
8
|
+
ansibleRoles {
|
9
|
+
totalCount
|
10
|
+
nodes {
|
11
|
+
id
|
12
|
+
name
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
16
|
+
GRAPHQL
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:data) { result['data']['ansibleRoles'] }
|
20
|
+
|
21
|
+
setup do
|
22
|
+
FactoryBot.create_list(:ansible_role, 2)
|
23
|
+
end
|
24
|
+
|
25
|
+
test 'should fetch Ansible roles' do
|
26
|
+
assert_empty result['errors']
|
27
|
+
|
28
|
+
expected_count = AnsibleRole.count
|
29
|
+
|
30
|
+
assert_not_equal 0, expected_count
|
31
|
+
assert_equal expected_count, data['totalCount']
|
32
|
+
assert_equal expected_count, data['nodes'].count
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -30,14 +30,11 @@ class AnsibleProviderTest < ActiveSupport::TestCase
|
|
30
30
|
end
|
31
31
|
|
32
32
|
context 'when using secrets' do
|
33
|
-
let(:host) { FactoryBot.
|
33
|
+
let(:host) { FactoryBot.create(:host) }
|
34
34
|
|
35
35
|
it 'generates secrets properly' do
|
36
|
-
|
37
|
-
|
38
|
-
'remote_execution_effective_user_password' => 'letmein'
|
39
|
-
}
|
40
|
-
host.expects(:params).twice.returns(params)
|
36
|
+
host.parameters << HostParameter.new(name: 'remote_execution_ssh_password', value: 'password')
|
37
|
+
host.parameters << HostParameter.new(name: 'remote_execution_effective_user_password', value: 'letmein')
|
41
38
|
secrets = ForemanAnsible::AnsibleProvider.secrets(host)
|
42
39
|
host_secrets = secrets['per-host'][host.name]
|
43
40
|
assert_equal host_secrets['ansible_password'], 'password'
|
@@ -62,4 +62,12 @@ class HostManagedExtensionsTest < ActiveSupport::TestCase
|
|
62
62
|
FactoryBot.create(:host_ansible_role, :ansible_role_id => @role1.id, :position => 0, :host_id => host.id)
|
63
63
|
host.all_ansible_roles.must_equal [@role2, @role1]
|
64
64
|
end
|
65
|
+
|
66
|
+
test 'should find hosts with role2' do
|
67
|
+
host1 = FactoryBot.create(:host, :ansible_roles => [@role2])
|
68
|
+
host2 = FactoryBot.create(:host, :hostgroup => @hostgroup)
|
69
|
+
result = Host::Managed.search_for("ansible_role= #{@role2.name}").pluck(:id)
|
70
|
+
assert_include result, host1.id
|
71
|
+
assert_include result, host2.id
|
72
|
+
end
|
65
73
|
end
|
@@ -55,4 +55,10 @@ class HostgroupExtensionsTest < ActiveSupport::TestCase
|
|
55
55
|
@hostgroup.parent = @hostgroup_parent
|
56
56
|
@hostgroup.inherited_and_own_ansible_roles.must_equal [@role2, @role1]
|
57
57
|
end
|
58
|
+
|
59
|
+
describe '#cloned_ansibe_roles' do
|
60
|
+
test 'clone ansible roles from hostgroup parent' do
|
61
|
+
@hostgroup_parent.clone.all_ansible_roles.must_equal @hostgroup_parent.all_ansible_roles
|
62
|
+
end
|
63
|
+
end
|
58
64
|
end
|
@@ -19,4 +19,17 @@ class HostgroupAnsibleRoleTest < ActiveSupport::TestCase
|
|
19
19
|
end
|
20
20
|
should validate_uniqueness_of(:ansible_role_id).scoped_to(:hostgroup_id)
|
21
21
|
end
|
22
|
+
|
23
|
+
describe 'ordering' do
|
24
|
+
test 'should list roles in correct order' do
|
25
|
+
hg = FactoryBot.create(:hostgroup)
|
26
|
+
5.times do |idx|
|
27
|
+
HostgroupAnsibleRole.create(:hostgroup => hg, :ansible_role => FactoryBot.create(:ansible_role), :position => idx)
|
28
|
+
end
|
29
|
+
|
30
|
+
hg.hostgroup_ansible_roles.each_with_index do |item, idx|
|
31
|
+
assert_equal(item.position, idx + 1)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
22
35
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_plugin_helper'
|
4
|
+
|
5
|
+
module ForemanAnsible
|
6
|
+
class OverrideResolverTest < ActiveSupport::TestCase
|
7
|
+
test 'should return no overrides when no roles assigned to host' do
|
8
|
+
assert_empty OverrideResolver.new(FactoryBot.build(:host)).overrides
|
9
|
+
end
|
10
|
+
|
11
|
+
test 'should return overrides for host' do
|
12
|
+
first_role = FactoryBot.create(:ansible_role)
|
13
|
+
first_var = FactoryBot.create(:ansible_variable, :override => true, :ansible_role => first_role)
|
14
|
+
second_role = FactoryBot.create(:ansible_role)
|
15
|
+
second_var = FactoryBot.create(:ansible_variable, :override => true, :ansible_role => second_role)
|
16
|
+
host = FactoryBot.create(:host, :ansible_roles => [first_role])
|
17
|
+
another_host = FactoryBot.create(:host)
|
18
|
+
|
19
|
+
FactoryBot.create(:lookup_value, :match => "fqdn=#{host.name}", :lookup_key_id => first_var.id)
|
20
|
+
FactoryBot.create(:lookup_value, :match => "fqdn=#{another_host.name}", :lookup_key_id => first_var.id)
|
21
|
+
FactoryBot.create(:lookup_value, :match => "fqdn=#{host.name}", :lookup_key_id => second_var.id)
|
22
|
+
|
23
|
+
assert_not_nil OverrideResolver.new(host).resolve(first_var)
|
24
|
+
assert_nil OverrideResolver.new(another_host).resolve(first_var)
|
25
|
+
assert_nil OverrideResolver.new(host).resolve(second_var)
|
26
|
+
end
|
27
|
+
|
28
|
+
test 'should raise when no host specified' do
|
29
|
+
assert_raises Foreman::Exception do
|
30
|
+
OverrideResolver.new(nil)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,35 +1,59 @@
|
|
1
|
-
import React
|
2
|
-
import
|
3
|
-
import {
|
4
|
-
|
5
|
-
import
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
|
4
|
+
import { useHistory } from 'react-router-dom';
|
5
|
+
import SkeletonLoader from 'foremanReact/components/common/SkeletonLoader';
|
6
6
|
|
7
|
+
import SecondaryTabRoutes from './components/SecondaryTabRoutes';
|
8
|
+
import { SECONDARY_TABS } from './constants';
|
7
9
|
import './AnsibleHostDetail.scss';
|
8
10
|
|
9
|
-
const AnsibleHostDetail =
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
const AnsibleHostDetail = ({
|
12
|
+
response,
|
13
|
+
status,
|
14
|
+
router,
|
15
|
+
location: { pathname },
|
16
|
+
history,
|
17
|
+
}) => {
|
18
|
+
const hashHistory = useHistory();
|
13
19
|
return (
|
14
|
-
<
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
20
|
+
<SkeletonLoader status={status} skeletonProps={{ count: 5 }}>
|
21
|
+
{response?.id && (
|
22
|
+
<>
|
23
|
+
<Tabs
|
24
|
+
onSelect={(evt, subTab) => hashHistory.push(subTab)}
|
25
|
+
activeKey={pathname?.split('/')[2]}
|
26
|
+
isSecondary
|
27
|
+
>
|
28
|
+
{SECONDARY_TABS.map(({ key, title }) => (
|
29
|
+
<Tab
|
30
|
+
key={key}
|
31
|
+
eventKey={key}
|
32
|
+
title={<TabTitleText>{title}</TabTitleText>}
|
33
|
+
/>
|
34
|
+
))}
|
35
|
+
</Tabs>
|
36
|
+
<SecondaryTabRoutes
|
37
|
+
response={response}
|
38
|
+
router={router}
|
39
|
+
history={history}
|
40
|
+
/>
|
41
|
+
</>
|
42
|
+
)}
|
43
|
+
</SkeletonLoader>
|
32
44
|
);
|
33
45
|
};
|
34
46
|
|
47
|
+
AnsibleHostDetail.propTypes = {
|
48
|
+
response: PropTypes.object.isRequired,
|
49
|
+
status: PropTypes.string.isRequired,
|
50
|
+
location: PropTypes.object,
|
51
|
+
router: PropTypes.object.isRequired,
|
52
|
+
history: PropTypes.object.isRequired,
|
53
|
+
};
|
54
|
+
|
55
|
+
AnsibleHostDetail.defaultProps = {
|
56
|
+
location: { pathname: '' },
|
57
|
+
};
|
58
|
+
|
35
59
|
export default AnsibleHostDetail;
|
@@ -1,14 +1,20 @@
|
|
1
1
|
import React from 'react';
|
2
|
-
import { render
|
2
|
+
import { render } from '@testing-library/react';
|
3
3
|
import '@testing-library/jest-dom';
|
4
|
-
|
5
4
|
import AnsibleHostDetail from './';
|
6
5
|
|
7
6
|
describe('AnsibleHostDetail', () => {
|
8
|
-
it('should show
|
9
|
-
render(
|
7
|
+
it('should show skeleton when loading', () => {
|
8
|
+
const { container } = render(
|
9
|
+
<AnsibleHostDetail
|
10
|
+
status="PENDING"
|
11
|
+
response={{ id: 5, name: 'test.example.com' }}
|
12
|
+
router={{}}
|
13
|
+
history={{}}
|
14
|
+
/>
|
15
|
+
);
|
10
16
|
expect(
|
11
|
-
|
12
|
-
).
|
17
|
+
container.getElementsByClassName('react-loading-skeleton')
|
18
|
+
).toHaveLength(5);
|
13
19
|
});
|
14
20
|
});
|
data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.js
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import Immutable from 'seamless-immutable';
|
3
|
+
import PropTypes from 'prop-types';
|
4
|
+
import './AnsibleHostInventory.scss';
|
5
|
+
import ReportJsonViewer from '../../../ReportJsonViewer';
|
6
|
+
|
7
|
+
const AnsibleHostInventory = ({ inventoryData }) => {
|
8
|
+
const mutableInventory =
|
9
|
+
inventoryData && Immutable.asMutable(inventoryData, { deep: true });
|
10
|
+
|
11
|
+
return (
|
12
|
+
<div className="ansible-host-inventory">
|
13
|
+
<ReportJsonViewer data={mutableInventory} />
|
14
|
+
</div>
|
15
|
+
);
|
16
|
+
};
|
17
|
+
|
18
|
+
AnsibleHostInventory.propTypes = {
|
19
|
+
inventoryData: PropTypes.object.isRequired,
|
20
|
+
};
|
21
|
+
|
22
|
+
export default AnsibleHostInventory;
|