foreman_ansible 9.0.1 → 10.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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/app/graphql/types/ansible_variable_override.rb +1 -1
  3. data/app/helpers/foreman_ansible/ansible_hostgroups_helper.rb +1 -1
  4. data/lib/foreman_ansible/register.rb +2 -2
  5. data/lib/foreman_ansible/version.rb +1 -1
  6. data/package.json +0 -1
  7. data/test/graphql/mutations/hosts/assign_ansible_roles_mutation_test.rb +7 -7
  8. data/test/integration/hostgroup_js_test.rb +30 -0
  9. data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.js +1 -0
  10. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTable.js +10 -4
  11. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTableHelper.js +1 -0
  12. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.js +3 -0
  13. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValue.js +4 -2
  14. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValueHelper.js +5 -1
  15. data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.js +3 -0
  16. data/webpack/components/AnsibleHostDetail/components/JobsTab/PreviousJobsTable.js +10 -4
  17. data/webpack/components/AnsibleHostDetail/components/JobsTab/RecurringJobsTable.js +3 -3
  18. data/webpack/components/AnsibleHostDetail/components/JobsTab/index.js +5 -1
  19. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/AllRolesTable.js +10 -4
  20. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js +1 -0
  21. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesForm.js +2 -1
  22. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/index.js +7 -1
  23. data/webpack/components/AnsibleHostDetail/components/RolesTab/RolesTable.js +14 -5
  24. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/EditRoles.test.js +2 -1
  25. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.test.js +3 -1
  26. data/webpack/components/AnsibleHostDetail/components/RolesTab/index.js +27 -2
  27. data/webpack/components/AnsibleHostDetail/components/SecondaryTabRoutes.js +1 -1
  28. data/webpack/testHelper.js +12 -3
  29. metadata +8 -7
  30. data/lib/foreman_ansible/register.rb.orig +0 -257
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf08e369d1c4b9a445123a9fab3a02b1da8c3bfe9261b7027693959d90e1e1a3
4
- data.tar.gz: 13ac8e3cb9b88bf5167a2fdeb9e95409ad9a93c6f4dce12f0500cc5c5bd787a9
3
+ metadata.gz: 98a2c9aa6bcc8e2feb5b8eee62d111926d0dab191561f808399587d7462debbc
4
+ data.tar.gz: 2fc48fc5f59725cd8000229bdf08e68f5e8e53a1158271ce2d6db3766ab5826b
5
5
  SHA512:
6
- metadata.gz: 8c515c517add1b91f020c63a50b6b58eaafdacfe08d52d39286ae79efd034cb8ca0ffc8ef4aa8aa2f5be9e5dcc6a797910dd9b32f444e61e525077c8239e3e56
7
- data.tar.gz: 18fac58f8a1d119af822b6be9f28ce33b626b0951791d5c3ca201d6e2475f2399c52d21ba92b19335f267a183306e568cbc384910942f1d7d0de9d1d74a88ef2
6
+ metadata.gz: a8be9228e0b1532cdbef43e88b0f7ba84f9bd8edc5a9d9e21e1ee6d601f63bbccf16eff5e146f33d18ea06d7cdd5adc528d310d7ddb723ff3a6b91ce3a6774c6
7
+ data.tar.gz: 35b55ca402a155fae45abd89c6fecde949d133e50b855473a25dba13df9452a13a5b9342b93e49fefd553902909a565dd59f9d57afb07d4f8efec6f0311057a9
@@ -1,5 +1,5 @@
1
1
  module Types
2
- class AnsibleVariableOverride < GraphQL::Types::Relay::BaseObject
2
+ class AnsibleVariableOverride < GraphQL::Schema::Object
3
3
  description 'Override value for Ansible Variable'
4
4
 
5
5
  field :value, ::Types::RawJson, :null => false
@@ -4,7 +4,7 @@ module ForemanAnsible
4
4
  module AnsibleHostgroupsHelper
5
5
  def ansible_hostgroups_actions(hostgroup)
6
6
  play_roles = if hostgroup.all_ansible_roles.empty?
7
- { action: (link_to _('Run all Ansible roles'), 'javascript:void(0);', disabled: true, title: 'No Roles assigned'), priority: 31 }
7
+ { action: { content: (link_to _('Run all Ansible roles'), 'javascript:void(0);', disabled: true, title: 'No roles assigned', class: 'disabled'), options: { class: 'disabled' } }, priority: 31 }
8
8
  else
9
9
  { action: display_link_if_authorized(_('Run all Ansible roles'), hash_for_play_roles_hostgroup_path(id: hostgroup), 'data-no-turbolink': true, title: _('Run all Ansible roles on hosts belonging to this host group')), priority: 31 }
10
10
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Foreman::Plugin.register :foreman_ansible do
4
- requires_foreman '>= 3.4'
4
+ requires_foreman '>= 3.5'
5
5
 
6
6
  settings do
7
7
  category :ansible, N_('Ansible') do
@@ -188,7 +188,7 @@ Foreman::Plugin.register :foreman_ansible do
188
188
  register_global_js_file 'global'
189
189
 
190
190
  extend_graphql_type :type => '::Types::Host' do
