foreman_remote_execution 9.0.1 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +14 -0
- data/.tx/config +3 -1
- data/app/assets/javascripts/foreman_remote_execution/locale/de/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/en/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/en_GB/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/es/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/fr/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ja/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ko/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/pt_BR/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/ru/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/zh_CN/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/locale/zh_TW/foreman_remote_execution.js +1 -0
- data/app/assets/javascripts/foreman_remote_execution/template_invocation.js +10 -1
- data/app/controllers/api/v2/job_invocations_controller.rb +1 -0
- data/app/controllers/job_invocations_controller.rb +30 -1
- data/app/controllers/ui_job_wizard_controller.rb +3 -1
- data/app/helpers/remote_execution_helper.rb +1 -1
- data/app/lib/actions/remote_execution/run_host_job.rb +1 -1
- data/app/lib/actions/remote_execution/run_hosts_job.rb +28 -2
- data/app/models/remote_execution_feature.rb +11 -8
- data/app/views/api/v2/job_invocations/base.json.rabl +1 -1
- data/app/views/job_invocations/_form.html.erb +1 -1
- data/app/views/job_invocations/show.html.erb +1 -1
- data/app/views/job_invocations/welcome.html.erb +1 -1
- data/app/views/templates/script/run_command.erb +1 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20210816100932_rex_setting_category_to_dsl.rb +1 -1
- data/db/migrate/20220426145007_add_unique_feature_label_index.rb +14 -0
- data/lib/foreman_remote_execution/engine.rb +9 -9
- data/lib/foreman_remote_execution/tasks/explain_proxy_selection.rake +12 -3
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/Makefile +6 -3
- data/locale/action_names.rb +1 -1
- data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/de/foreman_remote_execution.po +67 -19
- data/locale/en/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/en/foreman_remote_execution.po +56 -8
- data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/en_GB/foreman_remote_execution.po +58 -10
- data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/es/foreman_remote_execution.po +71 -23
- data/locale/foreman_remote_execution.pot +216 -141
- data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/fr/foreman_remote_execution.po +104 -56
- data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ja/foreman_remote_execution.po +72 -24
- data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ko/foreman_remote_execution.po +63 -15
- data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/pt_BR/foreman_remote_execution.po +68 -20
- data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ru/foreman_remote_execution.po +63 -15
- data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_CN/foreman_remote_execution.po +71 -23
- data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_TW/foreman_remote_execution.po +63 -15
- data/package.json +6 -6
- data/webpack/JobWizard/Footer.js +104 -0
- data/webpack/JobWizard/JobWizard.js +105 -32
- data/webpack/JobWizard/JobWizard.scss +6 -17
- data/webpack/JobWizard/JobWizardConstants.js +1 -1
- data/webpack/JobWizard/JobWizardPageRerun.js +2 -0
- data/webpack/JobWizard/StartsBeforeErrorAlert.js +17 -0
- data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +8 -0
- data/webpack/JobWizard/__tests__/fixtures.js +13 -1
- data/webpack/JobWizard/__tests__/integration.test.js +15 -0
- data/webpack/JobWizard/__tests__/validation.test.js +36 -0
- data/webpack/JobWizard/autofill.js +1 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +29 -10
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +8 -0
- data/webpack/JobWizard/steps/HostsAndInputs/HostPreviewModal.js +5 -0
- data/webpack/JobWizard/steps/HostsAndInputs/SelectedChips.js +28 -14
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/HostsAndInputs.test.js +41 -4
- data/webpack/JobWizard/steps/HostsAndInputs/buildHostQuery.js +16 -10
- data/webpack/JobWizard/steps/HostsAndInputs/index.js +51 -3
- data/webpack/JobWizard/steps/ReviewDetails/ReviewDetails.test.js +117 -0
- data/webpack/JobWizard/steps/ReviewDetails/helpers.js +43 -0
- data/webpack/JobWizard/steps/ReviewDetails/index.js +169 -18
- data/webpack/JobWizard/steps/Schedule/QueryType.js +1 -1
- data/webpack/JobWizard/steps/Schedule/RepeatHour.js +0 -1
- data/webpack/JobWizard/steps/Schedule/RepeatWeek.js +1 -1
- data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +52 -18
- data/webpack/JobWizard/steps/form/DateTimePicker.js +1 -2
- data/webpack/JobWizard/steps/form/GroupedSelectField.js +0 -1
- data/webpack/JobWizard/steps/form/SelectField.js +0 -1
- data/webpack/JobWizard/submit.js +14 -3
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +4 -4
- data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +2 -2
- data/webpack/react_app/components/RecentJobsCard/constants.js +2 -2
- data/webpack/react_app/components/RegistrationExtension/RexPull.js +0 -2
- metadata +23 -6
@@ -1,3 +1,4 @@
|
|
1
|
+
/* eslint-disable max-lines */
|
1
2
|
import React, { useEffect, useState } from 'react';
|
2
3
|
import {
|
3
4
|
Button,
|
@@ -5,12 +6,12 @@ import {
|
|
5
6
|
DescriptionListTerm,
|
6
7
|
DescriptionListGroup,
|
7
8
|
DescriptionListDescription,
|
9
|
+
WizardContextConsumer,
|
8
10
|
} from '@patternfly/react-core';
|
9
11
|
import PropTypes from 'prop-types';
|
10
12
|
import { useDispatch, useSelector } from 'react-redux';
|
11
|
-
import EllipsisWithTooltip from 'react-ellipsis-with-tooltip';
|
12
13
|
import { get } from 'foremanReact/redux/API';
|
13
|
-
import { translate as __
|
14
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
14
15
|
import {
|
15
16
|
selectJobTemplates,
|
16
17
|
selectHosts,
|
@@ -22,9 +23,12 @@ import {
|
|
22
23
|
HOSTS_API,
|
23
24
|
HOSTS_TO_PREVIEW_AMOUNT,
|
24
25
|
WIZARD_TITLES,
|
26
|
+
SCHEDULE_TYPES,
|
25
27
|
} from '../../JobWizardConstants';
|
26
28
|
import { buildHostQuery } from '../HostsAndInputs/buildHostQuery';
|
27
29
|
import { WizardTitle } from '../form/WizardTitle';
|
30
|
+
import { parseEnd, parseRepeat } from './helpers';
|
31
|
+
import { HostPreviewModal } from '../HostsAndInputs/HostPreviewModal';
|
28
32
|
|
29
33
|
const ReviewDetails = ({
|
30
34
|
jobCategory,
|
@@ -34,7 +38,20 @@ const ReviewDetails = ({
|
|
34
38
|
templateValues,
|
35
39
|
selectedTargets,
|
36
40
|
hostsSearchQuery,
|
41
|
+
goToStepByName,
|
37
42
|
}) => {
|
43
|
+
// eslint-disable-next-line react/prop-types
|
44
|
+
const StepButton = ({ stepName, children }) => (
|
45
|
+
<Button
|
46
|
+
variant="link"
|
47
|
+
isInline
|
48
|
+
onClick={() => {
|
49
|
+
goToStepByName(stepName);
|
50
|
+
}}
|
51
|
+
>
|
52
|
+
{children}
|
53
|
+
</Button>
|
54
|
+
);
|
38
55
|
const dispatch = useDispatch();
|
39
56
|
useEffect(() => {
|
40
57
|
dispatch(
|
@@ -58,6 +75,7 @@ const ReviewDetails = ({
|
|
58
75
|
const hosts = useSelector(selectHosts);
|
59
76
|
|
60
77
|
const hostsCount = useSelector(selectHostCount);
|
78
|
+
const [hostPreviewOpen, setHostPreviewOpen] = useState(false);
|
61
79
|
const stringHosts = () => {
|
62
80
|
if (hosts.length === 0) {
|
63
81
|
return __('No Target Hosts');
|
@@ -65,22 +83,57 @@ const ReviewDetails = ({
|
|
65
83
|
if (hosts.length === 1 || hosts.length === 2) {
|
66
84
|
return hosts.join(', ');
|
67
85
|
}
|
68
|
-
return
|
69
|
-
|
70
|
-
|
71
|
-
|
86
|
+
return (
|
87
|
+
<div>
|
88
|
+
{hostsCount} {__('hosts')}{' '}
|
89
|
+
<Button
|
90
|
+
variant="link"
|
91
|
+
isInline
|
92
|
+
onClick={() => setHostPreviewOpen(true)}
|
93
|
+
>
|
94
|
+
{__('view host names')}
|
95
|
+
</Button>
|
96
|
+
</div>
|
97
|
+
);
|
72
98
|
};
|
73
99
|
const [isAdvancedShown, setIsAdvancedShown] = useState(false);
|
74
100
|
const detailsFirstHalf = [
|
75
|
-
{
|
76
|
-
|
77
|
-
|
101
|
+
{
|
102
|
+
label: (
|
103
|
+
<StepButton stepName={WIZARD_TITLES.categoryAndTemplate}>
|
104
|
+
{__('Job category')}
|
105
|
+
</StepButton>
|
106
|
+
),
|
107
|
+
value: jobCategory,
|
108
|
+
},
|
109
|
+
{
|
110
|
+
label: (
|
111
|
+
<StepButton stepName={WIZARD_TITLES.categoryAndTemplate}>
|
112
|
+
{__('Job template')}
|
113
|
+
</StepButton>
|
114
|
+
),
|
115
|
+
value: jobTemplate,
|
116
|
+
},
|
117
|
+
{
|
118
|
+
label: (
|
119
|
+
<StepButton stepName={WIZARD_TITLES.hostsAndInputs}>
|
120
|
+
{__('Target hosts')}
|
121
|
+
</StepButton>
|
122
|
+
),
|
123
|
+
value: stringHosts(),
|
124
|
+
},
|
78
125
|
...templateInputs.map(({ name }) => ({
|
79
|
-
label:
|
126
|
+
label: (
|
127
|
+
<StepButton stepName={WIZARD_TITLES.hostsAndInputs}>{name}</StepButton>
|
128
|
+
),
|
80
129
|
value: templateValues[name],
|
81
130
|
})),
|
82
131
|
{
|
83
|
-
label:
|
132
|
+
label: (
|
133
|
+
<StepButton stepName={WIZARD_TITLES.advanced}>
|
134
|
+
{__('Advanced fields')}
|
135
|
+
</StepButton>
|
136
|
+
),
|
84
137
|
value: isAdvancedShown ? (
|
85
138
|
<Button
|
86
139
|
variant="link"
|
@@ -107,15 +160,98 @@ const ReviewDetails = ({
|
|
107
160
|
|
108
161
|
const detailsSecondHalf = [
|
109
162
|
{
|
110
|
-
label:
|
163
|
+
label: (
|
164
|
+
<StepButton stepName={WIZARD_TITLES.typeOfExecution}>
|
165
|
+
{__('Schedule type')}
|
166
|
+
</StepButton>
|
167
|
+
),
|
111
168
|
value: scheduleValue.scheduleType,
|
112
169
|
},
|
113
170
|
{
|
114
|
-
label:
|
171
|
+
label: (
|
172
|
+
<StepButton
|
173
|
+
stepName={
|
174
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING
|
175
|
+
? SCHEDULE_TYPES.RECURRING
|
176
|
+
: WIZARD_TITLES.typeOfExecution
|
177
|
+
}
|
178
|
+
>
|
179
|
+
{__('Recurrence')}
|
180
|
+
</StepButton>
|
181
|
+
),
|
115
182
|
value: scheduleValue.repeatType,
|
116
183
|
},
|
184
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.FUTURE &&
|
185
|
+
scheduleValue.startsAt && {
|
186
|
+
label: (
|
187
|
+
<StepButton
|
188
|
+
stepName={
|
189
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING
|
190
|
+
? SCHEDULE_TYPES.RECURRING
|
191
|
+
: SCHEDULE_TYPES.FUTURE
|
192
|
+
}
|
193
|
+
>
|
194
|
+
{__('Starts at')}
|
195
|
+
</StepButton>
|
196
|
+
),
|
197
|
+
value: new Date(scheduleValue.startsAt).toString(),
|
198
|
+
},
|
199
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.FUTURE &&
|
200
|
+
scheduleValue.startsBefore && {
|
201
|
+
label: (
|
202
|
+
<StepButton stepName={SCHEDULE_TYPES.FUTURE}>
|
203
|
+
{__('Starts Before')}
|
204
|
+
</StepButton>
|
205
|
+
),
|
206
|
+
value: new Date(scheduleValue.startsBefore).toString(),
|
207
|
+
},
|
208
|
+
|
209
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING && {
|
210
|
+
label: (
|
211
|
+
<StepButton stepName={SCHEDULE_TYPES.RECURRING}>
|
212
|
+
{__('Starts')}
|
213
|
+
</StepButton>
|
214
|
+
),
|
215
|
+
value: scheduleValue.isFuture
|
216
|
+
? new Date(scheduleValue.startsAt).toString()
|
217
|
+
: __('Now'),
|
218
|
+
},
|
219
|
+
|
220
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING && {
|
221
|
+
label: (
|
222
|
+
<StepButton stepName={SCHEDULE_TYPES.RECURRING}>
|
223
|
+
{__('Repeats')}
|
224
|
+
</StepButton>
|
225
|
+
),
|
226
|
+
value: parseRepeat(scheduleValue.repeatType, scheduleValue.repeatData),
|
227
|
+
},
|
228
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING && {
|
229
|
+
label: (
|
230
|
+
<StepButton stepName={SCHEDULE_TYPES.RECURRING}>
|
231
|
+
{__('Ends')}
|
232
|
+
</StepButton>
|
233
|
+
),
|
234
|
+
value: parseEnd(
|
235
|
+
scheduleValue.ends,
|
236
|
+
scheduleValue.isNeverEnds,
|
237
|
+
scheduleValue.repeatAmount
|
238
|
+
),
|
239
|
+
},
|
240
|
+
|
241
|
+
scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING && {
|
242
|
+
label: (
|
243
|
+
<StepButton stepName={SCHEDULE_TYPES.RECURRING}>
|
244
|
+
{__('Purpose')}
|
245
|
+
</StepButton>
|
246
|
+
),
|
247
|
+
value: scheduleValue.purpose,
|
248
|
+
},
|
117
249
|
{
|
118
|
-
label:
|
250
|
+
label: (
|
251
|
+
<StepButton stepName={WIZARD_TITLES.typeOfExecution}>
|
252
|
+
{__('Type of query')}
|
253
|
+
</StepButton>
|
254
|
+
),
|
119
255
|
value: scheduleValue.isTypeStatic
|
120
256
|
? __('Static query')
|
121
257
|
: __('Dynamic query'),
|
@@ -144,6 +280,11 @@ const ReviewDetails = ({
|
|
144
280
|
|
145
281
|
return (
|
146
282
|
<>
|
283
|
+
<HostPreviewModal
|
284
|
+
isOpen={hostPreviewOpen}
|
285
|
+
setIsOpen={setHostPreviewOpen}
|
286
|
+
searchQuery={buildHostQuery(selectedTargets, hostsSearchQuery)}
|
287
|
+
/>
|
147
288
|
<WizardTitle
|
148
289
|
title={WIZARD_TITLES.review}
|
149
290
|
className="advanced-fields-title"
|
@@ -153,7 +294,7 @@ const ReviewDetails = ({
|
|
153
294
|
<DescriptionListGroup key={index}>
|
154
295
|
<DescriptionListTerm>{label}</DescriptionListTerm>
|
155
296
|
<DescriptionListDescription>
|
156
|
-
|
297
|
+
{value || ''}
|
157
298
|
</DescriptionListDescription>
|
158
299
|
</DescriptionListGroup>
|
159
300
|
))}
|
@@ -162,7 +303,7 @@ const ReviewDetails = ({
|
|
162
303
|
<DescriptionListGroup key={index} className="advanced-fields">
|
163
304
|
<DescriptionListTerm>{label}</DescriptionListTerm>
|
164
305
|
<DescriptionListDescription>
|
165
|
-
|
306
|
+
{value || ''}
|
166
307
|
</DescriptionListDescription>
|
167
308
|
</DescriptionListGroup>
|
168
309
|
))}
|
@@ -170,7 +311,7 @@ const ReviewDetails = ({
|
|
170
311
|
<DescriptionListGroup key={index}>
|
171
312
|
<DescriptionListTerm>{label}</DescriptionListTerm>
|
172
313
|
<DescriptionListDescription>
|
173
|
-
|
314
|
+
{value || ''}
|
174
315
|
</DescriptionListDescription>
|
175
316
|
</DescriptionListGroup>
|
176
317
|
))}
|
@@ -187,7 +328,17 @@ ReviewDetails.propTypes = {
|
|
187
328
|
templateValues: PropTypes.object.isRequired,
|
188
329
|
selectedTargets: PropTypes.object.isRequired,
|
189
330
|
hostsSearchQuery: PropTypes.string.isRequired,
|
331
|
+
goToStepByName: PropTypes.func.isRequired,
|
190
332
|
};
|
191
333
|
|
192
334
|
ReviewDetails.defaultProps = { jobTemplateID: null };
|
193
|
-
|
335
|
+
|
336
|
+
const WrappedReviewDetails = props => (
|
337
|
+
<WizardContextConsumer>
|
338
|
+
{({ goToStepByName }) => (
|
339
|
+
<ReviewDetails goToStepByName={goToStepByName} {...props} />
|
340
|
+
)}
|
341
|
+
</WizardContextConsumer>
|
342
|
+
);
|
343
|
+
|
344
|
+
export default WrappedReviewDetails;
|
@@ -31,7 +31,7 @@ export const QueryType = ({ isTypeStatic, setIsTypeStatic }) => (
|
|
31
31
|
id="query-type-dynamic"
|
32
32
|
label={__('Dynamic query')}
|
33
33
|
body={__(
|
34
|
-
"evaluates just before the execution is started, so if it's
|
34
|
+
"evaluates just before the execution is started, so if it's planned in future, targeted hosts set may change before it"
|
35
35
|
)}
|
36
36
|
/>
|
37
37
|
</FormGroup>
|
@@ -47,7 +47,6 @@ export const RepeatHour = ({ repeatData, setRepeatData }) => {
|
|
47
47
|
}}
|
48
48
|
isOpen={minuteOpen}
|
49
49
|
width={125}
|
50
|
-
menuAppendTo={() => document.querySelector('.pf-c-form.schedule-tab')}
|
51
50
|
toggleAriaLabel="select minute toggle"
|
52
51
|
validated={
|
53
52
|
isValidMinute(minute)
|
@@ -5,7 +5,7 @@ import { translate as __, documentLocale } from 'foremanReact/common/I18n';
|
|
5
5
|
import { RepeatDaily } from './RepeatDaily';
|
6
6
|
import { noop } from '../../../helpers';
|
7
7
|
|
8
|
-
const getWeekDays = () => {
|
8
|
+
export const getWeekDays = () => {
|
9
9
|
const locale = documentLocale().replace(/-/g, '_');
|
10
10
|
const baseDate = new Date(Date.UTC(2017, 0, 1)); // just a Sunday
|
11
11
|
const weekDays = [];
|
@@ -39,15 +39,35 @@ api.get.mockImplementation(({ handleSuccess, ...action }) => {
|
|
39
39
|
handleSuccess({
|
40
40
|
data: { results: [jobTemplateResponse.job_template] },
|
41
41
|
});
|
42
|
+
} else if (action.key === 'HOST_IDS') {
|
43
|
+
handleSuccess &&
|
44
|
+
handleSuccess({
|
45
|
+
data: { results: [{ name: 'host1' }, { name: 'host3' }] },
|
46
|
+
});
|
42
47
|
}
|
43
48
|
return { type: 'get', ...action };
|
44
49
|
});
|
45
50
|
|
46
51
|
const mockStore = configureMockStore([]);
|
47
|
-
const store = mockStore({
|
52
|
+
const store = mockStore({
|
53
|
+
HOSTS_API: {
|
54
|
+
response: {
|
55
|
+
subtotal: 3,
|
56
|
+
},
|
57
|
+
},
|
58
|
+
});
|
48
59
|
jest.useFakeTimers();
|
49
60
|
|
50
61
|
describe('Schedule', () => {
|
62
|
+
beforeEach(() => {
|
63
|
+
jest.spyOn(selectors, 'selectRouterSearch');
|
64
|
+
selectors.selectRouterSearch.mockImplementation(() => ({
|
65
|
+
'host_ids[]': ['105', '37'],
|
66
|
+
}));
|
67
|
+
});
|
68
|
+
afterEach(() => {
|
69
|
+
selectors.selectRouterSearch.mockRestore();
|
70
|
+
});
|
51
71
|
it('sub steps appear', () => {
|
52
72
|
render(
|
53
73
|
<Provider store={store}>
|
@@ -85,7 +105,7 @@ describe('Schedule', () => {
|
|
85
105
|
});
|
86
106
|
act(() => {
|
87
107
|
fireEvent.click(screen.getByRole('button', { name: 'Future execution' }));
|
88
|
-
jest.
|
108
|
+
jest.advanceTimersByTime(1000); // to handle pf4 date picker popover useTimer
|
89
109
|
});
|
90
110
|
|
91
111
|
const newStartAtDate = '2030/03/12';
|
@@ -115,15 +135,15 @@ describe('Schedule', () => {
|
|
115
135
|
fireEvent.change(startsBeforeTimeField(), {
|
116
136
|
target: { value: newStartBeforeTime },
|
117
137
|
});
|
118
|
-
jest.
|
138
|
+
jest.advanceTimersByTime(1000);
|
119
139
|
});
|
120
140
|
|
121
141
|
act(() => {
|
122
|
-
fireEvent.click(screen.getByText('Category and
|
142
|
+
fireEvent.click(screen.getByText('Category and template'));
|
123
143
|
});
|
124
144
|
act(() => {
|
125
145
|
fireEvent.click(screen.getByRole('button', { name: 'Future execution' }));
|
126
|
-
jest.
|
146
|
+
jest.advanceTimersByTime(1000); // to handle pf4 date picker popover useTimer
|
127
147
|
});
|
128
148
|
expect(startsAtDateField().value).toBe(newStartAtDate);
|
129
149
|
expect(startsAtTimeField().value).toBe(newStartAtTime);
|
@@ -140,7 +160,7 @@ describe('Schedule', () => {
|
|
140
160
|
target: { value: '2030/03/11' },
|
141
161
|
});
|
142
162
|
await fireEvent.click(startsBeforeTimeField());
|
143
|
-
await jest.
|
163
|
+
await jest.advanceTimersByTime(1000);
|
144
164
|
});
|
145
165
|
expect(startsBeforeDateField().value).toBe('2030/03/11');
|
146
166
|
expect(
|
@@ -157,13 +177,13 @@ describe('Schedule', () => {
|
|
157
177
|
await fireEvent.change(startsAtDateField(), {
|
158
178
|
target: { value: '' },
|
159
179
|
});
|
160
|
-
jest.
|
180
|
+
jest.advanceTimersByTime(1000);
|
161
181
|
});
|
162
182
|
|
163
183
|
expect(startsBeforeDateField().value).toBe('2019/03/11');
|
164
184
|
expect(
|
165
185
|
screen.getAllByText("'Starts before' date must in the future")
|
166
|
-
).toHaveLength(
|
186
|
+
).toHaveLength(2);
|
167
187
|
});
|
168
188
|
|
169
189
|
it('Recurring execution - date pickers', async () => {
|
@@ -182,7 +202,7 @@ describe('Schedule', () => {
|
|
182
202
|
fireEvent.click(
|
183
203
|
screen.getByRole('button', { name: 'Recurring execution' })
|
184
204
|
);
|
185
|
-
jest.
|
205
|
+
jest.advanceTimersByTime(1000); // to handle pf4 date picker popover useTimer
|
186
206
|
});
|
187
207
|
|
188
208
|
const newStartAtDate = '2030/03/12';
|
@@ -207,7 +227,7 @@ describe('Schedule', () => {
|
|
207
227
|
fireEvent.change(startsAtTimeField(), {
|
208
228
|
target: { value: newStartAtTime },
|
209
229
|
});
|
210
|
-
jest.
|
230
|
+
jest.advanceTimersByTime(1000);
|
211
231
|
});
|
212
232
|
|
213
233
|
expect(endsAtDateField().disabled).toBeTruthy();
|
@@ -222,17 +242,17 @@ describe('Schedule', () => {
|
|
222
242
|
fireEvent.change(endsAtTimeField(), {
|
223
243
|
target: { value: newStartAtTime },
|
224
244
|
});
|
225
|
-
jest.
|
245
|
+
jest.advanceTimersByTime(1000);
|
226
246
|
});
|
227
247
|
|
228
248
|
act(() => {
|
229
|
-
fireEvent.click(screen.getByText('Category and
|
249
|
+
fireEvent.click(screen.getByText('Category and template'));
|
230
250
|
});
|
231
251
|
act(() => {
|
232
252
|
fireEvent.click(
|
233
253
|
screen.getByRole('button', { name: 'Recurring execution' })
|
234
254
|
);
|
235
|
-
jest.
|
255
|
+
jest.advanceTimersByTime(1000); // to handle pf4 date picker popover useTimer
|
236
256
|
});
|
237
257
|
expect(startsAtDateField().value).toBe(newStartAtDate);
|
238
258
|
expect(startsAtTimeField().value).toBe(newStartAtTime);
|
@@ -266,7 +286,7 @@ describe('Schedule', () => {
|
|
266
286
|
fireEvent.click(
|
267
287
|
screen.getByRole('button', { name: 'Recurring execution' })
|
268
288
|
);
|
269
|
-
jest.
|
289
|
+
jest.advanceTimersByTime(1000); // to handle pf4 date picker popover useTimer
|
270
290
|
});
|
271
291
|
await act(async () => {
|
272
292
|
fireEvent.click(screen.getByLabelText('Daily', { selector: 'button' }));
|
@@ -274,7 +294,7 @@ describe('Schedule', () => {
|
|
274
294
|
await act(async () => {
|
275
295
|
fireEvent.click(screen.getByText('Cronline'));
|
276
296
|
});
|
277
|
-
const newCronline = '1 2';
|
297
|
+
const newCronline = '1 2 3 4 5';
|
278
298
|
const cronline = screen.getByLabelText('cronline');
|
279
299
|
expect(cronline.value).toBe('');
|
280
300
|
await act(async () => {
|
@@ -286,19 +306,33 @@ describe('Schedule', () => {
|
|
286
306
|
expect(screen.getByText('Review details').disabled).toBeFalsy();
|
287
307
|
|
288
308
|
await act(async () => {
|
289
|
-
fireEvent.click(screen.getByText('Category and
|
309
|
+
fireEvent.click(screen.getByText('Category and template'));
|
290
310
|
});
|
291
|
-
expect(screen.getAllByText('Category and
|
311
|
+
expect(screen.getAllByText('Category and template')).toHaveLength(3);
|
292
312
|
|
293
313
|
await act(async () => {
|
294
314
|
fireEvent.click(
|
295
315
|
screen.getByRole('button', { name: 'Recurring execution' })
|
296
316
|
);
|
297
|
-
jest.
|
317
|
+
jest.advanceTimersByTime(1000);
|
298
318
|
});
|
299
319
|
expect(screen.queryAllByText('Recurring execution')).toHaveLength(3);
|
300
320
|
expect(cronline.value).toBe(newCronline);
|
301
321
|
|
322
|
+
await act(async () => {
|
323
|
+
fireEvent.click(screen.getByText('Review details'));
|
324
|
+
});
|
325
|
+
expect(screen.queryAllByText('Review details')).toHaveLength(3);
|
326
|
+
expect(screen.getAllByText('Cron line - 1 2 3 4 5')).toHaveLength(1);
|
327
|
+
|
328
|
+
await act(async () => {
|
329
|
+
fireEvent.click(
|
330
|
+
screen.getByRole('button', { name: 'Recurring execution' })
|
331
|
+
);
|
332
|
+
jest.runAllTimers();
|
333
|
+
});
|
334
|
+
expect(screen.queryAllByText('Recurring execution')).toHaveLength(3);
|
335
|
+
|
302
336
|
fireEvent.click(screen.getByText('Cronline'));
|
303
337
|
await act(async () => {
|
304
338
|
fireEvent.click(screen.getByText('Monthly'));
|
@@ -59,7 +59,7 @@ export const DateTimePicker = ({
|
|
59
59
|
return false;
|
60
60
|
};
|
61
61
|
|
62
|
-
const onDateChange = newDate => {
|
62
|
+
const onDateChange = (e, newDate) => {
|
63
63
|
const parsedNewDate = new Date(newDate);
|
64
64
|
if (!newDate.length && allowEmpty) {
|
65
65
|
setDateTime('');
|
@@ -119,7 +119,6 @@ export const DateTimePicker = ({
|
|
119
119
|
is24Hour
|
120
120
|
isDisabled={isDisabled || formattedDate.length === 0}
|
121
121
|
invalidFormatErrorMessage={__('Invalid time format')}
|
122
|
-
menuAppendTo={() => document.body}
|
123
122
|
includeSeconds={includeSeconds}
|
124
123
|
/>
|
125
124
|
</>
|
@@ -43,7 +43,6 @@ export const SelectField = ({
|
|
43
43
|
isOpen={isOpen}
|
44
44
|
className="without_select2"
|
45
45
|
maxHeight="45vh"
|
46
|
-
menuAppendTo={() => document.body}
|
47
46
|
placeholderText=" " // To prevent showing first option as selected
|
48
47
|
aria-labelledby={fieldId}
|
49
48
|
toggleAriaLabel={`${label} toggle`}
|
data/webpack/JobWizard/submit.js
CHANGED
@@ -12,6 +12,8 @@ export const submit = ({
|
|
12
12
|
location,
|
13
13
|
organization,
|
14
14
|
feature,
|
15
|
+
provider,
|
16
|
+
advancedInputs,
|
15
17
|
dispatch,
|
16
18
|
}) => {
|
17
19
|
const {
|
@@ -37,6 +39,13 @@ export const submit = ({
|
|
37
39
|
keyPassphrase,
|
38
40
|
timeToPickup,
|
39
41
|
} = advancedValues;
|
42
|
+
const providerInputs = advancedInputs.filter(v => v.provider_input);
|
43
|
+
const providerValues = {};
|
44
|
+
providerInputs.forEach(({ name }) => {
|
45
|
+
providerValues[name] = advancedTemplateValues[name];
|
46
|
+
delete advancedTemplateValues[name];
|
47
|
+
});
|
48
|
+
|
40
49
|
const getCronLine = () => {
|
41
50
|
const [hour, minute] = repeatData.at
|
42
51
|
? repeatData.at.split(':')
|
@@ -104,14 +113,16 @@ export const submit = ({
|
|
104
113
|
concurrency_level: concurrencyLevel,
|
105
114
|
},
|
106
115
|
bookmark_id: null,
|
107
|
-
search_query:
|
108
|
-
buildHostQuery(selectedTargets, hostsSearchQuery) || 'name ~ *',
|
116
|
+
search_query: buildHostQuery(selectedTargets, hostsSearchQuery),
|
109
117
|
description_format: description,
|
110
118
|
execution_timeout_interval: timeoutToKill,
|
111
119
|
feature,
|
112
120
|
time_to_pickup: timeToPickup,
|
113
121
|
},
|
114
122
|
};
|
123
|
+
if (Object.keys(providerValues).length) {
|
124
|
+
api.job_invocation[provider] = providerValues;
|
125
|
+
}
|
115
126
|
|
116
127
|
dispatch(
|
117
128
|
post({
|
@@ -122,7 +133,7 @@ export const submit = ({
|
|
122
133
|
window.location.href = `/job_invocations/${id}`;
|
123
134
|
},
|
124
135
|
errorToast: ({ response }) =>
|
125
|
-
response?.
|
136
|
+
response?.data?.error?.message ||
|
126
137
|
response?.message ||
|
127
138
|
response?.statusText,
|
128
139
|
})
|
@@ -25,7 +25,7 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
25
25
|
header={__('Recent jobs')}
|
26
26
|
dropdownItems={[
|
27
27
|
<DropdownItem
|
28
|
-
href={foremanUrl(`${JOB_BASE_URL}${
|
28
|
+
href={foremanUrl(`${JOB_BASE_URL}${id}`)}
|
29
29
|
key="link-to-all"
|
30
30
|
ouiaId="link-to-all-dropdown-item"
|
31
31
|
>
|
@@ -33,7 +33,7 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
33
33
|
</DropdownItem>,
|
34
34
|
<DropdownItem
|
35
35
|
href={foremanUrl(
|
36
|
-
`${JOB_BASE_URL}${
|
36
|
+
`${JOB_BASE_URL}${id}+and+status+%3D+failed+or+status%3D+succeeded`
|
37
37
|
)}
|
38
38
|
key="link-to-finished"
|
39
39
|
ouiaId="link-to-finished-dropdown-item"
|
@@ -41,14 +41,14 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
41
41
|
{__('View finished jobs')}
|
42
42
|
</DropdownItem>,
|
43
43
|
<DropdownItem
|
44
|
-
href={foremanUrl(`${JOB_BASE_URL}${
|
44
|
+
href={foremanUrl(`${JOB_BASE_URL}${id}+and+status+%3D+running`)}
|
45
45
|
key="link-to-running"
|
46
46
|
ouiaId="link-to-running-dropdown-item"
|
47
47
|
>
|
48
48
|
{__('View running jobs')}
|
49
49
|
</DropdownItem>,
|
50
50
|
<DropdownItem
|
51
|
-
href={foremanUrl(`${JOB_BASE_URL}${
|
51
|
+
href={foremanUrl(`${JOB_BASE_URL}${id}+and+status+%3D+queued`)}
|
52
52
|
key="link-to-scheduled"
|
53
53
|
ouiaId="link-to-scheduled-dropdown-item"
|
54
54
|
>
|
@@ -17,10 +17,10 @@ const RecentJobsTable = ({ status, hostId }) => {
|
|
17
17
|
const jobsUrl =
|
18
18
|
hostId &&
|
19
19
|
foremanUrl(
|
20
|
-
`${JOB_API_URL}${hostId}
|
20
|
+
`${JOB_API_URL}${hostId}&status=${status}&limit=${JOBS_IN_CARD}`
|
21
21
|
);
|
22
22
|
const {
|
23
|
-
response: {
|
23
|
+
response: { job_invocations: jobs },
|
24
24
|
status: responseStatus,
|
25
25
|
} = useAPI('get', jobsUrl, RECENT_JOBS_KEY);
|
26
26
|
|
@@ -6,8 +6,8 @@ export const SCHEDULED_TAB = 2;
|
|
6
6
|
export const JOB_SUCCESS_STATUS = 0;
|
7
7
|
export const JOB_ERROR_STATUS = 1;
|
8
8
|
|
9
|
-
export const JOB_BASE_URL = '/job_invocations?search=
|
9
|
+
export const JOB_BASE_URL = '/job_invocations?search=targeted_host_id+%3D+';
|
10
10
|
export const JOB_API_URL =
|
11
|
-
'/
|
11
|
+
'/job_invocations/preview_job_invocations_per_host?host_id=';
|
12
12
|
export const JOBS_IN_CARD = 3;
|
13
13
|
export const RECENT_JOBS_KEY = { key: 'RECENT_JOBS_KEY' };
|
@@ -26,7 +26,6 @@ const options = (value = '') => {
|
|
26
26
|
const RexPull = ({ isLoading, onChange, pluginValues, configParams }) => (
|
27
27
|
<FormGroup
|
28
28
|
label={__('REX pull mode')}
|
29
|
-
isRequired
|
30
29
|
labelIcon={
|
31
30
|
<LabelIcon
|
32
31
|
text={__(
|
@@ -44,7 +43,6 @@ const RexPull = ({ isLoading, onChange, pluginValues, configParams }) => (
|
|
44
43
|
className="without_select2"
|
45
44
|
id="registration_setup_remote_execution_pull"
|
46
45
|
isDisabled={isLoading}
|
47
|
-
isRequired
|
48
46
|
>
|
49
47
|
{/* eslint-disable-next-line camelcase */
|
50
48
|
options(configParams?.host_registration_remote_execution_pull)}
|