foreman_ansible 7.0.1 → 7.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/app/graphql/resolvers/ansible_role/path.rb +11 -0
  3. data/app/graphql/resolvers/ansible_variable/path.rb +11 -0
  4. data/app/graphql/types/ansible_role.rb +1 -0
  5. data/app/graphql/types/ansible_variable.rb +1 -0
  6. data/app/models/foreman_ansible/ansible_provider.rb +3 -2
  7. data/app/services/foreman_ansible/ansible_report_importer.rb +0 -4
  8. data/lib/foreman_ansible/engine.rb +1 -0
  9. data/lib/foreman_ansible/remote_execution.rb +0 -6
  10. data/lib/foreman_ansible/version.rb +1 -1
  11. data/test/unit/ansible_provider_test.rb +12 -0
  12. data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.js +1 -0
  13. data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.scss +4 -0
  14. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTable.js +5 -3
  15. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.fixtures.js +9 -0
  16. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesDelete.test.js +2 -2
  17. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/index.js +4 -1
  18. data/webpack/components/AnsibleHostDetail/components/JobsTab/JobsTabHelper.js +49 -7
  19. data/webpack/components/AnsibleHostDetail/components/JobsTab/PreviousJobsTable.js +4 -1
  20. data/webpack/components/AnsibleHostDetail/components/JobsTab/RecurringJobsTable.js +19 -3
  21. data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.fixtures.js +3 -0
  22. data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.test.js +8 -3
  23. data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTabHelper.test.js +11 -0
  24. data/webpack/components/AnsibleHostDetail/components/JobsTab/index.js +8 -1
  25. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js +8 -14
  26. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/index.js +5 -2
  27. data/webpack/components/AnsibleHostDetail/components/RolesTab/RolesTable.js +4 -2
  28. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/EditRoles.test.js +2 -2
  29. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.fixtures.js +11 -0
  30. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.test.js +7 -3
  31. data/webpack/components/AnsibleHostDetail/components/RolesTab/index.js +38 -14
  32. data/webpack/components/AnsibleHostDetail/components/SecondaryTabRoutes.js +1 -0
  33. data/webpack/components/DualList/index.js +2 -2
  34. data/webpack/graphql/queries/hostAnsibleRoles.gql +1 -0
  35. data/webpack/graphql/queries/hostVariableOverrides.gql +1 -0
  36. data/webpack/graphql/queries/recurringJobs.gql +1 -0
  37. data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.test.js +5 -2
  38. metadata +32 -30
  39. data/app/views/foreman_ansible/job_templates/configure_cloud_connector_-_ansible_default.erb +0 -37
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f339d8ddcc4cb0ae8402d9df48653f437cdfa509784a8fa5774a9ff8b9d6645
4
- data.tar.gz: ed2f5f5a56da334a955280c523cccf5488df9c37c61e267047b0653e5521031f
3
+ metadata.gz: 6974b31e097d2cb11b28ed665e3346b9b6b2b08111cc3a7701c062aeb41c6e09
4
+ data.tar.gz: 13d80c394e6e050d0b9009e86ce1a8b0f500e65c678e6d7b8596b286ae70489a
5
5
  SHA512:
6
- metadata.gz: 19ac46f57b3606646821aafa291523a0b5973f4cb2012011b30a84ff4696ea57a0cdf81fb84d0db435d6e1c1c43ec77f1b9e5b17b40318a626ccf5f7c30e6add
7
- data.tar.gz: 43c1ebc458690ac43ba94ceb7cac037d41517e9d2236ea7e7e64d220a51dc166c44448492eca45174bb16371170e2d2b5093c40eb5200431cb381c7f9f8882af
6
+ metadata.gz: df9ff9f909efd6602b11ac585fe8b3768b3168a869232038d4c92a5b1d2acdf635a9ce0834bfff2385aed1475e63c8361894b06b297eb38ec41adbccf8ea67ba
7
+ data.tar.gz: 68dc31b736c3ff6b787bf49fb4d24b1563b042d9845b8e8e122cfc87f50b8db97981add7b8b3ba06f91792c04460ccc0a08c6fc0193cc36fe5daaf6bba0e6e80
@@ -0,0 +1,11 @@
1
+ module Resolvers
2
+ module AnsibleRole
3
+ class Path < Resolvers::BaseResolver
4
+ type String, null: false
5
+
6
+ def resolve
7
+ Rails.application.routes.url_helpers.ansible_roles_path(search: "name = #{object.name}")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Resolvers
2
+ module AnsibleVariable
3
+ class Path < Resolvers::BaseResolver
4
+ type String, null: false
5
+
6
+ def resolve
7
+ Rails.application.routes.url_helpers.edit_ansible_variable_path(object.ansible_variable)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -5,5 +5,6 @@ module Types
5
5
  global_id_field :id
6
6
 
7
7
  field :name, String, :null => false
8
+ field :path, resolver: Resolvers::AnsibleRole::Path
8
9
  end
9
10
  end
@@ -5,6 +5,7 @@ module Types
5
5
  global_id_field :id
6
6
 
7
7
  field :key, String
8
+ field :path, resolver: Resolvers::AnsibleVariable::Path
8
9
  field :override, Boolean
9
10
  field :description, String
10
11
  field :hidden_value, Boolean
