foreman_ansible 6.3.3 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/ansible_inventories_controller.rb +1 -1
  3. data/app/graphql/mutations/ansible_variable_overrides/create.rb +26 -0
  4. data/app/graphql/mutations/ansible_variable_overrides/delete.rb +38 -0
  5. data/app/graphql/mutations/ansible_variable_overrides/update.rb +26 -0
  6. data/app/graphql/mutations/hosts/assign_ansible_roles.rb +37 -0
  7. data/app/graphql/presenters/ansible_role_presenter.rb +12 -0
  8. data/app/graphql/presenters/overriden_ansible_variable_presenter.rb +19 -0
  9. data/app/graphql/types/ansible_role.rb +9 -0
  10. data/app/graphql/types/ansible_variable.rb +23 -0
  11. data/app/graphql/types/ansible_variable_override.rb +9 -0
  12. data/app/graphql/types/inherited_ansible_role.rb +13 -0
  13. data/app/graphql/types/overriden_ansible_variable.rb +27 -0
  14. data/app/helpers/foreman_ansible/ansible_reports_helper.rb +35 -54
  15. data/app/models/concerns/foreman_ansible/host_managed_extensions.rb +23 -4
  16. data/app/models/concerns/foreman_ansible/hostgroup_extensions.rb +1 -0
  17. data/app/models/foreman_ansible/ansible_provider.rb +56 -6
  18. data/app/services/foreman_ansible/ansible_report_importer.rb +2 -2
  19. data/app/services/foreman_ansible/inventory_creator.rb +1 -1
  20. data/app/services/foreman_ansible/override_resolver.rb +22 -0
  21. data/app/views/api/v2/ansible_override_values/index.json.rabl +3 -0
  22. data/app/views/api/v2/ansible_variables/show.json.rabl +1 -1
  23. data/app/views/foreman_ansible/ansible_roles/_hostgroup_ansible_roles_button.erb +3 -0
  24. data/app/views/foreman_ansible/config_reports/_ansible.html.erb +14 -5
  25. data/app/views/foreman_ansible/job_templates/ansible_roles_-_ansible_default.erb +4 -0
  26. data/app/views/foreman_ansible/job_templates/convert_to_rhel.erb +6 -2
  27. data/app/views/foreman_ansible/job_templates/run_openscap_scans_-_ansible_default.erb +20 -0
  28. data/config/routes.rb +3 -0
  29. data/db/migrate/20210818083407_fix_ansible_setting_category_to_dsl.rb +5 -0
  30. data/lib/foreman_ansible/engine.rb +0 -18
  31. data/lib/foreman_ansible/register.rb +114 -2
  32. data/lib/foreman_ansible/version.rb +1 -1
  33. data/package.json +10 -6
  34. data/test/functional/api/v2/ansible_inventories_controller_test.rb +1 -2
  35. data/test/graphql/mutations/hosts/assign_ansible_roles_mutation_test.rb +96 -0
  36. data/test/graphql/queries/ansible_roles_query_test.rb +35 -0
  37. data/test/unit/ansible_provider_test.rb +3 -6
  38. data/test/unit/concerns/host_managed_extensions_test.rb +8 -0
  39. data/test/unit/concerns/hostgroup_extensions_test.rb +6 -0
  40. data/test/unit/helpers/ansible_reports_helper_test.rb +4 -30
  41. data/test/unit/services/override_resolver_test.rb +34 -0
  42. data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.js +59 -0
  43. data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.scss +6 -0
  44. data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.test.js +20 -0
  45. data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.js +22 -0
  46. data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.scss +4 -0
  47. data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.test.js +104 -0
  48. data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/index.js +38 -0
  49. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverrides.scss +3 -0
  50. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTable.js +238 -0
  51. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTableHelper.js +111 -0
  52. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.js +161 -0
  53. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.scss +7 -0
  54. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableActionHelper.js +49 -0
  55. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValue.js +70 -0
  56. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValueHelper.js +35 -0
  57. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.fixtures.js +429 -0
  58. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.test.js +71 -0
  59. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesDelete.test.js +74 -0
  60. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesUpdate.test.js +188 -0
  61. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/index.js +58 -0
  62. data/webpack/components/AnsibleHostDetail/components/JobsTab/JobsTabHelper.js +79 -0
  63. data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobHelper.js +106 -0
  64. data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.js +129 -0
  65. data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.scss +7 -0
  66. data/webpack/components/AnsibleHostDetail/components/JobsTab/PreviousJobsTable.js +103 -0
  67. data/webpack/components/AnsibleHostDetail/components/JobsTab/RecurringJobsTable.js +96 -0
  68. data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.fixtures.js +184 -0
  69. data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.test.js +195 -0
  70. data/webpack/components/AnsibleHostDetail/components/JobsTab/index.js +88 -0
  71. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/AllRolesTable.js +89 -0
  72. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js +80 -0
  73. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesForm.js +90 -0
  74. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesModal.scss +3 -0
  75. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesModalHelper.js +40 -0
  76. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/index.js +82 -0
  77. data/webpack/components/AnsibleHostDetail/components/RolesTab/RolesTable.js +129 -0
  78. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/EditRoles.test.js +85 -0
  79. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.fixtures.js +180 -0
  80. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.test.js +75 -0
  81. data/webpack/components/AnsibleHostDetail/components/RolesTab/index.js +51 -0
  82. data/webpack/components/AnsibleHostDetail/components/SecondaryTabRoutes.js +60 -0
  83. data/webpack/components/AnsibleHostDetail/components/TabLayout.js +12 -0
  84. data/webpack/components/AnsibleHostDetail/constants.js +9 -0
  85. data/webpack/components/AnsibleHostDetail/helpers.js +4 -0
  86. data/webpack/components/AnsibleHostDetail/index.js +6 -0
  87. data/webpack/components/AnsibleRolesAndVariables/__test__/AnsibleRolesAndVariablesImport.test.js +15 -10
  88. data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.js +29 -0
  89. data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.test.js +3 -0
  90. data/webpack/components/AnsibleRolesSwitcher/components/AvailableRolesList.js +2 -1
  91. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsiblePermissionDenied.test.js.snap +2 -0
  92. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsibleRole.test.js.snap +3 -3
  93. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AssignedRolesList.test.js.snap +4 -4
  94. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AvailableRolesList.test.js.snap +9 -0
  95. data/webpack/components/DualList/DualList.scss +3 -0
  96. data/webpack/components/DualList/ListControls.js +65 -0
  97. data/webpack/components/DualList/ListHeader.js +16 -0
  98. data/webpack/components/DualList/ListItem.js +69 -0
  99. data/webpack/components/DualList/ListPane.js +95 -0
  100. data/webpack/components/DualList/SelectedStatus.js +21 -0
  101. data/webpack/components/DualList/index.js +103 -0
  102. data/webpack/components/ErrorState.js +16 -0
  103. data/webpack/components/withLoading.js +135 -0
  104. data/webpack/components/withPagination.js +0 -0
  105. data/webpack/formHelper.js +131 -0
  106. data/webpack/globalIdHelper.js +13 -0
  107. data/webpack/global_index.js +18 -0
  108. data/webpack/graphql/mutations/assignAnsibleRoles.gql +17 -0
  109. data/webpack/graphql/mutations/cancelRecurringLogic.gql +12 -0
  110. data/webpack/graphql/mutations/createAnsibleVariableOverride.gql +28 -0
  111. data/webpack/graphql/mutations/createJobInvocation.gql +11 -0
  112. data/webpack/graphql/mutations/deleteAnsibleVariableOverride.gql +17 -0
  113. data/webpack/graphql/mutations/updateAnsibleVariableOverride.gql +29 -0
  114. data/webpack/graphql/queries/allAnsibleRoles.gql +13 -0
  115. data/webpack/graphql/queries/ansibleRoles.gql +13 -0
  116. data/webpack/graphql/queries/currentUserAttributes.gql +11 -0
  117. data/webpack/graphql/queries/hostAnsibleRoles.gql +17 -0
  118. data/webpack/graphql/queries/hostAvailableAnsibleRoles.gql +11 -0
  119. data/webpack/graphql/queries/hostVariableOverrides.gql +39 -0
  120. data/webpack/graphql/queries/recurringJobs.gql +28 -0
  121. data/webpack/helpers/pageParamsHelper.js +40 -0
  122. data/webpack/helpers/paginationHelper.js +9 -0
  123. data/webpack/permissionsHelper.js +58 -0
  124. data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.fixtures.js +63 -0
  125. data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.test.js +112 -0
  126. data/webpack/routes/HostgroupJobs/index.js +26 -0
  127. data/webpack/routes/routes.js +10 -0
  128. data/webpack/testHelper.js +165 -0
  129. data/webpack/toastHelper.js +4 -0
  130. metadata +130 -78
  131. data/app/assets/images/foreman_ansible/Ansible.png +0 -0
  132. data/app/models/foreman_ansible/fact_name.rb +0 -16
  133. data/app/models/setting/ansible.rb +0 -106
  134. data/app/services/foreman_ansible/fact_importer.rb +0 -99
  135. data/app/services/foreman_ansible/fact_parser.rb +0 -126
  136. data/app/services/foreman_ansible/fact_sparser.rb +0 -37
  137. data/app/services/foreman_ansible/operating_system_parser.rb +0 -102
  138. data/app/services/foreman_ansible/structured_fact_importer.rb +0 -25
  139. data/test/unit/lib/foreman_ansible_core/ansible_runner_test.rb +0 -51
  140. data/test/unit/lib/foreman_ansible_core/command_creator_test.rb +0 -64
  141. data/test/unit/lib/foreman_ansible_core/playbook_runner_test.rb +0 -110
  142. data/test/unit/services/fact_importer_test.rb +0 -52
  143. data/test/unit/services/fact_parser_test.rb +0 -281
  144. data/test/unit/services/fact_sparser_test.rb +0 -24
  145. data/test/unit/services/structured_fact_importer_test.rb +0 -30
  146. data/webpack/__mocks__/foremanReact/common/I18n.js +0 -1
  147. data/webpack/__mocks__/foremanReact/common/helpers.js +0 -13
  148. data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +0 -2
  149. data/webpack/__mocks__/foremanReact/components/common/EmptyState.js +0 -5
  150. data/webpack/__mocks__/foremanReact/components/common/forms/OrderableSelect/helpers.js +0 -5
  151. data/webpack/__mocks__/foremanReact/redux/API.js +0 -7
  152. data/webpack/components/AnsibleRolesAndVariables/__test__/__snapshots__/AnsibleRolesAndVariablesImport.test.js.snap +0 -177
