foreman_ansible 6.3.4 → 7.0.1
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/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_reports_helper.rb +35 -54
- data/app/models/concerns/foreman_ansible/host_managed_extensions.rb +23 -4
- data/app/models/concerns/foreman_ansible/hostgroup_extensions.rb +1 -0
- data/app/models/foreman_ansible/ansible_provider.rb +56 -2
- 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/config_reports/_ansible.html.erb +14 -5
- data/app/views/foreman_ansible/job_templates/ansible_roles_-_ansible_default.erb +4 -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 -18
- data/lib/foreman_ansible/register.rb +114 -2
- data/lib/foreman_ansible/version.rb +1 -1
- data/package.json +10 -6
- 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/concerns/host_managed_extensions_test.rb +8 -0
- data/test/unit/concerns/hostgroup_extensions_test.rb +6 -0
- data/test/unit/helpers/ansible_reports_helper_test.rb +4 -30
- data/test/unit/services/override_resolver_test.rb +34 -0
- data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.js +59 -0
- data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.scss +6 -0
- data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.test.js +20 -0
- 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/AnsibleHostDetail/index.js +6 -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__/AnsiblePermissionDenied.test.js.snap +2 -0
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsibleRole.test.js.snap +3 -3
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AssignedRolesList.test.js.snap +4 -4
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AvailableRolesList.test.js.snap +9 -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 +18 -0
- 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 +135 -83
- 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/lib/foreman_ansible_core/ansible_runner_test.rb +0 -51
- data/test/unit/lib/foreman_ansible_core/command_creator_test.rb +0 -64
- data/test/unit/lib/foreman_ansible_core/playbook_runner_test.rb +0 -110
- 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/__mocks__/foremanReact/common/I18n.js +0 -1
- data/webpack/__mocks__/foremanReact/common/helpers.js +0 -13
- data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +0 -2
- data/webpack/__mocks__/foremanReact/components/common/EmptyState.js +0 -5
- data/webpack/__mocks__/foremanReact/components/common/forms/OrderableSelect/helpers.js +0 -5
- data/webpack/__mocks__/foremanReact/redux/API.js +0 -7
- data/webpack/components/AnsibleRolesAndVariables/__test__/__snapshots__/AnsibleRolesAndVariablesImport.test.js.snap +0 -177
|
@@ -15,10 +15,14 @@ model: JobTemplate
|
|
|
15
15
|
- name: Display all parameters known for the Foreman host
|
|
16
16
|
debug:
|
|
17
17
|
var: foreman
|
|
18
|
+
tags:
|
|
19
|
+
- always
|
|
18
20
|
tasks:
|
|
19
21
|
- name: Apply roles
|
|
20
22
|
include_role:
|
|
21
23
|
name: "{{ role }}"
|
|
24
|
+
tags:
|
|
25
|
+
- always
|
|
22
26
|
loop: "{{ foreman_ansible_roles }}"
|
|
23
27
|
loop_control:
|
|
24
28
|
loop_var: role
|
|
@@ -5,13 +5,17 @@ template_inputs:
|
|
|
5
5
|
- name: Activation Key
|
|
6
6
|
required: true
|
|
7
7
|
input_type: user
|
|
8
|
+
description: Set the activation key to assign the desired RHEL subscription and
|
|
9
|
+
life cycle environment to the converted machine at the registration step.
|
|
8
10
|
advanced: false
|
|
9
|
-
value_type:
|
|
11
|
+
value_type: resource
|
|
10
12
|
resource_type: Katello::ActivationKey
|
|
11
13
|
hidden_value: false
|
|
12
14
|
- name: Restart
|
|
13
15
|
required: true
|
|
14
16
|
input_type: user
|
|
17
|
+
description: Restart the system when it is successfully converted to RHEL to boot
|
|
18
|
+
the new RHEL kernel.
|
|
15
19
|
options: "yes\r\nno"
|
|
16
20
|
advanced: false
|
|
17
21
|
value_type: plain
|
|
@@ -34,7 +38,7 @@ kind: job_template
|
|
|
34
38
|
url: <%= subscription_manager_configuration_url(@host) %>
|
|
35
39
|
dest: /usr/share/convert2rhel/subscription-manager/katello-ca-consumer-latest.noarch.rpm
|
|
36
40
|
- name: Start convert2rhel
|
|
37
|
-
command: convert2rhel -y --activationkey "<%=
|
|
41
|
+
command: convert2rhel -y --activationkey "<%= input_resource('Activation Key').name %>" --org "<%= @host.organization.label %>" --keep-rhsm
|
|
38
42
|
<%- if input('Restart') == "yes" -%>
|
|
39
43
|
- name: Reboot the machine
|
|
40
44
|
reboot:
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
name: Run OpenSCAP scans - Ansible Default
|
|
3
|
+
job_category: OpenSCAP Ansible Commands
|
|
4
|
+
description_format: Run scan for all OpenSCAP policies on given hosts
|
|
5
|
+
snippet: false
|
|
6
|
+
provider_type: Ansible
|
|
7
|
+
kind: job_template
|
|
8
|
+
model: JobTemplate
|
|
9
|
+
%>
|
|
10
|
+
|
|
11
|
+
<% raise "Create and assign a policy to this host before proceeding" if @host.policies_enc_raw.empty? -%>
|
|
12
|
+
---
|
|
13
|
+
- hosts: all
|
|
14
|
+
tasks:
|
|
15
|
+
<% @host.policies_enc_raw.each do |policy| -%>
|
|
16
|
+
- shell: /usr/bin/foreman_scap_client <%= policy['id'] %>
|
|
17
|
+
register: out
|
|
18
|
+
- debug: var=out
|
|
19
|
+
<% end -%>
|
|
20
|
+
|
data/config/routes.rb
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
Rails.application.routes.draw do
|
|
4
|
+
match '/ansible/hostgroups' => 'react#index', :via => [:get]
|
|
5
|
+
match '/ansible/hostgroups/*page' => 'react#index', :via => [:get]
|
|
6
|
+
|
|
4
7
|
namespace :api, defaults: { format: 'json' } do
|
|
5
8
|
scope '(:apiv)',
|
|
6
9
|
:module => :v2,
|
|
@@ -4,7 +4,6 @@ require 'deface'
|
|
|
4
4
|
require 'acts_as_list'
|
|
5
5
|
require 'fast_gettext'
|
|
6
6
|
require 'gettext_i18n_rails'
|
|
7
|
-
require 'foreman_ansible_core' if Rails.env.test?
|
|
8
7
|
require 'foreman_ansible/remote_execution'
|
|
9
8
|
|
|
10
9
|
module ForemanAnsible
|
|
@@ -20,12 +19,6 @@ module ForemanAnsible
|
|
|
20
19
|
config.autoload_paths += Dir["#{config.root}/app/views"]
|
|
21
20
|
config.autoload_paths += Dir["#{config.root}/app/lib"]
|
|
22
21
|
|
|
23
|
-
initializer 'foreman_ansible.load_default_settings',
|
|
24
|
-
:before => :load_config_initializers do
|
|
25
|
-
require_dependency(File.join(ForemanAnsible::Engine.root,
|
|
26
|
-
'app/models/setting/ansible.rb'))
|
|
27
|
-
end
|
|
28
|
-
|
|
29
22
|
initializer 'foreman_ansible.register_gettext',
|
|
30
23
|
:after => :load_config_initializers do
|
|
31
24
|
locale_dir = File.join(File.expand_path('../..', __dir__), 'locale')
|
|
@@ -72,17 +65,6 @@ module ForemanAnsible
|
|
|
72
65
|
end
|
|
73
66
|
|
|
74
67
|
config.to_prepare do
|
|
75
|
-
foreman_version = ::Foreman::Version.new
|
|
76
|
-
if Rails.env.test? ||
|
|
77
|
-
foreman_version.major.to_i == 1 && foreman_version.minor.to_i < 13
|
|
78
|
-
::Foreman::Plugin.fact_importer_registry.register(:ansible, ForemanAnsible::FactImporter)
|
|
79
|
-
else
|
|
80
|
-
::Foreman::Plugin.fact_importer_registry.register(
|
|
81
|
-
:ansible,
|
|
82
|
-
ForemanAnsible::StructuredFactImporter
|
|
83
|
-
)
|
|
84
|
-
end
|
|
85
|
-
::FactParser.register_fact_parser(:ansible, ForemanAnsible::FactParser)
|
|
86
68
|
::Host::Managed.prepend ForemanAnsible::HostManagedExtensions
|
|
87
69
|
::Hostgroup.include ForemanAnsible::HostgroupExtensions
|
|
88
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
|
},
|
|
@@ -97,6 +183,32 @@ Foreman::Plugin.register :foreman_ansible do
|
|
|
97
183
|
parameter_filter Host::Managed, base_role_assignment_params.merge(:host_ansible_roles_attributes => {})
|
|
98
184
|
parameter_filter Hostgroup, base_role_assignment_params.merge(:hostgroup_ansible_roles_attributes => {})
|
|
99
185
|
|
|
186
|
+
register_global_js_file 'global'
|
|
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
|
+
|
|
100
212
|
divider :top_menu, :caption => N_('Ansible'), :parent => :configure_menu
|
|
101
213
|
menu :top_menu, :ansible_roles,
|
|
102
214
|
:caption => N_('Roles'),
|
data/package.json
CHANGED
|
@@ -7,23 +7,27 @@
|
|
|
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
|
-
"@
|
|
18
|
-
"@theforeman/
|
|
19
|
-
"@theforeman/
|
|
20
|
-
"@theforeman/
|
|
17
|
+
"@testing-library/user-event": "^13.2.1",
|
|
18
|
+
"@theforeman/builder": "^8.4.1",
|
|
19
|
+
"@theforeman/eslint-plugin-foreman": "^8.4.1",
|
|
20
|
+
"@theforeman/find-foreman": "^8.4.1",
|
|
21
|
+
"@theforeman/stories": "^8.4.1",
|
|
22
|
+
"@theforeman/test": "^8.9.0",
|
|
23
|
+
"@theforeman/vendor-dev": "^8.4.1",
|
|
24
|
+
"@testing-library/user-event": "^13.2.1",
|
|
21
25
|
"babel-eslint": "^10.0.3",
|
|
22
26
|
"eslint": "^6.7.2",
|
|
23
27
|
"prettier": "^1.13.5"
|
|
24
28
|
},
|
|
25
29
|
"scripts": {
|
|
26
|
-
"test": "tfm-test --plugin",
|
|
30
|
+
"test": "tfm-test --plugin --config jest.config.js",
|
|
27
31
|
"lint": "tfm-lint --plugin -d webpack"
|
|
28
32
|
},
|
|
29
33
|
"repository": {
|
|
@@ -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
|
|
@@ -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
|
|
@@ -6,43 +6,17 @@ class AnsibleReportsHelperTest < ActiveSupport::TestCase
|
|
|
6
6
|
include ForemanAnsible::AnsibleReportsHelper
|
|
7
7
|
include ActionView::Helpers::TagHelper
|
|
8
8
|
|
|
9
|
-
test '
|
|
9
|
+
test 'module message extraction' do
|
|
10
10
|
log_value = <<-ANSIBLELOG.strip_heredoc
|
|
11
|
-
|
|
11
|
+
{"msg": "Nothing to do", "changed": false, "results": [], "rc": 0, "invocation": {"module_args": {"name": ["openssh"], "state": "present", "allow_downgrade": false, "autoremove": false, "bugfix": false, "disable_gpg_check": false, "disable_plugin": [], "disablerepo": [], "download_only": false, "enable_plugin": [], "enablerepo": [], "exclude": [], "installroot": "/", "install_repoquery": true, "install_weak_deps": true, "security": false, "skip_broken": false, "update_cache": false, "update_only": false, "validate_certs": true, "lock_timeout": 30, "conf_file": null, "disable_excludes": null, "download_dir": null, "list": null, "releasever": null}}, "_ansible_no_log": false, "failed": false, "module": "package"}
|
|
12
12
|
ANSIBLELOG
|
|
13
13
|
message = FactoryBot.build(:message)
|
|
14
14
|
message.value = log_value
|
|
15
15
|
log = FactoryBot.build(:log)
|
|
16
16
|
log.message = message
|
|
17
17
|
assert_match(
|
|
18
|
-
/
|
|
19
|
-
|
|
20
|
-
)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
test 'pretty print is able to print a hash' do
|
|
24
|
-
hash = {
|
|
25
|
-
'allow_downgrade' => false,
|
|
26
|
-
'name' => ['ntp'],
|
|
27
|
-
'list' => nil,
|
|
28
|
-
'disable_gpg_check' => false,
|
|
29
|
-
'conf_file' => nil,
|
|
30
|
-
'install_repoquery' => true,
|
|
31
|
-
'state' => 'installed',
|
|
32
|
-
'disablerepo' => nil,
|
|
33
|
-
'update_cache' => false,
|
|
34
|
-
'enablerepo' => nil,
|
|
35
|
-
'exclude' => nil,
|
|
36
|
-
'security' => false,
|
|
37
|
-
'validate_certs' => true,
|
|
38
|
-
'installroot' => '/',
|
|
39
|
-
'skip_broken' => false
|
|
40
|
-
}
|
|
41
|
-
assert_equal(
|
|
42
|
-
hash,
|
|
43
|
-
remove_keys(
|
|
44
|
-
hash
|
|
45
|
-
)
|
|
18
|
+
/Nothing to do/,
|
|
19
|
+
ansible_module_message(log).to_s
|
|
46
20
|
)
|
|
47
21
|
end
|
|
48
22
|
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
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
|
|
7
|
+
import SecondaryTabRoutes from './components/SecondaryTabRoutes';
|
|
8
|
+
import { SECONDARY_TABS } from './constants';
|
|
9
|
+
import './AnsibleHostDetail.scss';
|
|
10
|
+
|
|
11
|
+
const AnsibleHostDetail = ({
|
|
12
|
+
response,
|
|
13
|
+
status,
|
|
14
|
+
router,
|
|
15
|
+
location: { pathname },
|
|
16
|
+
history,
|
|
17
|
+
}) => {
|
|
18
|
+
const hashHistory = useHistory();
|
|
19
|
+
return (
|
|
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>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
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
|
+
|
|
59
|
+
export default AnsibleHostDetail;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import AnsibleHostDetail from './';
|
|
5
|
+
|
|
6
|
+
describe('AnsibleHostDetail', () => {
|
|
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
|
+
);
|
|
16
|
+
expect(
|
|
17
|
+
container.getElementsByClassName('react-loading-skeleton')
|
|
18
|
+
).toHaveLength(5);
|
|
19
|
+
});
|
|
20
|
+
});
|