foreman_remote_execution 6.2.0 → 7.1.1
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/ruby_ci.yml +2 -0
- data/app/controllers/api/v2/job_invocations_controller.rb +1 -0
- data/app/helpers/hosts_extensions_helper.rb +62 -0
- data/app/lib/actions/remote_execution/run_host_job.rb +4 -0
- data/app/lib/actions/remote_execution/run_hosts_job.rb +4 -0
- data/app/models/concerns/foreman_remote_execution/foreman_tasks_task_extensions.rb +6 -0
- data/app/models/host_status/execution_status.rb +2 -1
- data/app/models/job_invocation.rb +1 -1
- data/app/models/job_invocation_composer.rb +7 -3
- data/app/models/job_template.rb +6 -1
- data/app/models/remote_execution_provider.rb +4 -0
- data/app/models/ssh_execution_provider.rb +3 -3
- data/app/models/template_invocation.rb +2 -0
- data/app/services/remote_execution_proxy_selector.rb +1 -1
- data/app/views/api/v2/job_invocations/base.json.rabl +1 -1
- data/app/views/job_invocations/_card_target_hosts.html.erb +8 -0
- data/app/views/job_invocations/_form.html.erb +2 -0
- data/app/views/overrides/subnets/_rex_tab_pane.html.erb +1 -1
- data/db/migrate/20220331112719_add_ssh_user_to_job_invocation.rb +5 -0
- data/lib/foreman_remote_execution/engine.rb +6 -3
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/action_names.rb +3 -3
- data/locale/de/foreman_remote_execution.po +23 -23
- data/locale/en/foreman_remote_execution.po +23 -23
- data/locale/en_GB/foreman_remote_execution.po +23 -23
- data/locale/es/foreman_remote_execution.po +23 -23
- data/locale/foreman_remote_execution.pot +64 -66
- data/locale/fr/foreman_remote_execution.po +23 -23
- data/locale/ja/foreman_remote_execution.po +23 -23
- data/locale/ko/foreman_remote_execution.po +23 -23
- data/locale/pt_BR/foreman_remote_execution.po +23 -23
- data/locale/ru/foreman_remote_execution.po +23 -23
- data/locale/zh_CN/foreman_remote_execution.po +23 -23
- data/locale/zh_TW/foreman_remote_execution.po +23 -23
- data/test/unit/api_params_test.rb +33 -0
- data/test/unit/remote_execution_provider_test.rb +26 -0
- data/webpack/JobWizard/JobWizardConstants.js +2 -2
- data/webpack/JobWizard/__tests__/fixtures.js +8 -4
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +9 -0
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +21 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +7 -2
- data/webpack/JobWizard/steps/HostsAndInputs/SelectGQL.js +2 -1
- data/webpack/JobWizard/steps/HostsAndInputs/SelectedChips.js +1 -1
- data/webpack/JobWizard/steps/HostsAndInputs/buildHostQuery.js +1 -1
- data/webpack/JobWizard/steps/HostsAndInputs/hostgroups.gql +1 -0
- data/webpack/JobWizard/steps/HostsAndInputs/hosts.gql +1 -0
- data/webpack/JobWizard/steps/ReviewDetails/index.js +2 -1
- data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +15 -15
- data/webpack/JobWizard/steps/form/SearchSelect.js +0 -1
- data/webpack/JobWizard/submit.js +2 -0
- data/webpack/__mocks__/foremanReact/common/globalIdHelpers.js +1 -0
- data/webpack/global_index.js +2 -8
- data/webpack/react_app/components/FeaturesDropdown/index.js +1 -1
- data/webpack/react_app/components/HostKebab/KebabItems.js +6 -1
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +4 -4
- data/webpack/react_app/components/TargetingHosts/TargetingHostsConsts.js +1 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +8 -3
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +9 -1
- data/webpack/react_app/components/TargetingHosts/__tests__/fixtures.js +1 -4
- data/webpack/react_app/components/TargetingHosts/index.js +1 -0
- data/webpack/react_app/extend/Fills.js +48 -0
- metadata +9 -8
- data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +0 -62
- data/webpack/react_app/extend/fillKebabItems.js +0 -11
- data/webpack/react_app/extend/fillRecentJobsCard.js +0 -11
- data/webpack/react_app/extend/fillRexFeaturesDropdown.js +0 -11
- data/webpack/react_app/extend/fillregistrationAdvanced.js +0 -11
@@ -23,6 +23,9 @@ msgstr "%s"
|
|
23
23
|
msgid "%s ago"
|
24
24
|
msgstr "%s 之前"
|
25
25
|
|
26
|
+
msgid "%s job has been invoked"
|
27
|
+
msgstr ""
|
28
|
+
|
26
29
|
msgid "%{description} on %{host}"
|
27
30
|
msgstr "%{description} 於 %{host} 之上"
|
28
31
|
|
@@ -67,15 +70,15 @@ msgstr "要使用執行這 script 的使用者。如果使用者與 SSH 使用
|
|
67
70
|
msgid "Abort Job"
|
68
71
|
msgstr ""
|
69
72
|
|
73
|
+
msgid "Action with sub plans"
|
74
|
+
msgstr ""
|
75
|
+
|
70
76
|
msgid "Actions"
|
71
77
|
msgstr "動作"
|
72
78
|
|
73
79
|
msgid "Add Foreign Input Set"
|
74
80
|
msgstr "新增外部輸入集"
|
75
81
|
|
76
|
-
msgid "Advanced Fields"
|
77
|
-
msgstr ""
|
78
|
-
|
79
82
|
msgid "Advanced fields"
|
80
83
|
msgstr ""
|
81
84
|
|
@@ -466,9 +469,15 @@ msgstr ""
|
|
466
469
|
msgid "Import"
|
467
470
|
msgstr "匯入"
|
468
471
|
|
472
|
+
msgid "Import Puppet classes"
|
473
|
+
msgstr ""
|
474
|
+
|
469
475
|
msgid "Import a job template from ERB"
|
470
476
|
msgstr ""
|
471
477
|
|
478
|
+
msgid "Import facts"
|
479
|
+
msgstr ""
|
480
|
+
|
472
481
|
msgid "Include all inputs from the foreign template"
|
473
482
|
msgstr ""
|
474
483
|
|
@@ -502,9 +511,6 @@ msgstr "祈願類型,%s 之一"
|
|
502
511
|
msgid "Job"
|
503
512
|
msgstr "工作"
|
504
513
|
|
505
|
-
msgid "Job Category"
|
506
|
-
msgstr ""
|
507
|
-
|
508
514
|
msgid "Job Details"
|
509
515
|
msgstr ""
|
510
516
|
|
@@ -826,7 +832,7 @@ msgstr "解析至"
|
|
826
832
|
msgid "Results"
|
827
833
|
msgstr ""
|
828
834
|
|
829
|
-
msgid "Review
|
835
|
+
msgid "Review details"
|
830
836
|
msgstr ""
|
831
837
|
|
832
838
|
msgid "Run"
|
@@ -850,9 +856,6 @@ msgstr ""
|
|
850
856
|
msgid "Running"
|
851
857
|
msgstr ""
|
852
858
|
|
853
|
-
msgid "SSH"
|
854
|
-
msgstr "SSH"
|
855
|
-
|
856
859
|
msgid "SSH Port"
|
857
860
|
msgstr ""
|
858
861
|
|
@@ -868,6 +871,9 @@ msgstr "排程"
|
|
868
871
|
msgid "Schedule Remote Job"
|
869
872
|
msgstr ""
|
870
873
|
|
874
|
+
msgid "Schedule a job"
|
875
|
+
msgstr ""
|
876
|
+
|
871
877
|
msgid "Schedule for future execution"
|
872
878
|
msgstr ""
|
873
879
|
|
@@ -889,6 +895,9 @@ msgstr ""
|
|
889
895
|
msgid "Scheduled to start before"
|
890
896
|
msgstr ""
|
891
897
|
|
898
|
+
msgid "Script"
|
899
|
+
msgstr ""
|
900
|
+
|
892
901
|
msgid "Scroll to bottom"
|
893
902
|
msgstr "捲動至底端"
|
894
903
|
|
@@ -1209,16 +1218,16 @@ msgstr "使用者輸入"
|
|
1209
1218
|
msgid "Value"
|
1210
1219
|
msgstr "值"
|
1211
1220
|
|
1212
|
-
msgid "View
|
1221
|
+
msgid "View all jobs"
|
1213
1222
|
msgstr ""
|
1214
1223
|
|
1215
|
-
msgid "View
|
1224
|
+
msgid "View finished jobs"
|
1216
1225
|
msgstr ""
|
1217
1226
|
|
1218
|
-
msgid "View
|
1227
|
+
msgid "View running jobs"
|
1219
1228
|
msgstr ""
|
1220
1229
|
|
1221
|
-
msgid "View
|
1230
|
+
msgid "View scheduled jobs"
|
1222
1231
|
msgstr ""
|
1223
1232
|
|
1224
1233
|
msgid "Web Console"
|
@@ -1364,12 +1373,3 @@ msgstr ""
|
|
1364
1373
|
|
1365
1374
|
msgid "using Smart Proxy"
|
1366
1375
|
msgstr ""
|
1367
|
-
|
1368
|
-
msgid "»Action with sub plans«"
|
1369
|
-
msgstr ""
|
1370
|
-
|
1371
|
-
msgid "»Import Puppet classes«"
|
1372
|
-
msgstr ""
|
1373
|
-
|
1374
|
-
msgid "»Import facts«"
|
1375
|
-
msgstr ""
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_plugin_helper'
|
4
|
+
|
5
|
+
class ApiParamsTest < ActiveSupport::TestCase
|
6
|
+
describe '#format_datetime' do
|
7
|
+
let(:params) { JobInvocationComposer::ApiParams.allocate }
|
8
|
+
|
9
|
+
it 'leaves empty string as is' do
|
10
|
+
assert_equal params.send(:format_datetime, ''), ''
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'honors explicitly supplied time zone' do
|
14
|
+
in_time_zone(ActiveSupport::TimeZone['America/New_York']) do
|
15
|
+
assert_equal '2022-07-08 08:53', params.send(:format_datetime, '2022-07-08 12:53:20 UTC')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'implicitly honors current user\'s time zone' do
|
20
|
+
in_time_zone(ActiveSupport::TimeZone['America/New_York']) do
|
21
|
+
assert_equal '2022-07-08 12:53', params.send(:format_datetime, '2022-07-08 12:53:20')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def in_time_zone(zone)
|
27
|
+
old_tz = Time.zone
|
28
|
+
Time.zone = zone
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
Time.zone = old_tz
|
32
|
+
end
|
33
|
+
end
|
@@ -33,6 +33,10 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
|
|
33
33
|
it 'accepts strings' do
|
34
34
|
RemoteExecutionProvider.provider_for('SSH').must_equal SSHExecutionProvider
|
35
35
|
end
|
36
|
+
|
37
|
+
it 'returns a default one if unknown value is provided' do
|
38
|
+
RemoteExecutionProvider.provider_for('WinRM').must_equal ScriptExecutionProvider
|
39
|
+
end
|
36
40
|
end
|
37
41
|
|
38
42
|
describe '.provider_names' do
|
@@ -52,6 +56,28 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
|
|
52
56
|
end
|
53
57
|
end
|
54
58
|
|
59
|
+
describe '.provider_proxy_features' do
|
60
|
+
it 'returns correct values' do
|
61
|
+
RemoteExecutionProvider.stubs(:providers).returns(
|
62
|
+
:SSH => SSHExecutionProvider,
|
63
|
+
:script => ScriptExecutionProvider
|
64
|
+
)
|
65
|
+
|
66
|
+
features = RemoteExecutionProvider.provider_proxy_features
|
67
|
+
_(features).must_include 'SSH'
|
68
|
+
_(features).must_include 'Script'
|
69
|
+
RemoteExecutionProvider.unstub(:providers)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'can deal with non-arrays' do
|
73
|
+
provider = OpenStruct.new(proxy_feature: 'Testing')
|
74
|
+
RemoteExecutionProvider.stubs(:providers).returns(:testing => provider)
|
75
|
+
features = RemoteExecutionProvider.provider_proxy_features
|
76
|
+
_(features).must_include 'Testing'
|
77
|
+
RemoteExecutionProvider.unstub(:providers)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
55
81
|
describe '.host_setting' do
|
56
82
|
let(:host) { FactoryBot.create(:host) }
|
57
83
|
|
@@ -19,9 +19,9 @@ export const repeatTypes = {
|
|
19
19
|
export const WIZARD_TITLES = {
|
20
20
|
categoryAndTemplate: __('Category and Template'),
|
21
21
|
hostsAndInputs: __('Target hosts and inputs'),
|
22
|
-
advanced: __('Advanced
|
22
|
+
advanced: __('Advanced fields'),
|
23
23
|
schedule: __('Schedule'),
|
24
|
-
review: __('Review
|
24
|
+
review: __('Review details'),
|
25
25
|
};
|
26
26
|
|
27
27
|
export const initialScheduleState = {
|
@@ -193,7 +193,11 @@ export const gqlMock = [
|
|
193
193
|
data: {
|
194
194
|
hosts: {
|
195
195
|
totalCount: 3,
|
196
|
-
nodes: [
|
196
|
+
nodes: [
|
197
|
+
{ id: 'MDE6SG9zdC0x', name: 'host1' },
|
198
|
+
{ id: 'MDE6SG9zdC0y', name: 'host2' },
|
199
|
+
{ id: 'MDE6SG9zdC0z', name: 'host3' },
|
200
|
+
],
|
197
201
|
},
|
198
202
|
},
|
199
203
|
},
|
@@ -211,9 +215,9 @@ export const gqlMock = [
|
|
211
215
|
hostgroups: {
|
212
216
|
totalCount: 3,
|
213
217
|
nodes: [
|
214
|
-
{ name: 'host_group1' },
|
215
|
-
{ name: 'host_group2' },
|
216
|
-
{ name: 'host_group3' },
|
218
|
+
{ id: 'MDE6SG9zdGdyb3VwLTE=', name: 'host_group1' },
|
219
|
+
{ id: 'MDE6SG9zdGdyb3VwLTI=', name: 'host_group2' },
|
220
|
+
{ id: 'MDE6SG9zdGdyb3VwLTM=', name: 'host_group3' },
|
217
221
|
],
|
218
222
|
},
|
219
223
|
},
|
@@ -16,6 +16,7 @@ import {
|
|
16
16
|
TimeSpanLevelField,
|
17
17
|
TemplateInputsFields,
|
18
18
|
ExecutionOrderingField,
|
19
|
+
SSHUserField,
|
19
20
|
} from './Fields';
|
20
21
|
import { DescriptionField } from './DescriptionField';
|
21
22
|
import { WIZARD_TITLES } from '../../JobWizardConstants';
|
@@ -40,6 +41,14 @@ export const AdvancedFields = ({
|
|
40
41
|
value={advancedValues.templateValues}
|
41
42
|
setValue={newValue => setAdvancedValues({ templateValues: newValue })}
|
42
43
|
/>
|
44
|
+
<SSHUserField
|
45
|
+
value={advancedValues.sshUser}
|
46
|
+
setValue={newValue =>
|
47
|
+
setAdvancedValues({
|
48
|
+
sshUser: newValue,
|
49
|
+
})
|
50
|
+
}
|
51
|
+
/>
|
43
52
|
{effectiveUser?.overridable && (
|
44
53
|
<EffectiveUserField
|
45
54
|
value={advancedValues.effectiveUserValue}
|
@@ -203,6 +203,24 @@ export const ExecutionOrderingField = ({ isRandomizedOrdering, setValue }) => (
|
|
203
203
|
export const TemplateInputsFields = ({ inputs, value, setValue }) => (
|
204
204
|
<>{inputs?.map(input => formatter(input, value, setValue))}</>
|
205
205
|
);
|
206
|
+
|
207
|
+
export const SSHUserField = ({ value, setValue }) => (
|
208
|
+
<FormGroup
|
209
|
+
label={__('SSH user')}
|
210
|
+
labelIcon={helpLabel(__('A user to be used for SSH.'), 'ssh-user')}
|
211
|
+
fieldId="ssh-user"
|
212
|
+
>
|
213
|
+
<TextInput
|
214
|
+
aria-label="ssh user"
|
215
|
+
autoComplete="ssh-user"
|
216
|
+
id="ssh-user"
|
217
|
+
type="text"
|
218
|
+
value={value}
|
219
|
+
onChange={newValue => setValue(newValue)}
|
220
|
+
/>
|
221
|
+
</FormGroup>
|
222
|
+
);
|
223
|
+
|
206
224
|
EffectiveUserField.propTypes = {
|
207
225
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
208
226
|
setValue: PropTypes.func.isRequired,
|
@@ -240,3 +258,6 @@ TemplateInputsFields.propTypes = {
|
|
240
258
|
TemplateInputsFields.defaultProps = {
|
241
259
|
value: {},
|
242
260
|
};
|
261
|
+
|
262
|
+
SSHUserField.propTypes = EffectiveUserField.propTypes;
|
263
|
+
SSHUserField.defaultProps = EffectiveUserField.defaultProps;
|
@@ -143,7 +143,7 @@ describe('AdvancedFields', () => {
|
|
143
143
|
);
|
144
144
|
|
145
145
|
await act(async () => {
|
146
|
-
fireEvent.click(screen.getByText('Advanced
|
146
|
+
fireEvent.click(screen.getByText('Advanced fields'));
|
147
147
|
});
|
148
148
|
expect(textField.value).toBe(textValue);
|
149
149
|
expect(searchField.value).toBe(searchValue);
|
@@ -162,9 +162,14 @@ describe('AdvancedFields', () => {
|
|
162
162
|
</MockedProvider>
|
163
163
|
);
|
164
164
|
await act(async () => {
|
165
|
-
fireEvent.click(screen.getByText('Advanced
|
165
|
+
fireEvent.click(screen.getByText('Advanced fields'));
|
166
166
|
});
|
167
167
|
|
168
|
+
expect(
|
169
|
+
screen.getByLabelText('ssh user', {
|
170
|
+
selector: 'input',
|
171
|
+
}).value
|
172
|
+
).toBe('');
|
168
173
|
expect(
|
169
174
|
screen.getByLabelText('effective user', {
|
170
175
|
selector: 'input',
|
@@ -5,6 +5,7 @@ import {
|
|
5
5
|
useForemanOrganization,
|
6
6
|
useForemanLocation,
|
7
7
|
} from 'foremanReact/Root/Context/ForemanContext';
|
8
|
+
import { decodeId } from 'foremanReact/common/globalIdHelpers';
|
8
9
|
import { HOSTS, HOST_GROUPS, dataName } from '../../JobWizardConstants';
|
9
10
|
import { SearchSelect } from '../form/SearchSelect';
|
10
11
|
import hostsQuery from './hosts.gql';
|
@@ -35,7 +36,7 @@ export const useNameSearchGQL = apiKey => {
|
|
35
36
|
subtotal: data?.[dataName[apiKey]]?.totalCount,
|
36
37
|
results:
|
37
38
|
data?.[dataName[apiKey]]?.nodes.map(node => ({
|
38
|
-
id: node.
|
39
|
+
id: decodeId(node.id),
|
39
40
|
name: node.name,
|
40
41
|
})) || [],
|
41
42
|
},
|
@@ -1,6 +1,6 @@
|
|
1
1
|
export const buildHostQuery = (selected, search) => {
|
2
2
|
const { hosts, hostCollections, hostGroups } = selected;
|
3
|
-
const hostsSearch = `(
|
3
|
+
const hostsSearch = `(id ^ (${hosts.map(({ id }) => id).join(',')}))`;
|
4
4
|
const hostCollectionsSearch = `(host_collection_id ^ (${hostCollections
|
5
5
|
.map(({ id }) => id)
|
6
6
|
.join(',')}))`;
|
@@ -72,7 +72,7 @@ const ReviewDetails = ({
|
|
72
72
|
};
|
73
73
|
const [isAdvancedShown, setIsAdvancedShown] = useState(false);
|
74
74
|
const detailsFirstHalf = [
|
75
|
-
{ label: __('Job
|
75
|
+
{ label: __('Job category'), value: jobCategory },
|
76
76
|
{ label: __('Job template'), value: jobTemplate },
|
77
77
|
{ label: __('Target hosts'), value: stringHosts() },
|
78
78
|
...templateInputs.map(({ name }) => ({
|
@@ -125,6 +125,7 @@ const ReviewDetails = ({
|
|
125
125
|
].filter(d => d);
|
126
126
|
|
127
127
|
const advancedFields = [
|
128
|
+
{ label: __('SSH user'), value: advancedValues.sshUser },
|
128
129
|
{ label: __('Effective user'), value: advancedValues.effectiveUserValue },
|
129
130
|
{ label: __('Description Template'), value: advancedValues.description },
|
130
131
|
{ label: __('Timeout to kill'), value: advancedValues.timeoutToKill },
|
@@ -186,7 +186,7 @@ describe('Schedule', () => {
|
|
186
186
|
expect(
|
187
187
|
screen.getByPlaceholderText('Repeat N times').hasAttribute('disabled')
|
188
188
|
).toBeTruthy();
|
189
|
-
expect(screen.getByText('Review
|
189
|
+
expect(screen.getByText('Review details').disabled).toBeFalsy();
|
190
190
|
await act(async () => {
|
191
191
|
fireEvent.click(
|
192
192
|
screen.getByLabelText('Does not repeat', { selector: 'button' })
|
@@ -196,7 +196,7 @@ describe('Schedule', () => {
|
|
196
196
|
await act(async () => {
|
197
197
|
fireEvent.click(screen.getByText('Cronline'));
|
198
198
|
});
|
199
|
-
expect(screen.getByText('Review
|
199
|
+
expect(screen.getByText('Review details').disabled).toBeTruthy();
|
200
200
|
const newRepeatTimes = '3';
|
201
201
|
const repeatNTimes = screen.getByPlaceholderText('Repeat N times');
|
202
202
|
expect(repeatNTimes.value).toBe('');
|
@@ -216,7 +216,7 @@ describe('Schedule', () => {
|
|
216
216
|
});
|
217
217
|
});
|
218
218
|
expect(cronline.value).toBe(newCronline);
|
219
|
-
expect(screen.getByText('Review
|
219
|
+
expect(screen.getByText('Review details').disabled).toBeFalsy();
|
220
220
|
|
221
221
|
await act(async () => {
|
222
222
|
fireEvent.click(screen.getByText('Category and Template'));
|
@@ -236,7 +236,7 @@ describe('Schedule', () => {
|
|
236
236
|
fireEvent.click(screen.getByText('Monthly'));
|
237
237
|
});
|
238
238
|
|
239
|
-
expect(screen.getByText('Review
|
239
|
+
expect(screen.getByText('Review details').disabled).toBeTruthy();
|
240
240
|
const newDays = '1,2,3';
|
241
241
|
const days = screen.getByLabelText('days');
|
242
242
|
expect(days.value).toBe('');
|
@@ -248,7 +248,7 @@ describe('Schedule', () => {
|
|
248
248
|
});
|
249
249
|
expect(days.value).toBe(newDays);
|
250
250
|
|
251
|
-
expect(screen.getByText('Review
|
251
|
+
expect(screen.getByText('Review details').disabled).toBeTruthy();
|
252
252
|
const newAtMonthly = '13:07';
|
253
253
|
const at = () => screen.getByLabelText('repeat-at');
|
254
254
|
expect(at().value).toBe('');
|
@@ -259,13 +259,13 @@ describe('Schedule', () => {
|
|
259
259
|
});
|
260
260
|
expect(at().value).toBe(newAtMonthly);
|
261
261
|
|
262
|
-
expect(screen.getByText('Review
|
262
|
+
expect(screen.getByText('Review details').disabled).toBeFalsy();
|
263
263
|
fireEvent.click(screen.getByText('Monthly'));
|
264
264
|
await act(async () => {
|
265
265
|
fireEvent.click(screen.getByText('Weekly'));
|
266
266
|
});
|
267
267
|
|
268
|
-
expect(screen.getByText('Review
|
268
|
+
expect(screen.getByText('Review details').disabled).toBeTruthy();
|
269
269
|
const dayTue = screen.getByLabelText('Tue checkbox');
|
270
270
|
const daySat = screen.getByLabelText('Sat checkbox');
|
271
271
|
expect(dayTue.checked).toBe(false);
|
@@ -293,19 +293,19 @@ describe('Schedule', () => {
|
|
293
293
|
});
|
294
294
|
expect(at().value).toBe(newAtWeekly);
|
295
295
|
|
296
|
-
expect(screen.getByText('Review
|
296
|
+
expect(screen.getByText('Review details').disabled).toBeFalsy();
|
297
297
|
fireEvent.click(screen.getByText('Weekly'));
|
298
298
|
await act(async () => {
|
299
299
|
fireEvent.click(screen.getByText('Daily'));
|
300
300
|
});
|
301
301
|
|
302
|
-
expect(screen.getByText('Review
|
302
|
+
expect(screen.getByText('Review details').disabled).toBeFalsy();
|
303
303
|
await act(async () => {
|
304
304
|
fireEvent.change(at(), {
|
305
305
|
target: { value: '' },
|
306
306
|
});
|
307
307
|
});
|
308
|
-
expect(screen.getByText('Review
|
308
|
+
expect(screen.getByText('Review details').disabled).toBeTruthy();
|
309
309
|
const newAtDaily = '17:07';
|
310
310
|
expect(at().value).toBe('');
|
311
311
|
await act(async () => {
|
@@ -314,14 +314,14 @@ describe('Schedule', () => {
|
|
314
314
|
});
|
315
315
|
});
|
316
316
|
expect(at().value).toBe(newAtDaily);
|
317
|
-
expect(screen.getByText('Review
|
317
|
+
expect(screen.getByText('Review details').disabled).toBeFalsy();
|
318
318
|
|
319
319
|
fireEvent.click(screen.getByText('Daily'));
|
320
320
|
await act(async () => {
|
321
321
|
fireEvent.click(screen.getByText('Hourly'));
|
322
322
|
});
|
323
323
|
|
324
|
-
expect(screen.getByText('Review
|
324
|
+
expect(screen.getByText('Review details').disabled).toBeTruthy();
|
325
325
|
const newMinutes = '6';
|
326
326
|
const atHourly = screen.getByLabelText('repeat-at-minute-typeahead');
|
327
327
|
expect(atHourly.value).toBe('');
|
@@ -331,7 +331,7 @@ describe('Schedule', () => {
|
|
331
331
|
await act(async () => {
|
332
332
|
fireEvent.click(screen.getByText(newMinutes));
|
333
333
|
});
|
334
|
-
expect(screen.getByText('Review
|
334
|
+
expect(screen.getByText('Review details').disabled).toBeFalsy();
|
335
335
|
expect(atHourly.value).toBe(newMinutes);
|
336
336
|
});
|
337
337
|
it('should show invalid error on start date after end', async () => {
|
@@ -353,7 +353,7 @@ describe('Schedule', () => {
|
|
353
353
|
expect(
|
354
354
|
screen.queryAllByText('End time needs to be after start time')
|
355
355
|
).toHaveLength(0);
|
356
|
-
expect(screen.getByText('Review
|
356
|
+
expect(screen.getByText('Review details').disabled).toBeFalsy();
|
357
357
|
await act(async () => {
|
358
358
|
await fireEvent.change(startsDateField, {
|
359
359
|
target: { value: '2020/10/15' },
|
@@ -368,7 +368,7 @@ describe('Schedule', () => {
|
|
368
368
|
screen.queryAllByText('End time needs to be after start time')
|
369
369
|
).toHaveLength(1);
|
370
370
|
|
371
|
-
expect(screen.getByText('Review
|
371
|
+
expect(screen.getByText('Review details').disabled).toBeTruthy();
|
372
372
|
});
|
373
373
|
it('purpose and ends should be disabled when no reaccurence ', async () => {
|
374
374
|
render(
|
@@ -93,7 +93,6 @@ export const SearchSelect = ({
|
|
93
93
|
autoSearch(value || '');
|
94
94
|
}}
|
95
95
|
placeholderText={placeholderText}
|
96
|
-
onFilter={() => null} // https://github.com/patternfly/patternfly-react/issues/6321
|
97
96
|
typeAheadAriaLabel={`${name} typeahead input`}
|
98
97
|
>
|
99
98
|
{selectOptions}
|
data/webpack/JobWizard/submit.js
CHANGED
@@ -23,6 +23,7 @@ export const submit = ({
|
|
23
23
|
purpose,
|
24
24
|
} = scheduleValue;
|
25
25
|
const {
|
26
|
+
sshUser,
|
26
27
|
effectiveUserValue,
|
27
28
|
effectiveUserPassword,
|
28
29
|
description,
|
@@ -64,6 +65,7 @@ export const submit = ({
|
|
64
65
|
: 'dynamic_query',
|
65
66
|
randomized_ordering: isRandomizedOrdering,
|
66
67
|
inputs: { ...templateValues, ...advancedTemplateValues },
|
68
|
+
ssh_user: sshUser,
|
67
69
|
ssh: {
|
68
70
|
effective_user: effectiveUserValue,
|
69
71
|
effective_user_password: effectiveUserPassword,
|
@@ -0,0 +1 @@
|
|
1
|
+
export const decodeId = whatever => whatever;
|
data/webpack/global_index.js
CHANGED
@@ -1,14 +1,8 @@
|
|
1
1
|
import { registerRoutes } from 'foremanReact/routes/RoutingService';
|
2
2
|
import routes from './Routes/routes';
|
3
|
-
import fillregistrationAdvanced from './react_app/extend/fillregistrationAdvanced';
|
4
|
-
import fillRecentJobsCard from './react_app/extend/fillRecentJobsCard';
|
5
|
-
import fillFeaturesDropdown from './react_app/extend/fillRexFeaturesDropdown';
|
6
|
-
import fillKebabItems from './react_app/extend/fillKebabItems';
|
7
3
|
import registerReducers from './react_app/extend/reducers';
|
4
|
+
import registerFills from './react_app/extend/Fills';
|
8
5
|
|
9
6
|
registerReducers();
|
10
7
|
registerRoutes('foreman_remote_execution', routes);
|
11
|
-
|
12
|
-
fillRecentJobsCard();
|
13
|
-
fillregistrationAdvanced();
|
14
|
-
fillKebabItems();
|
8
|
+
registerFills();
|
@@ -52,7 +52,7 @@ const FeaturesDropdown = ({ hostId }) => {
|
|
52
52
|
toggle={
|
53
53
|
<DropdownToggle
|
54
54
|
splitButtonItems={scheduleJob}
|
55
|
-
toggleVariant="
|
55
|
+
toggleVariant="secondary"
|
56
56
|
onToggle={() => setIsOpen(prev => !prev)}
|
57
57
|
isDisabled={status === STATUS.PENDING}
|
58
58
|
splitButtonVariant="action"
|
@@ -13,7 +13,12 @@ const HostKebabItems = () => {
|
|
13
13
|
|
14
14
|
if (!consoleUrl) return null;
|
15
15
|
return (
|
16
|
-
<DropdownItem
|
16
|
+
<DropdownItem
|
17
|
+
icon={<CodeIcon />}
|
18
|
+
href={consoleUrl}
|
19
|
+
target="_blank"
|
20
|
+
rel="noreferrer"
|
21
|
+
>
|
17
22
|
{__('Web Console')}
|
18
23
|
</DropdownItem>
|
19
24
|
);
|
@@ -28,7 +28,7 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
28
28
|
href={foremanUrl(`${JOB_BASE_URL}${name}`)}
|
29
29
|
key="link-to-all"
|
30
30
|
>
|
31
|
-
{__('View
|
31
|
+
{__('View all jobs')}
|
32
32
|
</DropdownItem>,
|
33
33
|
<DropdownItem
|
34
34
|
href={foremanUrl(
|
@@ -36,19 +36,19 @@ const RecentJobsCard = ({ hostDetails: { name, id } }) => {
|
|
36
36
|
)}
|
37
37
|
key="link-to-finished"
|
38
38
|
>
|
39
|
-
{__('View
|
39
|
+
{__('View finished jobs')}
|
40
40
|
</DropdownItem>,
|
41
41
|
<DropdownItem
|
42
42
|
href={foremanUrl(`${JOB_BASE_URL}${name}+and+status+%3D+running`)}
|
43
43
|
key="link-to-running"
|
44
44
|
>
|
45
|
-
{__('View
|
45
|
+
{__('View running jobs')}
|
46
46
|
</DropdownItem>,
|
47
47
|
<DropdownItem
|
48
48
|
href={foremanUrl(`${JOB_BASE_URL}${name}+and+status+%3D+queued`)}
|
49
49
|
key="link-to-scheduled"
|
50
50
|
>
|
51
|
-
{__('View
|
51
|
+
{__('View scheduled jobs')}
|
52
52
|
</DropdownItem>,
|
53
53
|
]}
|
54
54
|
>
|