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.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/ansible_inventories_controller.rb +1 -1
  3. data/app/graphql/mutations/ansible_variable_overrides/create.rb +26 -0
  4. data/app/graphql/mutations/ansible_variable_overrides/delete.rb +38 -0
  5. data/app/graphql/mutations/ansible_variable_overrides/update.rb +26 -0
  6. data/app/graphql/mutations/hosts/assign_ansible_roles.rb +37 -0
  7. data/app/graphql/presenters/ansible_role_presenter.rb +12 -0
  8. data/app/graphql/presenters/overriden_ansible_variable_presenter.rb +19 -0
  9. data/app/graphql/types/ansible_role.rb +9 -0
  10. data/app/graphql/types/ansible_variable.rb +23 -0
  11. data/app/graphql/types/ansible_variable_override.rb +9 -0
  12. data/app/graphql/types/inherited_ansible_role.rb +13 -0
  13. data/app/graphql/types/overriden_ansible_variable.rb +27 -0
  14. data/app/helpers/foreman_ansible/ansible_roles_data_preparations.rb +22 -22
  15. data/app/models/concerns/foreman_ansible/host_managed_extensions.rb +23 -4
  16. data/app/models/concerns/foreman_ansible/hostgroup_extensions.rb +2 -1
  17. data/app/models/foreman_ansible/ansible_provider.rb +7 -5
  18. data/app/services/foreman_ansible/ansible_report_importer.rb +2 -2
  19. data/app/services/foreman_ansible/inventory_creator.rb +1 -1
  20. data/app/services/foreman_ansible/override_resolver.rb +22 -0
  21. data/app/views/api/v2/ansible_override_values/index.json.rabl +3 -0
  22. data/app/views/api/v2/ansible_variables/show.json.rabl +1 -1
  23. data/app/views/foreman_ansible/ansible_roles/_hostgroup_ansible_roles_button.erb +3 -0
  24. data/app/views/foreman_ansible/job_templates/convert_to_rhel.erb +6 -2
  25. data/app/views/foreman_ansible/job_templates/run_openscap_scans_-_ansible_default.erb +20 -0
  26. data/config/routes.rb +3 -0
  27. data/db/migrate/20210818083407_fix_ansible_setting_category_to_dsl.rb +5 -0
  28. data/lib/foreman_ansible/engine.rb +0 -17
  29. data/lib/foreman_ansible/register.rb +115 -4
  30. data/lib/foreman_ansible/version.rb +1 -1
  31. data/package.json +4 -2
  32. data/test/functional/api/v2/ansible_inventories_controller_test.rb +1 -2
  33. data/test/graphql/mutations/hosts/assign_ansible_roles_mutation_test.rb +96 -0
  34. data/test/graphql/queries/ansible_roles_query_test.rb +35 -0
  35. data/test/unit/ansible_provider_test.rb +3 -6
  36. data/test/unit/concerns/host_managed_extensions_test.rb +8 -0
  37. data/test/unit/concerns/hostgroup_extensions_test.rb +6 -0
  38. data/test/unit/hostgroup_ansible_role_test.rb +13 -0
  39. data/test/unit/services/override_resolver_test.rb +34 -0
  40. data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.js +51 -27
  41. data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.test.js +12 -6
  42. data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.js +22 -0
  43. data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.scss +4 -0
  44. data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.test.js +104 -0
  45. data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/index.js +38 -0
  46. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverrides.scss +3 -0
  47. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTable.js +238 -0
  48. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTableHelper.js +111 -0
  49. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.js +161 -0
  50. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.scss +7 -0
  51. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableActionHelper.js +49 -0
  52. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValue.js +70 -0
  53. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValueHelper.js +35 -0
  54. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.fixtures.js +429 -0
  55. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.test.js +71 -0
  56. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesDelete.test.js +74 -0
  57. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesUpdate.test.js +188 -0
  58. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/index.js +58 -0
  59. data/webpack/components/AnsibleHostDetail/components/JobsTab/JobsTabHelper.js +79 -0
  60. data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobHelper.js +106 -0
  61. data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.js +129 -0
  62. data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.scss +7 -0
  63. data/webpack/components/AnsibleHostDetail/components/JobsTab/PreviousJobsTable.js +103 -0
  64. data/webpack/components/AnsibleHostDetail/components/JobsTab/RecurringJobsTable.js +96 -0
  65. data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.fixtures.js +184 -0
  66. data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.test.js +195 -0
  67. data/webpack/components/AnsibleHostDetail/components/JobsTab/index.js +88 -0
  68. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/AllRolesTable.js +89 -0
  69. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js +80 -0
  70. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesForm.js +90 -0
  71. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesModal.scss +3 -0
  72. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesModalHelper.js +40 -0
  73. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/index.js +82 -0
  74. data/webpack/components/AnsibleHostDetail/components/RolesTab/RolesTable.js +129 -0
  75. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/EditRoles.test.js +85 -0
  76. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.fixtures.js +180 -0
  77. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.test.js +75 -0
  78. data/webpack/components/AnsibleHostDetail/components/RolesTab/index.js +51 -0
  79. data/webpack/components/AnsibleHostDetail/components/SecondaryTabRoutes.js +60 -0
  80. data/webpack/components/AnsibleHostDetail/components/TabLayout.js +12 -0
  81. data/webpack/components/AnsibleHostDetail/constants.js +9 -0
  82. data/webpack/components/AnsibleHostDetail/helpers.js +4 -0
  83. data/webpack/components/AnsibleRolesAndVariables/__test__/AnsibleRolesAndVariablesImport.test.js +15 -10
  84. data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.js +29 -0
  85. data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.test.js +3 -0
  86. data/webpack/components/AnsibleRolesSwitcher/components/AvailableRolesList.js +2 -1
  87. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsibleRole.test.js.snap +3 -3
  88. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AvailableRolesList.test.js.snap +4 -0
  89. data/webpack/components/DualList/DualList.scss +3 -0
  90. data/webpack/components/DualList/ListControls.js +65 -0
  91. data/webpack/components/DualList/ListHeader.js +16 -0
  92. data/webpack/components/DualList/ListItem.js +69 -0
  93. data/webpack/components/DualList/ListPane.js +95 -0
  94. data/webpack/components/DualList/SelectedStatus.js +21 -0
  95. data/webpack/components/DualList/index.js +103 -0
  96. data/webpack/components/ErrorState.js +16 -0
  97. data/webpack/components/withLoading.js +135 -0
  98. data/webpack/components/withPagination.js +0 -0
  99. data/webpack/formHelper.js +131 -0
  100. data/webpack/globalIdHelper.js +13 -0
  101. data/webpack/global_index.js +7 -1
  102. data/webpack/graphql/mutations/assignAnsibleRoles.gql +17 -0
  103. data/webpack/graphql/mutations/cancelRecurringLogic.gql +12 -0
  104. data/webpack/graphql/mutations/createAnsibleVariableOverride.gql +28 -0
  105. data/webpack/graphql/mutations/createJobInvocation.gql +11 -0
  106. data/webpack/graphql/mutations/deleteAnsibleVariableOverride.gql +17 -0
  107. data/webpack/graphql/mutations/updateAnsibleVariableOverride.gql +29 -0
  108. data/webpack/graphql/queries/allAnsibleRoles.gql +13 -0
  109. data/webpack/graphql/queries/ansibleRoles.gql +13 -0
  110. data/webpack/graphql/queries/currentUserAttributes.gql +11 -0
  111. data/webpack/graphql/queries/hostAnsibleRoles.gql +17 -0
  112. data/webpack/graphql/queries/hostAvailableAnsibleRoles.gql +11 -0
  113. data/webpack/graphql/queries/hostVariableOverrides.gql +39 -0
  114. data/webpack/graphql/queries/recurringJobs.gql +28 -0
  115. data/webpack/helpers/pageParamsHelper.js +40 -0
  116. data/webpack/helpers/paginationHelper.js +9 -0
  117. data/webpack/permissionsHelper.js +58 -0
  118. data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.fixtures.js +63 -0
  119. data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.test.js +112 -0
  120. data/webpack/routes/HostgroupJobs/index.js +26 -0
  121. data/webpack/routes/routes.js +10 -0
  122. data/webpack/testHelper.js +165 -0
  123. data/webpack/toastHelper.js +4 -0
  124. metadata +127 -54
  125. data/app/assets/images/foreman_ansible/Ansible.png +0 -0
  126. data/app/models/foreman_ansible/fact_name.rb +0 -16
  127. data/app/models/setting/ansible.rb +0 -106
  128. data/app/services/foreman_ansible/fact_importer.rb +0 -99
  129. data/app/services/foreman_ansible/fact_parser.rb +0 -126
  130. data/app/services/foreman_ansible/fact_sparser.rb +0 -37
  131. data/app/services/foreman_ansible/operating_system_parser.rb +0 -102
  132. data/app/services/foreman_ansible/structured_fact_importer.rb +0 -25
  133. data/test/unit/services/fact_importer_test.rb +0 -52
  134. data/test/unit/services/fact_parser_test.rb +0 -281
  135. data/test/unit/services/fact_sparser_test.rb +0 -24
  136. data/test/unit/services/structured_fact_importer_test.rb +0 -30
  137. 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 '>= 2.2'
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
- { :ansible_variables => [:edit, :update],
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'),
@@ -4,5 +4,5 @@
4
4
  # This way other parts of Foreman can just call ForemanAnsible::VERSION
5
5
  # and detect what version the plugin is running.
6
6
  module ForemanAnsible
7
- VERSION = '6.4.1'
7
+ VERSION = '7.0.0'
8
8
  end
data/package.json CHANGED
@@ -7,19 +7,21 @@
7
7
  "test": "test"
8
8
  },
