foreman_remote_execution 4.2.1 → 4.4.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.
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 %>