foreman_remote_execution 4.5.0 → 4.5.4
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/job_invocations_controller.rb +1 -1
- data/app/controllers/ui_job_wizard_controller.rb +7 -0
- data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +5 -1
- data/app/helpers/remote_execution_helper.rb +9 -3
- data/app/lib/actions/remote_execution/run_host_job.rb +5 -1
- data/app/lib/actions/remote_execution/run_hosts_job.rb +1 -1
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +2 -0
- 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 +3 -3
- data/app/models/job_invocation.rb +9 -6
- data/app/models/job_invocation_composer.rb +4 -4
- data/app/models/remote_execution_feature.rb +5 -1
- data/app/models/remote_execution_provider.rb +1 -1
- data/app/models/setting/remote_execution.rb +2 -2
- data/app/models/targeting.rb +5 -1
- 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/power_action.erb +2 -0
- data/app/views/templates/ssh/puppet_run_once.erb +1 -0
- data/db/migrate/2021051713291621250977_add_host_proxy_invocations.rb +12 -0
- data/lib/foreman_remote_execution/engine.rb +0 -2
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/unit/job_invocation_composer_test.rb +14 -1
- data/test/unit/job_invocation_test.rb +1 -1
- data/webpack/JobWizard/JobWizard.js +28 -8
- data/webpack/JobWizard/JobWizard.scss +39 -0
- data/webpack/JobWizard/JobWizardConstants.js +10 -0
- data/webpack/JobWizard/JobWizardSelectors.js +9 -0
- data/webpack/JobWizard/__tests__/fixtures.js +104 -2
- data/webpack/JobWizard/__tests__/integration.test.js +13 -85
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +21 -4
- data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +67 -0
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +73 -59
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +135 -16
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +23 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +122 -51
- 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 +1 -0
- data/webpack/JobWizard/steps/form/Formatter.js +149 -0
- data/webpack/JobWizard/steps/form/NumberInput.js +33 -0
- data/webpack/JobWizard/steps/form/SelectField.js +14 -2
- data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +76 -0
- data/webpack/__mocks__/foremanReact/components/SearchBar.js +18 -1
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -0
- metadata +15 -13
- 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 -13
- data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +0 -32
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +0 -249
- data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +0 -113
- 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 -37
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8db3f88983ab094bd33ad6cd7630fdc57924e73ba196b1393c1369512cbd3307
|
4
|
+
data.tar.gz: 8bdaa93cdeabbb00ff6af832842c2fc1bb6437b0f3467db511e86cf22a5742bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b03559c1efa565ec741874f4213ec86dcd4115b4dcf896c9846100033429baa5bbe1df427f8f2e4a4202d62e9dbc8768358bc3034d86362bb1d40236061c2f4
|
7
|
+
data.tar.gz: 7091c88b57902cadf6c59959563d25332a89731f9a2da44a267bcd5207611a3256c31b0971f0cd9f2dca05186a07e5f86fb1361ac47d9d69bd11d61df6628e69
|
@@ -67,7 +67,7 @@ class JobInvocationsController < ApplicationController
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def index
|
70
|
-
@job_invocations = resource_base_search_and_page.
|
70
|
+
@job_invocations = resource_base_search_and_page.preload(:task, :targeting).order('job_invocations.id DESC')
|
71
71
|
end
|
72
72
|
|
73
73
|
# refreshes the form
|
@@ -10,9 +10,12 @@ class UiJobWizardController < ::Api::V2::BaseController
|
|
10
10
|
|
11
11
|
def template
|
12
12
|
job_template = JobTemplate.authorized.find(params[:id])
|
13
|
+
advanced_template_inputs, template_inputs = map_template_inputs(job_template.template_inputs_with_foreign).partition { |x| x["advanced"] }
|
13
14
|
render :json => {
|
14
15
|
:job_template => job_template,
|
15
16
|
:effective_user => job_template.effective_user,
|
17
|
+
:template_inputs => template_inputs,
|
18
|
+
:advanced_template_inputs => advanced_template_inputs,
|
16
19
|
}
|
17
20
|
end
|
18
21
|
|
@@ -20,6 +23,10 @@ class UiJobWizardController < ::Api::V2::BaseController
|
|
20
23
|
nested_resource || 'job_template'
|
21
24
|
end
|
22
25
|
|
26
|
+
def map_template_inputs(template_inputs_with_foreign)
|
27
|
+
template_inputs_with_foreign.map { |input| input.attributes.merge({:resource_type => input.resource_type&.tableize }) }
|
28
|
+
end
|
29
|
+
|
23
30
|
def resource_class
|
24
31
|
JobTemplate
|
25
32
|
end
|
@@ -7,7 +7,9 @@ module ForemanRemoteExecution
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def multiple_actions
|
10
|
-
|
10
|
+
res = super
|
11
|
+
res += [ [_('Schedule Remote Job'), new_job_invocation_path, false] ] if authorized_for(controller: :job_invocations, action: :new)
|
12
|
+
res
|
11
13
|
end
|
12
14
|
|
13
15
|
def schedule_job_multi_button(*args)
|
@@ -21,12 +23,14 @@ module ForemanRemoteExecution
|
|
21
23
|
end
|
22
24
|
|
23
25
|
def rex_host_features(*args)
|
26
|
+
return unless authorized_for(controller: :job_invocations, action: :create)
|
24
27
|
RemoteExecutionFeature.with_host_action_button.order(:label).map do |feature|
|
25
28
|
link_to(_('%s') % feature.name, job_invocations_path(:host_ids => [args.first.id], :feature => feature.label), :method => :post)
|
26
29
|
end
|
27
30
|
end
|
28
31
|
|
29
32
|
def schedule_job_button(*args)
|
33
|
+
return unless authorized_for(controller: :job_invocations, action: :new)
|
30
34
|
link_to(_('Schedule Remote Job'), new_job_invocation_path(:host_ids => [args.first.id]), :id => :run_button, :class => 'btn btn-default')
|
31
35
|
end
|
32
36
|
|
@@ -7,6 +7,10 @@ module RemoteExecutionHelper
|
|
7
7
|
@job_hosts_authorizer ||= Authorizer.new(User.current, :collection => @hosts)
|
8
8
|
end
|
9
9
|
|
10
|
+
def host_tasks_authorizer
|
11
|
+
@host_tasks_authorizer ||= Authorizer.new(User.current, :collection => @job_invocation.sub_tasks)
|
12
|
+
end
|
13
|
+
|
10
14
|
def host_counter(label, count)
|
11
15
|
content_tag(:div, :class => 'host_counter') do
|
12
16
|
content_tag(:div, label, :class => 'header') + content_tag(:div, count.to_s, :class => 'count')
|
@@ -36,7 +40,7 @@ module RemoteExecutionHelper
|
|
36
40
|
'data-method': 'get', id: "#{host.name}-actions-rerun" } }
|
37
41
|
end
|
38
42
|
|
39
|
-
if host_task.present? && authorized_for(hash_for_foreman_tasks_task_path(host_task).merge(auth_object: host_task, permission: :view_foreman_tasks))
|
43
|
+
if host_task.present? && authorized_for(hash_for_foreman_tasks_task_path(host_task).merge(auth_object: host_task, permission: :view_foreman_tasks, authorizer: host_tasks_authorizer))
|
40
44
|
links << { title: _('Host task'),
|
41
45
|
action: { href: foreman_tasks_task_path(host_task),
|
42
46
|
'data-method': 'get', id: "#{host.name}-actions-task" } }
|
@@ -109,12 +113,14 @@ module RemoteExecutionHelper
|
|
109
113
|
:class => 'btn btn-danger',
|
110
114
|
:title => _('Try to cancel the job on a host'),
|
111
115
|
:disabled => !task.cancellable?,
|
112
|
-
:method => :post
|
116
|
+
:method => :post,
|
117
|
+
:remote => true)
|
113
118
|
buttons << link_to(_('Abort Job'), abort_foreman_tasks_task_path(task),
|
114
119
|
:class => 'btn btn-danger',
|
115
120
|
:title => _('Try to abort the job on a host without waiting for its result'),
|
116
121
|
:disabled => !task.cancellable?,
|
117
|
-
:method => :post
|
122
|
+
:method => :post,
|
123
|
+
:remote => true)
|
118
124
|
end
|
119
125
|
buttons
|
120
126
|
end
|
@@ -47,12 +47,16 @@ module Actions
|
|
47
47
|
script = renderer.render
|
48
48
|
raise _('Failed rendering template: %s') % renderer.error_message unless script
|
49
49
|
|
50
|
+
first_execution = host.executed_through_proxies.where(:id => proxy.id).none?
|
51
|
+
host.executed_through_proxies << proxy if first_execution
|
52
|
+
|
50
53
|
additional_options = { :hostname => provider.find_ip_or_hostname(host),
|
51
54
|
:script => script,
|
52
55
|
:execution_timeout_interval => job_invocation.execution_timeout_interval,
|
53
56
|
:secrets => secrets(host, job_invocation, provider),
|
54
57
|
:use_batch_triggering => true,
|
55
|
-
:use_concurrency_control => options[:use_concurrency_control]
|
58
|
+
:use_concurrency_control => options[:use_concurrency_control],
|
59
|
+
:first_execution => first_execution }
|
56
60
|
action_options = provider.proxy_command_options(template_invocation, host)
|
57
61
|
.merge(additional_options)
|
58
62
|
|
@@ -72,7 +72,7 @@ module Actions
|
|
72
72
|
|
73
73
|
def total_count
|
74
74
|
# For compatibility with already existing tasks
|
75
|
-
return output[:total_count] unless output.has_key?(:host_count) || task.pending?
|
75
|
+
return output[:total_count] || hosts.count unless output.has_key?(:host_count) || task.pending?
|
76
76
|
|
77
77
|
output[:host_count] || hosts.count
|
78
78
|
end
|
@@ -6,6 +6,8 @@ module ForemanRemoteExecution
|
|
6
6
|
has_many :template_invocations, :dependent => :destroy, :foreign_key => 'host_id'
|
7
7
|
has_one :execution_status_object, :class_name => 'HostStatus::ExecutionStatus', :foreign_key => 'host_id', :dependent => :destroy
|
8
8
|
has_many :run_host_job_tasks, :through => :template_invocations
|
9
|
+
has_many :host_proxy_invocations, :foreign_key => 'host_id', :dependent => :destroy
|
10
|
+
has_many :executed_through_proxies, :through => :host_proxy_invocations, :source => 'smart_proxy'
|
9
11
|
|
10
12
|
scoped_search :relation => :run_host_job_tasks, :on => :result, :rename => 'job_invocation.result',
|
11
13
|
:ext_method => :search_by_job_invocation,
|
@@ -64,11 +64,11 @@ class HostStatus::ExecutionStatus < HostStatus::Status
|
|
64
64
|
|
65
65
|
case status
|
66
66
|
when OK
|
67
|
-
[ "foreman_tasks_tasks.state = 'stopped' AND result = 'success'" ]
|
67
|
+
[ "foreman_tasks_tasks.state = 'stopped' AND foreman_tasks_tasks.result = 'success'" ]
|
68
68
|
when CANCELLED
|
69
|
-
[ "foreman_tasks_tasks.state = 'stopped' AND result = 'cancelled'" ]
|
69
|
+
[ "foreman_tasks_tasks.state = 'stopped' AND foreman_tasks_tasks.result = 'cancelled'" ]
|
70
70
|
when ERROR
|
71
|
-
[ "foreman_tasks_tasks.state = 'stopped' AND (result = 'error' OR result = 'warning')" ]
|
71
|
+
[ "foreman_tasks_tasks.state = 'stopped' AND (foreman_tasks_tasks.result = 'error' OR foreman_tasks_tasks.result = 'warning')" ]
|
72
72
|
when QUEUED
|
73
73
|
[ "foreman_tasks_tasks.state = 'scheduled' OR foreman_tasks_tasks.state IS NULL" ]
|
74
74
|
when RUNNING
|
@@ -65,8 +65,6 @@ class JobInvocation < ApplicationRecord
|
|
65
65
|
scoped_search :on => 'pattern_template_name', :rename => 'pattern_template_name', :operators => ['= '],
|
66
66
|
:complete_value => false, :only_explicit => true, :ext_method => :search_by_pattern_template
|
67
67
|
|
68
|
-
scope :with_task, -> { references(:task) }
|
69
|
-
|
70
68
|
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
|
71
69
|
|
72
70
|
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring',
|
@@ -99,7 +97,7 @@ class JobInvocation < ApplicationRecord
|
|
99
97
|
def self.search_by_status(key, operator, value)
|
100
98
|
conditions = HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.sql_conditions_for(value)
|
101
99
|
conditions[0] = "NOT (#{conditions[0]})" if operator == '<>'
|
102
|
-
{ :conditions => sanitize_sql_for_conditions(conditions), :
|
100
|
+
{ :conditions => sanitize_sql_for_conditions(conditions), :joins => :task }
|
103
101
|
end
|
104
102
|
|
105
103
|
def self.search_by_recurring_logic(key, operator, value)
|
@@ -193,11 +191,16 @@ class JobInvocation < ApplicationRecord
|
|
193
191
|
end
|
194
192
|
|
195
193
|
def total_hosts_count
|
194
|
+
count = _('N/A')
|
195
|
+
|
196
196
|
if targeting.resolved?
|
197
|
-
task&.main_action
|
198
|
-
|
199
|
-
|
197
|
+
count = if task&.main_action.respond_to?(:total_count)
|
198
|
+
task.main_action.total_count
|
199
|
+
else
|
200
|
+
targeting.hosts.count
|
201
|
+
end
|
200
202
|
end
|
203
|
+
count
|
201
204
|
end
|
202
205
|
|
203
206
|
def pattern_template_invocation_for_host(host)
|
@@ -174,7 +174,7 @@ class JobInvocationComposer
|
|
174
174
|
input = template.template_inputs_with_foreign.find { |i| i.name == name }
|
175
175
|
unless input
|
176
176
|
raise ::Foreman::Exception, _('Unknown input %{input_name} for template %{template_name}') %
|
177
|
-
|
177
|
+
{ :input_name => name, :template_name => template.name }
|
178
178
|
end
|
179
179
|
{ :template_input_id => input.id, :value => value }
|
180
180
|
end
|
@@ -209,7 +209,7 @@ class JobInvocationComposer
|
|
209
209
|
def format_datetime(datetime)
|
210
210
|
return datetime if datetime.blank?
|
211
211
|
|
212
|
-
Time.parse(datetime).strftime('%Y-%m-%d %H:%M')
|
212
|
+
Time.parse(datetime).utc.strftime('%Y-%m-%d %H:%M')
|
213
213
|
end
|
214
214
|
end
|
215
215
|
|
@@ -296,7 +296,7 @@ class JobInvocationComposer
|
|
296
296
|
attr_reader :feature_label, :feature, :provided_inputs
|
297
297
|
|
298
298
|
def initialize(feature_label, hosts, provided_inputs = {})
|
299
|
-
@feature = RemoteExecutionFeature.feature(feature_label)
|
299
|
+
@feature = RemoteExecutionFeature.feature!(feature_label)
|
300
300
|
@provided_inputs = provided_inputs
|
301
301
|
translator = HostIdsTranslator.new(hosts)
|
302
302
|
@host_bookmark = translator.bookmark
|
@@ -405,7 +405,7 @@ class JobInvocationComposer
|
|
405
405
|
job_invocation.effective_user_password = params[:effective_user_password]
|
406
406
|
|
407
407
|
if @reruns && job_invocation.targeting.static?
|
408
|
-
job_invocation.targeting.
|
408
|
+
job_invocation.targeting.assign_host_ids(JobInvocation.find(@reruns).targeting.host_ids)
|
409
409
|
job_invocation.targeting.mark_resolved!
|
410
410
|
end
|
411
411
|
|
@@ -20,7 +20,11 @@ class RemoteExecutionFeature < ApplicationRecord
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.feature(label)
|
23
|
-
self.find_by(label: label)
|
23
|
+
self.find_by(label: label)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.feature!(label)
|
27
|
+
feature(label) || raise(::Foreman::Exception.new(N_('Unknown remote execution feature %s'), label))
|
24
28
|
end
|
25
29
|
|
26
30
|
def self.register(label, name, options = {})
|
@@ -50,7 +50,7 @@ class RemoteExecutionProvider
|
|
50
50
|
method = host_setting(host, :remote_execution_effective_user_method)
|
51
51
|
unless EFFECTIVE_USER_METHODS.include?(method)
|
52
52
|
raise _('Effective user method "%{current_value}" is not one of %{valid_methods}') %
|
53
|
-
|
53
|
+
{ :current_value => method, :valid_methods => EFFECTIVE_USER_METHODS}
|
54
54
|
end
|
55
55
|
method
|
56
56
|
end
|
@@ -70,13 +70,13 @@ class Setting::RemoteExecution < Setting
|
|
70
70
|
self.set('remote_execution_form_job_template',
|
71
71
|
N_('Choose a job template that is pre-selected in job invocation form'),
|
72
72
|
'Run Command - SSH Default',
|
73
|
-
|
73
|
+
N_('Form Job Template'),
|
74
74
|
nil,
|
75
75
|
{ :collection => proc { Hash[JobTemplate.unscoped.map { |template| [template.name, template.name] }] } }),
|
76
76
|
self.set('remote_execution_job_invocation_report_template',
|
77
77
|
N_('Select a report template used for generating a report for a particular remote execution job'),
|
78
78
|
'Jobs - Invocation report template',
|
79
|
-
|
79
|
+
N_('Job Invocation Report Template'),
|
80
80
|
nil,
|
81
81
|
{ :collection => proc { self.job_invocation_report_templates_select } }),
|
82
82
|
]
|
data/app/models/targeting.rb
CHANGED
@@ -46,9 +46,13 @@ class Targeting < ApplicationRecord
|
|
46
46
|
# pluck(:id) returns duplicate results for HostCollections
|
47
47
|
host_ids = User.as(user.login) { Host.authorized(RESOLVE_PERMISSION, Host).search_for(search_query).order(:name, :id).pluck(:id).uniq }
|
48
48
|
host_ids.shuffle!(random: Random.new) if randomized_ordering
|
49
|
+
self.assign_host_ids(host_ids)
|
50
|
+
self.save(:validate => false)
|
51
|
+
end
|
52
|
+
|
53
|
+
def assign_host_ids(host_ids)
|
49
54
|
# this can be optimized even more, by introducing bulk insert
|
50
55
|
self.targeting_hosts.build(host_ids.map { |id| { :host_id => id } })
|
51
|
-
self.save(:validate => false)
|
52
56
|
end
|
53
57
|
|
54
58
|
def dynamic?
|
@@ -21,7 +21,7 @@
|
|
21
21
|
<% @job_invocations.each do |invocation| %>
|
22
22
|
<tr>
|
23
23
|
<td class="text_warp"><%= link_to_if_authorized invocation_description(invocation), hash_for_job_invocation_path(invocation).merge(:auth_object => invocation, :permission => :view_job_invocations, :authorizer => authorizer) %></td>
|
24
|
-
<td><%= trunc_with_tooltip(invocation
|
24
|
+
<td><%= trunc_with_tooltip(invocation.targeting.search_query, 15) %></td>
|
25
25
|
<td><%= link_to_invocation_task_if_authorized(invocation) %></td>
|
26
26
|
<td><%= invocation_result(invocation, :success_count) %></td>
|
27
27
|
<td><%= invocation_result(invocation, :failed_count) %></td>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class AddHostProxyInvocations < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
# rubocop:disable Rails/CreateTableWithTimestamps
|
4
|
+
create_table :host_proxy_invocations do |t|
|
5
|
+
t.references :host, :null => false
|
6
|
+
t.references :smart_proxy, :null => false
|
7
|
+
end
|
8
|
+
# rubocop:enable Rails/CreateTableWithTimestamps
|
9
|
+
|
10
|
+
add_index :host_proxy_invocations, [:host_id, :smart_proxy_id], unique: true
|
11
|
+
end
|
12
|
+
end
|
@@ -201,12 +201,10 @@ module ForemanRemoteExecution
|
|
201
201
|
|
202
202
|
Host::Managed.prepend ForemanRemoteExecution::HostExtensions
|
203
203
|
Host::Managed.include ForemanTasks::Concerns::HostActionSubject
|
204
|
-
Host::Managed.include ForemanRemoteExecution::Orchestration::SSH
|
205
204
|
|
206
205
|
(Nic::Base.descendants + [Nic::Base]).each do |klass|
|
207
206
|
klass.send(:include, ForemanRemoteExecution::NicExtensions)
|
208
207
|
end
|
209
|
-
Nic::Managed.include ForemanRemoteExecution::Orchestration::SSH
|
210
208
|
|
211
209
|
Bookmark.include ForemanRemoteExecution::BookmarkExtensions
|
212
210
|
HostsHelper.prepend ForemanRemoteExecution::HostsHelperExtensions
|
@@ -864,7 +864,9 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
|
|
864
864
|
it 'marks targeting as resolved if static' do
|
865
865
|
created = JobInvocationComposer.from_job_invocation(job_invocation).job_invocation
|
866
866
|
assert created.targeting.resolved?
|
867
|
-
|
867
|
+
created.targeting.save
|
868
|
+
created.targeting.reload
|
869
|
+
assert_equal job_invocation.template_invocations_host_ids, created.targeting.targeting_hosts.pluck(:host_id)
|
868
870
|
end
|
869
871
|
|
870
872
|
it 'takes randomized_ordering from the original job invocation when rerunning failed' do
|
@@ -874,6 +876,17 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
|
|
874
876
|
composer = JobInvocationComposer.from_job_invocation(job_invocation, :host_ids => host_ids)
|
875
877
|
assert composer.job_invocation.targeting.randomized_ordering
|
876
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
|
877
890
|
end
|
878
891
|
|
879
892
|
describe '.for_feature' 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,3 +1,4 @@
|
|
1
|
+
/* eslint-disable camelcase */
|
1
2
|
import React, { useState, useEffect, useCallback } from 'react';
|
2
3
|
import { useDispatch, useSelector } from 'react-redux';
|
3
4
|
import { Wizard } from '@patternfly/react-core';
|
@@ -8,6 +9,7 @@ import CategoryAndTemplate from './steps/CategoryAndTemplate/';
|
|
8
9
|
import { AdvancedFields } from './steps/AdvancedFields/AdvancedFields';
|
9
10
|
import { JOB_TEMPLATE } from './JobWizardConstants';
|
10
11
|
import { selectTemplateError } from './JobWizardSelectors';
|
12
|
+
import Schedule from './steps/Schedule/';
|
11
13
|
import './JobWizard.scss';
|
12
14
|
|
13
15
|
export const JobWizard = () => {
|
@@ -16,13 +18,31 @@ export const JobWizard = () => {
|
|
16
18
|
const [advancedValues, setAdvancedValues] = useState({});
|
17
19
|
const dispatch = useDispatch();
|
18
20
|
|
19
|
-
const setDefaults = useCallback(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
+
);
|
26
46
|
useEffect(() => {
|
27
47
|
if (jobTemplateID) {
|
28
48
|
dispatch(
|
@@ -72,7 +92,7 @@ export const JobWizard = () => {
|
|
72
92
|
},
|
73
93
|
{
|
74
94
|
name: __('Schedule'),
|
75
|
-
component: <
|
95
|
+
component: <Schedule />,
|
76
96
|
canJumpTo: isTemplate,
|
77
97
|
},
|
78
98
|
{
|