9
9
  "peerDependencies": {
10
- "@theforeman/vendor": ">= 6.0.0"
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.4.1",
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::Ansible.create! :name => 'ansible_inventory_template', :value => report.name,
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.build(:host) }
33
+ let(:host) { FactoryBot.create(:host) }
34
34
 
35
35
  it 'generates secrets properly' do
36
- params = {
37
- 'remote_execution_ssh_password' => 'password',
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, { useState } from 'react';
2
- import { Tabs, Tab, TabTitleText, Label } from '@patternfly/react-core';
3
- import { InfoCircleIcon } from '@patternfly/react-icons';
4
-
5
- import { translate as __ } from 'foremanReact/common/I18n';
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 = props => {
10
- // https://projects.theforeman.org/issues/32398
11
- const [activeTab] = useState('variables');
12
-
11
+ const AnsibleHostDetail = ({
12
+ response,
13
+ status,
14
+ router,
15
+ location: { pathname },
16
+ history,
17
+ }) => {
18
+ const hashHistory = useHistory();
13
19
  return (
14
- <Tabs activeKey={activeTab} isSecondary>
15
- <Tab
16
- eventKey="variables"
17
- title={<TabTitleText>{__('Variables')}</TabTitleText>}
18
- >
19
- <div className="host-details-tab-item">
20
- <div className="ansible-host-detail">
21
- <Label
22
- color="blue"
23
- icon={<InfoCircleIcon />}
24
- style={{ marginTop: '1.5rem' }}
25
- >
26
- Ansible Variables coming soon!
27
- </Label>
28
- </div>
29
- </div>
30
- </Tab>
31
- </Tabs>
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, screen } from '@testing-library/react';
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 content', () => {
9
- render(<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
+ );
10
16
  expect(
11
- screen.getByText('Ansible Variables coming soon!')
12
- ).toBeInTheDocument();
17
+ container.getElementsByClassName('react-loading-skeleton')
18
+ ).toHaveLength(5);
13
19
  });
14
20
  });
@@ -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;
@@ -0,0 +1,4 @@
1
+
2
+ .ansible-host-inventory {
3
+ padding: 1.8rem;
4
+ }