foreman_remote_execution 4.5.6 → 4.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/job_invocations_controller.rb +1 -1
- data/app/controllers/ui_job_wizard_controller.rb +0 -7
- data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +1 -5
- data/app/helpers/remote_execution_helper.rb +3 -9
- data/app/lib/actions/remote_execution/run_host_job.rb +1 -5
- data/app/lib/actions/remote_execution/run_hosts_job.rb +1 -1
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +0 -2
- data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +70 -0
- data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +0 -6
- data/app/models/host_status/execution_status.rb +3 -3
- data/app/models/job_invocation.rb +6 -9
- data/app/models/job_invocation_composer.rb +4 -4
- data/app/models/remote_execution_feature.rb +1 -5
- data/app/models/remote_execution_provider.rb +2 -3
- data/app/models/setting/remote_execution.rb +2 -2
- data/app/models/targeting.rb +1 -5
- data/app/views/job_invocations/index.html.erb +1 -1
- data/app/views/templates/ssh/module_action.erb +0 -1
- data/app/views/templates/ssh/power_action.erb +0 -2
- data/app/views/templates/ssh/puppet_run_once.erb +0 -1
- data/foreman_remote_execution.gemspec +0 -1
- data/lib/foreman_remote_execution/engine.rb +2 -0
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/models/orchestration/ssh_test.rb +56 -0
- data/test/unit/job_invocation_composer_test.rb +1 -14
- data/test/unit/job_invocation_test.rb +1 -1
- data/test/unit/remote_execution_provider_test.rb +0 -12
- data/webpack/JobWizard/JobWizard.js +16 -59
- data/webpack/JobWizard/JobWizard.scss +0 -58
- data/webpack/JobWizard/JobWizardConstants.js +0 -18
- data/webpack/JobWizard/JobWizardSelectors.js +0 -9
- data/webpack/JobWizard/__tests__/JobWizard.test.js +13 -0
- data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +32 -0
- data/webpack/JobWizard/__tests__/fixtures.js +2 -112
- data/webpack/JobWizard/__tests__/integration.test.js +92 -16
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +9 -37
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +55 -116
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +16 -150
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +249 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +2 -4
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +51 -123
- data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +113 -0
- data/webpack/JobWizard/steps/form/FormHelpers.js +0 -1
- data/webpack/JobWizard/steps/form/SelectField.js +2 -14
- data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +38 -0
- data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +23 -0
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +37 -0
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +23 -0
- data/webpack/__mocks__/foremanReact/components/SearchBar.js +1 -18
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +0 -1
- metadata +13 -35
- data/app/models/host_proxy_invocation.rb +0 -4
- data/db/migrate/2021051713291621250977_add_host_proxy_invocations.rb +0 -12
- data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +0 -67
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +0 -23
- data/webpack/JobWizard/steps/HostsAndInputs/SelectedChips.js +0 -25
- data/webpack/JobWizard/steps/HostsAndInputs/TemplateInputs.js +0 -23
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/SelectedChips.test.js +0 -37
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/TemplateInputs.test.js +0 -50
- data/webpack/JobWizard/steps/HostsAndInputs/index.js +0 -66
- data/webpack/JobWizard/steps/Schedule/QueryType.js +0 -48
- data/webpack/JobWizard/steps/Schedule/RepeatOn.js +0 -61
- data/webpack/JobWizard/steps/Schedule/ScheduleType.js +0 -25
- data/webpack/JobWizard/steps/Schedule/StartEndDates.js +0 -51
- data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +0 -22
- data/webpack/JobWizard/steps/Schedule/index.js +0 -44
- data/webpack/JobWizard/steps/form/Formatter.js +0 -150
- data/webpack/JobWizard/steps/form/NumberInput.js +0 -35
- data/webpack/JobWizard/steps/form/WizardTitle.js +0 -14
- data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +0 -76
@@ -1,150 +0,0 @@
|
|
1
|
-
import React, { useEffect } from 'react';
|
2
|
-
import { useSelector } from 'react-redux';
|
3
|
-
import { FormGroup, TextInput, TextArea } from '@patternfly/react-core';
|
4
|
-
import PropTypes from 'prop-types';
|
5
|
-
import SearchBar from 'foremanReact/components/SearchBar';
|
6
|
-
import { helpLabel } from './FormHelpers';
|
7
|
-
import { SelectField } from './SelectField';
|
8
|
-
|
9
|
-
const TemplateSearchField = ({
|
10
|
-
name,
|
11
|
-
controller,
|
12
|
-
labelText,
|
13
|
-
required,
|
14
|
-
defaultValue,
|
15
|
-
setValue,
|
16
|
-
values,
|
17
|
-
}) => {
|
18
|
-
const searchQuery = useSelector(
|
19
|
-
state => state.autocomplete?.[name]?.searchQuery
|
20
|
-
);
|
21
|
-
useEffect(() => {
|
22
|
-
setValue({ ...values, [name]: searchQuery });
|
23
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
24
|
-
}, [searchQuery]);
|
25
|
-
const id = name.replace(/ /g, '-');
|
26
|
-
return (
|
27
|
-
<FormGroup
|
28
|
-
label={name}
|
29
|
-
labelIcon={helpLabel(labelText, name)}
|
30
|
-
fieldId={id}
|
31
|
-
isRequired={required}
|
32
|
-
className="foreman-search-field"
|
33
|
-
>
|
34
|
-
<SearchBar
|
35
|
-
initialQuery={defaultValue}
|
36
|
-
data={{
|
37
|
-
controller,
|
38
|
-
autocomplete: {
|
39
|
-
id: name,
|
40
|
-
url: `/${controller}/auto_complete_search`,
|
41
|
-
useKeyShortcuts: true,
|
42
|
-
},
|
43
|
-
}}
|
44
|
-
onSearch={() => null}
|
45
|
-
/>
|
46
|
-
</FormGroup>
|
47
|
-
);
|
48
|
-
};
|
49
|
-
|
50
|
-
export const formatter = (input, values, setValue) => {
|
51
|
-
const isSelectType = !!input?.options;
|
52
|
-
const inputType = input.value_type;
|
53
|
-
const isTextType = inputType === 'plain' || !inputType; // null defaults to plain
|
54
|
-
|
55
|
-
const { name, required, hidden_value: hidden } = input;
|
56
|
-
const labelText = input.description;
|
57
|
-
const value = values[name];
|
58
|
-
const id = name.replace(/ /g, '-');
|
59
|
-
if (isSelectType) {
|
60
|
-
const options = input.options.split(/\r?\n/).map(option => option.trim());
|
61
|
-
return (
|
62
|
-
<SelectField
|
63
|
-
aria-label={name}
|
64
|
-
key={id}
|
65
|
-
isRequired={required}
|
66
|
-
label={name}
|
67
|
-
fieldId={id}
|
68
|
-
options={options}
|
69
|
-
labelIcon={helpLabel(labelText, name)}
|
70
|
-
value={value}
|
71
|
-
setValue={newValue => setValue({ ...values, [name]: newValue })}
|
72
|
-
/>
|
73
|
-
);
|
74
|
-
}
|
75
|
-
if (isTextType) {
|
76
|
-
return (
|
77
|
-
<FormGroup
|
78
|
-
key={name}
|
79
|
-
label={name}
|
80
|
-
labelIcon={helpLabel(labelText, name)}
|
81
|
-
fieldId={id}
|
82
|
-
isRequired={required}
|
83
|
-
>
|
84
|
-
<TextArea
|
85
|
-
aria-label={name}
|
86
|
-
className={hidden ? 'masked-input' : null}
|
87
|
-
required={required}
|
88
|
-
rows={2}
|
89
|
-
id={id}
|
90
|
-
value={value}
|
91
|
-
onChange={newValue => setValue({ ...values, [name]: newValue })}
|
92
|
-
/>
|
93
|
-
</FormGroup>
|
94
|
-
);
|
95
|
-
}
|
96
|
-
if (inputType === 'date') {
|
97
|
-
return (
|
98
|
-
<FormGroup
|
99
|
-
key={name}
|
100
|
-
label={name}
|
101
|
-
labelIcon={helpLabel(labelText, name)}
|
102
|
-
fieldId={id}
|
103
|
-
isRequired={required}
|
104
|
-
>
|
105
|
-
<TextInput
|
106
|
-
aria-label={name}
|
107
|
-
placeholder="YYYY-mm-dd HH:MM"
|
108
|
-
className={hidden ? 'masked-input' : null}
|
109
|
-
required={required}
|
110
|
-
id={id}
|
111
|
-
type="text"
|
112
|
-
value={value}
|
113
|
-
onChange={newValue => setValue({ ...values, [name]: newValue })}
|
114
|
-
/>
|
115
|
-
</FormGroup>
|
116
|
-
);
|
117
|
-
}
|
118
|
-
if (inputType === 'search') {
|
119
|
-
const controller = input.resource_type;
|
120
|
-
// TODO: get text from redux autocomplete
|
121
|
-
return (
|
122
|
-
<TemplateSearchField
|
123
|
-
key={id}
|
124
|
-
name={name}
|
125
|
-
defaultValue={value}
|
126
|
-
controller={controller}
|
127
|
-
labelText={labelText}
|
128
|
-
required={required}
|
129
|
-
setValue={setValue}
|
130
|
-
values={values}
|
131
|
-
/>
|
132
|
-
);
|
133
|
-
}
|
134
|
-
|
135
|
-
return null;
|
136
|
-
};
|
137
|
-
|
138
|
-
TemplateSearchField.propTypes = {
|
139
|
-
name: PropTypes.string.isRequired,
|
140
|
-
controller: PropTypes.string.isRequired,
|
141
|
-
labelText: PropTypes.string,
|
142
|
-
required: PropTypes.bool.isRequired,
|
143
|
-
defaultValue: PropTypes.string,
|
144
|
-
setValue: PropTypes.func.isRequired,
|
145
|
-
values: PropTypes.object.isRequired,
|
146
|
-
};
|
147
|
-
TemplateSearchField.defaultProps = {
|
148
|
-
labelText: null,
|
149
|
-
defaultValue: '',
|
150
|
-
};
|
@@ -1,35 +0,0 @@
|
|
1
|
-
import React, { useState } from 'react';
|
2
|
-
import PropTypes from 'prop-types';
|
3
|
-
import { FormGroup, TextInput, ValidatedOptions } from '@patternfly/react-core';
|
4
|
-
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
-
|
6
|
-
export const NumberInput = ({ formProps, inputProps }) => {
|
7
|
-
const [validated, setValidated] = useState();
|
8
|
-
const name = inputProps.id.replace(/-/g, ' ');
|
9
|
-
return (
|
10
|
-
<FormGroup
|
11
|
-
{...formProps}
|
12
|
-
helperTextInvalid={__('Has to be a number')}
|
13
|
-
validated={validated}
|
14
|
-
>
|
15
|
-
<TextInput
|
16
|
-
aria-label={name}
|
17
|
-
type="text"
|
18
|
-
{...inputProps}
|
19
|
-
onChange={newValue => {
|
20
|
-
setValidated(
|
21
|
-
/^\d+$/.test(newValue) || newValue === ''
|
22
|
-
? ValidatedOptions.noval
|
23
|
-
: ValidatedOptions.error
|
24
|
-
);
|
25
|
-
inputProps.onChange(newValue);
|
26
|
-
}}
|
27
|
-
/>
|
28
|
-
</FormGroup>
|
29
|
-
);
|
30
|
-
};
|
31
|
-
|
32
|
-
NumberInput.propTypes = {
|
33
|
-
formProps: PropTypes.object.isRequired,
|
34
|
-
inputProps: PropTypes.object.isRequired,
|
35
|
-
};
|
@@ -1,14 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import PropTypes from 'prop-types';
|
3
|
-
import { Title } from '@patternfly/react-core';
|
4
|
-
|
5
|
-
export const WizardTitle = ({ title, ...props }) => (
|
6
|
-
<Title headingLevel="h2" className="wizard-title" {...props}>
|
7
|
-
{title}
|
8
|
-
</Title>
|
9
|
-
);
|
10
|
-
|
11
|
-
WizardTitle.propTypes = {
|
12
|
-
title: PropTypes.string.isRequired,
|
13
|
-
};
|
14
|
-
export default WizardTitle;
|
@@ -1,76 +0,0 @@
|
|
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, shallow } from '@theforeman/test';
|
6
|
-
import { formatter } from '../Formatter';
|
7
|
-
|
8
|
-
jest.spyOn(patternfly, 'Select');
|
9
|
-
jest.spyOn(patternfly, 'SelectOption');
|
10
|
-
jest.spyOn(patternfly, 'FormGroup');
|
11
|
-
patternfly.Select.mockImplementation(props => <div props={props} />);
|
12
|
-
patternfly.SelectOption.mockImplementation(props => <div props={props} />);
|
13
|
-
patternfly.FormGroup.mockImplementation(props => <div props={props} />);
|
14
|
-
const mockStore = configureMockStore([]);
|
15
|
-
const store = mockStore({});
|
16
|
-
|
17
|
-
describe('formatter', () => {
|
18
|
-
it('render date input', () => {
|
19
|
-
const props = {
|
20
|
-
name: 'date adv',
|
21
|
-
required: false,
|
22
|
-
options: '',
|
23
|
-
advanced: true,
|
24
|
-
value_type: 'date',
|
25
|
-
resource_type: 'ansible_roles',
|
26
|
-
default: '',
|
27
|
-
hidden_value: false,
|
28
|
-
};
|
29
|
-
expect(shallow(formatter(props, {}, jest.fn()))).toMatchSnapshot();
|
30
|
-
});
|
31
|
-
it('render text input', () => {
|
32
|
-
const props = {
|
33
|
-
name: 'plain adv hidden',
|
34
|
-
required: true,
|
35
|
-
description: 'some Description',
|
36
|
-
options: '',
|
37
|
-
advanced: true,
|
38
|
-
value_type: 'plain',
|
39
|
-
resource_type: 'ansible_roles',
|
40
|
-
default: 'Default val',
|
41
|
-
hidden_value: true,
|
42
|
-
};
|
43
|
-
expect(shallow(formatter(props, {}, jest.fn()))).toMatchSnapshot();
|
44
|
-
});
|
45
|
-
it('render select input', () => {
|
46
|
-
const props = {
|
47
|
-
name: 'adv plain search',
|
48
|
-
required: false,
|
49
|
-
input_type: 'user',
|
50
|
-
options: 'option 1\r\noption 2\r\noption 3\r\noption 4',
|
51
|
-
advanced: true,
|
52
|
-
value_type: 'plain',
|
53
|
-
resource_type: 'ansible_roles',
|
54
|
-
default: '',
|
55
|
-
hidden_value: false,
|
56
|
-
};
|
57
|
-
expect(shallow(formatter(props, {}, jest.fn()))).toMatchSnapshot();
|
58
|
-
});
|
59
|
-
it('render search input', () => {
|
60
|
-
const props = {
|
61
|
-
name: 'search adv',
|
62
|
-
required: false,
|
63
|
-
options: '',
|
64
|
-
advanced: true,
|
65
|
-
value_type: 'search',
|
66
|
-
resource_type: 'foreman_tasks/tasks',
|
67
|
-
default: '',
|
68
|
-
hidden_value: false,
|
69
|
-
};
|
70
|
-
expect(
|
71
|
-
mount(
|
72
|
-
<Provider store={store}>{formatter(props, {}, jest.fn())}</Provider>
|
73
|
-
)
|
74
|
-
).toMatchSnapshot();
|
75
|
-
});
|
76
|
-
});
|