foreman_remote_execution 4.4.0 → 4.5.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
};
|