foreman_remote_execution 4.4.0 → 4.5.3
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/app/controllers/api/v2/job_invocations_controller.rb +13 -24
- data/app/controllers/job_invocations_controller.rb +1 -1
- data/app/controllers/job_templates_controller.rb +4 -4
- data/app/controllers/ui_job_wizard_controller.rb +19 -0
- data/app/helpers/job_invocations_helper.rb +2 -2
- data/app/helpers/remote_execution_helper.rb +13 -9
- data/app/lib/actions/remote_execution/run_host_job.rb +36 -6
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +7 -5
- data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +6 -0
- data/app/models/host_proxy_invocation.rb +4 -0
- data/app/models/host_status/execution_status.rb +5 -5
- data/app/models/job_invocation.rb +31 -12
- data/app/models/job_invocation_composer.rb +61 -19
- data/app/models/remote_execution_provider.rb +1 -1
- data/app/models/setting/remote_execution.rb +2 -2
- data/app/models/ssh_execution_provider.rb +4 -4
- data/app/models/targeting.rb +5 -1
- data/app/overrides/execution_interface.rb +8 -8
- data/app/overrides/subnet_proxies.rb +6 -6
- data/app/views/job_invocations/index.html.erb +1 -1
- data/app/views/templates/ssh/module_action.erb +1 -0
- data/app/views/templates/ssh/puppet_run_once.erb +1 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20180110104432_rename_template_invocation_permission.rb +1 -1
- data/db/migrate/20190111153330_remove_remote_execution_without_proxy_setting.rb +4 -4
- data/db/migrate/2021051713291621250977_add_host_proxy_invocations.rb +12 -0
- data/extra/cockpit/foreman-cockpit-session +6 -6
- data/lib/foreman_remote_execution/engine.rb +11 -8
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/package.json +2 -1
- data/test/functional/api/v2/job_invocations_controller_test.rb +14 -1
- data/test/unit/job_invocation_composer_test.rb +59 -2
- data/test/unit/job_invocation_test.rb +1 -1
- data/webpack/JobWizard/JobWizard.js +80 -19
- data/webpack/JobWizard/JobWizard.scss +42 -1
- data/webpack/JobWizard/JobWizardConstants.js +11 -0
- data/webpack/JobWizard/JobWizardSelectors.js +27 -1
- data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +43 -0
- data/webpack/JobWizard/__tests__/fixtures.js +128 -0
- data/webpack/JobWizard/__tests__/integration.test.js +84 -0
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +110 -0
- data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +67 -0
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +195 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +144 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +23 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +34 -2
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +122 -44
- data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +9 -1
- data/webpack/JobWizard/steps/Schedule/QueryType.js +48 -0
- data/webpack/JobWizard/steps/Schedule/RepeatOn.js +61 -0
- data/webpack/JobWizard/steps/Schedule/ScheduleType.js +25 -0
- data/webpack/JobWizard/steps/Schedule/StartEndDates.js +51 -0
- data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +22 -0
- data/webpack/JobWizard/steps/Schedule/index.js +41 -0
- data/webpack/JobWizard/steps/form/FormHelpers.js +20 -0
- data/webpack/JobWizard/steps/form/Formatter.js +149 -0
- data/webpack/JobWizard/steps/form/GroupedSelectField.js +3 -0
- data/webpack/JobWizard/steps/form/NumberInput.js +33 -0
- data/webpack/JobWizard/steps/form/SelectField.js +24 -3
- data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +76 -0
- data/webpack/__mocks__/foremanReact/components/SearchBar.js +18 -1
- data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +21 -2
- data/webpack/global_index.js +5 -3
- data/webpack/index.js +3 -0
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +1 -5
- data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +8 -3
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +7 -2
- data/webpack/react_app/extend/{fills.js → fillRecentJobsCard.js} +7 -6
- data/webpack/react_app/extend/fillregistrationAdvanced.js +11 -0
- data/webpack/react_app/extend/reducers.js +2 -1
- metadata +24 -14
- data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +0 -70
- data/test/models/orchestration/ssh_test.rb +0 -56
- data/webpack/JobWizard/__tests__/JobWizard.test.js +0 -20
- data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +0 -83
- data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +0 -64
- data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +0 -38
- data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +0 -23
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +0 -36
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +0 -22
- data/webpack/fills_index.js +0 -11
|
@@ -622,7 +622,7 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
|
|
|
622
622
|
end
|
|
623
623
|
end
|
|
624
624
|
|
|
625
|
-
describe '
|
|
625
|
+
describe '.from_api_params' do
|
|
626
626
|
let(:composer) do
|
|
627
627
|
JobInvocationComposer.from_api_params(params)
|
|
628
628
|
end
|
|
@@ -754,12 +754,23 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
|
|
|
754
754
|
assert composer.save!
|
|
755
755
|
_(composer.job_invocation.remote_execution_feature).must_equal feature
|
|
756
756
|
end
|
|
757
|
+
|
|
758
|
+
it 'sets the remote execution_feature id based on `feature` param' do
|
|
759
|
+
params[:remote_execution_feature_id] = nil
|
|
760
|
+
params[:feature] = feature.label
|
|
761
|
+
params[:job_template_id] = trying_job_template_1.id
|
|
762
|
+
refute_equal feature.job_template, trying_job_template_1
|
|
763
|
+
|
|
764
|
+
assert composer.save!
|
|
765
|
+
_(composer.job_invocation.remote_execution_feature).must_equal feature
|
|
766
|
+
end
|
|
757
767
|
end
|
|
758
768
|
|
|
759
769
|
context 'with invalid targeting' do
|
|
760
770
|
let(:params) do
|
|
761
771
|
{ :job_category => trying_job_template_1.job_category,
|
|
762
772
|
:job_template_id => trying_job_template_1.id,
|
|
773
|
+
:targeting_type => 'fake',
|
|
763
774
|
:search_query => 'some hosts',
|
|
764
775
|
:inputs => {input1.name => 'some_value'}}
|
|
765
776
|
end
|
|
@@ -853,7 +864,9 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
|
|
|
853
864
|
it 'marks targeting as resolved if static' do
|
|
854
865
|
created = JobInvocationComposer.from_job_invocation(job_invocation).job_invocation
|
|
855
866
|
assert created.targeting.resolved?
|
|
856
|
-
|
|
867
|
+
created.targeting.save
|
|
868
|
+
created.targeting.reload
|
|
869
|
+
assert_equal job_invocation.template_invocations_host_ids, created.targeting.targeting_hosts.pluck(:host_id)
|
|
857
870
|
end
|
|
858
871
|
|
|
859
872
|
it 'takes randomized_ordering from the original job invocation when rerunning failed' do
|
|
@@ -863,6 +876,50 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
|
|
|
863
876
|
composer = JobInvocationComposer.from_job_invocation(job_invocation, :host_ids => host_ids)
|
|
864
877
|
assert composer.job_invocation.targeting.randomized_ordering
|
|
865
878
|
end
|
|
879
|
+
|
|
880
|
+
it 'works with invalid hosts' do
|
|
881
|
+
host = job_invocation.targeting.hosts.first
|
|
882
|
+
::Host::Managed.any_instance.stubs(:valid?).returns(false)
|
|
883
|
+
composer = JobInvocationComposer.from_job_invocation(job_invocation, {})
|
|
884
|
+
targeting = composer.compose.job_invocation.targeting
|
|
885
|
+
targeting.save!
|
|
886
|
+
targeting.reload
|
|
887
|
+
assert targeting.valid?
|
|
888
|
+
assert_equal targeting.hosts.pluck(:id), [host.id]
|
|
889
|
+
end
|
|
890
|
+
end
|
|
891
|
+
|
|
892
|
+
describe '.for_feature' do
|
|
893
|
+
let(:feature) { FactoryBot.create(:remote_execution_feature, job_template: trying_job_template_1) }
|
|
894
|
+
let(:host) { FactoryBot.create(:host) }
|
|
895
|
+
let(:bookmark) { Bookmark.create!(:query => 'b', :name => 'bookmark', :public => true, :controller => 'hosts') }
|
|
896
|
+
|
|
897
|
+
context 'specifying hosts' do
|
|
898
|
+
it 'takes a bookmarked search' do
|
|
899
|
+
composer = JobInvocationComposer.for_feature(feature.label, bookmark, {})
|
|
900
|
+
assert_equal bookmark.id, composer.params['targeting']['bookmark_id']
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
it 'takes an array of host ids' do
|
|
904
|
+
composer = JobInvocationComposer.for_feature(feature.label, [host.id], {})
|
|
905
|
+
assert_match(/#{host.name}/, composer.params['targeting']['search_query'])
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
it 'takes a single host object' do
|
|
909
|
+
composer = JobInvocationComposer.for_feature(feature.label, host, {})
|
|
910
|
+
assert_match(/#{host.name}/, composer.params['targeting']['search_query'])
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
it 'takes an array of host FQDNs' do
|
|
914
|
+
composer = JobInvocationComposer.for_feature(feature.label, [host.fqdn], {})
|
|
915
|
+
assert_match(/#{host.name}/, composer.params['targeting']['search_query'])
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
it 'takes a search query string' do
|
|
919
|
+
composer = JobInvocationComposer.for_feature(feature.label, 'host.example.com', {})
|
|
920
|
+
assert_equal 'host.example.com', composer.search_query
|
|
921
|
+
end
|
|
922
|
+
end
|
|
866
923
|
end
|
|
867
924
|
|
|
868
925
|
describe '#resolve_job_category and #resolve job_templates' do
|
|
@@ -10,7 +10,7 @@ class JobInvocationTest < ActiveSupport::TestCase
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
it 'is able to perform search through job invocations' do
|
|
13
|
-
found_jobs = JobInvocation.search_for(%{job_category = "#{job_invocation.job_category}"}).paginate(:page => 1).
|
|
13
|
+
found_jobs = JobInvocation.search_for(%{job_category = "#{job_invocation.job_category}"}).paginate(:page => 1).order('job_invocations.id DESC')
|
|
14
14
|
_(found_jobs).must_equal [job_invocation]
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -1,55 +1,116 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
3
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
2
4
|
import { Wizard } from '@patternfly/react-core';
|
|
5
|
+
import { get } from 'foremanReact/redux/API';
|
|
3
6
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
4
7
|
import history from 'foremanReact/history';
|
|
5
8
|
import CategoryAndTemplate from './steps/CategoryAndTemplate/';
|
|
9
|
+
import { AdvancedFields } from './steps/AdvancedFields/AdvancedFields';
|
|
10
|
+
import { JOB_TEMPLATE } from './JobWizardConstants';
|
|
11
|
+
import { selectTemplateError } from './JobWizardSelectors';
|
|
12
|
+
import Schedule from './steps/Schedule/';
|
|
6
13
|
import './JobWizard.scss';
|
|
7
14
|
|
|
8
15
|
export const JobWizard = () => {
|
|
9
|
-
const [
|
|
16
|
+
const [jobTemplateID, setJobTemplateID] = useState(null);
|
|
10
17
|
const [category, setCategory] = useState('');
|
|
18
|
+
const [advancedValues, setAdvancedValues] = useState({});
|
|
19
|
+
const dispatch = useDispatch();
|
|
20
|
+
|
|
21
|
+
const setDefaults = useCallback(
|
|
22
|
+
({
|
|
23
|
+
data: {
|
|
24
|
+
advanced_template_inputs,
|
|
25
|
+
effective_user,
|
|
26
|
+
job_template: { executionTimeoutInterval, description_format },
|
|
27
|
+
},
|
|
28
|
+
}) => {
|
|
29
|
+
const advancedTemplateValues = {};
|
|
30
|
+
const advancedInputs = advanced_template_inputs;
|
|
31
|
+
if (advancedInputs) {
|
|
32
|
+
advancedInputs.forEach(input => {
|
|
33
|
+
advancedTemplateValues[input.name] = input?.default || '';
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
setAdvancedValues(currentAdvancedValues => ({
|
|
37
|
+
...currentAdvancedValues,
|
|
38
|
+
effectiveUserValue: effective_user?.value || '',
|
|
39
|
+
timeoutToKill: executionTimeoutInterval || '',
|
|
40
|
+
templateValues: advancedTemplateValues,
|
|
41
|
+
description: description_format || '',
|
|
42
|
+
}));
|
|
43
|
+
},
|
|
44
|
+
[]
|
|
45
|
+
);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (jobTemplateID) {
|
|
48
|
+
dispatch(
|
|
49
|
+
get({
|
|
50
|
+
key: JOB_TEMPLATE,
|
|
51
|
+
url: `/ui_job_wizard/template/${jobTemplateID}`,
|
|
52
|
+
handleSuccess: setDefaults,
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}, [jobTemplateID, setDefaults, dispatch]);
|
|
57
|
+
|
|
58
|
+
const templateError = !!useSelector(selectTemplateError);
|
|
59
|
+
const isTemplate = !templateError && !!jobTemplateID;
|
|
11
60
|
const steps = [
|
|
12
61
|
{
|
|
13
|
-
name: __('Category and
|
|
62
|
+
name: __('Category and Template'),
|
|
14
63
|
component: (
|
|
15
64
|
<CategoryAndTemplate
|
|
16
|
-
jobTemplate={
|
|
17
|
-
setJobTemplate={
|
|
65
|
+
jobTemplate={jobTemplateID}
|
|
66
|
+
setJobTemplate={setJobTemplateID}
|
|
18
67
|
category={category}
|
|
19
68
|
setCategory={setCategory}
|
|
20
69
|
/>
|
|
21
70
|
),
|
|
22
71
|
},
|
|
23
72
|
{
|
|
24
|
-
name: __('Target
|
|
25
|
-
component: <p>
|
|
26
|
-
canJumpTo:
|
|
73
|
+
name: __('Target Hosts'),
|
|
74
|
+
component: <p>Target Hosts</p>,
|
|
75
|
+
canJumpTo: isTemplate,
|
|
27
76
|
},
|
|
28
77
|
{
|
|
29
|
-
name: __('Advanced
|
|
30
|
-
component:
|
|
31
|
-
|
|
78
|
+
name: __('Advanced Fields'),
|
|
79
|
+
component: (
|
|
80
|
+
<AdvancedFields
|
|
81
|
+
advancedValues={advancedValues}
|
|
82
|
+
setAdvancedValues={newValues => {
|
|
83
|
+
setAdvancedValues(currentAdvancedValues => ({
|
|
84
|
+
...currentAdvancedValues,
|
|
85
|
+
...newValues,
|
|
86
|
+
}));
|
|
87
|
+
}}
|
|
88
|
+
jobTemplateID={jobTemplateID}
|
|
89
|
+
/>
|
|
90
|
+
),
|
|
91
|
+
canJumpTo: isTemplate,
|
|
32
92
|
},
|
|
33
93
|
{
|
|
34
94
|
name: __('Schedule'),
|
|
35
|
-
component: <
|
|
36
|
-
canJumpTo:
|
|
95
|
+
component: <Schedule />,
|
|
96
|
+
canJumpTo: isTemplate,
|
|
37
97
|
},
|
|
38
98
|
{
|
|
39
|
-
name: __('Review
|
|
40
|
-
component: <p>
|
|
99
|
+
name: __('Review Details'),
|
|
100
|
+
component: <p>Review Details</p>,
|
|
41
101
|
nextButtonText: 'Run',
|
|
42
|
-
canJumpTo:
|
|
102
|
+
canJumpTo: isTemplate,
|
|
43
103
|
},
|
|
44
104
|
];
|
|
45
|
-
const title = __('Run Job');
|
|
46
105
|
return (
|
|
47
106
|
<Wizard
|
|
48
107
|
onClose={() => history.goBack()}
|
|
49
|
-
navAriaLabel=
|
|
108
|
+
navAriaLabel="Run Job steps"
|
|
50
109
|
steps={steps}
|
|
51
|
-
height="
|
|
110
|
+
height="100%"
|
|
52
111
|
className="job-wizard"
|
|
53
112
|
/>
|
|
54
113
|
);
|
|
55
114
|
};
|
|
115
|
+
|
|
116
|
+
export default JobWizard;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
.job-wizard {
|
|
2
2
|
.pf-c-wizard__main {
|
|
3
|
-
overflow: visible;
|
|
4
3
|
z-index: calc(
|
|
5
4
|
var(--pf-c-wizard__footer--ZIndex) + 1
|
|
6
5
|
); // So the select box can be shown above the wizard footer
|
|
@@ -8,5 +7,47 @@
|
|
|
8
7
|
|
|
9
8
|
.pf-c-wizard__main-body {
|
|
10
9
|
max-width: 500px;
|
|
10
|
+
.advanced-fields-title {
|
|
11
|
+
margin-bottom: 10px;
|
|
12
|
+
}
|
|
13
|
+
#advanced-fields-job-template {
|
|
14
|
+
.foreman-search-field {
|
|
15
|
+
// Giving pf3 search bar a pf4 look
|
|
16
|
+
.search-bar {
|
|
17
|
+
display: block;
|
|
18
|
+
}
|
|
19
|
+
.input-group-btn {
|
|
20
|
+
display: none;
|
|
21
|
+
}
|
|
22
|
+
li {
|
|
23
|
+
font-size: 16px;
|
|
24
|
+
}
|
|
25
|
+
input {
|
|
26
|
+
font-size: 16px;
|
|
27
|
+
height: 36px;
|
|
28
|
+
}
|
|
29
|
+
.foreman-autocomplete .autocomplete-focus-shortcut {
|
|
30
|
+
top: 8px;
|
|
31
|
+
font-size: 16px;
|
|
32
|
+
}
|
|
33
|
+
.foreman-autocomplete .autocomplete-aux {
|
|
34
|
+
top: 8px;
|
|
35
|
+
font-size: 16px;
|
|
36
|
+
.autocomplete-clear-button {
|
|
37
|
+
font-size: 16px;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.schedule-tab {
|
|
45
|
+
input[type='radio'],
|
|
46
|
+
input[type='checkbox'] {
|
|
47
|
+
margin: 0;
|
|
48
|
+
}
|
|
49
|
+
.advanced-scheduling-button {
|
|
50
|
+
text-align: start;
|
|
51
|
+
}
|
|
11
52
|
}
|
|
12
53
|
}
|
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
1
2
|
import { foremanUrl } from 'foremanReact/common/helpers';
|
|
2
3
|
|
|
3
4
|
export const JOB_TEMPLATES = 'JOB_TEMPLATES';
|
|
4
5
|
export const JOB_CATEGORIES = 'JOB_CATEGORIES';
|
|
6
|
+
export const JOB_TEMPLATE = 'JOB_TEMPLATE';
|
|
5
7
|
export const templatesUrl = foremanUrl('/api/v2/job_templates');
|
|
8
|
+
|
|
9
|
+
export const repeatTypes = {
|
|
10
|
+
noRepeat: __('Does not repeat'),
|
|
11
|
+
cronline: __('Cronline'),
|
|
12
|
+
monthly: __('Monthly'),
|
|
13
|
+
weekly: __('Weekly'),
|
|
14
|
+
daily: __('Daily'),
|
|
15
|
+
hourly: __('Hourly'),
|
|
16
|
+
};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
selectAPIResponse,
|
|
3
3
|
selectAPIStatus,
|
|
4
|
+
selectAPIErrorMessage,
|
|
4
5
|
} from 'foremanReact/redux/API/APISelectors';
|
|
5
6
|
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
JOB_TEMPLATES,
|
|
9
|
+
JOB_CATEGORIES,
|
|
10
|
+
JOB_TEMPLATE,
|
|
11
|
+
} from './JobWizardConstants';
|
|
7
12
|
|
|
8
13
|
export const selectJobTemplatesStatus = state =>
|
|
9
14
|
selectAPIStatus(state, JOB_TEMPLATES);
|
|
@@ -19,3 +24,24 @@ export const selectJobCategories = state =>
|
|
|
19
24
|
|
|
20
25
|
export const selectJobCategoriesStatus = state =>
|
|
21
26
|
selectAPIStatus(state, JOB_CATEGORIES);
|
|
27
|
+
|
|
28
|
+
export const selectCategoryError = state =>
|
|
29
|
+
selectAPIErrorMessage(state, JOB_CATEGORIES);
|
|
30
|
+
|
|
31
|
+
export const selectAllTemplatesError = state =>
|
|
32
|
+
selectAPIErrorMessage(state, JOB_TEMPLATES);
|
|
33
|
+
|
|
34
|
+
export const selectTemplateError = state =>
|
|
35
|
+
selectAPIErrorMessage(state, JOB_TEMPLATE);
|
|
36
|
+
|
|
37
|
+
export const selectJobTemplate = state =>
|
|
38
|
+
selectAPIResponse(state, JOB_TEMPLATE);
|
|
39
|
+
|
|
40
|
+
export const selectEffectiveUser = state =>
|
|
41
|
+
selectAPIResponse(state, JOB_TEMPLATE).effective_user;
|
|
42
|
+
|
|
43
|
+
export const selectAdvancedTemplateInputs = state =>
|
|
44
|
+
selectAPIResponse(state, JOB_TEMPLATE).advanced_template_inputs || [];
|
|
45
|
+
|
|
46
|
+
export const selectTemplateInputs = state =>
|
|
47
|
+
selectAPIResponse(state, JOB_TEMPLATE).template_inputs || [];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`Job wizard fill should select template: initial 1`] = `
|
|
4
|
+
Array [
|
|
5
|
+
Object {
|
|
6
|
+
"key": "JOB_CATEGORIES",
|
|
7
|
+
"type": "get",
|
|
8
|
+
"url": "/ui_job_wizard/categories",
|
|
9
|
+
},
|
|
10
|
+
Object {
|
|
11
|
+
"key": "JOB_TEMPLATES",
|
|
12
|
+
"type": "get",
|
|
13
|
+
"url": URI {
|
|
14
|
+
"_deferred_build": true,
|
|
15
|
+
"_parts": Object {
|
|
16
|
+
"duplicateQueryParameters": false,
|
|
17
|
+
"escapeQuerySpace": true,
|
|
18
|
+
"fragment": null,
|
|
19
|
+
"hostname": null,
|
|
20
|
+
"password": null,
|
|
21
|
+
"path": "foreman/api/v2/job_templates",
|
|
22
|
+
"port": null,
|
|
23
|
+
"preventInvalidHostname": false,
|
|
24
|
+
"protocol": null,
|
|
25
|
+
"query": "search=job_category%3D%22Ansible+Commands%22&per_page=all",
|
|
26
|
+
"urn": null,
|
|
27
|
+
"username": null,
|
|
28
|
+
},
|
|
29
|
+
"_string": "",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
]
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
exports[`Job wizard fill should select template: select template 1`] = `
|
|
36
|
+
Array [
|
|
37
|
+
Object {
|
|
38
|
+
"key": "JOB_TEMPLATE",
|
|
39
|
+
"type": "get",
|
|
40
|
+
"url": "/ui_job_wizard/template/178",
|
|
41
|
+
},
|
|
42
|
+
]
|
|
43
|
+
`;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import configureMockStore from 'redux-mock-store';
|
|
2
|
+
|
|
3
|
+
export const jobTemplate = {
|
|
4
|
+
id: 178,
|
|
5
|
+
name: 'template1',
|
|
6
|
+
template:
|
|
7
|
+
"---\n- hosts: all\n tasks:\n - shell:\n cmd: |\n<%= indent(10) { input('command') } %>\n register: out\n - debug: var=out",
|
|
8
|
+
snippet: false,
|
|
9
|
+
default: true,
|
|
10
|
+
job_category: 'Ansible Commands',
|
|
11
|
+
provider_type: 'Ansible',
|
|
12
|
+
description_format: 'Run %{command}',
|
|
13
|
+
execution_timeout_interval: 2,
|
|
14
|
+
description: null,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const jobTemplates = [jobTemplate];
|
|
18
|
+
|
|
19
|
+
export const jobTemplateResponse = {
|
|
20
|
+
job_template: jobTemplate,
|
|
21
|
+
effective_user: {
|
|
22
|
+
id: null,
|
|
23
|
+
job_template_id: 178,
|
|
24
|
+
value: 'default effective user',
|
|
25
|
+
overridable: true,
|
|
26
|
+
current_user: false,
|
|
27
|
+
},
|
|
28
|
+
advanced_template_inputs: [
|
|
29
|
+
{
|
|
30
|
+
name: 'adv plain hidden',
|
|
31
|
+
required: true,
|
|
32
|
+
input_type: 'user',
|
|
33
|
+
description: 'some Description',
|
|
34
|
+
advanced: true,
|
|
35
|
+
value_type: 'plain',
|
|
36
|
+
resource_type: 'ansible_roles',
|
|
37
|
+
default: 'Default val',
|
|
38
|
+
hidden_value: true,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'adv plain select',
|
|
42
|
+
required: false,
|
|
43
|
+
input_type: 'user',
|
|
44
|
+
options: 'option 1\r\noption 2\r\noption 3\r\noption 4',
|
|
45
|
+
advanced: true,
|
|
46
|
+
value_type: 'plain',
|
|
47
|
+
resource_type: 'ansible_roles',
|
|
48
|
+
default: '',
|
|
49
|
+
hidden_value: false,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'adv search',
|
|
53
|
+
required: false,
|
|
54
|
+
options: '',
|
|
55
|
+
advanced: true,
|
|
56
|
+
value_type: 'search',
|
|
57
|
+
resource_type: 'foreman_tasks/tasks',
|
|
58
|
+
default: '',
|
|
59
|
+
hidden_value: false,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'adv date',
|
|
63
|
+
required: false,
|
|
64
|
+
options: '',
|
|
65
|
+
advanced: true,
|
|
66
|
+
value_type: 'date',
|
|
67
|
+
resource_type: 'ansible_roles',
|
|
68
|
+
default: '',
|
|
69
|
+
hidden_value: false,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
template_inputs: [
|
|
73
|
+
{
|
|
74
|
+
name: 'plain hidden',
|
|
75
|
+
required: true,
|
|
76
|
+
input_type: 'user',
|
|
77
|
+
description: 'some Description',
|
|
78
|
+
advanced: false,
|
|
79
|
+
value_type: 'plain',
|
|
80
|
+
resource_type: 'ansible_roles',
|
|
81
|
+
default: 'Default val',
|
|
82
|
+
hidden_value: true,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const jobCategories = ['Ansible Commands', 'Puppet', 'Services'];
|
|
88
|
+
|
|
89
|
+
export const testSetup = (selectors, api) => {
|
|
90
|
+
jest.spyOn(api, 'get');
|
|
91
|
+
jest.spyOn(selectors, 'selectJobTemplate');
|
|
92
|
+
jest.spyOn(selectors, 'selectJobTemplates');
|
|
93
|
+
jest.spyOn(selectors, 'selectJobCategories');
|
|
94
|
+
jest.spyOn(selectors, 'selectJobCategoriesStatus');
|
|
95
|
+
|
|
96
|
+
selectors.selectJobCategories.mockImplementation(() => jobCategories);
|
|
97
|
+
selectors.selectJobTemplates.mockImplementation(() => [
|
|
98
|
+
jobTemplate,
|
|
99
|
+
{ ...jobTemplate, id: 2, name: 'template2' },
|
|
100
|
+
]);
|
|
101
|
+
const mockStore = configureMockStore([]);
|
|
102
|
+
const store = mockStore({});
|
|
103
|
+
return store;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const mockTemplate = selectors => {
|
|
107
|
+
selectors.selectJobTemplate.mockImplementation(() => jobTemplate);
|
|
108
|
+
selectors.selectJobCategoriesStatus.mockImplementation(() => 'RESOLVED');
|
|
109
|
+
};
|
|
110
|
+
export const mockApi = api => {
|
|
111
|
+
api.get.mockImplementation(({ handleSuccess, ...action }) => {
|
|
112
|
+
if (action.key === 'JOB_CATEGORIES') {
|
|
113
|
+
handleSuccess &&
|
|
114
|
+
handleSuccess({ data: { job_categories: jobCategories } });
|
|
115
|
+
} else if (action.key === 'JOB_TEMPLATE') {
|
|
116
|
+
handleSuccess &&
|
|
117
|
+
handleSuccess({
|
|
118
|
+
data: jobTemplateResponse,
|
|
119
|
+
});
|
|
120
|
+
} else if (action.key === 'JOB_TEMPLATES') {
|
|
121
|
+
handleSuccess &&
|
|
122
|
+
handleSuccess({
|
|
123
|
+
data: { results: [jobTemplate] },
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return { type: 'get', ...action };
|
|
127
|
+
});
|
|
128
|
+
};
|