foreman_ansible 9.0.1 → 10.0.1

Sign up to get free protection for your applications and to get access to all the features.
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