foreman_remote_execution 4.2.1 → 4.4.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/js_ci.yml +29 -0
- data/.github/workflows/{ci.yml → ruby_ci.yml} +22 -50
- data/.prettierrc +4 -0
- data/.rubocop.yml +13 -49
- data/.rubocop_todo.yml +326 -102
- data/Gemfile +1 -4
- data/app/controllers/api/v2/job_invocations_controller.rb +17 -1
- data/app/controllers/foreman_remote_execution/concerns/api/v2/registration_commands_controller_extensions.rb +19 -0
- data/app/controllers/ui_job_wizard_controller.rb +18 -0
- data/app/helpers/remote_execution_helper.rb +27 -0
- data/app/lib/actions/remote_execution/run_host_job.rb +38 -1
- data/app/lib/foreman_remote_execution/provider_input.rb +29 -0
- data/app/lib/foreman_remote_execution/renderer/scope/input.rb +1 -0
- data/app/models/foreign_input_set.rb +1 -1
- data/app/models/host_status/execution_status.rb +7 -0
- data/app/models/invocation_provider_input_value.rb +12 -0
- data/app/models/job_invocation.rb +5 -1
- data/app/models/job_invocation_composer.rb +13 -0
- data/app/models/remote_execution_provider.rb +17 -2
- data/app/models/setting/remote_execution.rb +10 -0
- data/app/models/template_invocation.rb +2 -0
- data/app/services/renderer_methods.rb +12 -0
- data/app/views/job_invocations/_form.html.erb +8 -0
- data/app/views/template_invocations/show.html.erb +30 -23
- data/config/routes.rb +4 -0
- data/db/migrate/20210312074713_add_provider_inputs.rb +10 -0
- data/foreman_remote_execution.gemspec +1 -2
- data/lib/foreman_remote_execution/engine.rb +17 -7
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/lib/tasks/foreman_remote_execution_tasks.rake +1 -18
- data/locale/action_names.rb +1 -0
- data/locale/de/foreman_remote_execution.po +77 -27
- data/locale/en/foreman_remote_execution.po +77 -27
- data/locale/en_GB/foreman_remote_execution.po +77 -27
- data/locale/es/foreman_remote_execution.po +77 -27
- data/locale/foreman_remote_execution.pot +241 -163
- data/locale/fr/foreman_remote_execution.po +77 -27
- data/locale/ja/foreman_remote_execution.po +77 -27
- data/locale/ko/foreman_remote_execution.po +77 -27
- data/locale/pt_BR/foreman_remote_execution.po +77 -27
- data/locale/ru/foreman_remote_execution.po +77 -27
- data/locale/zh_CN/foreman_remote_execution.po +77 -27
- data/locale/zh_TW/foreman_remote_execution.po +77 -27
- data/package.json +3 -2
- data/test/functional/api/v2/job_invocations_controller_test.rb +24 -4
- data/test/functional/api/v2/registration_controller_test.rb +4 -13
- data/test/functional/ui_job_wizard_controller_test.rb +16 -0
- data/test/helpers/remote_execution_helper_test.rb +16 -0
- data/test/unit/job_invocation_composer_test.rb +41 -1
- data/test/unit/job_invocation_report_template_test.rb +57 -0
- data/webpack/JobWizard/JobWizard.js +55 -0
- data/webpack/JobWizard/JobWizard.scss +12 -0
- data/webpack/JobWizard/JobWizardConstants.js +5 -0
- data/webpack/JobWizard/JobWizardSelectors.js +21 -0
- data/webpack/JobWizard/__tests__/JobWizard.test.js +20 -0
- data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +83 -0
- data/webpack/JobWizard/index.js +32 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +77 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +45 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +64 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +86 -0
- data/webpack/JobWizard/steps/form/GroupedSelectField.js +88 -0
- data/webpack/JobWizard/steps/form/SelectField.js +39 -0
- data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +38 -0
- data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +23 -0
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +36 -0
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +22 -0
- data/webpack/Routes/routes.js +12 -0
- data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
- data/webpack/__mocks__/foremanReact/history.js +1 -0
- data/webpack/__mocks__/foremanReact/redux/API/index.js +5 -0
- data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +10 -0
- data/webpack/fills_index.js +11 -0
- data/webpack/global_index.js +8 -0
- data/webpack/index.js +0 -4
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +87 -0
- data/webpack/react_app/components/RecentJobsCard/constants.js +1 -0
- data/webpack/react_app/components/RecentJobsCard/index.js +1 -0
- data/webpack/react_app/components/RecentJobsCard/styles.css +15 -0
- data/webpack/react_app/components/RegistrationExtension/RexInterface.js +50 -0
- data/webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js +9 -0
- data/webpack/react_app/components/RegistrationExtension/__tests__/__snapshots__/RexInterface.test.js.snap +35 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +1 -1
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss +0 -3
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -1
- data/webpack/react_app/extend/fills.js +10 -0
- data/webpack/react_app/extend/reducers.js +4 -0
- metadata +54 -24
- data/app/views/api/v2/registration/_form.html.erb +0 -12
data/Gemfile
CHANGED
@@ -64,6 +64,16 @@ module Api
|
|
64
64
|
param :description_format, String, :required => false, :desc => N_('Override the description format from the template for this invocation only')
|
65
65
|
param :execution_timeout_interval, Integer, :required => false, :desc => N_('Override the timeout interval from the template for this invocation only')
|
66
66
|
param :feature, String, :required => false, :desc => N_('Remote execution feature label that should be triggered, job template assigned to this feature will be used')
|
67
|
+
|
68
|
+
RemoteExecutionProvider.providers.each_value do |provider|
|
69
|
+
next if !provider.respond_to?(:provider_inputs_doc) || provider.provider_inputs_doc.empty?
|
70
|
+
doc = provider.provider_inputs_doc
|
71
|
+
param doc[:namespace], Hash, doc[:opts] do
|
72
|
+
doc[:children].map do |input|
|
73
|
+
param input[:name], input[:type], input[:opts]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
67
77
|
end
|
68
78
|
end
|
69
79
|
|
@@ -195,9 +205,15 @@ module Api
|
|
195
205
|
end
|
196
206
|
job_invocation_params[:inputs] ||= {}
|
197
207
|
job_invocation_params[:inputs].permit!
|
208
|
+
permit_provider_inputs job_invocation_params
|
198
209
|
@job_invocation_params = job_invocation_params
|
199
210
|
end
|
200
211
|
|
212
|
+
def permit_provider_inputs(invocation_params)
|
213
|
+
providers = RemoteExecutionProvider.providers.values.reject { |provider| !provider.respond_to?(:provider_input_namespace) || provider.provider_input_namespace.empty? }
|
214
|
+
providers.each { |provider| invocation_params[provider.provider_input_namespace]&.permit! }
|
215
|
+
end
|
216
|
+
|
201
217
|
def composer_for_feature
|
202
218
|
JobInvocationComposer.for_feature(
|
203
219
|
job_invocation_params[:feature],
|
@@ -214,7 +230,7 @@ module Api
|
|
214
230
|
end
|
215
231
|
|
216
232
|
def host_output(job_invocation, host, default: nil, since: nil, raw: false)
|
217
|
-
refresh =
|
233
|
+
refresh = !job_invocation.finished?
|
218
234
|
|
219
235
|
if (task = job_invocation.sub_task_for_host(host))
|
220
236
|
refresh = task.pending?
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ForemanRemoteExecution
|
2
|
+
module Concerns
|
3
|
+
module Api
|
4
|
+
module V2
|
5
|
+
module RegistrationCommandsControllerExtensions
|
6
|
+
module ApipieExtensions
|
7
|
+
extend Apipie::DSL::Concern
|
8
|
+
|
9
|
+
update_api(:create) do
|
10
|
+
param :registration_command, Hash do
|
11
|
+
param :remote_execution_interface, String, desc: N_("Identifier of the Host interface for Remote execution")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class UiJobWizardController < ::Api::V2::BaseController
|
2
|
+
def categories
|
3
|
+
job_categories = resource_scope
|
4
|
+
.search_for("job_category ~ \"#{params[:search]}\"")
|
5
|
+
.select(:job_category).distinct
|
6
|
+
.reorder(:job_category)
|
7
|
+
.pluck(:job_category)
|
8
|
+
render :json => {:job_categories =>job_categories}
|
9
|
+
end
|
10
|
+
|
11
|
+
def resource_class
|
12
|
+
JobTemplate
|
13
|
+
end
|
14
|
+
|
15
|
+
def action_permission
|
16
|
+
:view_job_templates
|
17
|
+
end
|
18
|
+
end
|
@@ -60,6 +60,12 @@ module RemoteExecutionHelper
|
|
60
60
|
job_invocation = task.task_groups.find { |group| group.class == JobInvocationTaskGroup }.job_invocation
|
61
61
|
task_authorizer = Authorizer.new(User.current, :collection => [task])
|
62
62
|
buttons = []
|
63
|
+
if (template = job_report_template) && authorized_for(controller: :report_templates, action: :generate)
|
64
|
+
buttons << link_to(_('Create Report'), generate_report_template_path(template, job_report_template_parameters(job_invocation, template)),
|
65
|
+
class: 'btn btn-default',
|
66
|
+
title: _('Create report for this job'),
|
67
|
+
disabled: task.pending?)
|
68
|
+
end
|
63
69
|
if authorized_for(hash_for_new_job_invocation_path)
|
64
70
|
buttons << link_to(_('Rerun'), rerun_job_invocation_path(:id => job_invocation.id),
|
65
71
|
:class => 'btn btn-default',
|
@@ -230,6 +236,27 @@ module RemoteExecutionHelper
|
|
230
236
|
task.execution_plan.actions[1].try(:input).try(:[], 'script')
|
231
237
|
end
|
232
238
|
|
239
|
+
def job_report_template
|
240
|
+
template = ReportTemplate.where(name: Setting['remote_execution_job_invocation_report_template']).first
|
241
|
+
|
242
|
+
template if template.template_inputs.where(name: 'job_id').exists?
|
243
|
+
end
|
244
|
+
|
245
|
+
def job_report_template_parameters(job_invocation, template)
|
246
|
+
template_input = template.template_inputs.where(name: 'job_id').first
|
247
|
+
raise "#job_report_template_parameters need template that has 'job_id' input" unless template_input
|
248
|
+
|
249
|
+
{
|
250
|
+
report_template_report: {
|
251
|
+
input_values: {
|
252
|
+
"#{template_input.id}": {
|
253
|
+
value: job_invocation.id,
|
254
|
+
},
|
255
|
+
},
|
256
|
+
},
|
257
|
+
}
|
258
|
+
end
|
259
|
+
|
233
260
|
def targeting_hosts(job_invocation, hosts)
|
234
261
|
hosts.map do |host|
|
235
262
|
template_invocation = job_invocation.template_invocations.find { |template_inv| template_inv.host_id == host.id }
|
@@ -3,6 +3,7 @@ module Actions
|
|
3
3
|
class RunHostJob < Actions::EntryAction
|
4
4
|
include ::Actions::Helpers::WithContinuousOutput
|
5
5
|
include ::Actions::Helpers::WithDelegatedAction
|
6
|
+
include ::Actions::ObservableAction
|
6
7
|
|
7
8
|
middleware.do_not_use Dynflow::Middleware::Common::Transaction
|
8
9
|
middleware.use Actions::Middleware::HideSecrets
|
@@ -16,7 +17,7 @@ module Actions
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def plan(job_invocation, host, template_invocation, proxy_selector = ::RemoteExecutionProxySelector.new, options = {})
|
19
|
-
action_subject(host, :job_category => job_invocation.job_category, :description => job_invocation.description)
|
20
|
+
action_subject(host, :job_category => job_invocation.job_category, :description => job_invocation.description, :job_invocation_id => job_invocation.id)
|
20
21
|
|
21
22
|
template_invocation.host_id = host.id
|
22
23
|
template_invocation.run_host_job_task_id = task.id
|
@@ -121,6 +122,26 @@ module Actions
|
|
121
122
|
delegated_output[:exit_status]
|
122
123
|
end
|
123
124
|
|
125
|
+
def host_id
|
126
|
+
input['host']['id']
|
127
|
+
end
|
128
|
+
|
129
|
+
def host_name
|
130
|
+
input['host']['name']
|
131
|
+
end
|
132
|
+
|
133
|
+
def job_invocation_id
|
134
|
+
input['job_invocation_id']
|
135
|
+
end
|
136
|
+
|
137
|
+
def job_invocation
|
138
|
+
@job_invocation ||= ::JobInvocation.authorized.find(job_invocation_id)
|
139
|
+
end
|
140
|
+
|
141
|
+
def host
|
142
|
+
@host ||= ::Host.authorized.find(host_id)
|
143
|
+
end
|
144
|
+
|
124
145
|
private
|
125
146
|
|
126
147
|
def update_host_status
|
@@ -173,6 +194,22 @@ module Actions
|
|
173
194
|
end
|
174
195
|
proxy
|
175
196
|
end
|
197
|
+
|
198
|
+
extend ApipieDSL::Class
|
199
|
+
apipie :class, "An action representing execution of a job against a host" do
|
200
|
+
name 'Actions::RemoteExecution::RunHostJob'
|
201
|
+
refs 'Actions::RemoteExecution::RunHostJob'
|
202
|
+
sections only: %w[all webhooks]
|
203
|
+
property :task, object_of: 'Task', desc: 'Returns the task to which this action belongs'
|
204
|
+
property :host_name, String, desc: "Returns the name of the host"
|
205
|
+
property :host_id, Integer, desc: "Returns the id of the host"
|
206
|
+
property :host, object_of: 'Host', desc: "Returns the host"
|
207
|
+
property :job_invocation_id, Integer, desc: "Returns the id of the job invocation"
|
208
|
+
property :job_invocation, object_of: 'JobInvocation', desc: "Returns the job invocation"
|
209
|
+
end
|
210
|
+
class Jail < ::Actions::ObservableAction::Jail
|
211
|
+
allow :host_name, :host_id, :host, :job_invocation_id, :job_invocation
|
212
|
+
end
|
176
213
|
end
|
177
214
|
end
|
178
215
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ForemanRemoteExecution
|
2
|
+
class ProviderInput
|
3
|
+
attr_reader :name, :label, :description, :options, :value_type, :required
|
4
|
+
attr_accessor :value
|
5
|
+
|
6
|
+
def initialize(name:, label:, value:, description: nil, options: nil, value_type: nil, required: false, hidden: false)
|
7
|
+
@name = name
|
8
|
+
@label = label
|
9
|
+
@value = value
|
10
|
+
@description = description
|
11
|
+
@options = options
|
12
|
+
@value_type = value_type
|
13
|
+
@required = required
|
14
|
+
@hidden = hidden
|
15
|
+
end
|
16
|
+
|
17
|
+
def template_input
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def hidden_value?
|
22
|
+
@hidden
|
23
|
+
end
|
24
|
+
|
25
|
+
def options_array
|
26
|
+
options.blank? ? [] : options.split(/\r?\n/).map(&:strip)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -6,6 +6,7 @@ module ForemanRemoteExecution
|
|
6
6
|
extend ApipieDSL::Class
|
7
7
|
|
8
8
|
attr_reader :template, :host, :invocation, :input_template_instance, :current_user
|
9
|
+
attr_accessor :error_message
|
9
10
|
delegate :input, to: :input_template_instance
|
10
11
|
|
11
12
|
apipie :class, 'Macros related to template rendering' do
|
@@ -4,7 +4,7 @@ class ForeignInputSet < ApplicationRecord
|
|
4
4
|
class CircularDependencyError < Foreman::Exception
|
5
5
|
end
|
6
6
|
|
7
|
-
attr_exportable :exclude, :include, :include_all, :template => ->(input_set) { input_set.
|
7
|
+
attr_exportable :exclude, :include, :include_all, :template => ->(input_set) { input_set.target_template.name }
|
8
8
|
|
9
9
|
belongs_to :template
|
10
10
|
belongs_to :target_template, :class_name => 'Template'
|
@@ -49,6 +49,13 @@ class HostStatus::ExecutionStatus < HostStatus::Status
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
+
def status_link
|
53
|
+
job_invocation = last_stopped_task.parent_task.job_invocations.first
|
54
|
+
return nil unless User.current.can?(:view_job_invocations, job_invocation)
|
55
|
+
|
56
|
+
Rails.application.routes.url_helpers.job_invocation_path(job_invocation)
|
57
|
+
end
|
58
|
+
|
52
59
|
class ExecutionTaskStatusMapper
|
53
60
|
attr_accessor :task
|
54
61
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class InvocationProviderInputValue < ApplicationRecord
|
2
|
+
belongs_to :template_invocation
|
3
|
+
|
4
|
+
validates :name, :presence => true
|
5
|
+
|
6
|
+
validates :value, :inclusion => { :in => proc { |v| v.provider_input.options_array } },
|
7
|
+
:if => proc { |v| v.provider_input.options_array.present? }
|
8
|
+
|
9
|
+
def provider_input
|
10
|
+
template_invocation.template.provider.provider_inputs.find { |item| item.name == name }
|
11
|
+
end
|
12
|
+
end
|
@@ -76,6 +76,10 @@ class JobInvocation < ApplicationRecord
|
|
76
76
|
|
77
77
|
encrypts :password, :key_passphrase, :effective_user_password
|
78
78
|
|
79
|
+
class Jail < Safemode::Jail
|
80
|
+
allow :sub_task_for_host, :template_invocations_hosts
|
81
|
+
end
|
82
|
+
|
79
83
|
def self.search_by_status(key, operator, value)
|
80
84
|
conditions = HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.sql_conditions_for(value)
|
81
85
|
conditions[0] = "NOT (#{conditions[0]})" if operator == '<>'
|
@@ -241,7 +245,7 @@ class JobInvocation < ApplicationRecord
|
|
241
245
|
end
|
242
246
|
|
243
247
|
def finished?
|
244
|
-
!task.pending?
|
248
|
+
!(task.nil? || task.pending?)
|
245
249
|
end
|
246
250
|
|
247
251
|
def missing_hosts_count
|
@@ -64,6 +64,10 @@ class JobInvocationComposer
|
|
64
64
|
values.merge(:template_input_id => id)
|
65
65
|
end
|
66
66
|
|
67
|
+
provider_values_params = template_base.fetch(:provider_input_values, {})
|
68
|
+
template_base[:provider_input_values] = provider_values_params.map do |key, hash|
|
69
|
+
{ :name => key, :value => hash[:value] }
|
70
|
+
end
|
67
71
|
template_base
|
68
72
|
end
|
69
73
|
end
|
@@ -147,6 +151,7 @@ class JobInvocationComposer
|
|
147
151
|
|
148
152
|
def template_invocations_params
|
149
153
|
template_invocation_params = { :template_id => template.id, :effective_user => api_params[:effective_user] }
|
154
|
+
template_invocation_params[:provider_input_values] = filter_provider_inputs api_params
|
150
155
|
template_invocation_params[:input_values] = api_params.fetch(:inputs, {}).to_h.map do |name, value|
|
151
156
|
input = template.template_inputs_with_foreign.find { |i| i.name == name }
|
152
157
|
unless input
|
@@ -158,6 +163,13 @@ class JobInvocationComposer
|
|
158
163
|
[template_invocation_params]
|
159
164
|
end
|
160
165
|
|
166
|
+
def filter_provider_inputs(api_params)
|
167
|
+
return [] if template.provider.provider_input_namespace.empty?
|
168
|
+
inputs = api_params[template.provider.provider_input_namespace].to_h
|
169
|
+
provider_input_names = template.provider.provider_inputs.map(&:name)
|
170
|
+
inputs.select { |key, value| provider_input_names.include? key }.map { |key, value| { :name => key, :value => value } }
|
171
|
+
end
|
172
|
+
|
161
173
|
def template
|
162
174
|
@template ||= JobTemplate.authorized(:view_job_templates).find(api_params[:job_template_id])
|
163
175
|
end
|
@@ -503,6 +515,7 @@ class JobInvocationComposer
|
|
503
515
|
input = template_invocation.template.template_inputs_with_foreign.find { |i| i.id.to_s == attributes[:template_input_id].to_s }
|
504
516
|
input ? input.template_invocation_input_values.build(attributes) : nil
|
505
517
|
end.compact
|
518
|
+
template_invocation.provider_input_values.build job_template_base.fetch('provider_input_values', [])
|
506
519
|
end
|
507
520
|
|
508
521
|
def build_targeting
|
@@ -20,7 +20,7 @@ class RemoteExecutionProvider
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def proxy_command_options(template_invocation, host)
|
23
|
-
{:proxy_operation_name => proxy_operation_name}
|
23
|
+
{:proxy_operation_name => proxy_operation_name}.merge(proxy_command_provider_inputs(template_invocation))
|
24
24
|
end
|
25
25
|
|
26
26
|
def secrets(_host)
|
@@ -35,6 +35,9 @@ class RemoteExecutionProvider
|
|
35
35
|
self.name
|
36
36
|
end
|
37
37
|
|
38
|
+
def provider_input_namespace
|
39
|
+
end
|
40
|
+
|
38
41
|
def supports_effective_user?
|
39
42
|
false
|
40
43
|
end
|
@@ -95,8 +98,20 @@ class RemoteExecutionProvider
|
|
95
98
|
def ssh_key_passphrase(_host)
|
96
99
|
end
|
97
100
|
|
101
|
+
def provider_inputs
|
102
|
+
[]
|
103
|
+
end
|
104
|
+
|
105
|
+
def provider_inputs_doc
|
106
|
+
{}
|
107
|
+
end
|
108
|
+
|
109
|
+
def proxy_command_provider_inputs(template_invocation)
|
110
|
+
{}
|
111
|
+
end
|
112
|
+
|
98
113
|
def proxy_action_class
|
99
|
-
ForemanRemoteExecutionCore::Actions::RunScript
|
114
|
+
'ForemanRemoteExecutionCore::Actions::RunScript'
|
100
115
|
end
|
101
116
|
|
102
117
|
# Return a specific proxy selector to use for running a given template
|
@@ -73,6 +73,16 @@ class Setting::RemoteExecution < Setting
|
|
73
73
|
_('Form Job Template'),
|
74
74
|
nil,
|
75
75
|
{ :collection => proc { Hash[JobTemplate.unscoped.map { |template| [template.name, template.name] }] } }),
|
76
|
+
self.set('remote_execution_job_invocation_report_template',
|
77
|
+
N_('Select a report template used for generating a report for a particular remote execution job'),
|
78
|
+
'Jobs - Invocation report template',
|
79
|
+
_('Job Invocation Report Template'),
|
80
|
+
nil,
|
81
|
+
{ :collection => proc { self.job_invocation_report_templates_select } }),
|
76
82
|
]
|
77
83
|
end
|
84
|
+
|
85
|
+
def self.job_invocation_report_templates_select
|
86
|
+
Hash[ReportTemplate.unscoped.joins(:template_inputs).where(template_inputs: TemplateInput.where(name: 'job_id')).map { |template| [template.name, template.name] }]
|
87
|
+
end
|
78
88
|
end
|
@@ -11,6 +11,7 @@ class TemplateInvocation < ApplicationRecord
|
|
11
11
|
belongs_to :template, :class_name => 'JobTemplate', :foreign_key => 'template_id'
|
12
12
|
belongs_to :job_invocation, :inverse_of => :template_invocations
|
13
13
|
has_many :input_values, :class_name => 'TemplateInvocationInputValue', :dependent => :destroy
|
14
|
+
has_many :provider_input_values, :class_name => 'InvocationProviderInputValue', :dependent => :destroy
|
14
15
|
has_one :targeting, :through => :job_invocation
|
15
16
|
belongs_to :host, :class_name => 'Host::Managed', :foreign_key => :host_id
|
16
17
|
has_one :host_group, :through => :host, :source => :hostgroup
|
@@ -77,6 +78,7 @@ class TemplateInvocation < ApplicationRecord
|
|
77
78
|
def deep_clone
|
78
79
|
self.dup.tap do |invocation|
|
79
80
|
invocation.input_values = self.input_values.map(&:dup)
|
81
|
+
invocation.provider_input_values = self.provider_input_values.map(&:dup)
|
80
82
|
end
|
81
83
|
end
|
82
84
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# macros to fetch information about invoked jobs
|
4
|
+
module RendererMethods
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def find_job_invocation_by_id(job_id, preload: nil)
|
8
|
+
JobInvocation.preload(preload).find_by(id: job_id)
|
9
|
+
rescue ActiveRecord::NotFound => _e
|
10
|
+
raise ::Foreman::Exception.new(N_("Can't find Job Invocation for an id %s"), job_id)
|
11
|
+
end
|
12
|
+
end
|
@@ -74,6 +74,14 @@
|
|
74
74
|
<% end %>
|
75
75
|
|
76
76
|
<div class="advanced hidden">
|
77
|
+
<%= job_template_fields.fields_for :provider_input_values do |provider_input_fields| %>
|
78
|
+
<% job_template.provider.provider_inputs.each do |input| %>
|
79
|
+
<%= provider_input_fields.fields_for input.name, input do |input_fields| %>
|
80
|
+
<%= template_input_f(input_fields) %>
|
81
|
+
<% end %>
|
82
|
+
<% end %>
|
83
|
+
<% end %>
|
84
|
+
|
77
85
|
<% if job_template.effective_user.overridable? %>
|
78
86
|
<%= text_f job_template_fields, :effective_user, :value => @composer.template_invocation(job_template).try(:effective_user), :label => _('Effective user'), :label_help => N_("A user to be used for executing the script. If it differs from the SSH user, su or sudo is used to switch the accounts.") %>
|
79
87
|
<% end %>
|