@@ -88,7 +88,7 @@ if defined? ForemanRemoteExecution
88
88
  :children => [
89
89
  {
90
90
  :name => :tags,
91
- :type => Array,
91
+ :type => String,
92
92
  :opts => { :required => false, :desc => N_('A comma separated list of tags to use for Ansible run') }
93
93
  },
94
94
  {
@@ -115,7 +115,8 @@ if defined? ForemanRemoteExecution
115
115
  end
116
116
 
117
117
  def proxy_batch_size
118
- Setting['foreman_ansible_proxy_batch_size']
118
+ value = Setting['foreman_ansible_proxy_batch_size']
119
+ value.presence && value.to_i
119
120
  end
120
121
 
121
122
  private
@@ -18,10 +18,6 @@ module ForemanAnsible
18
18
  partial_hostname_match(hostname)
19
19
  end
20
20
 
21
- def self.authorized_smart_proxy_features
22
- super + ['Ansible']
23
- end
24
-
25
21
  def partial_hostname_match(hostname)
26
22
  return @host unless @host.new_record?
27
23
  hosts = Host.where(Host.arel_table[:name].matches("#{hostname}.%"))
@@ -75,6 +75,7 @@ module ForemanAnsible
75
75
  ::Api::V2::HostgroupsController.include ForemanAnsible::Api::V2::HostgroupsControllerExtensions
76
76
  ::Api::V2::HostgroupsController.include ForemanAnsible::Api::V2::HostgroupsParamGroupExtensions
77
77
  ::ConfigReportImporter.include ForemanAnsible::AnsibleReportImporter
78
+ ReportImporter.register_smart_proxy_feature('Ansible')
78
79
  rescue StandardError => e
79
80
  Rails.logger.warn "Foreman Ansible: skipping engine hook (#{e})"
80
81
  end
@@ -48,12 +48,6 @@ module ForemanAnsible
48
48
  :description => N_('Upgrade Capsules on given Capsule server hosts'),
49
49
  :proxy_selector_override => ::RemoteExecutionProxySelector::INTERNAL_PROXY
50
50
  )
51
- RemoteExecutionFeature.register(
52
- :ansible_configure_cloud_connector,
53
- N_('Configure Cloud Connector on given hosts'),
54
- :description => N_('Configure Cloud Connector on given hosts'),
55
- :proxy_selector_override => ::RemoteExecutionProxySelector::INTERNAL_PROXY
56
- )
57
51
  end
58
52
  end
59
53
  end
@@ -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 = '7.0.1'
7
+ VERSION = '7.0.4'
8
8
  end
@@ -47,4 +47,16 @@ class AnsibleProviderTest < ActiveSupport::TestCase
47
47
  proxy_command_options(template_invocation, dummyhost)
48
48
  end
49
49
  end
50
+
51
+ describe '#proxy_batch_size' do
52
+ it 'returns integer if setting is string' do
53
+ Setting.expects(:[]).with('foreman_ansible_proxy_batch_size').returns('10')
54
+ _(ForemanAnsible::AnsibleProvider.proxy_batch_size).must_equal(10)
55
+ end
56
+
57
+ it 'returns nil if setting is empty' do
58
+ Setting.expects(:[]).with('foreman_ansible_proxy_batch_size').returns('')
59
+ _(ForemanAnsible::AnsibleProvider.proxy_batch_size).must_equal(nil)
60
+ end
61
+ end
50
62
  end
@@ -21,6 +21,7 @@ const AnsibleHostDetail = ({
21
21
  {response?.id && (
22
22
  <>
23
23
  <Tabs
24
+ className="ansible-host-details-tabs"
24
25
  onSelect={(evt, subTab) => hashHistory.push(subTab)}
25
26
  activeKey={pathname?.split('/')[2]}
26
27
  isSecondary
@@ -4,3 +4,7 @@
4
4
  background-color: $pf-color-white;
5
5
  padding: 1.8rem;
6
6
  }
7
+
8
+ .ansible-host-details-tabs {
9
+ margin: 0 24px;
10
+ }
@@ -63,7 +63,7 @@ const AnsibleVariableOverridesTable = ({
63
63
  }) => {
64
64
  const columns = [
65
65
  __('Name'),
66
- __('Ansible Role'),
66
+ __('Ansible role'),
67
67
  __('Type'),
68
68
  __('Value'),
69
69
  __('Source attribute'),
@@ -131,7 +131,7 @@ const AnsibleVariableOverridesTable = ({
131
131
  onClick: () => {
132
132
  dispatch(
133
133
  openConfirmModal({
134
- title: __('Delete Ansible Variable Override'),
134
+ title: __('Delete Ansible variable override'),
135
135
  message:
136
136
  variable &&
137
137
  sprintf(
@@ -187,7 +187,9 @@ const AnsibleVariableOverridesTable = ({
187
187
  <Tbody>
188
188
  {variables.map((variable, idx) => (
189
189
  <Tr key={idx}>
190
- <Td>{variable.key}</Td>
190
+ <Td>
191
+ <a href={variable.path}>{variable.key}</a>
192
+ </Td>
191
193
  <Td>{variable.ansibleRoleName}</Td>
192
194
  <Td>{variable.parameterType}</Td>
193
195
  <Td>
@@ -29,6 +29,7 @@ const withFqdnOverride = canEdit => ({
29
29
  },
30
30
  id: ansibleVariableId,
31
31
  key: 'rectangle',
32
+ path: '/ansible/ansible_variables/1/edit',
32
33
  defaultValue: 17,
33
34
  parameterType: 'integer',
34
35
  ansibleRoleName: 'test.role',
@@ -62,6 +63,7 @@ const withDomainOverride = canEdit => ({
62
63
  },
63
64
  id: 'MDE6QW5zaWJsZVZhcmlhYmxlLTc4',
64
65
  key: 'circle',
66
+ path: '/ansible/ansible_variables/2/edit',
65
67
  defaultValue: 'd',
66
68
  parameterType: 'string',
67
69
  ansibleRoleName: 'test.role',
@@ -133,6 +135,7 @@ export const mocks = [
133
135
  },
134
136
  id: barVariableGlobalId,
135
137
  key: 'bar',
138
+ path: '/ansible/ansible_variables/11/edit',
136
139
  defaultValue: 'a',
137
140
  parameterType: 'string',
138
141
  ansibleRoleName: 'test.role',
@@ -160,6 +163,7 @@ export const mocks = [
160
163
  },
161
164
  id: 'MDE6QW5zaWJsZVZhcmlhYmxlLTY1',
162
165
  key: 'square',
166
+ path: '/ansible/ansible_variables/12/edit',
163
167
  defaultValue: true,
164
168
  parameterType: 'boolean',
165
169
  ansibleRoleName: 'test.role',
@@ -179,6 +183,7 @@ export const mocks = [
179
183
  },
180
184
  id: 'MDE6QW5zaWJsZVZhcmlhYmxlLTc4',
181
185
  key: 'circle',
186
+ path: '/ansible/ansible_variables/13/edit',
182
187
  defaultValue: 'd',
183
188
  parameterType: 'string',
184
189
  ansibleRoleName: 'test.role',
@@ -203,6 +208,7 @@ export const mocks = [
203
208
  },
204
209
  id: 'MDE6QW5zaWJsZVZhcmlhYmxlLTc5',
205
210
  key: 'ellipse',
211
+ path: '/ansible/ansible_variables/14/edit',
206
212
  defaultValue: ['seven', 'eight'],
207
213
  parameterType: 'array',
208
214
  ansibleRoleName: 'test.role',
@@ -227,6 +233,7 @@ export const mocks = [
227
233
  },
228
234
  id: 'MDE6QW5zaWJsZVZhcmlhYmxlLTY2Ng==',
229
235
  key: 'spiral',
236
+ path: '/ansible/ansible_variables/15/edit',
230
237
  defaultValue: { one: 'one', two: 'two' },
231
238
  parameterType: 'hash',
232
239
  ansibleRoleName: 'test.role',
@@ -246,6 +253,7 @@ export const mocks = [
246
253
  },
247
254
  id: 'MDE6QW5zaWJsZVZhcmlhYmxlLTY3Mg==',
248
255
  key: 'sun',
256
+ path: '/ansible/ansible_variables/16/edit',
249
257
  defaultValue: "{ one: 'one', two: 'two' }",
250
258
  parameterType: 'json',
251
259
  ansibleRoleName: 'test.role',
@@ -265,6 +273,7 @@ export const mocks = [
265
273
  },
266
274
  id: 'MDE6QW5zaWJsZVZhcmlhYmxlLTY3Mw==',
267
275
  key: 'moon',
276
+ path: '/ansible/ansible_variables/17/edit',
268
277
  defaultValue: [
269
278
  { hosts: 'all', become: 'true', roles: ['foo'] },
270
279
  ],
@@ -37,12 +37,12 @@ describe('AnsibleVariableOverrides', () => {
37
37
  userEvent.click(screen.getByText('Delete'));
38
38
  await waitFor(tick);
39
39
  expect(
40
- screen.getByText('Delete Ansible Variable Override')
40
+ screen.getByText('Delete Ansible variable override')
41
41
  ).toBeInTheDocument();
42
42
  userEvent.click(screen.getByText('Cancel'));
43
43
  await waitFor(tick);
44
44
  expect(
45
- screen.queryByText('Delete Ansible Variable Override')
45
+ screen.queryByText('Delete Ansible variable override')
46
46
  ).not.toBeInTheDocument();
47
47
  });
48
48
  it('should delete override', async () => {
@@ -41,7 +41,10 @@ const AnsibleVariableOverrides = ({ hostId, hostAttrs, history }) => {
41
41
  renameData={renameData}
42
42
  fetchFn={useFetchFn}
43
43
  renamedDataPath="variables"
44
- emptyStateTitle={__('No Ansible Variables found for Host')}
44
+ emptyStateProps={{
45
+ header: __('No Ansible variables found for Host'),
46
+ description: __('Only variables marked to Override are shown here.'),
47
+ }}
45
48
  permissions={['view_ansible_variables']}
46
49
  pagination={pagination}
47
50
  history={history}
@@ -7,21 +7,36 @@ import { showToast } from '../../../../toastHelper';
7
7
  export const ansiblePurpose = (resourceName, resourceId) =>
8
8
  `ansible-${resourceName}-${resourceId}`;
9
9
 
10
- const jobSearch = (resourceName, resourceId, statusSearch) =>
11
- `recurring = true && pattern_template_name = "Ansible Roles - Ansible Default" && ${statusSearch} && recurring_logic.purpose = ${ansiblePurpose(
10
+ const jobSearch = (resourceName, resourceId, status, hostGroupId) => {
11
+ const search = `recurring = true && pattern_template_name = "Ansible Roles - Ansible Default"`;
12
+ const searchStatus = ` && ${status}`;
13
+ const searchHost = ` && recurring_logic.purpose = ${ansiblePurpose(
12
14
  resourceName,
13
15
  resourceId
14
16
  )}`;
17
+ const searchHostGroup = hostGroupId
18
+ ? ` or recurring_logic.purpose = ${ansiblePurpose(
19
+ 'hostgroup',
20
+ hostGroupId
21
+ )}`
22
+ : '';
15
23
 
16
- export const scheduledJobsSearch = (resourceName, resourceId) =>
17
- jobSearch(resourceName, resourceId, 'status = queued');
18
- export const previousJobsSearch = (resourceName, resourceId) =>
19
- jobSearch(resourceName, resourceId, 'status != queued');
24
+ return search + searchStatus + searchHost + searchHostGroup;
25
+ };
26
+
27
+ export const scheduledJobsSearch = (resourceName, resourceId, hostGroupId) =>
28
+ jobSearch(resourceName, resourceId, 'status = queued', hostGroupId);
29
+ export const previousJobsSearch = (resourceName, resourceId, hostGroupId) =>
30
+ jobSearch(resourceName, resourceId, 'status != queued', hostGroupId);
20
31
 
21
32
  const fetchJobsFn = (searchFn, pagination = {}) => componentProps =>
22
33
  useQuery(jobsQuery, {
23
34
  variables: {
24
- search: searchFn(componentProps.resourceName, componentProps.resourceId),
35
+ search: searchFn(
36
+ componentProps.resourceName,
37
+ componentProps.resourceId,
38
+ componentProps.hostGroupId
39
+ ),
25
40
  ...pagination,
26
41
  },
27
42
  });
@@ -77,3 +92,30 @@ export const useCancelMutation = (resourceName, resourceId) =>
77
92
  },
78
93
  ],
79
94
  });
95
+
96
+ export const readableCron = (cron = '') => {
97
+ if (cron.match(/(\d+ \* \* \* \*)/)) {
98
+ return 'hourly';
99
+ }
100
+
101
+ if (cron.match(/(\d+ \d+ \* \* \*)/)) {
102
+ return 'daily';
103
+ }
104
+
105
+ if (cron.match(/(\d+ \d+ \* \* \d+)/)) {
106
+ return 'weekly';
107
+ }
108
+
109
+ if (cron.match(/(\d+ \d+ \d+ \* \*)/)) {
110
+ return 'monthly';
111
+ }
112
+
113
+ return 'custom';
114
+ };
115
+
116
+ export const readablePurpose = (purpose = '') => {
117
+ if (window.location.href.match(/ansible\/hostgroup/)) {
118
+ return '';
119
+ }
120
+ return purpose.match(/hostgroup/) ? __('(from host group)') : '';
121
+ };
@@ -17,6 +17,7 @@ import { Flex, FlexItem, Pagination } from '@patternfly/react-core';
17
17
 
18
18
  import { decodeId } from '../../../../globalIdHelper';
19
19
  import withLoading from '../../../withLoading';
20
+ import { readableCron, readablePurpose } from './JobsTabHelper';
20
21
  import {
21
22
  preparePerPageOptions,
22
23
  refreshPage,
@@ -78,13 +79,15 @@ const PreviousJobsTable = ({ history, totalCount, jobs, pagination }) => {
78
79
  >
79
80
  {job.description}
80
81
  </a>
82
+ &nbsp;
83
+ {readablePurpose(job.recurringLogic.purpose)}
81
84
  </Td>
82
85
  <Td>{job.task.result}</Td>
83
86
  <Td>{job.task.state}</Td>
84
87
  <Td>
85
88
  <RelativeDateTime date={job.startAt} />
86
89
  </Td>
87
- <Td>{job.recurringLogic.cronLine}</Td>
90
+ <Td>{readableCron(job.recurringLogic.cronLine)}</Td>
88
91
  </Tr>
89
92
  ))}
90
93
  </Tbody>
@@ -14,11 +14,20 @@ import {
14
14
  Td,
15
15
  } from '@patternfly/react-table';
16
16
 
17
- import { useCancelMutation } from './JobsTabHelper';
17
+ import {
18
+ useCancelMutation,
19
+ readableCron,
20
+ readablePurpose,
21
+ } from './JobsTabHelper';
18
22
  import withLoading from '../../../withLoading';
19
23
  import { decodeId } from '../../../../globalIdHelper';
20
24
 
21
- const RecurringJobsTable = ({ jobs, resourceName, resourceId }) => {
25
+ const RecurringJobsTable = ({
26
+ jobs,
27
+ resourceName,
28
+ resourceId,
29
+ hostGroupId,
30
+ }) => {
22
31
  const columns = [__('Description'), __('Schedule'), __('Next Run')];
23
32
  const dispatch = useDispatch();
24
33
 
@@ -73,8 +82,10 @@ const RecurringJobsTable = ({ jobs, resourceName, resourceId }) => {
73
82
  >
74
83
  {job.description}
75
84
  </a>
85
+ &nbsp;
86
+ {readablePurpose(job.recurringLogic.purpose)}
76
87
  </Td>
77
- <Td>{job.recurringLogic.cronLine}</Td>
88
+ <Td>{readableCron(job.recurringLogic.cronLine)}</Td>
78
89
  <Td>
79
90
  <RelativeDateTime date={job.startAt} />
80
91
  </Td>
@@ -91,6 +102,11 @@ RecurringJobsTable.propTypes = {
91
102
  jobs: PropTypes.array.isRequired,
92
103
  resourceId: PropTypes.number.isRequired,
93
104
  resourceName: PropTypes.string.isRequired,
105
+ hostGroupId: PropTypes.number,
106
+ };
107
+
108
+ RecurringJobsTable.defaultProps = {
109
+ hostGroupId: undefined,
94
110
  };
95
111
 
96
112
  export default withLoading(RecurringJobsTable);
@@ -23,10 +23,12 @@ const viewer = userFactory('viewer', [
23
23
 
24
24
  const firstRecurringLogicGlobalId =
25
25
  'MDE6Rm9yZW1hblRhc2tzOjpSZWN1cnJpbmdMb2dpYy0x';
26
+
26
27
  const firstRecurringLogic = {
27
28
  __typename: 'ForemanTasks::RecurringLogic',
28
29
  id: firstRecurringLogicGlobalId,
29
30
  cronLine: toCron(futureDate, 'weekly'),
31
+ purpose: '',
30
32
  meta: {
31
33
  canEdit: true,
32
34
  },
@@ -66,6 +68,7 @@ export const secondJob = {
66
68
  __typename: 'ForemanTasks::RecurringLogic',
67
69
  id: 'MDE6Rm9yZW1hblRhc2tzOjpSZWN1cnJpbmdMb2dpYy0yMw==',
68
70
  cronLine: '54 10 15 * *',
71
+ purpose: '',
69
72
  meta: {
70
73
  canEdit: true,
71
74
  },
@@ -18,6 +18,7 @@ import {
18
18
  import * as toasts from '../../../../../toastHelper';
19
19
 
20
20
  import { toCron } from '../NewRecurringJobHelper';
21
+ import { readableCron } from '../JobsTabHelper';
21
22
 
22
23
  import {
23
24
  tick,
@@ -49,8 +50,10 @@ describe('JobsTab', () => {
49
50
  .map(element => expect(element).toBeInTheDocument());
50
51
  expect(screen.getByText('Scheduled recurring jobs')).toBeInTheDocument();
51
52
  expect(screen.getByText('Previously executed jobs')).toBeInTheDocument();
52
- expect(screen.getByText(toCron(futureDate, 'weekly'))).toBeInTheDocument();
53
- expect(screen.getByText('54 10 15 * *')).toBeInTheDocument();
53
+ expect(
54
+ screen.getByText(readableCron(toCron(futureDate, 'weekly')))
55
+ ).toBeInTheDocument();
56
+ expect(screen.getByText('monthly')).toBeInTheDocument();
54
57
  });
55
58
  it('should show empty state', async () => {
56
59
  render(
@@ -129,7 +132,9 @@ describe('JobsTab', () => {
129
132
  message: 'Ansible job was successfully created.',
130
133
  });
131
134
  await waitFor(tick);
132
- expect(screen.getByText(toCron(futureDate, 'weekly'))).toBeInTheDocument();
135
+ expect(
136
+ screen.getByText(readableCron(toCron(futureDate, 'weekly')))
137
+ ).toBeInTheDocument();
133
138
  expect(screen.getByText('in 3 days')).toBeInTheDocument();
134
139
  expect(
135
140
  screen.queryByText('No config job for Ansible roles scheduled')
@@ -0,0 +1,11 @@
1
+ import { readableCron } from '../JobsTabHelper';
2
+
3
+ describe('JobTabsHelper', () => {
4
+ it('readableCron', () => {
5
+ expect(readableCron('01 * * * *')).toBe('hourly');
6
+ expect(readableCron('01 01 * * *')).toBe('daily');
7
+ expect(readableCron('01 01 * * 01')).toBe('weekly');
8
+ expect(readableCron('01 01 01 * *')).toBe('monthly');
9
+ expect(readableCron()).toBe('custom');
10
+ });
11
+ });
@@ -15,7 +15,7 @@ import RecurringJobsTable from './RecurringJobsTable';
15
15
  import PreviousJobsTable from './PreviousJobsTable';
16
16
  import NewRecurringJobModal from './NewRecurringJobModal';
17
17
 
18
- const JobsTab = ({ resourceName, resourceId, history }) => {
18
+ const JobsTab = ({ resourceName, resourceId, hostGroupId, history }) => {
19
19
  const [modalOpen, setModalOpen] = useState(false);
20
20
  const toggleModal = () => setModalOpen(!modalOpen);
21
21
 
@@ -44,6 +44,7 @@ const JobsTab = ({ resourceName, resourceId, history }) => {
44
44
  <RecurringJobsTable
45
45
  resourceId={resourceId}
46
46
  resourceName={resourceName}
47
+ hostGroupId={hostGroupId}
47
48
  fetchFn={fetchRecurringFn}
48
49
  renameData={renameData}
49
50
  renamedDataPath="jobs"
@@ -59,6 +60,7 @@ const JobsTab = ({ resourceName, resourceId, history }) => {
59
60
  <PreviousJobsTable
60
61
  resourceId={resourceId}
61
62
  resourceName={resourceName}
63
+ hostGroupId={hostGroupId}
62
64
  fetchFn={fetchPreviousFn(useParamsToVars(history))}
63
65
  renameData={renameData}
64
66
  emptyWrapper={() => null}
@@ -81,8 +83,13 @@ const JobsTab = ({ resourceName, resourceId, history }) => {
81
83
 
82
84
  JobsTab.propTypes = {
83
85
  resourceName: PropTypes.string.isRequired,
86
+ hostGroupId: PropTypes.number,
84
87
  resourceId: PropTypes.number.isRequired,
85
88
  history: PropTypes.object.isRequired,
86
89
  };
87
90
 
91
+ JobsTab.defaultProps = {
92
+ hostGroupId: undefined,
93
+ };
94
+
88
95
  export default JobsTab;
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import { useQuery } from '@apollo/client';
4
4
  import { translate as __ } from 'foremanReact/common/I18n';
5
5
 
6
- import { Modal, Button, ModalVariant } from '@patternfly/react-core';
6
+ import { Modal, ModalVariant } from '@patternfly/react-core';
7
7
 
8
8
  import allAnsibleRolesQuery from '../../../../../graphql/queries/allAnsibleRoles.gql';
9
9
  import AllRolesTable from './AllRolesTable';
@@ -17,25 +17,19 @@ const AllRolesModal = ({ hostGlobalId, onClose, history }) => {
17
17
  const baseModalProps = {
18
18
  variant: ModalVariant.large,
19
19
  isOpen: true,
20
+ onClose,
20
21
  className: 'foreman-modal',
21
- showClose: false,
22
- title: __('All Ansible Roles'),
22
+ showClose: true,
23
+ title: __('All assigned Ansible roles'),
23
24
  disableFocusTrap: true,
25
+ description: __(
26
+ 'This list consists of host assigned roles and group assigned roles. Group assigned roles will always be executed prior to host assigned roles'
27
+ ),
24
28
  };
25
29
 
26
30
  const paginationKeys = { page: 'allPage', perPage: 'allPerPage' };
27
31
 
28
- const actions = [
29
- <Button variant="link" onClick={onClose} key="close">
30
- {__('Close')}
31
- </Button>,
32
- ];
33
-
34
- const wrapper = child => (
35
- <Modal {...baseModalProps} actions={actions}>
36
- {child}
37
- </Modal>
38
- );
32
+ const wrapper = child => <Modal {...baseModalProps}>{child}</Modal>;
39
33
 
40
34
  const loadingWrapper = child => <Modal {...baseModalProps}>{child}</Modal>;
41
35
 
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { translate as __ } from 'foremanReact/common/I18n';
3
3
  import PropTypes from 'prop-types';
4
4
 
5
- import { Modal, Button, ModalVariant } from '@patternfly/react-core';
5
+ import { Modal, Button } from '@patternfly/react-core';
6
6
  import { useQuery } from '@apollo/client';
7
7
 
8
8
  import EditRolesForm from './EditRolesForm';
@@ -20,12 +20,15 @@ const EditRolesModal = ({
20
20
  canEditHost,
21
21
  }) => {
22
22
  const baseModalProps = {
23
- variant: ModalVariant.large,
23
+ width: '70%',
24
24
  isOpen,
25
25
  className: 'foreman-modal',
26
26
  showClose: false,
27
27
  title: __('Edit Ansible Roles'),
28
28
  disableFocusTrap: true,
29
+ description: __(
30
+ 'Add, remove or reorder host assigned Ansible roles. This host has also group assigned roles that are not displayed here and will always be executed prior to host assigned roles'
31
+ ),
29
32
  };
30
33
 
31
34
  const actions = [
@@ -48,7 +48,7 @@ const RolesTable = ({
48
48
  <FlexItem>
49
49
  <Link to="/Ansible/roles/edit">
50
50
  <Button aria-label="edit ansible roles">
51
- {__('Edit Ansible Roles')}
51
+ {__('Edit Ansible roles')}
52
52
  </Button>
53
53
  </Link>
54
54
  </FlexItem>
@@ -90,7 +90,9 @@ const RolesTable = ({
90
90
  <Tbody>
91
91
  {ansibleRoles.map(role => (
92
92
  <Tr key={role.id}>
93
- <Td>{role.name}</Td>
93
+ <Td>
94
+ <a href={role.path}>{role.name}</a>
95
+ </Td>
94
96
  </Tr>
95
97
  ))}
96
98
  </Tbody>
@@ -39,7 +39,7 @@ describe('assigning Ansible roles', () => {
39
39
  userEvent.click(screen.getByRole('button', { name: 'edit ansible roles' }));
40
40
  await waitFor(tick);
41
41
  await waitFor(tick);
42
- expect(screen.getByText('Available options')).toBeInTheDocument();
42
+ expect(screen.getByText('Available Ansible roles')).toBeInTheDocument();
43
43
  userEvent.click(screen.getAllByText('another.role')[1]);
44
44
  userEvent.click(screen.getByRole('button', { name: 'Remove selected' }));
45
45
  userEvent.click(screen.getByText('geerlingguy.ceylon'));
@@ -67,7 +67,7 @@ describe('assigning Ansible roles', () => {
67
67
  await waitFor(tick);
68
68
  userEvent.click(screen.getByRole('button', { name: 'edit ansible roles' }));
69
69
  await waitFor(tick);
70
- expect(screen.getByText('Available options')).toBeInTheDocument();
70
+ expect(screen.getByText('Available Ansible roles')).toBeInTheDocument();
71
71
  userEvent.click(screen.getAllByText('another.role')[1]);
72
72
  userEvent.click(screen.getByRole('button', { name: 'Remove selected' }));
73
73
  userEvent.click(screen.getByText('geerlingguy.ceylon'));
@@ -34,24 +34,28 @@ const role1 = {
34
34
  __typename: 'AnsibleRole',
35
35
  id: 'MDE6QW5zaWJsZVJvbGUtMw==',
36
36
  name: 'aardvaark.cube',
37
+ path: '/ansible/ansible_roles/search="name = aardvaark.cube"',
37
38
  };
38
39
 
39
40
  const role2 = {
40
41
  __typename: 'AnsibleRole',
41
42
  id: 'MDE6QW5zaWJsZVJvbGUtNQ==',
42
43
  name: 'aardvaark.sphere',
44
+ path: '/ansible/ansible_roles/search="name = aardvaark.sphere"',
43
45
  };
44
46
 
45
47
  const role3 = {
46
48
  __typename: 'AnsibleRole',
47
49
  id: 'MDE6QW5zaWJsZVJvbGUtMzA=',
48
50
  name: 'another.role',
51
+ path: '/ansible/ansible_roles/search="name = another.role"',
49
52
  };
50
53
 
51
54
  const role4 = {
52
55
  __typename: 'AnsibleRole',
53
56
  id: 'MDE6QW5zaWJsZVJvbGUtMzk=',
54
57
  name: 'geerlingguy.ceylon',
58
+ path: '/ansible/ansible_roles/search="name = geerlingguy.ceylon"',
55
59
  };
56
60
 
57
61
  const ansibleRolesMock = {
@@ -71,26 +75,32 @@ const availableRoles = {
71
75
  __typename: 'AnsibleRole',
72
76
  id: 'MDE6QW5zaWJsZVJvbGUtMQ==',
73
77
  name: 'theforeman.foreman_scap_client',
78
+ path:
79
+ '/ansible/ansible_roles/search="name = theforeman.foreman_scap_client"',
74
80
  },
75
81
  {
76
82
  __typename: 'AnsibleRole',
77
83
  id: 'MDE6QW5zaWJsZVJvbGUtMg==',
78
84
  name: 'adriagalin.motd',
85
+ path: '/ansible/ansible_roles/search="name = adriagalin.motd"',
79
86
  },
80
87
  {
81
88
  __typename: 'AnsibleRole',
82
89
  id: 'MDE6QW5zaWJsZVJvbGUtMjI=',
83
90
  name: 'geerlingguy.php',
91
+ path: '/ansible/ansible_roles/search="name = geerlingguy.php"',
84
92
  },
85
93
  {
86
94
  __typename: 'AnsibleRole',
87
95
  id: 'MDE6QW5zaWJsZVJvbGUtNTc=',
88
96
  name: 'robertdebock.epel',
97
+ path: '/ansible/ansible_roles/search="name = robertdebock.epel"',
89
98
  },
90
99
  {
91
100
  __typename: 'AnsibleRole',
92
101
  id: 'MDE6QW5zaWJsZVJvbGUtNTg=',
93
102
  name: 'geerlingguy.nfs',
103
+ path: '/ansible/ansible_roles/search="name = geerlingguy.nfs"',
94
104
  },
95
105
  ],
96
106
  };
@@ -106,6 +116,7 @@ export const allRolesMocks = allAnsibleRolesMockFactory(
106
116
  {
107
117
  id: 'MDE6QW5zaWJsZVJvbGUtMg==',
108
118
  name: 'adriagalin.motd',
119
+ path: '/ansible/ansible_roles/search="name = adriagalin.motd"',
109
120
  inherited: true,
110
121
  },
111
122
  { ...role1, inherited: false },
@@ -38,14 +38,18 @@ describe('RolesTab', () => {
38
38
  );
39
39
  await waitFor(tick);
40
40
  expect(screen.getByText('view all assigned roles')).toBeInTheDocument();
41
- expect(screen.queryByText('All Ansible Roles')).not.toBeInTheDocument();
41
+ expect(
42
+ screen.queryByText('All assigned Ansible roles')
43
+ ).not.toBeInTheDocument();
42
44
  userEvent.click(screen.getByText('view all assigned roles'));
43
45
  await waitFor(tick);
44
- expect(screen.getByText('All Ansible Roles')).toBeInTheDocument();
46
+ expect(screen.getByText('All assigned Ansible roles')).toBeInTheDocument();
45
47
  expect(screen.getByText('Inherited from Hostgroup')).toBeInTheDocument();
46
48
  userEvent.click(screen.getByRole('button', { name: 'Close' }));
47
49
  await waitFor(tick);
48
- expect(screen.queryByText('All Ansible Roles')).not.toBeInTheDocument();
50
+ expect(
51
+ screen.queryByText('All assigned Ansible roles')
52
+ ).not.toBeInTheDocument();
49
53
  });
50
54
  it('should load Ansible Roles as viewer', async () => {
51
55
  render(
@@ -1,6 +1,7 @@
1
- import React from 'react';
1
+ import React, { useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useQuery } from '@apollo/client';
4
+ import { Button } from '@patternfly/react-core';
4
5
  import { translate as __ } from 'foremanReact/common/I18n';
5
6
 
6
7
  import ansibleRolesQuery from '../../../../graphql/queries/hostAnsibleRoles.gql';
@@ -10,11 +11,12 @@ import {
10
11
  useParamsToVars,
11
12
  useCurrentPagination,
12
13
  } from '../../../../helpers/pageParamsHelper';
14
+ import EditRolesModal from './EditRolesModal';
13
15
 
14
16
  const RolesTab = ({ hostId, history, canEditHost }) => {
15
17
  const hostGlobalId = encodeId('Host', hostId);
16
18
  const pagination = useCurrentPagination(history);
17
-
19
+ const [assignModal, setAssignModal] = useState(false);
18
20
  const renameData = data => ({
19
21
  ansibleRoles: data.host.ownAnsibleRoles.nodes,
20
22
  totalCount: data.host.ownAnsibleRoles.totalCount,
@@ -26,19 +28,41 @@ const RolesTab = ({ hostId, history, canEditHost }) => {
26
28
  fetchPolicy: 'network-only',
27
29
  });
28
30
 
31
+ const editBtn = canEditHost ? (
32
+ <Button
33
+ onClick={() => setAssignModal(true)}
34
+ aria-label="edit ansible roles"
35
+ >
36
+ {__('Assign Ansible roles')}
37
+ </Button>
38
+ ) : null;
29
39
  return (
30
- <RolesTable
31
- fetchFn={useFetchFn}
32
- renamedDataPath="ansibleRoles"
33
- renameData={renameData}
34
- permissions={['view_ansible_roles']}
35
- history={history}
36
- hostGlobalId={hostGlobalId}
37
- emptyStateProps={{ title: __('No Ansible roles assigned') }}
38
- pagination={pagination}
39
- canEditHost={canEditHost}
40
- hostId={hostId}
41
- />
40
+ <>
41
+ <RolesTable
42
+ fetchFn={useFetchFn}
43
+ renamedDataPath="ansibleRoles"
44
+ renameData={renameData}
45
+ permissions={['view_ansible_roles']}
46
+ history={history}
47
+ hostGlobalId={hostGlobalId}
48
+ emptyStateProps={{
49
+ header: __('No Ansible roles assigned'),
50
+ action: editBtn,
51
+ }}
52
+ pagination={pagination}
53
+ canEditHost={canEditHost}
54
+ hostId={hostId}
55
+ />
56
+ {assignModal && (
57
+ <EditRolesModal
58
+ closeModal={() => setAssignModal(false)}
59
+ isOpen={assignModal}
60
+ assignedRoles={[]}
61
+ hostId={hostId}
62
+ canEditHost={canEditHost}
63
+ />
64
+ )}
65
+ </>
42
66
  );
43
67
  };
44
68
 
@@ -44,6 +44,7 @@ const SecondaryTabRoutes = ({ response, router, history }) => (
44
44
  <JobsTab
45
45
  resourceId={response.id}
46
46
  resourceName="host"
47
+ hostGroupId={response.hostgroup_id}
47
48
  history={history}
48
49
  />
49
50
  </TabLayout>
@@ -63,7 +63,7 @@ const DualList = props => {
63
63
  return (
64
64
  <div className="pf-c-dual-list-selector">
65
65
  <ListPane
66
- title={__('Available options')}
66
+ title={__('Available Ansible roles')}
67
67
  items={props.availableOptions}
68
68
  paneClass="pf-m-available"
69
69
  onItemClick={onItemClick('availableSelected')}
@@ -81,7 +81,7 @@ const DualList = props => {
81
81
  removeSelectedDisabled={selectState.chosenSelected.length === 0}
82
82
  />
83
83
  <ListPane
84
- title={__('Chosen options')}
84
+ title={__('Host assigned Ansible roles')}
85
85
  items={props.chosenOptions}
86
86
  paneClass="pf-m-chosen"
87
87
  draggable
@@ -8,6 +8,7 @@ query($id: String!, $first: Int, $last: Int) {
8
8
  nodes {
9
9
  id
10
10
  name
11
+ path
11
12
  }
12
13
  }
13
14
  }
@@ -8,6 +8,7 @@ query($id: String!, $match: String, $first: Int, $last: Int) {
8
8
  nodes {
9
9
  id
10
10
  key
11
+ path
11
12
  meta {
12
13
  canEdit
13
14
  }
@@ -14,6 +14,7 @@ query($search: String, $first: Int, $last: Int) {
14
14
  meta {
15
15
  canEdit
16
16
  }
17
+ purpose
17
18
  }
18
19
  task {
19
20
  id
@@ -24,6 +24,7 @@ import {
24
24
  } from '../../../testHelper';
25
25
 
26
26
  import { toCron } from '../../../components/AnsibleHostDetail/components/JobsTab/NewRecurringJobHelper';
27
+ import { readableCron } from '../../../components/AnsibleHostDetail/components/JobsTab/JobsTabHelper';
27
28
 
28
29
  const TestComponent = withRedux(withRouter(withMockedProvider(HostgroupJobs)));
29
30
 
@@ -46,7 +47,7 @@ describe('HostgroupJobs', () => {
46
47
  .map(element => expect(element).toBeInTheDocument());
47
48
  expect(screen.getByText('Scheduled recurring jobs')).toBeInTheDocument();
48
49
  expect(screen.getByText('Previously executed jobs')).toBeInTheDocument();
49
- expect(screen.getByText('54 10 15 * *')).toBeInTheDocument();
50
+ expect(screen.getByText(readableCron('54 10 15 * *'))).toBeInTheDocument();
50
51
  });
51
52
  it('should show empty state', async () => {
52
53
  render(
@@ -103,7 +104,9 @@ describe('HostgroupJobs', () => {
103
104
  type: 'success',
104
105
  message: 'Ansible job was successfully created.',
105
106
  });
106
- expect(screen.getByText(toCron(futureDate, 'weekly'))).toBeInTheDocument();
107
+ expect(
108
+ screen.getByText(readableCron(toCron(futureDate, 'weekly')))
109
+ ).toBeInTheDocument();
107
110
  expect(screen.getByText('in 3 days')).toBeInTheDocument();
108
111
  expect(
109
112
  screen.queryByText('No config job for Ansible roles scheduled')
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: 7.0.1
4
+ version: 7.0.4
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: 2021-12-07 00:00:00.000000000 Z
11
+ date: 2022-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: acts_as_list
@@ -101,6 +101,8 @@ files:
101
101
  - app/graphql/mutations/hosts/assign_ansible_roles.rb
102
102
  - app/graphql/presenters/ansible_role_presenter.rb
103
103
  - app/graphql/presenters/overriden_ansible_variable_presenter.rb
104
+ - app/graphql/resolvers/ansible_role/path.rb
105
+ - app/graphql/resolvers/ansible_variable/path.rb
104
106
  - app/graphql/types/ansible_role.rb
105
107
  - app/graphql/types/ansible_variable.rb
106
108
  - app/graphql/types/ansible_variable_override.rb
@@ -177,7 +179,6 @@ files:
177
179
  - app/views/foreman_ansible/job_templates/ansible_roles_-_install_from_galaxy.erb
178
180
  - app/views/foreman_ansible/job_templates/ansible_roles_-_install_from_git.erb
179
181
  - app/views/foreman_ansible/job_templates/capsule_upgrade_-_ansible_default.erb
180
- - app/views/foreman_ansible/job_templates/configure_cloud_connector_-_ansible_default.erb
181
182
  - app/views/foreman_ansible/job_templates/convert_to_rhel.erb
182
183
  - app/views/foreman_ansible/job_templates/maintenance_plan.erb
183
184
  - app/views/foreman_ansible/job_templates/module_action_-_ansible_default.erb
@@ -328,6 +329,7 @@ files:
328
329
  - webpack/components/AnsibleHostDetail/components/JobsTab/RecurringJobsTable.js
329
330
  - webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.fixtures.js
330
331
  - webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.test.js
332
+ - webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTabHelper.test.js
331
333
  - webpack/components/AnsibleHostDetail/components/JobsTab/index.js
332
334
  - webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/AllRolesTable.js
333
335
  - webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js
@@ -445,50 +447,50 @@ required_rubygems_version: !ruby/object:Gem::Requirement
445
447
  - !ruby/object:Gem::Version
446
448
  version: '0'
447
449
  requirements: []
448
- rubygems_version: 3.1.6
450
+ rubygems_version: 3.1.4
449
451
  signing_key:
450
452
  specification_version: 4
451
453
  summary: Ansible integration with Foreman (theforeman.org)
452
454
  test_files:
453
- - test/functional/ansible_variables_controller_test.rb
455
+ - test/factories/ansible_proxy.rb
456
+ - test/factories/ansible_roles.rb
457
+ - test/factories/ansible_variables.rb
458
+ - test/factories/host_ansible_enhancements.rb
459
+ - test/fixtures/insights_playbook.yaml
460
+ - test/fixtures/sample_facts.json
461
+ - test/fixtures/report.json
454
462
  - test/functional/ansible_roles_controller_test.rb
455
- - test/functional/api/v2/ansible_variables_controller_test.rb
463
+ - test/functional/api/v2/ansible_inventories_controller_test.rb
456
464
  - test/functional/api/v2/ansible_roles_controller_test.rb
465
+ - test/functional/api/v2/ansible_variables_controller_test.rb
457
466
  - test/functional/api/v2/hostgroups_controller_test.rb
458
- - test/functional/api/v2/ansible_inventories_controller_test.rb
459
467
  - test/functional/api/v2/hosts_controller_test.rb
460
468
  - test/functional/ui_ansible_roles_controller_test.rb
469
+ - test/functional/ansible_variables_controller_test.rb
461
470
  - test/functional/hosts_controller_test.rb
462
- - test/graphql/mutations/hosts/assign_ansible_roles_mutation_test.rb
463
- - test/graphql/queries/ansible_roles_query_test.rb
464
- - test/foreman_ansible/helpers/ansible_roles_helper_test.rb
471
+ - test/test_plugin_helper.rb
465
472
  - test/unit/actions/run_ansible_job_test.rb
466
473
  - test/unit/actions/run_proxy_ansible_command_test.rb
467
- - test/unit/services/api_roles_importer_test.rb
468
- - test/unit/services/ui_roles_importer_test.rb
474
+ - test/unit/ansible_role_test.rb
475
+ - test/unit/concerns/config_reports_extensions_test.rb
476
+ - test/unit/concerns/host_managed_extensions_test.rb
477
+ - test/unit/concerns/hostgroup_extensions_test.rb
478
+ - test/unit/helpers/ansible_reports_helper_test.rb
479
+ - test/unit/lib/proxy_api/ansible_test.rb
469
480
  - test/unit/services/ansible_report_importer_test.rb
470
481
  - test/unit/services/insights_plan_runner_test.rb
471
- - test/unit/services/override_resolver_test.rb
472
482
  - test/unit/services/roles_importer_test.rb
473
- - test/unit/services/inventory_creator_test.rb
474
483
  - test/unit/services/ansible_variables_importer_test.rb
475
- - test/unit/lib/proxy_api/ansible_test.rb
476
- - test/unit/ansible_role_test.rb
477
- - test/unit/hostgroup_ansible_role_test.rb
484
+ - test/unit/services/api_roles_importer_test.rb
485
+ - test/unit/services/inventory_creator_test.rb
486
+ - test/unit/services/override_resolver_test.rb
487
+ - test/unit/services/ui_roles_importer_test.rb
488
+ - test/unit/ansible_provider_test.rb
478
489
  - test/unit/ansible_variable_test.rb
479
490
  - test/unit/host_ansible_role_test.rb
480
- - test/unit/helpers/ansible_reports_helper_test.rb
481
- - test/unit/ansible_provider_test.rb
491
+ - test/unit/hostgroup_ansible_role_test.rb
482
492
  - test/unit/ignore_roles_test.rb
483
493
  - test/unit/import_roles_and_variables.rb
484
- - test/unit/concerns/config_reports_extensions_test.rb
485
- - test/unit/concerns/host_managed_extensions_test.rb
486
- - test/unit/concerns/hostgroup_extensions_test.rb
487
- - test/factories/ansible_proxy.rb
488
- - test/factories/host_ansible_enhancements.rb
489
- - test/factories/ansible_variables.rb
490
- - test/factories/ansible_roles.rb
491
- - test/fixtures/report.json
492
- - test/fixtures/sample_facts.json
493
- - test/fixtures/insights_playbook.yaml
494
- - test/test_plugin_helper.rb
494
+ - test/foreman_ansible/helpers/ansible_roles_helper_test.rb
495
+ - test/graphql/mutations/hosts/assign_ansible_roles_mutation_test.rb
496
+ - test/graphql/queries/ansible_roles_query_test.rb
@@ -1,37 +0,0 @@
1
- <%#
2
- name: Configure Cloud Connector
3
- snippet: false
4
- template_inputs:
5
- - name: satellite_user
6
- required: true
7
- input_type: user
8
- advanced: false
9
- value_type: plain
10
- hidden_value: false
11
- - name: satellite_password
12
- required: true
13
- input_type: user
14
- advanced: false
15
- value_type: plain
16
- hidden_value: true
17
- - name: http_proxy
18
- required: false
19
- input_type: user
20
- advanced: true
21
- value_type: plain
22
- hidden_value: false
23
- description: You can specify a HTTP proxy address that should be used for Cloud Connector connection to the cloud.redhat.com. Note that it must be HTTP proxy, not HTTPS. The tunelling of SSL (secured web socket connection) in SSL (HTTPS proxy) is currently unsupported.
24
- model: JobTemplate
25
- job_category: Maintenance Operations
26
- description_format: "%{template_name}"
27
- provider_type: Ansible
28
- kind: job_template
29
- feature: ansible_configure_cloud_connector
30
- %>
31
-
32
- ---
33
- - hosts: all
34
- vars:
35
- satellite_url: "<%= foreman_server_url %>"
36
- roles:
37
- - project-receptor.satellite_receptor_installer