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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/js_ci.yml +29 -0
  3. data/.github/workflows/{ci.yml → ruby_ci.yml} +22 -50
  4. data/.prettierrc +4 -0
  5. data/.rubocop.yml +13 -49
  6. data/.rubocop_todo.yml +326 -102
  7. data/Gemfile +1 -4
  8. data/app/controllers/api/v2/job_invocations_controller.rb +17 -1
  9. data/app/controllers/foreman_remote_execution/concerns/api/v2/registration_commands_controller_extensions.rb +19 -0
  10. data/app/controllers/ui_job_wizard_controller.rb +18 -0
  11. data/app/helpers/remote_execution_helper.rb +27 -0
  12. data/app/lib/actions/remote_execution/run_host_job.rb +38 -1
  13. data/app/lib/foreman_remote_execution/provider_input.rb +29 -0
  14. data/app/lib/foreman_remote_execution/renderer/scope/input.rb +1 -0
  15. data/app/models/foreign_input_set.rb +1 -1
  16. data/app/models/host_status/execution_status.rb +7 -0
  17. data/app/models/invocation_provider_input_value.rb +12 -0
  18. data/app/models/job_invocation.rb +5 -1
  19. data/app/models/job_invocation_composer.rb +13 -0
  20. data/app/models/remote_execution_provider.rb +17 -2
  21. data/app/models/setting/remote_execution.rb +10 -0
  22. data/app/models/template_invocation.rb +2 -0
  23. data/app/services/renderer_methods.rb +12 -0
  24. data/app/views/job_invocations/_form.html.erb +8 -0
  25. data/app/views/template_invocations/show.html.erb +30 -23
  26. data/config/routes.rb +4 -0
  27. data/db/migrate/20210312074713_add_provider_inputs.rb +10 -0
  28. data/foreman_remote_execution.gemspec +1 -2
  29. data/lib/foreman_remote_execution/engine.rb +17 -7
  30. data/lib/foreman_remote_execution/version.rb +1 -1
  31. data/lib/tasks/foreman_remote_execution_tasks.rake +1 -18
  32. data/locale/action_names.rb +1 -0
  33. data/locale/de/foreman_remote_execution.po +77 -27
  34. data/locale/en/foreman_remote_execution.po +77 -27
  35. data/locale/en_GB/foreman_remote_execution.po +77 -27
  36. data/locale/es/foreman_remote_execution.po +77 -27
  37. data/locale/foreman_remote_execution.pot +241 -163
  38. data/locale/fr/foreman_remote_execution.po +77 -27
  39. data/locale/ja/foreman_remote_execution.po +77 -27
  40. data/locale/ko/foreman_remote_execution.po +77 -27
  41. data/locale/pt_BR/foreman_remote_execution.po +77 -27
  42. data/locale/ru/foreman_remote_execution.po +77 -27
  43. data/locale/zh_CN/foreman_remote_execution.po +77 -27
  44. data/locale/zh_TW/foreman_remote_execution.po +77 -27
  45. data/package.json +3 -2
  46. data/test/functional/api/v2/job_invocations_controller_test.rb +24 -4
  47. data/test/functional/api/v2/registration_controller_test.rb +4 -13
  48. data/test/functional/ui_job_wizard_controller_test.rb +16 -0
  49. data/test/helpers/remote_execution_helper_test.rb +16 -0
  50. data/test/unit/job_invocation_composer_test.rb +41 -1
  51. data/test/unit/job_invocation_report_template_test.rb +57 -0
  52. data/webpack/JobWizard/JobWizard.js +55 -0
  53. data/webpack/JobWizard/JobWizard.scss +12 -0
  54. data/webpack/JobWizard/JobWizardConstants.js +5 -0
  55. data/webpack/JobWizard/JobWizardSelectors.js +21 -0
  56. data/webpack/JobWizard/__tests__/JobWizard.test.js +20 -0
  57. data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +83 -0
  58. data/webpack/JobWizard/index.js +32 -0
  59. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +77 -0
  60. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +45 -0
  61. data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +64 -0
  62. data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +86 -0
  63. data/webpack/JobWizard/steps/form/GroupedSelectField.js +88 -0
  64. data/webpack/JobWizard/steps/form/SelectField.js +39 -0
  65. data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +38 -0
  66. data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +23 -0
  67. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +36 -0
  68. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +22 -0
  69. data/webpack/Routes/routes.js +12 -0
  70. data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
  71. data/webpack/__mocks__/foremanReact/history.js +1 -0
  72. data/webpack/__mocks__/foremanReact/redux/API/index.js +5 -0
  73. data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +10 -0
  74. data/webpack/fills_index.js +11 -0
  75. data/webpack/global_index.js +8 -0
  76. data/webpack/index.js +0 -4
  77. data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +87 -0
  78. data/webpack/react_app/components/RecentJobsCard/constants.js +1 -0
  79. data/webpack/react_app/components/RecentJobsCard/index.js +1 -0
  80. data/webpack/react_app/components/RecentJobsCard/styles.css +15 -0
  81. data/webpack/react_app/components/RegistrationExtension/RexInterface.js +50 -0
  82. data/webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js +9 -0
  83. data/webpack/react_app/components/RegistrationExtension/__tests__/__snapshots__/RexInterface.test.js.snap +35 -0
  84. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +1 -1
  85. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss +0 -3
  86. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -1
  87. data/webpack/react_app/extend/fills.js +10 -0
  88. data/webpack/react_app/extend/reducers.js +4 -0
  89. metadata +54 -24
  90. data/app/views/api/v2/registration/_form.html.erb +0 -12
data/Gemfile CHANGED
@@ -2,7 +2,4 @@ source 'http://rubygems.org'
2
2
 
3
3
  gemspec :name => 'foreman_remote_execution'
4
4
 
5
- gem 'rubocop', '~> 0.80.0'
6
- gem 'rubocop-minitest'
7
- gem 'rubocop-performance'
8
- gem 'rubocop-rails'
5
+ gem 'theforeman-rubocop', '~> 0.1.0.pre'
@@ -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 = true
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.template.name }
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 %>