foreman_remote_execution 4.4.0 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/job_invocations_controller.rb +13 -24
  3. data/app/controllers/job_templates_controller.rb +4 -4
  4. data/app/controllers/ui_job_wizard_controller.rb +12 -0
  5. data/app/helpers/job_invocations_helper.rb +2 -2
  6. data/app/helpers/remote_execution_helper.rb +8 -8
  7. data/app/lib/actions/remote_execution/run_host_job.rb +31 -5
  8. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +5 -5
  9. data/app/models/host_status/execution_status.rb +5 -5
  10. data/app/models/job_invocation.rb +23 -7
  11. data/app/models/job_invocation_composer.rb +59 -17
  12. data/app/models/ssh_execution_provider.rb +4 -4
  13. data/app/overrides/execution_interface.rb +8 -8
  14. data/app/overrides/subnet_proxies.rb +6 -6
  15. data/config/routes.rb +1 -0
  16. data/db/migrate/20180110104432_rename_template_invocation_permission.rb +1 -1
  17. data/db/migrate/20190111153330_remove_remote_execution_without_proxy_setting.rb +4 -4
  18. data/extra/cockpit/foreman-cockpit-session +6 -6
  19. data/lib/foreman_remote_execution/engine.rb +11 -6
  20. data/lib/foreman_remote_execution/version.rb +1 -1
  21. data/package.json +2 -1
  22. data/test/functional/api/v2/job_invocations_controller_test.rb +14 -1
  23. data/test/unit/job_invocation_composer_test.rb +45 -1
  24. data/webpack/JobWizard/JobWizard.js +59 -18
  25. data/webpack/JobWizard/JobWizard.scss +3 -1
  26. data/webpack/JobWizard/JobWizardConstants.js +1 -0
  27. data/webpack/JobWizard/JobWizardSelectors.js +18 -1
  28. data/webpack/JobWizard/__tests__/JobWizard.test.js +4 -11
  29. data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +0 -51
  30. data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +43 -0
  31. data/webpack/JobWizard/__tests__/fixtures.js +26 -0
  32. data/webpack/JobWizard/__tests__/integration.test.js +156 -0
  33. data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +93 -0
  34. data/webpack/JobWizard/steps/AdvancedFields/Fields.js +181 -0
  35. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +25 -0
  36. data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +249 -0
  37. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +34 -2
  38. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +10 -3
  39. data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +50 -1
  40. data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +9 -1
  41. data/webpack/JobWizard/steps/form/FormHelpers.js +19 -0
  42. data/webpack/JobWizard/steps/form/GroupedSelectField.js +3 -0
  43. data/webpack/JobWizard/steps/form/SelectField.js +10 -1
  44. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +1 -0
  45. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +1 -0
  46. data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +21 -2
  47. data/webpack/global_index.js +5 -3
  48. data/webpack/index.js +3 -0
  49. data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +1 -5
  50. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +8 -3
  51. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +7 -2
  52. data/webpack/react_app/extend/{fills.js → fillRecentJobsCard.js} +7 -6
  53. data/webpack/react_app/extend/fillregistrationAdvanced.js +11 -0
  54. data/webpack/react_app/extend/reducers.js +2 -1
  55. metadata +12 -4
  56. data/webpack/fills_index.js +0 -11
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { Provider } from 'react-redux';
3
+ import configureMockStore from 'redux-mock-store';
4
+ import * as patternfly from '@patternfly/react-core';
5
+ import { mount } from '@theforeman/test';
6
+ import { AdvancedFields } from '../AdvancedFields';
7
+
8
+ jest.spyOn(patternfly, 'FormGroup');
9
+ patternfly.FormGroup.mockImplementation(props => (
10
+ <div>{props.navAriaLabel}</div>
11
+ ));
12
+ const mockStore = configureMockStore([]);
13
+ const store = mockStore({
14
+ JOB_TEMPLATE: { response: { effective_user: { overridable: true } } },
15
+ });
16
+ describe('AdvancedFields', () => {
17
+ it('rendring', () => {
18
+ const component = mount(
19
+ <Provider store={store}>
20
+ <AdvancedFields advancedValues={{}} setAdvancedValues={jest.fn()} />
21
+ </Provider>
22
+ );
23
+ expect(component).toMatchSnapshot();
24
+ });
25
+ });
@@ -0,0 +1,249 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`AdvancedFields rendring 1`] = `
4
+ <Provider
5
+ store={
6
+ Object {
7
+ "clearActions": [Function],
8
+ "dispatch": [Function],
9
+ "getActions": [Function],
10
+ "getState": [Function],
11
+ "replaceReducer": [Function],
12
+ "subscribe": [Function],
13
+ }
14
+ }
15
+ >
16
+ <AdvancedFields
17
+ advancedValues={Object {}}
18
+ setAdvancedValues={[MockFunction]}
19
+ >
20
+ <Title
21
+ className="advanced-fields-title"
22
+ headingLevel="h2"
23
+ >
24
+ <h2
25
+ className="pf-c-title pf-m-xl advanced-fields-title"
26
+ >
27
+ Advanced Fields
28
+ </h2>
29
+ </Title>
30
+ <Form>
31
+ <form
32
+ className="pf-c-form"
33
+ noValidate={true}
34
+ >
35
+ <EffectiveUserField
36
+ setValue={[Function]}
37
+ value=""
38
+ >
39
+ <mockConstructor
40
+ fieldId="effective-user"
41
+ label="Effective user"
42
+ labelIcon={
43
+ <Popover
44
+ aria-label="help-text"
45
+ bodyContent="A user to be used for executing the script. If it differs from the SSH user, su or sudo is used to switch the accounts."
46
+ id="effective-user-help"
47
+ >
48
+ <button
49
+ aria-label="open-help-tooltip-button"
50
+ className="pf-c-form__group-label-help"
51
+ onClick={[Function]}
52
+ >
53
+ <HelpIcon
54
+ color="currentColor"
55
+ noVerticalAlign={true}
56
+ size="sm"
57
+ />
58
+ </button>
59
+ </Popover>
60
+ }
61
+ >
62
+ <div />
63
+ </mockConstructor>
64
+ </EffectiveUserField>
65
+ <TimeoutToKillField
66
+ setValue={[Function]}
67
+ value=""
68
+ >
69
+ <mockConstructor
70
+ fieldId="timeout-to-kill"
71
+ label="Timeout to kill"
72
+ labelIcon={
73
+ <Popover
74
+ aria-label="help-text"
75
+ bodyContent="Time in seconds from the start on the remote host after which the job should be killed."
76
+ id="timeout-to-kill-help"
77
+ >
78
+ <button
79
+ aria-label="open-help-tooltip-button"
80
+ className="pf-c-form__group-label-help"
81
+ onClick={[Function]}
82
+ >
83
+ <HelpIcon
84
+ color="currentColor"
85
+ noVerticalAlign={true}
86
+ size="sm"
87
+ />
88
+ </button>
89
+ </Popover>
90
+ }
91
+ >
92
+ <div />
93
+ </mockConstructor>
94
+ </TimeoutToKillField>
95
+ <PasswordField
96
+ setValue={[Function]}
97
+ value=""
98
+ >
99
+ <mockConstructor
100
+ fieldId="password"
101
+ label="Password"
102
+ labelIcon={
103
+ <Popover
104
+ aria-label="help-text"
105
+ bodyContent="Password is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution."
106
+ id="password-help"
107
+ >
108
+ <button
109
+ aria-label="open-help-tooltip-button"
110
+ className="pf-c-form__group-label-help"
111
+ onClick={[Function]}
112
+ >
113
+ <HelpIcon
114
+ color="currentColor"
115
+ noVerticalAlign={true}
116
+ size="sm"
117
+ />
118
+ </button>
119
+ </Popover>
120
+ }
121
+ >
122
+ <div />
123
+ </mockConstructor>
124
+ </PasswordField>
125
+ <KeyPassphraseField
126
+ setValue={[Function]}
127
+ value=""
128
+ >
129
+ <mockConstructor
130
+ fieldId="key-passphrase"
131
+ label="Private key passphrase"
132
+ labelIcon={
133
+ <Popover
134
+ aria-label="help-text"
135
+ bodyContent="Key passphrase is only applicable for SSH provider. Other providers ignore this field. Passphrase is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution."
136
+ id="key-passphrase-help"
137
+ >
138
+ <button
139
+ aria-label="open-help-tooltip-button"
140
+ className="pf-c-form__group-label-help"
141
+ onClick={[Function]}
142
+ >
143
+ <HelpIcon
144
+ color="currentColor"
145
+ noVerticalAlign={true}
146
+ size="sm"
147
+ />
148
+ </button>
149
+ </Popover>
150
+ }
151
+ >
152
+ <div />
153
+ </mockConstructor>
154
+ </KeyPassphraseField>
155
+ <EffectiveUserPasswordField
156
+ setValue={[Function]}
157
+ value=""
158
+ >
159
+ <mockConstructor
160
+ fieldId="effective-user-password"
161
+ label="Effective user password"
162
+ labelIcon={
163
+ <Popover
164
+ aria-label="help-text"
165
+ bodyContent="Effective user password is only applicable for SSH provider. Other providers ignore this field. Password is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution."
166
+ id="effective-user-password-help"
167
+ >
168
+ <button
169
+ aria-label="open-help-tooltip-button"
170
+ className="pf-c-form__group-label-help"
171
+ onClick={[Function]}
172
+ >
173
+ <HelpIcon
174
+ color="currentColor"
175
+ noVerticalAlign={true}
176
+ size="sm"
177
+ />
178
+ </button>
179
+ </Popover>
180
+ }
181
+ >
182
+ <div />
183
+ </mockConstructor>
184
+ </EffectiveUserPasswordField>
185
+ <ConcurrencyLevelField
186
+ setValue={[Function]}
187
+ value=""
188
+ >
189
+ <mockConstructor
190
+ fieldId="concurrency-level"
191
+ label="Concurrency level"
192
+ labelIcon={
193
+ <Popover
194
+ aria-label="help-text"
195
+ bodyContent="Run at most N tasks at a time. If this is set and proxy batch triggering is enabled, then tasks are triggered on the smart proxy in batches of size 1."
196
+ id="concurrency-level-help"
197
+ >
198
+ <button
199
+ aria-label="open-help-tooltip-button"
200
+ className="pf-c-form__group-label-help"
201
+ onClick={[Function]}
202
+ >
203
+ <HelpIcon
204
+ color="currentColor"
205
+ noVerticalAlign={true}
206
+ size="sm"
207
+ />
208
+ </button>
209
+ </Popover>
210
+ }
211
+ >
212
+ <div />
213
+ </mockConstructor>
214
+ </ConcurrencyLevelField>
215
+ <TimeSpanLevelField
216
+ setValue={[Function]}
217
+ value=""
218
+ >
219
+ <mockConstructor
220
+ fieldId="time-span"
221
+ label="Time span"
222
+ labelIcon={
223
+ <Popover
224
+ aria-label="help-text"
225
+ bodyContent="Distribute execution over N seconds. If this is set and proxy batch triggering is enabled, then tasks are triggered on the smart proxy in batches of size 1."
226
+ id="time-span-help"
227
+ >
228
+ <button
229
+ aria-label="open-help-tooltip-button"
230
+ className="pf-c-form__group-label-help"
231
+ onClick={[Function]}
232
+ >
233
+ <HelpIcon
234
+ color="currentColor"
235
+ noVerticalAlign={true}
236
+ size="sm"
237
+ />
238
+ </button>
239
+ </Popover>
240
+ }
241
+ >
242
+ <div />
243
+ </mockConstructor>
244
+ </TimeSpanLevelField>
245
+ </form>
246
+ </Form>
247
+ </AdvancedFields>
248
+ </Provider>
249
+ `;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Title, Text, TextVariants, Form } from '@patternfly/react-core';
3
+ import { Title, Text, TextVariants, Form, Alert } from '@patternfly/react-core';
4
4
  import { translate as __ } from 'foremanReact/common/I18n';