191
- field :all_ansible_roles, ::Types::InheritedAnsibleRole.connection_type, :null => true, :method => :present_all_ansible_roles
191
+ field :all_ansible_roles, ::Types::InheritedAnsibleRole.connection_type, :null => true, :resolver_method => :present_all_ansible_roles
192
192
  field :own_ansible_roles, ::Types::AnsibleRole.connection_type, :null => true
193
193
  field :available_ansible_roles, ::Types::AnsibleRole.connection_type, :null => true
194
194
  field :ansible_variables_with_overrides, Types::OverridenAnsibleVariable.connection_type, :null => false
@@ -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 = '9.0.1'
7
+ VERSION = '10.0.1'
8
8
  end
data/package.json CHANGED
@@ -21,7 +21,6 @@
21
21
  "@theforeman/stories": "^10.0",
22
22
  "@theforeman/test": "^10.0",
23
23
  "@theforeman/vendor-dev": "^10.0",
24
- "@testing-library/user-event": "^13.2.1",
25
24
  "babel-eslint": "^10.0.3",
26
25
  "eslint": "^6.7.2",
27
26
  "prettier": "^1.13.5"
@@ -3,15 +3,15 @@ require 'test_plugin_helper'
3
3
  module Mutations
4
4
  module Hosts
5
5
  class CreateMutationTest < GraphQLQueryTestCase
6
- let(:tax_location) { FactoryBot.create(:location) }
6
+ let(:tax_location) { as_admin { FactoryBot.create(:location) } }
7
7
  let(:location_id) { Foreman::GlobalId.for(tax_location) }
8
- let(:organization) { FactoryBot.create(:organization) }
8
+ let(:organization) { as_admin { FactoryBot.create(:organization) } }
9
9
  let(:organization_id) { Foreman::GlobalId.for(organization) }
10
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) }
11
+ let(:role1) { as_admin { FactoryBot.create(:ansible_role) } }
12
+ let(:role2) { as_admin { FactoryBot.create(:ansible_role) } }
13
+ let(:role3) { as_admin { FactoryBot.create(:ansible_role) } }
14
+ let(:host) { as_admin { FactoryBot.create(:host, :ansible_roles => [role1, role2, role3], :organization => organization, :location => tax_location) } }
15
15
 
16
16
  let(:variables) { { id: Foreman::GlobalId.for(host), ansibleRoleIds: [role3.id, role2.id, role1.id] } }
17
17
  let(:query) do
@@ -37,7 +37,7 @@ module Mutations
37
37
  end
38
38
 
39
39
  context 'with admin permissions' do
40
- let(:context_user) { FactoryBot.create(:user, :admin) }
40
+ let(:context_user) { as_admin { FactoryBot.create(:user, :admin) } }
41
41
  let(:data) { result['data']['assignAnsibleRoles']['host'] }
42
42
 
43
43
  it 'reorderes ansible roles' do
