foreman_remote_execution 8.0.0 → 8.1.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/app/controllers/job_invocations_controller.rb +1 -2
- data/app/controllers/job_templates_controller.rb +1 -1
- data/app/controllers/ui_job_wizard_controller.rb +1 -1
- data/app/helpers/job_invocations_helper.rb +0 -7
- data/app/helpers/remote_execution_helper.rb +1 -1
- data/app/lib/actions/remote_execution/proxy_action.rb +46 -0
- data/app/lib/actions/remote_execution/run_host_job.rb +38 -11
- data/app/lib/actions/remote_execution/run_hosts_job.rb +7 -6
- data/app/lib/actions/remote_execution/template_invocation_progress_logging.rb +27 -0
- data/app/models/job_invocation.rb +5 -9
- data/app/models/job_invocation_composer.rb +4 -0
- data/app/models/remote_execution_provider.rb +10 -2
- data/app/models/ssh_execution_provider.rb +1 -0
- data/app/models/template_invocation.rb +1 -0
- data/app/models/template_invocation_event.rb +11 -0
- data/app/views/job_invocations/_form.html.erb +4 -0
- data/app/views/job_invocations/new.html.erb +5 -0
- data/app/views/templates/script/package_action.erb +1 -1
- data/config/routes.rb +5 -5
- data/db/migrate/20220713095705_create_template_invocation_events.rb +17 -0
- data/db/migrate/20220822155946_add_time_to_pickup_to_job_invocation.rb +5 -0
- data/extra/cockpit/foreman-cockpit-session +303 -230
- data/extra/cockpit/foreman-cockpit.service +1 -0
- data/foreman_remote_execution.gemspec +1 -1
- data/lib/foreman_remote_execution/engine.rb +12 -7
- data/lib/foreman_remote_execution/tasks/explain_proxy_selection.rake +131 -0
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/unit/remote_execution_provider_test.rb +22 -0
- data/webpack/JobWizard/JobWizard.js +53 -18
- data/webpack/JobWizard/JobWizard.scss +3 -0
- data/webpack/JobWizard/JobWizardConstants.js +1 -1
- data/webpack/JobWizard/JobWizardHelpers.js +15 -0
- data/webpack/JobWizard/JobWizardPageRerun.js +29 -5
- data/webpack/JobWizard/JobWizardSelectors.js +8 -2
- data/webpack/JobWizard/__tests__/JobWizardPageRerun.test.js +5 -0
- data/webpack/JobWizard/__tests__/fixtures.js +26 -2
- data/webpack/JobWizard/autofill.js +32 -10
- data/webpack/JobWizard/index.js +25 -6
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +25 -0
- data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +12 -1
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +41 -6
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +1 -1
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +1 -1
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +4 -2
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +6 -2
- data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +28 -20
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/TemplateInputs.test.js +32 -0
- data/webpack/JobWizard/steps/HostsAndInputs/index.js +2 -2
- data/webpack/JobWizard/steps/ReviewDetails/index.js +1 -0
- data/webpack/JobWizard/steps/form/FormHelpers.js +21 -1
- data/webpack/JobWizard/steps/form/Formatter.js +22 -6
- data/webpack/JobWizard/steps/form/ResourceSelect.js +97 -10
- data/webpack/JobWizard/steps/form/SearchSelect.js +2 -2
- data/webpack/JobWizard/steps/form/SelectField.js +4 -0
- data/webpack/JobWizard/submit.js +3 -1
- data/webpack/JobWizard/validation.js +1 -0
- data/webpack/Routes/routes.js +3 -3
- data/webpack/react_app/components/FeaturesDropdown/actions.js +23 -2
- data/webpack/react_app/components/FeaturesDropdown/index.js +2 -0
- data/webpack/react_app/components/HostKebab/KebabItems.js +1 -0
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +5 -0
- data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +51 -59
- data/webpack/react_app/extend/Fills.js +3 -3
- metadata +12 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a4cbf1cdf74f0bd20c4729a8bc05a7a3bb2a958bace18d30755fb600d0a999c
|
4
|
+
data.tar.gz: 7eac3c732b1c9b05e9c0909d952b859010cdeef677324353901859232d8ab3b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42280d6aadec2d5ede443a1c81f688ce838bf388a65b4abdd30ab3d03953c33986023f0aee445e06cf908b5d6040e716a7bc8582b6059f553f400a4328ace60a
|
7
|
+
data.tar.gz: 3248ccc1bc2f48d61d3538f171d8f6c005b241c654ca2949833993896489bbc4d2ca2c2e0f7f0776dbd6d352f63d0fc2a292edb29f1df3cf6d402ffc16feb1c8
|
@@ -45,8 +45,7 @@ class JobInvocationsController < ApplicationController
|
|
45
45
|
if @composer.trigger
|
46
46
|
redirect_to job_invocation_path(@composer.job_invocation)
|
47
47
|
else
|
48
|
-
|
49
|
-
render :action => 'new'
|
48
|
+
redirect_to new_job_invocation_path({:inputs => params[:inputs], :feature => params[:feature], :host_ids => params[:host_ids]})
|
50
49
|
end
|
51
50
|
end
|
52
51
|
|
@@ -34,7 +34,7 @@ class JobTemplatesController < ::TemplatesController
|
|
34
34
|
def import
|
35
35
|
contents = params.fetch(:imported_template, {}).fetch(:template, nil).try(:read)
|
36
36
|
|
37
|
-
@template = JobTemplate.import_raw(contents, :update =>
|
37
|
+
@template = JobTemplate.import_raw(contents, :update => ActiveRecord::Type::Boolean.new.deserialize(params[:imported_template][:overwrite]))
|
38
38
|
if @template&.save
|
39
39
|
flash[:success] = _('Job template imported successfully.')
|
40
40
|
redirect_to job_templates_path(:search => "name = \"#{@template.name}\"")
|
@@ -62,7 +62,7 @@ class UiJobWizardController < ApplicationController
|
|
62
62
|
job = JobInvocation.authorized.find(params[:id])
|
63
63
|
composer = JobInvocationComposer.from_job_invocation(job, params).params
|
64
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]
|
65
|
+
inputs = Hash[job_template_inputs.map { |input| ["inputs[#{input[:name]}]", {:advanced => input[:advanced], :value => (composer[:template_invocations][0][:input_values].find { |value| value[:template_input_id] == input[:id] }).try(:[], :value)}] }]
|
66
66
|
job_organization = Taxonomy.find_by(id: job.task.input[:current_organization_id])
|
67
67
|
job_location = Taxonomy.find_by(id: job.task.input[:current_location_id])
|
68
68
|
render :json => {
|
@@ -13,13 +13,6 @@ module JobInvocationsHelper
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
def job_invocations_buttons
|
17
|
-
[
|
18
|
-
documentation_button_rex('3.2ExecutingaJob'),
|
19
|
-
display_link_if_authorized(_('Run Job'), hash_for_new_job_invocation_path),
|
20
|
-
]
|
21
|
-
end
|
22
|
-
|
23
16
|
def template_name_and_provider_link(template)
|
24
17
|
template_name = content_tag(:strong, template.name)
|
25
18
|
provider = template.provider.humanized_name
|
@@ -56,7 +56,7 @@ module RemoteExecutionHelper
|
|
56
56
|
def job_invocations_buttons
|
57
57
|
[
|
58
58
|
documentation_button_rex('3.2ExecutingaJob'),
|
59
|
-
|
59
|
+
display_link_if_authorized(_('Run Job'), hash_for_new_job_invocation_path, {:class => "btn btn-primary"}),
|
60
60
|
]
|
61
61
|
end
|
62
62
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Actions
|
2
|
+
module RemoteExecution
|
3
|
+
class ProxyAction < ::Actions::ProxyAction
|
4
|
+
include Actions::RemoteExecution::TemplateInvocationProgressLogging
|
5
|
+
|
6
|
+
def on_data(data, meta = {})
|
7
|
+
super
|
8
|
+
process_proxy_data(output[:proxy_output])
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(event = nil)
|
12
|
+
with_template_invocation_error_logging { super }
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def get_proxy_data(response)
|
18
|
+
data = super
|
19
|
+
process_proxy_data(data)
|
20
|
+
data
|
21
|
+
end
|
22
|
+
|
23
|
+
def process_proxy_data(data)
|
24
|
+
events = data['result'].map do |update|
|
25
|
+
{
|
26
|
+
template_invocation_id: template_invocation.id,
|
27
|
+
event: update['output'],
|
28
|
+
timestamp: Time.at(update['timestamp']).getlocal,
|
29
|
+
event_type: update['output_type'],
|
30
|
+
}
|
31
|
+
end
|
32
|
+
if data['exit_status']
|
33
|
+
events << {
|
34
|
+
template_invocation_id: template_invocation.id,
|
35
|
+
event: data['exit_status'],
|
36
|
+
timestamp: events.last[:timestamp] + 0.0001,
|
37
|
+
event_type: 'exit',
|
38
|
+
}
|
39
|
+
end
|
40
|
+
events.each_slice(1000) do |batch|
|
41
|
+
TemplateInvocationEvent.upsert_all(batch, unique_by: [:template_invocation_id, :timestamp, :event_type]) # rubocop:disable Rails/SkipsModelValidations
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -4,6 +4,7 @@ module Actions
|
|
4
4
|
include ::Actions::Helpers::WithContinuousOutput
|
5
5
|
include ::Actions::Helpers::WithDelegatedAction
|
6
6
|
include ::Actions::ObservableAction
|
7
|
+
include ::Actions::RemoteExecution::TemplateInvocationProgressLogging
|
7
8
|
|
8
9
|
execution_plan_hooks.use :emit_feature_event, :on => :success
|
9
10
|
|
@@ -19,6 +20,13 @@ module Actions
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def plan(job_invocation, host, template_invocation, proxy_selector = ::RemoteExecutionProxySelector.new, options = {})
|
23
|
+
with_template_invocation_error_logging do
|
24
|
+
inner_plan(job_invocation, host, template_invocation, proxy_selector, options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def inner_plan(job_invocation, host, template_invocation, proxy_selector, options)
|
29
|
+
raise _('Could not use any template used in the job invocation') if template_invocation.blank?
|
22
30
|
features = template_invocation.template.remote_execution_features.pluck(:label).uniq
|
23
31
|
action_subject(host,
|
24
32
|
:job_category => job_invocation.job_category,
|
@@ -35,8 +43,6 @@ module Actions
|
|
35
43
|
|
36
44
|
verify_permissions(host, template_invocation)
|
37
45
|
|
38
|
-
raise _('Could not use any template used in the job invocation') if template_invocation.blank?
|
39
|
-
|
40
46
|
provider = template_invocation.template.provider
|
41
47
|
proxy_selector = provider.required_proxy_selector_for(template_invocation.template) || proxy_selector
|
42
48
|
|
@@ -63,13 +69,15 @@ module Actions
|
|
63
69
|
action_options = provider.proxy_command_options(template_invocation, host)
|
64
70
|
.merge(additional_options)
|
65
71
|
|
66
|
-
plan_delegated_action(proxy, provider.proxy_action_class, action_options)
|
67
|
-
plan_self
|
72
|
+
plan_delegated_action(proxy, provider.proxy_action_class, action_options, proxy_action_class: ::Actions::RemoteExecution::ProxyAction)
|
73
|
+
plan_self :with_event_logging => true
|
68
74
|
end
|
69
75
|
|
70
76
|
def finalize(*args)
|
71
|
-
|
72
|
-
|
77
|
+
with_template_invocation_error_logging do
|
78
|
+
update_host_status
|
79
|
+
check_exit_status
|
80
|
+
end
|
73
81
|
end
|
74
82
|
|
75
83
|
def self.feature_job_event_name(label, suffix = :success)
|
@@ -128,6 +136,11 @@ module Actions
|
|
128
136
|
end
|
129
137
|
|
130
138
|
def fill_continuous_output(continuous_output)
|
139
|
+
if input[:with_event_logging]
|
140
|
+
continuous_output_from_template_invocation_events(continuous_output)
|
141
|
+
return
|
142
|
+
end
|
143
|
+
|
131
144
|
delegated_output.fetch('result', []).each do |raw_output|
|
132
145
|
continuous_output.add_raw_output(raw_output)
|
133
146
|
end
|
@@ -148,8 +161,26 @@ module Actions
|
|
148
161
|
continuous_output.add_exception(_('Error loading data from proxy'), e)
|
149
162
|
end
|
150
163
|
|
164
|
+
def continuous_output_from_template_invocation_events(continuous_output)
|
165
|
+
begin
|
166
|
+
# Trigger reload
|
167
|
+
delegated_output unless task.state == 'stopped'
|
168
|
+
rescue => e
|
169
|
+
# This is enough, the error will get shown using add_exception at the end of the method
|
170
|
+
end
|
171
|
+
|
172
|
+
task.template_invocation.template_invocation_events.find_each do |output|
|
173
|
+
if output.event_type == 'exit'
|
174
|
+
continuous_output.add_output(_('Exit status: %s') % output.event, 'stdout', output.timestamp)
|
175
|
+
else
|
176
|
+
continuous_output.add_raw_output(output.as_raw_continuous_output)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
continuous_output.add_exception(_('Error loading data from proxy'), e) if e
|
180
|
+
end
|
181
|
+
|
151
182
|
def exit_status
|
152
|
-
delegated_output[:exit_status]
|
183
|
+
input[:with_event_logging] ? task.template_invocation.template_invocation_events.find_by(event_type: 'exit').event : delegated_output[:exit_status]
|
153
184
|
end
|
154
185
|
|
155
186
|
def host_id
|
@@ -172,10 +203,6 @@ module Actions
|
|
172
203
|
@host ||= ::Host.authorized.find(host_id)
|
173
204
|
end
|
174
205
|
|
175
|
-
def self.cleanup_after
|
176
|
-
'90d'
|
177
|
-
end
|
178
|
-
|
179
206
|
private
|
180
207
|
|
181
208
|
def update_host_status
|
@@ -47,7 +47,6 @@ module Actions
|
|
47
47
|
# composer creates just "pattern" for template_invocations because target is evaluated
|
48
48
|
# during actual run (here) so we build template invocations from these patterns
|
49
49
|
template_invocation = job_invocation.pattern_template_invocation_for_host(host).deep_clone
|
50
|
-
template_invocation.host_id = host.id
|
51
50
|
trigger(RunHostJob, job_invocation, host, template_invocation, proxy_selector, { :use_concurrency_control => uses_concurrency_control })
|
52
51
|
end
|
53
52
|
end
|
@@ -76,7 +75,7 @@ module Actions
|
|
76
75
|
job_invocation.save!
|
77
76
|
|
78
77
|
Rails.logger.debug "cleaning cache for keys that begin with 'job_invocation_#{job_invocation.id}'"
|
79
|
-
Rails.cache.delete_matched(
|
78
|
+
Rails.cache.delete_matched(cache_deletion_query(job_invocation.id))
|
80
79
|
end
|
81
80
|
|
82
81
|
def notify_on_success(_plan)
|
@@ -153,15 +152,17 @@ module Actions
|
|
153
152
|
input[:proxy_batch_size]
|
154
153
|
end
|
155
154
|
|
156
|
-
def self.cleanup_after
|
157
|
-
'90d'
|
158
|
-
end
|
159
|
-
|
160
155
|
private
|
161
156
|
|
162
157
|
def mail_notification_preference
|
163
158
|
UserMailNotification.where(mail_notification_id: RexMailNotification.first, user_id: User.current.id).first
|
164
159
|
end
|
160
|
+
|
161
|
+
def cache_deletion_query(job_invocation_id)
|
162
|
+
return "#{JobInvocation::CACHE_PREFIX}_#{job_invocation_id}*" if Rails.cache.kind_of? ActiveSupport::Cache::RedisCacheStore
|
163
|
+
|
164
|
+
/\A#{JobInvocation::CACHE_PREFIX}_#{job_invocation_id}/
|
165
|
+
end
|
165
166
|
end
|
166
167
|
end
|
167
168
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Actions
|
2
|
+
module RemoteExecution
|
3
|
+
module TemplateInvocationProgressLogging
|
4
|
+
def template_invocation
|
5
|
+
@template_invocation ||= TemplateInvocation.find_by(:run_host_job_task_id => task.id)
|
6
|
+
end
|
7
|
+
|
8
|
+
def log_template_invocation_exception(exception)
|
9
|
+
template_invocation.template_invocation_events.create!(
|
10
|
+
:event_type => 'debug',
|
11
|
+
:event => "#{exception.class}: #{exception.message}",
|
12
|
+
:timestamp => Time.zone.now
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def with_template_invocation_error_logging
|
17
|
+
unless catch(::Dynflow::Action::ERROR) { yield || true }
|
18
|
+
log_template_invocation_exception(error.exception)
|
19
|
+
throw ::Dynflow::Action::ERROR
|
20
|
+
end
|
21
|
+
rescue => e # rubocop:disable Style/RescueStandardError
|
22
|
+
log_template_invocation_exception(e)
|
23
|
+
raise e
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -62,14 +62,16 @@ 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
|
65
|
+
:complete_value => false, :only_explicit => true, :ext_method => :search_by_targeted_host,
|
66
|
+
:validator => ScopedSearch::Validators::INTEGER
|
66
67
|
|
67
68
|
scoped_search :on => 'pattern_template_name', :rename => 'pattern_template_name', :operators => ['= '],
|
68
69
|
:complete_value => false, :only_explicit => true, :ext_method => :search_by_pattern_template
|
69
70
|
|
70
71
|
scoped_search :relation => :recurring_logic, :on => 'purpose', :rename => 'recurring_logic.purpose'
|
71
72
|
|
72
|
-
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
|
73
|
+
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id',
|
74
|
+
:validator => ScopedSearch::Validators::INTEGER
|
73
75
|
|
74
76
|
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring',
|
75
77
|
:ext_method => :search_by_recurring_logic, :only_explicit => true,
|
@@ -211,13 +213,7 @@ class JobInvocation < ApplicationRecord
|
|
211
213
|
|
212
214
|
def pattern_template_invocation_for_host(host)
|
213
215
|
providers = available_providers(host)
|
214
|
-
|
215
|
-
pattern_template_invocations.each do |template_invocation|
|
216
|
-
if template_invocation.template.provider_type == provider
|
217
|
-
return template_invocation
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
216
|
+
pattern_template_invocations.find { |pti| providers.include? pti.template.provider_type }
|
221
217
|
end
|
222
218
|
|
223
219
|
# TODO: determine from the host and job_invocation details
|
@@ -22,6 +22,7 @@ class JobInvocationComposer
|
|
22
22
|
:effective_user_password => blank_to_nil(job_invocation_base[:effective_user_password]),
|
23
23
|
:concurrency_control => concurrency_control_params,
|
24
24
|
:execution_timeout_interval => execution_timeout_interval,
|
25
|
+
:time_to_pickup => job_invocation_base[:time_to_pickup],
|
25
26
|
:template_invocations => template_invocations_params }.with_indifferent_access
|
26
27
|
end
|
27
28
|
|
@@ -129,6 +130,7 @@ class JobInvocationComposer
|
|
129
130
|
:key_passphrase => api_params[:key_passphrase],
|
130
131
|
:concurrency_control => concurrency_control_params,
|
131
132
|
:execution_timeout_interval => api_params[:execution_timeout_interval] || template.execution_timeout_interval,
|
133
|
+
:time_to_pickup => api_params[:time_to_pickup],
|
132
134
|
:template_invocations => template_invocations_params }.with_indifferent_access
|
133
135
|
end
|
134
136
|
|
@@ -245,6 +247,7 @@ class JobInvocationComposer
|
|
245
247
|
:execution_timeout_interval => job_invocation.execution_timeout_interval,
|
246
248
|
:remote_execution_feature_id => job_invocation.remote_execution_feature_id,
|
247
249
|
:template_invocations => template_invocations_params,
|
250
|
+
:time_to_pickup => job_invocation.time_to_pickup,
|
248
251
|
:reruns => job_invocation.id }.with_indifferent_access
|
249
252
|
end
|
250
253
|
|
@@ -415,6 +418,7 @@ class JobInvocationComposer
|
|
415
418
|
job_invocation.key_passphrase = params[:key_passphrase]
|
416
419
|
job_invocation.effective_user_password = params[:effective_user_password]
|
417
420
|
job_invocation.ssh_user = params[:ssh_user]
|
421
|
+
job_invocation.time_to_pickup = params[:time_to_pickup]
|
418
422
|
|
419
423
|
if @reruns && job_invocation.targeting.static?
|
420
424
|
job_invocation.targeting.assign_host_ids(JobInvocation.find(@reruns).targeting.host_ids)
|
@@ -9,7 +9,7 @@ class RemoteExecutionProvider
|
|
9
9
|
|
10
10
|
def registered_name
|
11
11
|
klass = self
|
12
|
-
providers.key(klass)
|
12
|
+
::RemoteExecutionProvider.providers.key(klass)
|
13
13
|
end
|
14
14
|
|
15
15
|
def proxy_feature
|
@@ -33,7 +33,10 @@ class RemoteExecutionProvider
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def proxy_command_options(template_invocation, host)
|
36
|
-
{
|
36
|
+
{
|
37
|
+
:proxy_operation_name => proxy_operation_name,
|
38
|
+
:time_to_pickup => time_to_pickup(template_invocation.job_invocation, host),
|
39
|
+
}.merge(proxy_command_provider_inputs(template_invocation))
|
37
40
|
end
|
38
41
|
|
39
42
|
def secrets(_host)
|
@@ -152,5 +155,10 @@ class RemoteExecutionProvider
|
|
152
155
|
def alternative_names(host)
|
153
156
|
{ :fqdn => find_fqdn(effective_interfaces(host)) }
|
154
157
|
end
|
158
|
+
|
159
|
+
def time_to_pickup(job_invocation, host)
|
160
|
+
time = job_invocation.time_to_pickup || host_setting(host, 'remote_execution_time_to_pickup')
|
161
|
+
Integer(time) if time
|
162
|
+
end
|
155
163
|
end
|
156
164
|
end
|
@@ -17,6 +17,7 @@ class TemplateInvocation < ApplicationRecord
|
|
17
17
|
has_one :host_group, :through => :host, :source => :hostgroup
|
18
18
|
belongs_to :run_host_job_task, :class_name => 'ForemanTasks::Task'
|
19
19
|
has_many :remote_execution_features, :through => :template
|
20
|
+
has_many :template_invocation_events, :dependent => :destroy
|
20
21
|
|
21
22
|
validates_associated :input_values
|
22
23
|
validate :provides_required_input_values
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class TemplateInvocationEvent < ::ApplicationRecord
|
2
|
+
belongs_to :template_invocation
|
3
|
+
|
4
|
+
def as_raw_continuous_output
|
5
|
+
raw = attributes
|
6
|
+
raw['output_type'] = raw.delete('event_type')
|
7
|
+
raw['output'] = raw.delete('event')
|
8
|
+
raw['timestamp'] = raw['timestamp'].to_f
|
9
|
+
raw
|
10
|
+
end
|
11
|
+
end
|
@@ -102,6 +102,10 @@
|
|
102
102
|
</fieldset>
|
103
103
|
<% end %>
|
104
104
|
|
105
|
+
<div class="advanced hidden">
|
106
|
+
<%= number_f f, :time_to_pickup, :value => f.object.time_to_pickup, :label => _('Time to pickup'), :label_help => N_('Interval in seconds, if the job is not picked up by a client within this interval it will be cancelled.') %>
|
107
|
+
</div>
|
108
|
+
|
105
109
|
<div class="advanced hidden">
|
106
110
|
<%= password_f f, :password, :placeholder => '*****', :label => _('Password'), :label_help => N_('Password is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution.') %>
|
107
111
|
<%= password_f f, :key_passphrase, :placeholder => '*****', :label => _('Private key passphrase'), :label_help => N_('Key passhprase is only applicable for SSH provider. Other providers ignore this field. <br> Passphrase is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution.') %>
|
@@ -1,4 +1,9 @@
|
|
1
1
|
<% javascript 'foreman_remote_execution/template_invocation' %>
|
2
2
|
<% stylesheet 'foreman_remote_execution/foreman_remote_execution' %>
|
3
3
|
<% title _('Job invocation') %>
|
4
|
+
<% if(request.path_parameters[:action] == "rerun") %>
|
5
|
+
<%= display_link_if_authorized(_('Use new job wizard'), hash_for_rerun_job_invocation_path({:id => request.path_parameters[:id]}).merge(request.query_parameters)) %>
|
6
|
+
<% else %>
|
7
|
+
<%= display_link_if_authorized(_('Use new job wizard'), hash_for_new_job_invocation_path().merge(request.query_parameters)) %>
|
8
|
+
<%end%>
|
4
9
|
<%= render :partial => 'form' %>
|
@@ -108,7 +108,7 @@ handle_zypp_res_codes () {
|
|
108
108
|
[ -x "$(command -v subscription-manager)" ] && subscription-manager refresh
|
109
109
|
export DEBIAN_FRONTEND=noninteractive
|
110
110
|
apt-get -y update
|
111
|
-
apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y <%= input("options") %> <%= action %> <%= input("package") %>
|
111
|
+
apt-get -o APT::Get::Upgrade-Allow-New="true" -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y <%= input("options") %> <%= action %> <%= input("package") %>
|
112
112
|
<% elsif package_manager == 'zypper' -%>
|
113
113
|
<%-
|
114
114
|
if action == "group install"
|
data/config/routes.rb
CHANGED
@@ -16,7 +16,11 @@ Rails.application.routes.draw do
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
match 'job_invocations/new', to: '
|
19
|
+
match 'job_invocations/new', to: 'react#index', :via => [:get], as: 'new_job_invocation'
|
20
|
+
match 'job_invocations/new', to: 'job_invocations#create', via: [:post], as: 'create_job_invocation'
|
21
|
+
match 'job_invocations/:id/rerun', to: 'react#index', :via => [:get], as: 'rerun_job_invocation'
|
22
|
+
match 'old/job_invocations/new', to: 'job_invocations#new', via: [:get], as: 'form_new_job_invocation'
|
23
|
+
match 'old/job_invocations/:id/rerun', to: 'job_invocations#rerun', via: [:get, :post], as: 'form_rerun_job_invocation'
|
20
24
|
resources :job_invocations, :only => [:create, :show, :index] do
|
21
25
|
collection do
|
22
26
|
post 'refresh'
|
@@ -25,7 +29,6 @@ Rails.application.routes.draw do
|
|
25
29
|
get 'auto_complete_search'
|
26
30
|
end
|
27
31
|
member do
|
28
|
-
get 'rerun'
|
29
32
|
post 'cancel'
|
30
33
|
end
|
31
34
|
end
|
@@ -48,9 +51,6 @@ Rails.application.routes.draw do
|
|
48
51
|
get 'ui_job_wizard/resources', to: 'ui_job_wizard#resources'
|
49
52
|
get 'ui_job_wizard/job_invocation', to: 'ui_job_wizard#job_invocation'
|
50
53
|
|
51
|
-
match '/experimental/job_wizard/new', to: 'react#index', :via => [:get]
|
52
|
-
match '/experimental/job_wizard/:id/rerun', to: 'react#index', :via => [:get]
|
53
|
-
|
54
54
|
namespace :api, :defaults => {:format => 'json'} do
|
55
55
|
scope '(:apiv)', :module => :v2, :defaults => {:apiv => 'v2'}, :apiv => /v1|v2/, :constraints => ApiConstraints.new(:version => 2, :default => true) do
|
56
56
|
resources :job_invocations, :except => [:new, :edit, :update, :destroy] do
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateTemplateInvocationEvents < ActiveRecord::Migration[6.1]
|
2
|
+
def change
|
3
|
+
# rubocop:disable Rails/CreateTableWithTimestamps
|
4
|
+
create_table :template_invocation_events do |t|
|
5
|
+
t.references :template_invocation, :null => false
|
6
|
+
t.timestamp :timestamp, :null => false
|
7
|
+
t.string :event_type, :null => false
|
8
|
+
t.string :event, :null => false
|
9
|
+
t.string :meta
|
10
|
+
|
11
|
+
t.index [:template_invocation_id, :timestamp, :event_type],
|
12
|
+
unique: true,
|
13
|
+
name: 'unique_template_invocation_events_index'
|
14
|
+
end
|
15
|
+
# rubocop:enable Rails/CreateTableWithTimestamps
|
16
|
+
end
|
17
|
+
end
|