foreman_remote_execution 7.2.1 → 8.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/ruby_ci.yml +2 -2
- data/app/controllers/ui_job_wizard_controller.rb +15 -0
- data/app/helpers/remote_execution_helper.rb +1 -1
- data/app/models/job_invocation.rb +2 -4
- data/app/models/job_invocation_composer.rb +5 -2
- data/app/views/templates/script/package_action.erb +8 -3
- data/config/routes.rb +3 -1
- data/lib/foreman_remote_execution/engine.rb +5 -5
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/functional/api/v2/job_invocations_controller_test.rb +8 -0
- data/test/helpers/remote_execution_helper_test.rb +4 -0
- data/test/unit/job_invocation_report_template_test.rb +1 -1
- data/test/unit/job_invocation_test.rb +1 -2
- data/webpack/JobWizard/JobWizard.js +154 -20
- data/webpack/JobWizard/JobWizard.scss +43 -1
- data/webpack/JobWizard/JobWizardConstants.js +11 -1
- data/webpack/JobWizard/JobWizardPageRerun.js +112 -0
- data/webpack/JobWizard/__tests__/JobWizardPageRerun.test.js +79 -0
- data/webpack/JobWizard/__tests__/fixtures.js +73 -0
- data/webpack/JobWizard/__tests__/integration.test.js +17 -3
- data/webpack/JobWizard/autofill.js +8 -1
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +36 -17
- data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +3 -3
- data/webpack/JobWizard/steps/ReviewDetails/index.js +1 -3
- data/webpack/JobWizard/steps/Schedule/PurposeField.js +1 -3
- data/webpack/JobWizard/steps/Schedule/QueryType.js +33 -40
- data/webpack/JobWizard/steps/Schedule/RepeatHour.js +55 -16
- data/webpack/JobWizard/steps/Schedule/RepeatOn.js +19 -56
- data/webpack/JobWizard/steps/Schedule/RepeatWeek.js +1 -1
- data/webpack/JobWizard/steps/Schedule/ScheduleFuture.js +126 -0
- data/webpack/JobWizard/steps/Schedule/ScheduleRecurring.js +287 -0
- data/webpack/JobWizard/steps/Schedule/ScheduleType.js +88 -20
- data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +206 -186
- data/webpack/JobWizard/steps/form/DateTimePicker.js +23 -6
- data/webpack/JobWizard/steps/form/Formatter.js +7 -8
- data/webpack/JobWizard/submit.js +8 -3
- data/webpack/Routes/routes.js +8 -2
- data/webpack/__mocks__/foremanReact/common/hooks/API/APIHooks.js +1 -0
- data/webpack/react_app/components/HostKebab/KebabItems.js +0 -1
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +0 -5
- data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +59 -51
- data/webpack/react_app/extend/Fills.js +4 -4
- metadata +8 -6
- data/webpack/JobWizard/steps/Schedule/StartEndDates.js +0 -106
- data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +0 -32
- data/webpack/JobWizard/steps/Schedule/index.js +0 -178
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa3c6d67549990da3bbd1d7cbf55408bd4a1dc4d7fc5cbcec8db99eded747964
|
4
|
+
data.tar.gz: ca4e519773be6b0493341fd25ba55e8d4345367c88e0b068183ebe66d020e22e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bbeb56766cc65ed9fcd587f260fb29fca965b961b0787230f0510166702351cb5ff0c78387b3f818b9741f281519f33931bc8d0aa36a923d92e0d50f620d113e
|
7
|
+
data.tar.gz: 3238dc8bc7cdbaa361a6c077e5d611839cb549424ad98daea9e68ac81a932dd6c8de2f233b31f019cc7a1d037b6fea4a656ca3d7e789eb9a794170fb26ac1f68
|
@@ -16,7 +16,7 @@ jobs:
|
|
16
16
|
- name: Setup Ruby
|
17
17
|
uses: ruby/setup-ruby@v1
|
18
18
|
with:
|
19
|
-
ruby-version: 2.
|
19
|
+
ruby-version: 2.7
|
20
20
|
bundler-cache: true
|
21
21
|
cache-version: 1
|
22
22
|
rubygems: 3.0.0
|
@@ -38,7 +38,7 @@ jobs:
|
|
38
38
|
fail-fast: false
|
39
39
|
matrix:
|
40
40
|
foreman-core-branch: [develop]
|
41
|
-
ruby-version: [2.
|
41
|
+
ruby-version: [2.7]
|
42
42
|
node-version: [12]
|
43
43
|
steps:
|
44
44
|
- run: sudo apt-get update
|
@@ -57,4 +57,19 @@ class UiJobWizardController < ApplicationController
|
|
57
57
|
render :json => { :results =>
|
58
58
|
resource_list.sort_by { |r| r[:name] }.take(100), :subtotal => resource_list.count}
|
59
59
|
end
|
60
|
+
|
61
|
+
def job_invocation
|
62
|
+
job = JobInvocation.authorized.find(params[:id])
|
63
|
+
composer = JobInvocationComposer.from_job_invocation(job, params).params
|
64
|
+
job_template_inputs = JobTemplate.authorized.find(composer[:template_invocations][0][:template_id]).template_inputs_with_foreign
|
65
|
+
inputs = Hash[job_template_inputs.map { |input| ["inputs[#{input[:name]}]", (composer[:template_invocations][0][:input_values].find { |value| value[:template_input_id] == input[:id] })[:value]] }]
|
66
|
+
job_organization = Taxonomy.find_by(id: job.task.input[:current_organization_id])
|
67
|
+
job_location = Taxonomy.find_by(id: job.task.input[:current_location_id])
|
68
|
+
render :json => {
|
69
|
+
:job => composer,
|
70
|
+
:job_organization => job_organization,
|
71
|
+
:job_location => job_location,
|
72
|
+
:inputs => inputs,
|
73
|
+
}
|
74
|
+
end
|
60
75
|
end
|
@@ -246,7 +246,7 @@ module RemoteExecutionHelper
|
|
246
246
|
def job_report_template
|
247
247
|
template = ReportTemplate.where(name: Setting['remote_execution_job_invocation_report_template']).first
|
248
248
|
|
249
|
-
template if template.template_inputs.where(name: 'job_id').exists?
|
249
|
+
template if template && template.template_inputs.where(name: 'job_id').exists?
|
250
250
|
end
|
251
251
|
|
252
252
|
def job_report_template_parameters(job_invocation, template)
|
@@ -62,16 +62,14 @@ class JobInvocation < ApplicationRecord
|
|
62
62
|
|
63
63
|
has_many :targeted_hosts, :through => :targeting, :source => :hosts
|
64
64
|
scoped_search :on => 'targeted_host_id', :rename => 'targeted_host_id', :operators => ['= '],
|
65
|
-
:complete_value => false, :only_explicit => true, :ext_method => :search_by_targeted_host
|
66
|
-
:validator => ScopedSearch::Validators::INTEGER
|
65
|
+
:complete_value => false, :only_explicit => true, :ext_method => :search_by_targeted_host
|
67
66
|
|
68
67
|
scoped_search :on => 'pattern_template_name', :rename => 'pattern_template_name', :operators => ['= '],
|
69
68
|
:complete_value => false, :only_explicit => true, :ext_method => :search_by_pattern_template
|
70
69
|
|
71
70
|
scoped_search :relation => :recurring_logic, :on => 'purpose', :rename => 'recurring_logic.purpose'
|
72
71
|
|
73
|
-
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
|
74
|
-
:validator => ScopedSearch::Validators::INTEGER
|
72
|
+
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
|
75
73
|
|
76
74
|
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring',
|
77
75
|
:ext_method => :search_by_recurring_logic, :only_explicit => true,
|
@@ -441,8 +441,11 @@ class JobInvocationComposer
|
|
441
441
|
end
|
442
442
|
|
443
443
|
def valid?
|
444
|
-
|
445
|
-
triggering
|
444
|
+
unless triggering.valid?
|
445
|
+
job_invocation.errors.add(:triggering, 'is invalid')
|
446
|
+
return false
|
447
|
+
end
|
448
|
+
targeting.valid? & job_invocation.valid? & !pattern_template_invocations.map(&:valid?).include?(false)
|
446
449
|
end
|
447
450
|
|
448
451
|
def save
|
@@ -16,6 +16,11 @@ template_inputs:
|
|
16
16
|
input_type: user
|
17
17
|
required: true
|
18
18
|
options: "install\nupdate\nremove\ngroup install\ngroup remove"
|
19
|
+
- name: options
|
20
|
+
description: Additional options for the package manager
|
21
|
+
input_type: user
|
22
|
+
required: false
|
23
|
+
advanced: true
|
19
24
|
- name: package
|
20
25
|
description: The name of the package, if any
|
21
26
|
input_type: user
|
@@ -87,7 +92,7 @@ handle_zypp_res_codes () {
|
|
87
92
|
|
88
93
|
# Action
|
89
94
|
<% if package_manager == 'yum' -%>
|
90
|
-
yum -y <%= action %> <%= input("package") %>
|
95
|
+
yum -y <%= input("options") %> <%= action %> <%= input("package") %>
|
91
96
|
<% elsif package_manager == 'apt' -%>
|
92
97
|
<%-
|
93
98
|
action = 'install' if action == 'group install'
|
@@ -103,7 +108,7 @@ handle_zypp_res_codes () {
|
|
103
108
|
[ -x "$(command -v subscription-manager)" ] && subscription-manager refresh
|
104
109
|
export DEBIAN_FRONTEND=noninteractive
|
105
110
|
apt-get -y update
|
106
|
-
apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y <%= action %> <%= input("package") %>
|
111
|
+
apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y <%= input("options") %> <%= action %> <%= input("package") %>
|
107
112
|
<% elsif package_manager == 'zypper' -%>
|
108
113
|
<%-
|
109
114
|
if action == "group install"
|
@@ -113,7 +118,7 @@ handle_zypp_res_codes () {
|
|
113
118
|
end
|
114
119
|
-%>
|
115
120
|
zypper refresh
|
116
|
-
zypper -n <%= action %> <%= input("package") %>
|
121
|
+
zypper -n <%= action %> <%= input("options") %> <%= input("package") %>
|
117
122
|
handle_zypp_res_codes $?
|
118
123
|
<% end -%>
|
119
124
|
RETVAL=$?
|
data/config/routes.rb
CHANGED
@@ -46,8 +46,10 @@ Rails.application.routes.draw do
|
|
46
46
|
get 'ui_job_wizard/categories', to: 'ui_job_wizard#categories'
|
47
47
|
get 'ui_job_wizard/template/:id', to: 'ui_job_wizard#template'
|
48
48
|
get 'ui_job_wizard/resources', to: 'ui_job_wizard#resources'
|
49
|
+
get 'ui_job_wizard/job_invocation', to: 'ui_job_wizard#job_invocation'
|
49
50
|
|
50
|
-
match '/experimental/job_wizard', to: 'react#index', :via => [:get]
|
51
|
+
match '/experimental/job_wizard/new', to: 'react#index', :via => [:get]
|
52
|
+
match '/experimental/job_wizard/:id/rerun', to: 'react#index', :via => [:get]
|
51
53
|
|
52
54
|
namespace :api, :defaults => {:format => 'json'} do
|
53
55
|
scope '(:apiv)', :module => :v2, :defaults => {:apiv => 'v2'}, :apiv => /v1|v2/, :constraints => ApiConstraints.new(:version => 2, :default => true) do
|
@@ -47,7 +47,7 @@ module ForemanRemoteExecution
|
|
47
47
|
|
48
48
|
initializer 'foreman_remote_execution.register_plugin', before: :finisher_hook do |_app|
|
49
49
|
Foreman::Plugin.register :foreman_remote_execution do
|
50
|
-
requires_foreman '>= 3.
|
50
|
+
requires_foreman '>= 3.4'
|
51
51
|
register_global_js_file 'global'
|
52
52
|
|
53
53
|
apipie_documented_controllers ["#{ForemanRemoteExecution::Engine.root}/app/controllers/api/v2/*.rb"]
|
@@ -152,7 +152,7 @@ module ForemanRemoteExecution
|
|
152
152
|
setting 'remote_execution_job_invocation_report_template',
|
153
153
|
type: :string,
|
154
154
|
description: N_('Select a report template used for generating a report for a particular remote execution job'),
|
155
|
-
default: 'Job
|
155
|
+
default: 'Job - Invocation Report',
|
156
156
|
full_name: N_('Job Invocation Report Template'),
|
157
157
|
collection: proc { ForemanRemoteExecution.job_invocation_report_templates_select }
|
158
158
|
end
|
@@ -164,7 +164,7 @@ module ForemanRemoteExecution
|
|
164
164
|
:'api/v2/job_templates' => [:index, :show, :revision, :export],
|
165
165
|
:'api/v2/template_inputs' => [:index, :show],
|
166
166
|
:'api/v2/foreign_input_sets' => [:index, :show],
|
167
|
-
:ui_job_wizard => [:categories, :template, :resources]}, :resource_type => 'JobTemplate'
|
167
|
+
:ui_job_wizard => [:categories, :template, :resources, :job_invocation]}, :resource_type => 'JobTemplate'
|
168
168
|
permission :create_job_templates, { :job_templates => [:new, :create, :clone_template, :import],
|
169
169
|
:'api/v2/job_templates' => [:create, :clone, :import] }, :resource_type => 'JobTemplate'
|
170
170
|
permission :edit_job_templates, { :job_templates => [:edit, :update],
|
@@ -181,7 +181,7 @@ module ForemanRemoteExecution
|
|
181
181
|
permission :view_job_invocations, { :job_invocations => [:index, :chart, :show, :auto_complete_search], :template_invocations => [:show],
|
182
182
|
'api/v2/job_invocations' => [:index, :show, :output, :raw_output, :outputs] }, :resource_type => 'JobInvocation'
|
183
183
|
permission :view_template_invocations, { :template_invocations => [:show],
|
184
|
-
'api/v2/template_invocations' => [:template_invocations] }, :resource_type => 'TemplateInvocation'
|
184
|
+
'api/v2/template_invocations' => [:template_invocations], :ui_job_wizard => [:job_invocation] }, :resource_type => 'TemplateInvocation'
|
185
185
|
permission :create_template_invocations, {}, :resource_type => 'TemplateInvocation'
|
186
186
|
permission :execute_jobs_on_infrastructure_hosts, {}, :resource_type => 'JobInvocation'
|
187
187
|
permission :cancel_job_invocations, { :job_invocations => [:cancel], 'api/v2/job_invocations' => [:cancel] }, :resource_type => 'JobInvocation'
|
@@ -242,7 +242,7 @@ module ForemanRemoteExecution
|
|
242
242
|
url_hash: { controller: 'job_wizard', action: :index },
|
243
243
|
caption: N_('Job wizard'),
|
244
244
|
parent: :lab_features_menu,
|
245
|
-
url: '/experimental/job_wizard',
|
245
|
+
url: '/experimental/job_wizard/new',
|
246
246
|
after: :host_wizard
|
247
247
|
|
248
248
|
register_custom_status HostStatus::ExecutionStatus
|
@@ -82,6 +82,14 @@ module Api
|
|
82
82
|
assert_response :success
|
83
83
|
end
|
84
84
|
|
85
|
+
test 'should propagate errors from triggering' do
|
86
|
+
@attrs[:recurrence] = { cron_line: 'foo' }
|
87
|
+
post :create, params: { job_invocation: @attrs }
|
88
|
+
invocation = ActiveSupport::JSON.decode(@response.body)
|
89
|
+
assert_match(/foo is not valid format of cron line/, invocation['error']['message'])
|
90
|
+
assert_response 500
|
91
|
+
end
|
92
|
+
|
85
93
|
test 'should create with schedule' do
|
86
94
|
@attrs[:scheduling] = { start_at: Time.now.to_s }
|
87
95
|
post :create, params: { job_invocation: @attrs }
|
@@ -9,7 +9,7 @@ class JobReportTemplateTest < ActiveSupport::TestCase
|
|
9
9
|
|
10
10
|
context 'with valid job invocation report template' do
|
11
11
|
let(:job_invocation_template) do
|
12
|
-
file_path = File.read(File.expand_path(Rails.root + "app/views/unattended/report_templates/
|
12
|
+
file_path = File.read(File.expand_path(Rails.root + "app/views/unattended/report_templates/job_-_invocation_report.erb"))
|
13
13
|
template = ReportTemplate.import_without_save("Job Invocation Report Template", file_path)
|
14
14
|
template.save!
|
15
15
|
template
|
@@ -95,9 +95,8 @@ class JobInvocationTest < ActiveSupport::TestCase
|
|
95
95
|
end
|
96
96
|
|
97
97
|
it 'truncates generated description to 255 characters' do
|
98
|
-
column_limit = 255
|
98
|
+
column_limit = 255 # There is a 255 character limit on the database level
|
99
99
|
expected_result = 'a' * column_limit
|
100
|
-
JobInvocation.columns_hash['description'].expects(:limit).returns(column_limit)
|
101
100
|
job_invocation.description_format = '%{job_category}'
|
102
101
|
job_invocation.job_category = 'a' * 1000
|
103
102
|
job_invocation.generate_description
|
@@ -1,6 +1,8 @@
|
|
1
|
+
/* eslint-disable max-lines */
|
1
2
|
/* eslint-disable camelcase */
|
2
3
|
import React, { useState, useEffect, useCallback } from 'react';
|
3
4
|
import { useDispatch, useSelector } from 'react-redux';
|
5
|
+
import PropTypes from 'prop-types';
|
4
6
|
import { Wizard } from '@patternfly/react-core';
|
5
7
|
import { get } from 'foremanReact/redux/API';
|
6
8
|
import history from 'foremanReact/history';
|
@@ -14,6 +16,7 @@ import { AdvancedFields } from './steps/AdvancedFields/AdvancedFields';
|
|
14
16
|
import {
|
15
17
|
JOB_TEMPLATE,
|
16
18
|
WIZARD_TITLES,
|
19
|
+
SCHEDULE_TYPES,
|
17
20
|
initialScheduleState,
|
18
21
|
} from './JobWizardConstants';
|
19
22
|
import {
|
@@ -23,7 +26,9 @@ import {
|
|
23
26
|
selectRouterSearch,
|
24
27
|
selectIsLoading,
|
25
28
|
} from './JobWizardSelectors';
|
26
|
-
import
|
29
|
+
import { ScheduleType } from './steps/Schedule/ScheduleType';
|
30
|
+
import { ScheduleFuture } from './steps/Schedule/ScheduleFuture';
|
31
|
+
import { ScheduleRecurring } from './steps/Schedule/ScheduleRecurring';
|
27
32
|
import HostsAndInputs from './steps/HostsAndInputs/';
|
28
33
|
import ReviewDetails from './steps/ReviewDetails/';
|
29
34
|
import { useValidation } from './validation';
|
@@ -31,9 +36,11 @@ import { useAutoFill } from './autofill';
|
|
31
36
|
import { submit } from './submit';
|
32
37
|
import './JobWizard.scss';
|
33
38
|
|
34
|
-
export const JobWizard = () => {
|
35
|
-
const [jobTemplateID, setJobTemplateID] = useState(
|
36
|
-
|
39
|
+
export const JobWizard = ({ rerunData }) => {
|
40
|
+
const [jobTemplateID, setJobTemplateID] = useState(
|
41
|
+
rerunData?.template_invocations?.[0]?.template_id
|
42
|
+
);
|
43
|
+
const [category, setCategory] = useState(rerunData?.job_category || '');
|
37
44
|
const [advancedValues, setAdvancedValues] = useState({ templateValues: {} });
|
38
45
|
const [templateValues, setTemplateValues] = useState({}); // TODO use templateValues in advanced fields - description https://github.com/theforeman/foreman_remote_execution/pull/605
|
39
46
|
const [scheduleValue, setScheduleValue] = useState(initialScheduleState);
|
@@ -43,14 +50,22 @@ export const JobWizard = () => {
|
|
43
50
|
hostGroups: [],
|
44
51
|
});
|
45
52
|
const [hostsSearchQuery, setHostsSearchQuery] = useState('');
|
46
|
-
const
|
53
|
+
const routerSearch = useSelector(selectRouterSearch);
|
54
|
+
const [fills, setFills] = useState(
|
55
|
+
rerunData
|
56
|
+
? {
|
57
|
+
search: rerunData?.targeting?.search_query,
|
58
|
+
...rerunData.inputs,
|
59
|
+
}
|
60
|
+
: routerSearch
|
61
|
+
);
|
47
62
|
const dispatch = useDispatch();
|
48
63
|
|
49
64
|
const setDefaults = useCallback(
|
50
65
|
({
|
51
66
|
data: {
|
52
|
-
template_inputs,
|
53
|
-
advanced_template_inputs,
|
67
|
+
template_inputs = [],
|
68
|
+
advanced_template_inputs = [],
|
54
69
|
effective_user,
|
55
70
|
job_template: {
|
56
71
|
name,
|
@@ -58,6 +73,9 @@ export const JobWizard = () => {
|
|
58
73
|
description_format,
|
59
74
|
job_category,
|
60
75
|
},
|
76
|
+
randomized_ordering,
|
77
|
+
ssh_user,
|
78
|
+
concurrency_control = {},
|
61
79
|
},
|
62
80
|
}) => {
|
63
81
|
if (!category.length) {
|
@@ -98,23 +116,43 @@ export const JobWizard = () => {
|
|
98
116
|
timeoutToKill: execution_timeout_interval || '',
|
99
117
|
templateValues: advancedTemplateValues,
|
100
118
|
description: generateDefaultDescription() || '',
|
101
|
-
isRandomizedOrdering:
|
119
|
+
isRandomizedOrdering: randomized_ordering,
|
120
|
+
sshUser: ssh_user || '',
|
121
|
+
timeSpan: concurrency_control.time_span || '',
|
122
|
+
concurrencyLevel: concurrency_control.level || '',
|
102
123
|
};
|
103
124
|
});
|
104
125
|
},
|
105
126
|
[category.length]
|
106
127
|
);
|
128
|
+
useEffect(() => {
|
129
|
+
if (rerunData) {
|
130
|
+
setDefaults({
|
131
|
+
data: {
|
132
|
+
effective_user: {
|
133
|
+
value: rerunData.template_invocations[0].effective_user,
|
134
|
+
},
|
135
|
+
job_template: {
|
136
|
+
execution_timeout_interval: rerunData.execution_timeout_interval,
|
137
|
+
},
|
138
|
+
randomized_ordering: rerunData.targeting.randomized_ordering,
|
139
|
+
ssh_user: rerunData.ssh_user,
|
140
|
+
concurrency_control: rerunData.concurrency_control,
|
141
|
+
},
|
142
|
+
});
|
143
|
+
}
|
144
|
+
}, [rerunData, setDefaults]);
|
107
145
|
useEffect(() => {
|
108
146
|
if (jobTemplateID) {
|
109
147
|
dispatch(
|
110
148
|
get({
|
111
149
|
key: JOB_TEMPLATE,
|
112
150
|
url: `/ui_job_wizard/template/${jobTemplateID}`,
|
113
|
-
handleSuccess: setDefaults,
|
151
|
+
handleSuccess: rerunData ? () => {} : setDefaults,
|
114
152
|
})
|
115
153
|
);
|
116
154
|
}
|
117
|
-
}, [jobTemplateID, setDefaults, dispatch]);
|
155
|
+
}, [rerunData, jobTemplateID, setDefaults, dispatch]);
|
118
156
|
|
119
157
|
const [valid, setValid] = useValidation({
|
120
158
|
advancedValues,
|
@@ -127,6 +165,7 @@ export const JobWizard = () => {
|
|
127
165
|
setHostsSearchQuery,
|
128
166
|
setJobTemplateID,
|
129
167
|
setTemplateValues,
|
168
|
+
setAdvancedValues,
|
130
169
|
});
|
131
170
|
const templateError = !!useSelector(selectTemplateError);
|
132
171
|
const templateResponse = useSelector(selectJobTemplate);
|
@@ -148,7 +187,7 @@ export const JobWizard = () => {
|
|
148
187
|
setJobTemplate={setJobTemplateID}
|
149
188
|
category={category}
|
150
189
|
setCategory={setCategory}
|
151
|
-
|
190
|
+
isCategoryPreselected={!!rerunData || !!fills.feature}
|
152
191
|
/>
|
153
192
|
),
|
154
193
|
enableNext: isTemplate,
|
@@ -187,18 +226,80 @@ export const JobWizard = () => {
|
|
187
226
|
},
|
188
227
|
{
|
189
228
|
name: WIZARD_TITLES.schedule,
|
190
|
-
component: (
|
191
|
-
<Schedule
|
192
|
-
scheduleValue={scheduleValue}
|
193
|
-
setScheduleValue={setScheduleValue}
|
194
|
-
setValid={newValue => {
|
195
|
-
setValid(currentValid => ({ ...currentValid, schedule: newValue }));
|
196
|
-
}}
|
197
|
-
/>
|
198
|
-
),
|
199
229
|
canJumpTo: isTemplate && valid.hostsAndInputs && valid.advanced,
|
200
230
|
enableNext:
|
201
231
|
isTemplate && valid.hostsAndInputs && valid.advanced && valid.schedule,
|
232
|
+
steps: [
|
233
|
+
{
|
234
|
+
name: WIZARD_TITLES.typeOfExecution,
|
235
|
+
component: (
|
236
|
+
<ScheduleType
|
237
|
+
scheduleType={scheduleValue.scheduleType}
|
238
|
+
isTypeStatic={scheduleValue.isTypeStatic}
|
239
|
+
setScheduleValue={setScheduleValue}
|
240
|
+
setValid={newValue => {
|
241
|
+
setValid(currentValid => ({
|
242
|
+
...currentValid,
|
243
|
+
schedule: newValue,
|
244
|
+
}));
|
245
|
+
}}
|
246
|
+
/>
|
247
|
+
),
|
248
|
+
canJumpTo: isTemplate && valid.hostsAndInputs && valid.advanced,
|
249
|
+
|
250
|
+
enableNext: isTemplate && valid.hostsAndInputs && valid.advanced,
|
251
|
+
},
|
252
|
+
...(scheduleValue.scheduleType === SCHEDULE_TYPES.FUTURE
|
253
|
+
? [
|
254
|
+
{
|
255
|
+
name: SCHEDULE_TYPES.FUTURE,
|
256
|
+
component: (
|
257
|
+
<ScheduleFuture
|
258
|
+
scheduleValue={scheduleValue}
|
259
|
+
setScheduleValue={setScheduleValue}
|
260
|
+
setValid={newValue => {
|
261
|
+
setValid(currentValid => ({
|
262
|
+
...currentValid,
|
263
|
+
schedule: newValue,
|
264
|
+
}));
|
265
|
+
}}
|
266
|
+
/>
|
267
|
+
),
|
268
|
+
canJumpTo: isTemplate && valid.hostsAndInputs && valid.advanced,
|
269
|
+
enableNext:
|
270
|
+
isTemplate &&
|
271
|
+
valid.hostsAndInputs &&
|
272
|
+
valid.advanced &&
|
273
|
+
valid.schedule,
|
274
|
+
},
|
275
|
+
]
|
276
|
+
: []),
|
277
|
+
...(scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING
|
278
|
+
? [
|
279
|
+
{
|
280
|
+
name: SCHEDULE_TYPES.RECURRING,
|
281
|
+
component: (
|
282
|
+
<ScheduleRecurring
|
283
|
+
scheduleValue={scheduleValue}
|
284
|
+
setScheduleValue={setScheduleValue}
|
285
|
+
setValid={newValue => {
|
286
|
+
setValid(currentValid => ({
|
287
|
+
...currentValid,
|
288
|
+
schedule: newValue,
|
289
|
+
}));
|
290
|
+
}}
|
291
|
+
/>
|
292
|
+
),
|
293
|
+
canJumpTo: isTemplate && valid.hostsAndInputs && valid.advanced,
|
294
|
+
enableNext:
|
295
|
+
isTemplate &&
|
296
|
+
valid.hostsAndInputs &&
|
297
|
+
valid.advanced &&
|
298
|
+
valid.schedule,
|
299
|
+
},
|
300
|
+
]
|
301
|
+
: []),
|
302
|
+
],
|
202
303
|
},
|
203
304
|
{
|
204
305
|
name: WIZARD_TITLES.review,
|
@@ -250,4 +351,37 @@ export const JobWizard = () => {
|
|
250
351
|
);
|
251
352
|
};
|
252
353
|
|
354
|
+
JobWizard.propTypes = {
|
355
|
+
rerunData: PropTypes.shape({
|
356
|
+
job_category: PropTypes.string,
|
357
|
+
targeting: PropTypes.shape({
|
358
|
+
search_query: PropTypes.string,
|
359
|
+
targeting_type: PropTypes.string,
|
360
|
+
randomized_ordering: PropTypes.bool,
|
361
|
+
}),
|
362
|
+
triggering: PropTypes.shape({
|
363
|
+
mode: PropTypes.string,
|
364
|
+
start_at: PropTypes.string,
|
365
|
+
start_before: PropTypes.string,
|
366
|
+
}),
|
367
|
+
ssh_user: PropTypes.string,
|
368
|
+
concurrency_control: PropTypes.shape({
|
369
|
+
level: PropTypes.number,
|
370
|
+
time_span: PropTypes.number,
|
371
|
+
}),
|
372
|
+
execution_timeout_interval: PropTypes.number,
|
373
|
+
remote_execution_feature_id: PropTypes.string,
|
374
|
+
template_invocations: PropTypes.arrayOf(
|
375
|
+
PropTypes.shape({
|
376
|
+
template_id: PropTypes.number,
|
377
|
+
effective_user: PropTypes.string,
|
378
|
+
input_values: PropTypes.array,
|
379
|
+
})
|
380
|
+
),
|
381
|
+
inputs: PropTypes.object,
|
382
|
+
}),
|
383
|
+
};
|
384
|
+
JobWizard.defaultProps = {
|
385
|
+
rerunData: null,
|
386
|
+
};
|
253
387
|
export default JobWizard;
|
@@ -71,8 +71,42 @@
|
|
71
71
|
max-height: 300px;
|
72
72
|
overflow: scroll;
|
73
73
|
}
|
74
|
+
.schedule-radio label {
|
75
|
+
width: 100%;
|
76
|
+
}
|
77
|
+
.schedule-radio input {
|
78
|
+
align-self: center;
|
79
|
+
}
|
80
|
+
.schedule-radio-repeat-text {
|
81
|
+
width: 100px;
|
82
|
+
display: inline-block;
|
83
|
+
margin-right: 5px;
|
84
|
+
align-self: center;
|
85
|
+
}
|
86
|
+
.schedule-radio-title {
|
87
|
+
width: 80px;
|
88
|
+
display: inline-block;
|
89
|
+
align-self: center;
|
90
|
+
}
|
91
|
+
.schedule-radio-occurences {
|
92
|
+
display: inline-block;
|
93
|
+
align-self: center;
|
94
|
+
}
|
95
|
+
.scheudle-radio-wrapper {
|
96
|
+
display: flex;
|
97
|
+
}
|
98
|
+
}
|
99
|
+
.future-schedule-tab {
|
100
|
+
.clear-datetime-button {
|
101
|
+
margin-left: 10px;
|
102
|
+
align-self: center;
|
103
|
+
font-size: var(--pf-global--FontSize--md);
|
104
|
+
}
|
105
|
+
.pf-c-form__group-control {
|
106
|
+
display: flex;
|
107
|
+
flex-wrap: wrap;
|
108
|
+
}
|
74
109
|
}
|
75
|
-
|
76
110
|
.pf-c-date-picker {
|
77
111
|
vertical-align: top;
|
78
112
|
}
|
@@ -108,4 +142,12 @@
|
|
108
142
|
display: none;
|
109
143
|
}
|
110
144
|
}
|
145
|
+
.pf-c-radio__body {
|
146
|
+
font-size: var(--pf-c-radio__label--FontSize);
|
147
|
+
}
|
111
148
|
}
|
149
|
+
|
150
|
+
.job-wizard-alert.pf-c-alert.pf-m-warning {
|
151
|
+
margin-bottom: 10px;
|
152
|
+
margin-top: 10px;
|
153
|
+
}
|
@@ -16,23 +16,31 @@ export const repeatTypes = {
|
|
16
16
|
hourly: __('Hourly'),
|
17
17
|
};
|
18
18
|
|
19
|
+
export const SCHEDULE_TYPES = {
|
20
|
+
NOW: __('Immediate execution'),
|
21
|
+
FUTURE: __('Future execution'),
|
22
|
+
RECURRING: __('Recurring execution'),
|
23
|
+
};
|
24
|
+
|
19
25
|
export const WIZARD_TITLES = {
|
20
26
|
categoryAndTemplate: __('Category and Template'),
|
21
27
|
hostsAndInputs: __('Target hosts and inputs'),
|
22
28
|
advanced: __('Advanced fields'),
|
23
29
|
schedule: __('Schedule'),
|
24
30
|
review: __('Review details'),
|
31
|
+
typeOfExecution: __('Type of execution'),
|
25
32
|
};
|
26
33
|
|
27
34
|
export const initialScheduleState = {
|
28
35
|
repeatType: repeatTypes.noRepeat,
|
36
|
+
scheduleType: SCHEDULE_TYPES.NOW,
|
29
37
|
repeatAmount: '',
|
30
38
|
repeatData: {},
|
31
39
|
startsAt: '',
|
32
40
|
startsBefore: '',
|
33
41
|
ends: '',
|
34
42
|
isFuture: false,
|
35
|
-
isNeverEnds:
|
43
|
+
isNeverEnds: true,
|
36
44
|
isTypeStatic: true,
|
37
45
|
purpose: '',
|
38
46
|
};
|
@@ -59,3 +67,5 @@ export const HOSTS_TO_PREVIEW_AMOUNT = 20;
|
|
59
67
|
export const DEBOUNCE_HOST_COUNT = 700;
|
60
68
|
export const HOST_IDS = 'HOST_IDS';
|
61
69
|
export const REX_FEATURE = 'REX_FEATURE';
|
70
|
+
|
71
|
+
export const JOB_API_KEY = 'JOB_API_KEY';
|