@@ -0,0 +1,161 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useMutation } from '@apollo/client';
4
+ import classNames from 'classnames';
5
+
6
+ import { Button, Spinner } from '@patternfly/react-core';
7
+ import { TimesIcon, CheckIcon, PencilAltIcon } from '@patternfly/react-icons';
8
+
9
+ import './EditableAction.scss';
10
+
11
+ import { decodeModelId } from '../../../../globalIdHelper';
12
+ import updateAnsibleVariableOverrideMutation from '../../../../graphql/mutations/updateAnsibleVariableOverride.gql';
13
+ import createAnsibleVariableOverrideMutation from '../../../../graphql/mutations/createAnsibleVariableOverride.gql';
14
+ import {
15
+ onCompleted,
16
+ onError,
17
+ hasError,
18
+ createMatcher,
19
+ } from './EditableActionHelper';
20
+
21
+ const EditableAction = ({
22
+ onValidationError,
23
+ toggleWorking,
24
+ onSubmitSuccess,
25
+ open,
26
+ onClose,
27
+ onOpen,
28
+ state,
29
+ variable,
30
+ hostId,
31
+ hostName,
32
+ }) => {
33
+ const [callUpdateMutation] = useMutation(
34
+ updateAnsibleVariableOverrideMutation,
35
+ {
36
+ onCompleted: onCompleted(
37
+ 'updateAnsibleVariableOverride',
38
+ onValidationError,
39
+ toggleWorking,
40
+ onSubmitSuccess
41
+ ),
42
+ onError: onError(toggleWorking),
43
+ }
44
+ );
45
+
46
+ const [callCreateMutation] = useMutation(
47
+ createAnsibleVariableOverrideMutation,
48
+ {
49
+ onCompleted: onCompleted(
50
+ 'createAnsibleVariableOverride',
51
+ onValidationError,
52
+ toggleWorking,
53
+ onSubmitSuccess
54
+ ),
55
+ onError: onError(toggleWorking),
56
+ }
57
+ );
58
+
59
+ const onSubmit = event => {
60
+ if (!variable.currentValue || variable.currentValue.element !== 'fqdn') {
61
+ return createOverride();
62
+ }
63
+ return updateOverride();
64
+ };
65
+
66
+ const updateOverride = () => {
67
+ const match = createMatcher(variable.currentValue.elementName);
68
+ const lookupValue = variable.lookupValues.nodes.find(
69
+ item => item.match === match
70
+ );
71
+ toggleWorking(true);
72
+ callUpdateMutation({
73
+ variables: {
74
+ id: lookupValue.id,
75
+ value: state.value,
76
+ hostId,
77
+ ansibleVariableId: decodeModelId(variable),
78
+ match,
79
+ },
80
+ });
81
+ };
82
+
83
+ const createOverride = () => {
84
+ const match = createMatcher(hostName);
85
+ toggleWorking(true);
86
+ callCreateMutation({
87
+ variables: {
88
+ hostId,
89
+ lookupKeyId: decodeModelId(variable),
90
+ value: state.value,
91
+ match,
92
+ },
93
+ });
94
+ };
95
+
96
+ if (!variable.meta.canEdit) {
97
+ return null;
98
+ }
99
+
100
+ return (
101
+ <React.Fragment>
102
+ <div>
103
+ <div
104
+ className={classNames({
105
+ hideElement: !open,
106
+ editableActionItem: true,
107
+ })}
108
+ >
109
+ <Button
110
+ variant="plain"
111
+ onClick={onClose}
112
+ isDisabled={state.working}
113
+ aria-label="Cancel editing override button"
114
+ >
115
+ <TimesIcon />
116
+ </Button>
117
+ <Button
118
+ variant="plain"
119
+ onClick={onSubmit}
120
+ isDisabled={state.working || hasError(state)}
121
+ aria-label="Submit editing override button"
122
+ >
123
+ <CheckIcon />
124
+ </Button>
125
+ <span className={!state.working ? 'hideElement' : ''}>
126
+ <Spinner size="md" />
127
+ </span>
128
+ </div>
129
+ <div
130
+ className={classNames({
131
+ hideElement: open,
132
+ editableActionItem: true,
133
+ })}
134
+ >
135
+ <Button
136
+ onClick={onOpen}
137
+ variant="plain"
138
+ aria-label="Edit override button"
139
+ >
140
+ <PencilAltIcon />
141
+ </Button>
142
+ </div>
143
+ </div>
144
+ </React.Fragment>
145
+ );
146
+ };
147
+
148
+ EditableAction.propTypes = {
149
+ onValidationError: PropTypes.func.isRequired,
150
+ toggleWorking: PropTypes.func.isRequired,
151
+ onSubmitSuccess: PropTypes.func.isRequired,
152
+ onClose: PropTypes.func.isRequired,
153
+ onOpen: PropTypes.func.isRequired,
154
+ variable: PropTypes.object.isRequired,
155
+ state: PropTypes.object.isRequired,
156
+ open: PropTypes.bool.isRequired,
157
+ hostId: PropTypes.number.isRequired,
158
+ hostName: PropTypes.string.isRequired,
159
+ };
160
+
161
+ export default EditableAction;
@@ -0,0 +1,7 @@
1
+ .hideElement {
2
+ display: none;
3
+ }
4
+
5
+ .editableActionItem {
6
+ width: 150px;
7
+ }
@@ -0,0 +1,49 @@
1
+ import { translate as __, sprintf } from 'foremanReact/common/I18n';
2
+ import { showToast } from '../../../../toastHelper';
3
+
4
+ export const formatError = error =>
5
+ sprintf(
6
+ __(
7
+ 'There was a following error when changing Ansible variable override: %s'
8
+ ),
9
+ error
10
+ );
11
+
12
+ export const joinErrors = errors => errors.map(err => err.message).join(', ');
13
+
14
+ export const onCompleted = (
15
+ dataPath,
16
+ onValidationError,
17
+ toggleWorking,
18
+ onSubmitSuccess
19
+ ) => data => {
20
+ const { errors, overridenAnsibleVariable } = data[dataPath];
21
+ if (Array.isArray(errors) && errors.length > 0) {
22
+ if (
23
+ errors.length === 1 &&
24
+ errors[0].path.join(' ') === 'attributes value'
25
+ ) {
26
+ onValidationError(errors[0].message);
27
+ } else {
28
+ toggleWorking(false);
29
+ showToast({
30
+ type: 'error',
31
+ message: formatError(joinErrors(errors)),
32
+ });
33
+ }
34
+ } else {
35
+ onSubmitSuccess(overridenAnsibleVariable.currentValue.value);
36
+ showToast({
37
+ type: 'success',
38
+ message: __('Ansible variable override successfully changed.'),
39
+ });
40
+ }
41
+ };
42
+
43
+ export const onError = toggleWorking => error => {
44
+ toggleWorking(false);
45
+ showToast({ type: 'error', message: formatError(error.message) });
46
+ };
47
+
48
+ export const hasError = state => state.validation.key === 'error';
49
+ export const createMatcher = value => `fqdn=${value}`;
@@ -0,0 +1,70 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { translate as __ } from 'foremanReact/common/I18n';
4
+
5
+ import { formatValue } from './AnsibleVariableOverridesTableHelper';
6
+
7
+ import {
8
+ TextAreaField,
9
+ TextInputField,
10
+ SelectField,
11
+ } from './EditableValueHelper';
12
+
13
+ const EditableValue = props => {
14
+ if (!props.editing) {
15
+ return formatValue(props.variable);
16
+ }
17
+
18
+ const type = props.variable.parameterType;
19
+
20
+ if (type === 'array' || type === 'hash') {
21
+ return (
22
+ <TextAreaField
23
+ onChange={props.onChange}
24
+ value={props.value}
25
+ validation={props.validation}
26
+ isDisabled={props.working}
27
+ />
28
+ );
29
+ }
30
+
31
+ if (type === 'boolean') {
32
+ return (
33
+ <SelectField
34
+ selectItems={[
35
+ { id: 'trueSelectOpt', value: true, name: __('true') },
36
+ { id: 'falseSelectOpt', value: false, name: __('false') },
37
+ ]}
38
+ onChange={props.onChange}
39
+ validation={props.validation}
40
+ isDisabled={props.working}
41
+ value={props.value}
42
+ />
43
+ );
44
+ }
45
+
46
+ return (
47
+ <TextInputField
48
+ onChange={props.onChange}
49
+ value={props.value}
50
+ validation={props.validation}
51
+ isDisabled={props.working}
52
+ aria-label="Edit override field"
53
+ />
54
+ );
55
+ };
56
+
57
+ EditableValue.propTypes = {
58
+ editing: PropTypes.bool.isRequired,
59
+ variable: PropTypes.object.isRequired,
60
+ onChange: PropTypes.func.isRequired,
61
+ value: PropTypes.any,
62
+ validation: PropTypes.object.isRequired,
63
+ working: PropTypes.bool.isRequired,
64
+ };
65
+
66
+ EditableValue.defaultProps = {
67
+ value: '',
68
+ };
69
+
70
+ export default EditableValue;
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import {
3
+ TextInput,
4
+ TextArea,
5
+ FormGroup,
6
+ FormSelect,
7
+ FormSelectOption,
8
+ } from '@patternfly/react-core';
9
+
10
+ const withFormGroup = Component => componentProps => {
11
+ const { validation, ...rest } = componentProps;
12
+ return (
13
+ <FormGroup
14
+ label=""
15
+ helperTextInvalid={validation.msg}
16
+ validated={validation.key}
17
+ >
18
+ <Component {...rest} validated={validation.key} />
19
+ </FormGroup>
20
+ );
21
+ };
22
+
23
+ export const SelectField = componentProps => {
24
+ const { selectItems, ...rest } = componentProps;
25
+ return (
26
+ <FormSelect className="without_select2" {...rest}>
27
+ {selectItems.map(item => (
28
+ <FormSelectOption key={item.id} value={item.value} label={item.name} />
29
+ ))}
30
+ </FormSelect>
31
+ );
32
+ };
33
+
34
+ export const TextInputField = withFormGroup(TextInput);
35
+ export const TextAreaField = withFormGroup(TextArea);