5
5
  import { SelectField } from '../form/SelectField';
6
6
  import { GroupedSelectField } from '../form/GroupedSelectField';
@@ -12,6 +12,7 @@ export const CategoryAndTemplate = ({
12
12
  selectedTemplateID,
13
13
  selectedCategory,
14
14
  setCategory,
15
+ errors,
15
16
  }) => {
16
17
  const templatesGroups = {};
17
18
  jobTemplates.forEach(template => {
@@ -35,9 +36,11 @@ export const CategoryAndTemplate = ({
35
36
  setCategory(newCategory);
36
37
  setJobTemplate(null);
37
38
  };
39
+ const { categoryError, allTemplatesError, templateError } = errors;
40
+ const isError = !!(categoryError || allTemplatesError || templateError);
38
41
  return (
39
42
  <>
40
- <Title headingLevel="h2">{__('Category And Template')}</Title>
43
+ <Title headingLevel="h2">{__('Category and Template')}</Title>
41
44
  <Text component={TextVariants.p}>{__('All fields are required.')}</Text>
42
45
  <Form>
43
46
  <SelectField
@@ -46,6 +49,8 @@ export const CategoryAndTemplate = ({
46
49
  options={jobCategories}
47
50
  setValue={onSelectCategory}
48
51
  value={selectedCategory}
52
+ placeholderText={categoryError ? __('Error') : ''}
53
+ isDisabled={!!categoryError}
49
54
  />
50
55
  <GroupedSelectField
51
56
  label={__('Job template')}
@@ -53,7 +58,28 @@ export const CategoryAndTemplate = ({
53
58
  groups={Object.values(templatesGroups)}
54
59
  setSelected={setJobTemplate}
55
60
  selected={selectedTemplate}
61
+ isDisabled={!!(categoryError || allTemplatesError)}
62
+ placeholderText={allTemplatesError ? __('Error') : ''}
56
63
  />
64
+ {isError && (
65
+ <Alert variant="danger" title={__('Errors:')}>
66
+ {categoryError && (
67
+ <span>
68
+ {__('Categories list failed with:')} {categoryError}
69
+ </span>
70
+ )}
71
+ {allTemplatesError && (
72
+ <span>
73
+ {__('Templates list failed with:')} {allTemplatesError}
74
+ </span>
75
+ )}
76
+ {templateError && (
77
+ <span>
78
+ {__('Template failed with:')} {templateError}
79
+ </span>
80
+ )}
81
+ </Alert>
82
+ )}
57
83
  </Form>
58
84
  </>
59
85
  );
@@ -66,12 +92,18 @@ CategoryAndTemplate.propTypes = {
66
92
  selectedTemplateID: PropTypes.number,
67
93
  setCategory: PropTypes.func.isRequired,
68
94
  selectedCategory: PropTypes.string,
95
+ errors: PropTypes.shape({
96
+ categoryError: PropTypes.string,
97
+ allTemplatesError: PropTypes.string,
98
+ templateError: PropTypes.string,
99
+ }),
69
100
  };
70
101
  CategoryAndTemplate.defaultProps = {
71
102
  jobCategories: [],
72
103
  jobTemplates: [],
73
104
  selectedTemplateID: null,
74
105
  selectedCategory: null,
106
+ errors: {},
75
107
  };
76
108
 
77
109
  export default CategoryAndTemplate;
@@ -1,8 +1,14 @@
1
1
  import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
2
2
  import { CategoryAndTemplate } from './CategoryAndTemplate';
3
3
 
4
+ const baseProps = {
5
+ setJobTemplate: jest.fn(),
6
+ selectedTemplateID: 190,
7
+ setCategory: jest.fn(),
8
+ };
4
9
  const fixtures = {
5
10
  'renders with props': {
11
+ ...baseProps,
6
12
  jobCategories: [
7
13
  'Commands',
8
14
  'Ansible Playbook',
@@ -32,11 +38,12 @@ const fixtures = {
32
38
  snippet: false,
33
39
  },
34
40
  ],
35
- setJobTemplate: jest.fn(),
36
- selectedTemplateID: 190,
37
- setCategory: jest.fn(),
38
41
  selectedCategory: 'I am a category',
39
42
  },
43
+ 'render with error': {
44
+ ...baseProps,
45
+ errors: { allTemplatesError: 'I have an error' },
46
+ },
40
47
  };
41
48
 
42
49
  describe('CategoryAndTemplate', () => {
@@ -1,11 +1,56 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
+ exports[`CategoryAndTemplate rendering render with error 1`] = `
4
+ <Fragment>
5
+ <Title
6
+ headingLevel="h2"
7
+ >
8
+ Category and Template
9
+ </Title>
10
+ <Text
11
+ component="p"
12
+ >
13
+ All fields are required.
14
+ </Text>
15
+ <Form>
16
+ <SelectField
17
+ fieldId="job_category"
18
+ isDisabled={false}
19
+ label="Job category"
20
+ options={Array []}
21
+ placeholderText=""
22
+ setValue={[Function]}
23
+ value={null}
24
+ />
25
+ <GroupedSelectField
26
+ fieldId="job_template"
27
+ groups={Array []}
28
+ isDisabled={true}
29
+ label="Job template"
30
+ placeholderText="Error"
31
+ selected={null}
32
+ setSelected={[MockFunction]}
33
+ />
34
+ <Alert
35
+ title="Errors:"
36
+ variant="danger"
37
+ >
38
+ <span>
39
+ Templates list failed with:
40
+
41
+ I have an error
42
+ </span>
43
+ </Alert>
44
+ </Form>
45
+ </Fragment>
46
+ `;
47
+
3
48
  exports[`CategoryAndTemplate rendering renders with props 1`] = `
4
49
  <Fragment>
5
50
  <Title
6
51
  headingLevel="h2"
7
52
  >
8
- Category And Template
53
+ Category and Template
9
54
  </Title>
10
55
  <Text
11
56
  component="p"
@@ -15,6 +60,7 @@ exports[`CategoryAndTemplate rendering renders with props 1`] = `
15
60
  <Form>
16
61
  <SelectField
17
62
  fieldId="job_category"
63
+ isDisabled={false}
18
64
  label="Job category"
19
65
  options={
20
66
  Array [
@@ -24,6 +70,7 @@ exports[`CategoryAndTemplate rendering renders with props 1`] = `
24
70
  "Ansible Roles Installation",
25
71
  ]
26
72
  }
73
+ placeholderText=""
27
74
  setValue={[Function]}
28
75
  value="I am a category"
29
76
  />
@@ -55,7 +102,9 @@ exports[`CategoryAndTemplate rendering renders with props 1`] = `
55
102
  },
56
103
  ]
57
104
  }
105
+ isDisabled={false}
58
106
  label="Job template"
107
+ placeholderText=""
59
108
  selected="ab Run Command - SSH Default clone"
60
109
  setSelected={[MockFunction]}
61
110
  />