foreman_remote_execution 7.2.2 → 8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/models/remote_execution_provider.rb +1 -1
- 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/test/unit/remote_execution_provider_test.rb +0 -22
- 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
|
@@ -56,28 +56,6 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
-
describe '.proxy_feature' do
|
60
|
-
# rubocop:disable Naming/ConstantName
|
61
|
-
it 'handles provider subclasses properly' do
|
62
|
-
old = ::RemoteExecutionProvider
|
63
|
-
|
64
|
-
class P2 < old
|
65
|
-
end
|
66
|
-
::RemoteExecutionProvider = P2
|
67
|
-
|
68
|
-
class CustomProvider < ::RemoteExecutionProvider
|
69
|
-
end
|
70
|
-
|
71
|
-
::RemoteExecutionProvider.register('custom', CustomProvider)
|
72
|
-
|
73
|
-
feature = CustomProvider.proxy_feature
|
74
|
-
_(feature).must_equal 'custom'
|
75
|
-
ensure
|
76
|
-
::RemoteExecutionProvider = old
|
77
|
-
end
|
78
|
-
# rubocop:enable Naming/ConstantName
|
79
|
-
end
|
80
|
-
|
81
59
|
describe '.provider_proxy_features' do
|
82
60
|
it 'returns correct values' do
|
83
61
|
RemoteExecutionProvider.stubs(:providers).returns(
|
@@ -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';
|