@@ -0,0 +1,30 @@
1
+ require_relative '../test_plugin_helper'
2
+ require 'integration_test_helper'
3
+
4
+ class HostgroupJsTest < IntegrationTestWithJavascript
5
+ let(:hostgroup) { FactoryBot.create(:hostgroup, :name => 'HostgroupWithoutRoles') }
6
+ let(:hostgroup_with_roles) { FactoryBot.create(:hostgroup, :with_ansible_roles, :name => 'HostgroupWithRoles') }
7
+
8
+ setup do
9
+ FactoryBot.create(:host, :hostgroup_id => hostgroup.id)
10
+ FactoryBot.create(:host, :hostgroup_id => hostgroup_with_roles.id)
11
+ end
12
+
13
+ test 'hostgroup without roles should have disabled link' do
14
+ visit hostgroups_path(search: hostgroup.name)
15
+
16
+ first_row = page.find('table > tbody > tr:nth-child(1)')
17
+ first_row.find('td:nth-child(4) > div > a').click
18
+
19
+ assert_includes first(:link, 'Run all Ansible roles')[:class], 'disabled'
20
+ end
21
+
22
+ test 'hostgroup with roles should have clickable link' do
23
+ visit hostgroups_path(search: hostgroup_with_roles.name)
24
+
25
+ first_row = page.find('table > tbody > tr:nth-child(1)')
26
+ first_row.find('td:nth-child(4) > div > a').click
27
+
28
+ assert_not first(:link, 'Run all Ansible roles')[:class].include?('disabled')
29
+ end
30
+ end
@@ -22,6 +22,7 @@ const AnsibleHostDetail = ({
22
22
  <>
23
23
  <Tabs
24
24
  className="ansible-host-details-tabs"
25
+ ouiaId="ansible-host-details-tabs"
25
26
  onSelect={(evt, subTab) => hashHistory.push(subTab)}
26
27
  activeKey={pathname?.split('/')[2]}
27
28
  isSecondary
@@ -148,12 +148,17 @@ const AnsibleVariableOverridesTable = ({
148
148
  <React.Fragment>
149
149
  <Flex direction={{ default: 'column' }}>
150
150
  <FlexItem align={{ default: 'alignRight' }}>
151
- <Pagination updateParamsByUrl itemCount={totalCount} variant="top" />
151
+ <Pagination
152
+ ouiaId="pagination-top"
153
+ updateParamsByUrl
154
+ itemCount={totalCount}
155
+ variant="top"
156
+ />
152
157
  </FlexItem>
153
158
  <FlexItem>
154
- <TableComposable variant="compact">
159
+ <TableComposable ouiaId="table-composable-compact" variant="compact">
155
160
  <Thead>
156
- <Tr>
161
+ <Tr ouiaId="row-header">
157
162
  {columns.map(col => (
158
163
  <Th key={col}>{col}</Th>
159
164
  ))}
@@ -162,7 +167,7 @@ const AnsibleVariableOverridesTable = ({
162
167
  </Thead>
163
168
  <Tbody>
164
169
  {variables.map((variable, idx) => (
165
- <Tr key={idx}>
170
+ <Tr key={idx} ouiaId={`row-${idx}`}>
166
171
  <Td>
167
172
  <a href={variable.path}>{variable.key}</a>
168
173
  </Td>
@@ -202,6 +207,7 @@ const AnsibleVariableOverridesTable = ({
202
207
  </FlexItem>
203
208
  <FlexItem align={{ default: 'alignRight' }}>
204
209
  <Pagination
210
+ ouiaId="pagination-bottom"
205
211
  updateParamsByUrl
206
212
  itemCount={totalCount}
207
213
  variant="bottom"
@@ -23,6 +23,7 @@ export const formatValue = variable => {
23
23
  case 'yaml':
24
24
  case 'hash':
25
25
  case 'array':
26
+ case 'json':
26
27
  return JSON.stringify(value);
27
28
  default:
28
29
  return value;
@@ -111,6 +111,7 @@ const EditableAction = ({
111
111
  onClick={onClose}
112
112
  isDisabled={state.working}
113
113
  aria-label="Cancel editing override button"
114
+ ouiaId="cancel-editing-override-button"
114
115
  >
115
116
  <TimesIcon />
116
117
  </Button>
@@ -119,6 +120,7 @@ const EditableAction = ({
119
120
  onClick={onSubmit}
120
121
  isDisabled={state.working || hasError(state)}
121
122
  aria-label="Submit editing override button"
123
+ ouiaId="submit-editing-override-button"
122
124
  >
123
125
  <CheckIcon />
124
126
  </Button>
@@ -136,6 +138,7 @@ const EditableAction = ({
136
138
  onClick={onOpen}
137
139
  variant="plain"
138
140
  aria-label="Edit override button"
141
+ ouiaId="edit-override-button"
139
142
  >
140
143
  <PencilAltIcon />
141
144
  </Button>
@@ -17,11 +17,12 @@ const EditableValue = props => {
17
17
 
18
18
  const type = props.variable.parameterType;
19
19
 
20
- if (type === 'array' || type === 'hash') {
20
+ if (['json', 'yaml', 'array', 'hash'].includes(type)) {
21
21
  return (
22
22
  <TextAreaField
23
+ aria-label="Edit override field"
23
24
  onChange={props.onChange}
24
- value={props.value}
25
+ value={JSON.stringify(props.value)}
25
26
  validation={props.validation}
26
27
  isDisabled={props.working}
27
28
  />
@@ -31,6 +32,7 @@ const EditableValue = props => {
31
32
  if (type === 'boolean') {
32
33
  return (
33
34
  <SelectField
35
+ aria-label="Edit override field"
34
36
  selectItems={[
35
37
  { id: 'trueSelectOpt', value: true, name: __('true') },
36
38
  { id: 'falseSelectOpt', value: false, name: __('false') },
@@ -23,7 +23,11 @@ const withFormGroup = Component => componentProps => {
23
23
  export const SelectField = componentProps => {
24
24
  const { selectItems, ...rest } = componentProps;
25
25
  return (
26
- <FormSelect className="without_select2" {...rest}>
26
+ <FormSelect
27
+ className="without_select2"
28
+ ouiaId="without-form-select"
29
+ {...rest}
30
+ >
27
31
  {selectItems.map(item => (
28
32
  <FormSelectOption key={item.id} value={item.value} label={item.name} />
29
33
  ))}
@@ -57,6 +57,7 @@ const NewRecurringJobModal = props => {
57
57
  const actions = [
58
58
  <Button
59
59
  aria-label="submit creating job"
60
+ ouiaId="submit-creating-job"
60
61
  key="confirm"
61
62
  variant="primary"
62
63
  onClick={formProps.handleSubmit}
@@ -66,6 +67,7 @@ const NewRecurringJobModal = props => {
66
67
  </Button>,
67
68
  <Button
68
69
  aria-label="cancel creating job"
70
+ ouiaId="cancel-creating-job"
69
71
  key="cancel"
70
72
  variant="link"
71
73
  onClick={onClose}
@@ -83,6 +85,7 @@ const NewRecurringJobModal = props => {
83
85
  <Modal
84
86
  variant={ModalVariant.large}
85
87
  title="Create New Recurring Ansible Run"
88
+ ouiaId="modal-recurring-ansible-run"
86
89
  isOpen={props.isOpen}
87
90
  className="foreman-modal modal-high"
88
91
  showClose={false}
@@ -32,12 +32,17 @@ const PreviousJobsTable = ({ history, totalCount, jobs, pagination }) => {
32
32
  <h3>{__('Previously executed jobs')}</h3>
33
33
  <Flex direction={{ default: 'column' }} className="pf-u-pt-md">
34
34
  <FlexItem align={{ default: 'alignRight' }}>
35
- <Pagination updateParamsByUrl itemCount={totalCount} variant="top" />
35
+ <Pagination
36
+ ouiaId="pagination-top"
37
+ updateParamsByUrl
38
+ itemCount={totalCount}
39
+ variant="top"
40
+ />
36
41
  </FlexItem>
37
42
  <FlexItem>
38
- <TableComposable variant="compact">
43
+ <TableComposable ouiaId="table-composable-compact" variant="compact">
39
44
  <Thead>
40
- <Tr>
45
+ <Tr ouiaId="row-header">
41
46
  {columns.map(col => (
42
47
  <Th key={col}>{col}</Th>
43
48
  ))}
@@ -45,7 +50,7 @@ const PreviousJobsTable = ({ history, totalCount, jobs, pagination }) => {
45
50
  </Thead>
46
51
  <Tbody>
47
52
  {jobs.map(job => (
48
- <Tr key={job.id}>
53
+ <Tr key={job.id} ouiaId={`row-${job.id}`}>
49
54
  <Td>
50
55
  <a
51
56
  onClick={() =>
@@ -72,6 +77,7 @@ const PreviousJobsTable = ({ history, totalCount, jobs, pagination }) => {
72
77
  </FlexItem>
73
78
  <FlexItem align={{ default: 'alignRight' }}>
74
79
  <Pagination
80
+ ouiaId="pagination-bottom"
75
81
  updateParamsByUrl
76
82
  itemCount={totalCount}
77
83
  variant="bottom"
@@ -60,9 +60,9 @@ const RecurringJobsTable = ({
60
60
  return (
61
61
  <React.Fragment>
62
62
  <h3>{__('Scheduled recurring jobs')}</h3>
63
- <TableComposable variant="compact">
63
+ <TableComposable ouiaId="table-composable-compact" variant="compact">
64
64
  <Thead>
65
- <Tr>
65
+ <Tr ouiaId="row-header">
66
66
  {columns.map(col => (
67
67
  <Th key={col}>{col}</Th>
68
68
  ))}
@@ -71,7 +71,7 @@ const RecurringJobsTable = ({
71
71
  </Thead>
72
72
  <Tbody>
73
73
  {jobs.map(job => (
74
- <Tr key={job.id}>
74
+ <Tr key={job.id} ouiaId={`row-${job.id}`}>
75
75
  <Td>
76
76
  <a
77
77
  onClick={() =>
@@ -33,7 +33,11 @@ const JobsTab = ({ resourceName, resourceId, hostGroupId, history }) => {
33
33
  ];
34
34
 
35
35
  const scheduleBtn = (
36
- <Button aria-label="schedule recurring job" onClick={toggleModal}>
36
+ <Button
37
+ aria-label="schedule recurring job"
38
+ ouiaId="schedule-recurring-job"
39
+ onClick={toggleModal}
40
+ >
37
41
  {__('Schedule recurring job')}
38
42
  </Button>
39
43
  );
@@ -21,11 +21,16 @@ const AllRolesTable = ({ allAnsibleRoles, totalCount }) => {
21
21
  <React.Fragment>
22
22
  <Flex direction={{ default: 'column' }} className="pf-u-pt-md">
23
23
  <FlexItem align={{ default: 'alignRight' }}>
24
- <Pagination updateParamsByUrl itemCount={totalCount} variant="top" />
24
+ <Pagination
25
+ ouiaId="pagination-top"
26
+ updateParamsByUrl
27
+ itemCount={totalCount}
28
+ variant="top"
29
+ />
25
30
  </FlexItem>
26
- <TableComposable variant="compact">
31
+ <TableComposable ouiaId="table-composable-compact" variant="compact">
27
32
  <Thead>
28
- <Tr>
33
+ <Tr ouiaId="row-header">
29
34
  <Th />
30
35
  {columns.map(col => (
31
36
  <Th key={`${col}-all`}>{col}</Th>
@@ -34,7 +39,7 @@ const AllRolesTable = ({ allAnsibleRoles, totalCount }) => {
34
39
  </Thead>
35
40
  <Tbody>
36
41
  {allAnsibleRoles.map(role => (
37
- <Tr key={`${role.id}-all`} id={role.id}>
42
+ <Tr key={`${role.id}-all`} id={role.id} ouiaId={`row-${role.id}`}>
38
43
  <Td />
39
44
  <Td>{role.name}</Td>
40
45
  <Td>
@@ -58,6 +63,7 @@ const AllRolesTable = ({ allAnsibleRoles, totalCount }) => {
58
63
  </TableComposable>
59
64
  <FlexItem align={{ default: 'alignRight' }}>
60
65
  <Pagination
66
+ ouiaId="pagination-bottom"
61
67
  updateParamsByUrl
62
68
  itemCount={totalCount}
63
69
  variant="bottom"
@@ -15,6 +15,7 @@ import {
15
15
 
16
16
  const AllRolesModal = ({ hostGlobalId, onClose, history }) => {
17
17
  const baseModalProps = {
18
+ ouiaId: 'modal-ansible-roles',
18
19
  variant: ModalVariant.large,
19
20
  isOpen: true,
20
21
  onClose,
@@ -54,6 +54,7 @@ const EditRolesForm = props => {
54
54
  onClick={() => callMutation({ variables })}
55
55
  isDisabled={loading || didNotModifyOptions()}
56
56
  aria-label="submit ansible roles"
57
+ ouiaId="submit-ansible-roles-button"
57
58
  >
58
59
  {__('Confirm')}
59
60
  </Button>,
@@ -65,7 +66,7 @@ const EditRolesForm = props => {
65
66
  }
66
67
 
67
68
  return (
68
- <Modal {...baseModalProps} actions={formActions}>
69
+ <Modal ouiaId="modal-edit-roles" {...baseModalProps} actions={formActions}>
69
70
  <DualList
70
71
  availableOptions={availableOptions}
71
72
  chosenOptions={chosenOptions}
@@ -20,6 +20,7 @@ const EditRolesModal = ({
20
20
  canEditHost,
21
21
  }) => {
22
22
  const baseModalProps = {
23
+ ouiaId: 'edit-ansible-roles-modal',
23
24
  width: '50%',
24
25
  isOpen,
25
26
  className: 'foreman-modal',
@@ -32,7 +33,12 @@ const EditRolesModal = ({
32
33
  };
33
34
 
34
35
  const actions = [
35
- <Button variant="link" onClick={event => closeModal()} key="close">
36
+ <Button
37
+ ouiaId="close-button"
38
+ variant="link"
39
+ onClick={event => closeModal()}
40
+ key="close"
41
+ >
36
42
  {__('Close')}
37
43
  </Button>,
38
44
  ];
@@ -32,7 +32,10 @@ const RolesTable = ({
32
32
  const editBtn = canEditHost ? (
33
33
  <FlexItem>
34
34
  <Link to="/Ansible/roles/edit">
35
- <Button aria-label="edit ansible roles">
35
+ <Button
36
+ aria-label="edit ansible roles"
37
+ ouiaId="edit-ansible-roles-button"
38
+ >
36
39
  {__('Edit Ansible roles')}
37
40
  </Button>
38
41
  </Link>
@@ -53,14 +56,19 @@ const RolesTable = ({
53
56
  <Flex>
54
57
  <FlexItem>{editBtn}</FlexItem>
55
58
  <FlexItem align={{ default: 'alignRight' }}>
56
- <Pagination updateParamsByUrl itemCount={totalCount} variant="top" />
59
+ <Pagination
60
+ ouiaId="pagination-top"
61
+ updateParamsByUrl
62
+ itemCount={totalCount}
63
+ variant="top"
64
+ />
57
65
  </FlexItem>
58
66
  </Flex>
59
67
  <Flex direction={{ default: 'column' }}>
60
68
  <FlexItem>
61
- <TableComposable variant="compact">
69
+ <TableComposable variant="compact" ouiaId="table-composable-compact">
62
70
  <Thead>
63
- <Tr>
71
+ <Tr ouiaId="row-header">
64
72
  {columns.map(col => (
65
73
  <Th key={col}>{col}</Th>
66
74
  ))}
@@ -68,7 +76,7 @@ const RolesTable = ({
68
76
  </Thead>
69
77
  <Tbody>
70
78
  {ansibleRoles.map(role => (
71
- <Tr key={role.id}>
79
+ <Tr key={role.id} ouiaId={`row-${role.id}`}>
72
80
  <Td>
73
81
  <a href={role.path}>{role.name}</a>
74
82
  </Td>
@@ -88,6 +96,7 @@ const RolesTable = ({
88
96
  </FlexItem>
89
97
  <FlexItem align={{ default: 'alignRight' }}>
90
98
  <Pagination
99
+ ouiaId="pagination-bottom"
91
100
  updateParamsByUrl
92
101
  itemCount={totalCount}
93
102
  variant="bottom"
@@ -21,7 +21,8 @@ import {
21
21
  assignRolesErrorMock,
22
22
  } from './RolesTab.fixtures';
23
23
 
24
- const TestComponent = withReactRouter(withRedux(withMockedProvider(RolesTab)));
24
+ jest.mock('axios');
25
+ const TestComponent = withRedux(withReactRouter(withMockedProvider(RolesTab)));
25
26
 
26
27
  describe('assigning Ansible roles', () => {
27
28
  it('should assign Ansible roles', async () => {
@@ -6,6 +6,7 @@ import {
6
6
  tick,
7
7
  withMockedProvider,
8
8
  withReactRouter,
9
+ withRedux,
9
10
  } from '../../../../../testHelper';
10
11
 
11
12
  import {
@@ -18,7 +19,8 @@ import {
18
19
 
19
20
  import RolesTab from '../';
20
21
 
21
- const TestComponent = withReactRouter(withMockedProvider(RolesTab));
22
+ jest.mock('axios');
23
+ const TestComponent = withRedux(withReactRouter(withMockedProvider(RolesTab)));
22
24
 
23
25
  describe('RolesTab', () => {
24
26
  it('should load Ansible Roles as admin', async () => {
@@ -2,7 +2,10 @@ import React, { useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useQuery } from '@apollo/client';
4
4
  import { Button } from '@patternfly/react-core';
5
+ import { Link, Route } from 'react-router-dom';
5
6
  import { translate as __ } from 'foremanReact/common/I18n';
7
+ import { foremanUrl } from 'foremanReact/common/helpers';
8
+ import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
6
9
 
7
10
  import ansibleRolesQuery from '../../../../graphql/queries/hostAnsibleRoles.gql';
8
11
  import { encodeId } from '../../../../globalIdHelper';
@@ -12,6 +15,7 @@ import {
12
15
  useCurrentPagination,
13
16
  } from '../../../../helpers/pageParamsHelper';
14
17
  import EditRolesModal from './EditRolesModal';
18
+ import AllRolesModal from './AllRolesModal';
15
19
 
16
20
  const RolesTab = ({ hostId, history, canEditHost }) => {
17
21
  const hostGlobalId = encodeId('Host', hostId);
@@ -32,10 +36,30 @@ const RolesTab = ({ hostId, history, canEditHost }) => {
32
36
  <Button
33
37
  onClick={() => setAssignModal(true)}
34
38
  aria-label="edit ansible roles"
39
+ ouiaId="edit-ansible-roles-button"
35
40
  >
36
- {__('Assign Ansible roles')}
41
+ {__('Assign roles directly to the host')}
37
42
  </Button>
38
43
  ) : null;
44
+
45
+ const url = hostId && foremanUrl(`/api/v2/hosts/${hostId}/ansible_roles`);
46
+ const { response: allAnsibleRoles } = useAPI('get', url, {
47
+ key: 'ANSIBLE_ROLES',
48
+ });
49
+ const emptyStateDescription = allAnsibleRoles.length > 0 && (
50
+ <>
51
+ <Route path="/Ansible/roles/all">
52
+ <AllRolesModal
53
+ onClose={() => history.push('/Ansible/roles')}
54
+ isOpen
55
+ hostGlobalId={hostGlobalId}
56
+ history={history}
57
+ />
58
+ </Route>
59
+ <Link to="/Ansible/roles/all">{__('View inherited roles')}</Link>
60
+ </>
61
+ );
62
+
39
63
  return (
40
64
  <>
41
65
  <RolesTable
@@ -46,8 +70,9 @@ const RolesTab = ({ hostId, history, canEditHost }) => {
46
70
  history={history}
47
71
  hostGlobalId={hostGlobalId}
48
72
  emptyStateProps={{
49
- header: __('No Ansible roles assigned'),
73
+ header: __('No roles assigned directly to the host'),
50
74
  action: editBtn,
75
+ description: emptyStateDescription,
51
76
  }}
52
77
  pagination={pagination}
53
78
  canEditHost={canEditHost}
@@ -12,7 +12,7 @@ import { ANSIBLE_KEY } from '../constants';
12
12
  import { route } from '../helpers';
13
13
 
14
14
  const SecondaryTabRoutes = ({ response, router, history }) => (
15
- <Switch>
15
+ <Switch ouiaId="switch">
16
16
  <Route exact path={`/${ANSIBLE_KEY}`}>
17
17
  <Redirect to={route('roles')} />
18
18
  </Route>
@@ -1,15 +1,24 @@
1
1
  import React from 'react';
2
2
  import { Provider } from 'react-redux';
3
+ import thunk from 'redux-thunk';
4
+ import { applyMiddleware, createStore, compose, combineReducers } from 'redux';
3
5
  import { MockedProvider } from '@apollo/react-testing';
4
6
  import { Router, MemoryRouter } from 'react-router-dom';
5
7
  import { createMemoryHistory } from 'history';
6
8
 
7
- import store from 'foremanReact/redux';
8
- import ConfirmModal from 'foremanReact/components/ConfirmModal';
9
+ import { reducers as apiReducer, APIMiddleware } from 'foremanReact/redux/API';
10
+ import ConfirmModal, {
11
+ reducers as confirmModalReducers,
12
+ } from 'foremanReact/components/ConfirmModal';
9
13
  import { getForemanContext } from 'foremanReact/Root/Context/ForemanContext';
10
14
 
15
+ const reducers = combineReducers({ ...apiReducer, ...confirmModalReducers });
16
+
17
+ export const generateStore = () =>
18
+ createStore(reducers, compose(applyMiddleware(thunk, APIMiddleware)));
19
+
11
20
  export const withRedux = Component => props => (
12
- <Provider store={store}>
21
+ <Provider store={generateStore()}>
13
22
  <Component {...props} />
14
23
  <ConfirmModal />
15
24
  </Provider>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_ansible
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.0.1
4
+ version: 10.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Lobato Garcia
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-05 00:00:00.000000000 Z
11
+ date: 2022-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: acts_as_list
@@ -44,28 +44,28 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 4.4.0
47
+ version: 8.0.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 4.4.0
54
+ version: 8.0.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: foreman-tasks
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 5.2.0
61
+ version: 7.0.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: 5.2.0
68
+ version: 7.0.0
69
69
  description: Ansible integration with Foreman
70
70
  email:
71
71
  - elobatocs@gmail.com
@@ -222,7 +222,6 @@ files:
222
222
  - lib/foreman_ansible.rb
223
223
  - lib/foreman_ansible/engine.rb
224
224
  - lib/foreman_ansible/register.rb
225
- - lib/foreman_ansible/register.rb.orig
226
225
  - lib/foreman_ansible/remote_execution.rb
227
226
  - lib/foreman_ansible/version.rb
228
227
  - locale/Makefile
@@ -325,6 +324,7 @@ files:
325
324
  - test/graphql/mutations/hosts/assign_ansible_roles_mutation_test.rb
326
325
  - test/graphql/queries/ansible_roles_query_test.rb
327
326
  - test/graphql/queries/host_ansible_roles_query_test.rb
327
+ - test/integration/hostgroup_js_test.rb
328
328
  - test/test_plugin_helper.rb
329
329
  - test/unit/actions/run_ansible_job_test.rb
330
330
  - test/unit/actions/run_proxy_ansible_command_test.rb
@@ -547,3 +547,4 @@ test_files:
547
547
  - test/unit/import_playbooks_test.rb
548
548
  - test/unit/import_roles_and_variables.rb
549
549
  - test/test_plugin_helper.rb
550
+ - test/integration/hostgroup_js_test.rb
@@ -1,257 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- Foreman::Plugin.register :foreman_ansible do
4
- requires_foreman '>= 3.3'
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
87
-
88
- security_block :foreman_ansible do
89
- permission :play_roles_on_host,
90
- { :hosts => [:play_roles, :multiple_play_roles],
91
- :'api/v2/hosts' => [:play_roles,
92
- :multiple_play_roles] },
93
- :resource_type => 'Host'
94
- permission :play_roles_on_hostgroup,
95
- { :hostgroups => [:play_roles],
96
- :'api/v2/hostgroups' => [:play_roles,
97
- :multiple_play_roles] },
98
- :resource_type => 'Hostgroup'
99
- permission :view_ansible_roles,
100
- { :ansible_roles => [:index, :auto_complete_search],
101
- :'api/v2/ansible_roles' => [:index, :show, :fetch],
102
- :ui_ansible_roles => [:index] },
103
- :resource_type => 'AnsibleRole'
104
- permission :destroy_ansible_roles,
105
- { :ansible_roles => [:destroy],
106
- :'api/v2/ansible_roles' => [:destroy, :obsolete] },
107
- :resource_type => 'AnsibleRole'
108
- permission :import_ansible_roles,
109
- { :ansible_roles => [:import, :confirm_import],
110
- :'api/v2/ansible_roles' => [:import, :sync] },
111
- :resource_type => 'AnsibleRole'
112
- permission :view_ansible_variables,
113
- {
114
- :lookup_values => [:index],
115
- :ansible_variables => [:index, :auto_complete_search],
116
- :'api/v2/ansible_variables' => [:index, :show]
117
- },
118
- :resource_type => 'AnsibleVariable'
119
- permission :edit_ansible_variables,
120
- { :lookup_values => [:update],
121
- :ansible_variables => [:edit, :update],
122
- :'api/v2/ansible_variables' => [:update],
123
- :'api/v2/ansible_override_values' => [:create, :destroy] },
124
- :resource_type => 'AnsibleVariable'
125
- permission :destroy_ansible_variables,
126
- {
127
- :lookup_values => [:destroy],
128
- :ansible_variables => [:destroy],
129
- :'api/v2/ansible_variables' => [:destroy, :obsolete]
130
- },
131
- :resource_type => 'AnsibleVariable'
132
- permission :create_ansible_variables,
133
- {
134
- :lookup_values => [:create],
135
- :ansible_variables => [:new, :create],
136
- :'api/v2/ansible_variables' => [:create]
137
- },
138
- :resource_type => 'AnsibleVariable'
139
- permission :import_ansible_variables,
140
- {
141
- :ansible_variables => [:import, :confirm_import],
142
- :'api/v2/ansible_variables' => [:import]
143
- },
144
- :resource_type => 'AnsibleVariable'
145
- permission :view_hosts,
146
- { :'api/v2/hosts' => [:ansible_roles],
147
- :'api/v2/ansible_inventories' => [:hosts] },
148
- :resource_type => 'Host'
149
- permission :view_hostgroups,
150
- { :'api/v2/hostgroups' => [:ansible_roles],
151
- :'api/v2/ansible_inventories' => [:hostgroups] },
152
- :resource_type => 'Hostgroup'
153
- permission :edit_hosts,
154
- { :'api/v2/hosts' => [:assign_ansible_roles] },
155
- :resource_type => 'Host'
156
- permission :edit_hostgroups,
157
- { :'api/v2/hostgroups' => [:assign_ansible_roles] },
158
- :resource_type => 'Hostgroup'
159
- permission :generate_ansible_inventory,
160
- { :'api/v2/ansible_inventories' => [:schedule] }
161
- permission :import_ansible_playbooks,
162
- { :'api/v2/ansible_playbooks' => [:sync, :fetch] }
163
- end
164
-
165
- role 'Ansible Roles Manager',
166
- [:play_roles_on_host, :play_roles_on_hostgroup,
167
- :create_job_invocations, :view_job_templates, # to allow the play_roles
168
- :create_template_invocations, :view_smart_proxies, # ...
169
- :view_ansible_roles, :destroy_ansible_roles,
170
- :import_ansible_roles, :view_ansible_variables,
171
- :create_ansible_variables, :import_ansible_variables,
172
- :edit_ansible_variables, :destroy_ansible_variables, :import_ansible_playbooks]
173
-
174
- role 'Ansible Tower Inventory Reader',
175
- [:view_hosts, :view_hostgroups, :view_facts, :generate_report_templates, :generate_ansible_inventory,
176
- :view_report_templates],
177
- 'Permissions required for the user which is used by Ansible Tower Dynamic Inventory Item'
178
-
179
- add_all_permissions_to_default_roles
180
- extend_template_helpers ForemanAnsible::RendererMethods
181
- allowed_template_helpers :insights_remediation
182
-
183
- base_role_assignment_params = { :ansible_role_ids => [],
184
- :ansible_roles => [] }
185
- parameter_filter Host::Managed, base_role_assignment_params.merge(:host_ansible_roles_attributes => {})
186
- parameter_filter Hostgroup, base_role_assignment_params.merge(:hostgroup_ansible_roles_attributes => {})
187
-
188
- register_global_js_file 'global'
189
-
190
- extend_graphql_type :type => '::Types::Host' do
191
- field :all_ansible_roles, ::Types::InheritedAnsibleRole.connection_type, :null => true, :method => :present_all_ansible_roles
192
- field :own_ansible_roles, ::Types::AnsibleRole.connection_type, :null => true
193
- field :available_ansible_roles, ::Types::AnsibleRole.connection_type, :null => true
194
- field :ansible_variables_with_overrides, Types::OverridenAnsibleVariable.connection_type, :null => false
195
-
196
- def present_all_ansible_roles
197
- inherited_ansible_roles = object.inherited_ansible_roles.map { |role| ::Presenters::AnsibleRolePresenter.new(role, true) }
198
- ansible_roles = object.ansible_roles.map { |role| ::Presenters::AnsibleRolePresenter.new(role, false) }
199
- (inherited_ansible_roles + ansible_roles).uniq
200
- end
201
-
202
- def ansible_variables_with_overrides
203
- resolver = ::ForemanAnsible::OverrideResolver.new(object)
204
- AnsibleVariable.where(:ansible_role_id => object.all_ansible_roles.pluck(:id), :override => true).map { |variable| ::Presenters::OverridenAnsibleVariablePresenter.new variable, resolver }
205
- end
206
- end
207
-
208
- register_graphql_query_field :ansible_roles, '::Types::AnsibleRole', :collection_field
209
- register_graphql_mutation_field :assign_ansible_roles, '::Mutations::Hosts::AssignAnsibleRoles'
210
- register_graphql_mutation_field :delete_ansible_variable_override, ::Mutations::AnsibleVariableOverrides::Delete
211
- register_graphql_mutation_field :update_ansible_variable_override, ::Mutations::AnsibleVariableOverrides::Update
212
- register_graphql_mutation_field :create_ansible_variable_override, ::Mutations::AnsibleVariableOverrides::Create
213
-
214
- divider :top_menu, :caption => N_('Ansible'), :parent => :configure_menu
215
- menu :top_menu, :ansible_roles,
216
- :caption => N_('Roles'),
217
- :url_hash => { :controller => :ansible_roles, :action => :index },
218
- :parent => :configure_menu
219
- menu :top_menu, :ansible_variables,
220
- :caption => N_('Variables'),
221
- :url_hash => { :controller => :ansible_variables, :action => :index },
222
- :parent => :configure_menu
223
-
224
- apipie_documented_controllers [
225
- "#{ForemanAnsible::Engine.root}/app/controllers/api/v2/*.rb"
226
- ]
227
- ApipieDSL.configuration.dsl_classes_matchers += [
228
- "#{ForemanAnsible::Engine.root}/app/models/*.rb",
229
- "#{ForemanAnsible::Engine.root}/app/services/foreman_ansible/*.rb"
230
- ]
231
-
232
- register_info_provider ForemanAnsible::AnsibleInfo
233
-
234
- # For backwards compatiblity with 1.17
235
- if respond_to?(:register_report_scanner)
236
- register_report_scanner ForemanAnsible::AnsibleReportScanner
237
- register_report_origin 'Ansible', 'ConfigReport'
238
- end
239
-
240
- <<<<<<< HEAD
241
- describe_host do
242
- multiple_actions_provider :ansible_hosts_multiple_actions
243
- =======
244
- extend_page('smart_proxies/show') do |context|
245
- context.add_pagelet :smart_proxy_title_actions,
246
- :name => _('Update Smart Proxy'),
247
- :partial => 'foreman/smart_proxies/update_smart_proxy',
248
- :onlyif => ->(proxy, view) { view.can_update_proxy?(proxy) }
249
- end
250
- extend_page('smart_proxies/index') do |context|
251
- context.add_pagelet :smart_proxy_title_actions,
252
- :name => _('Update Smart Proxy'),
253
- :partial => 'foreman/smart_proxies/update_smart_proxy',
254
- :onlyif => ->(proxy, view) { view.can_update_proxy?(proxy) }
255
- >>>>>>> a707dab (Fixes #35143 - Add a smart proxy update button)
256
- end
257
- end