foreman_remote_execution 4.2.2 → 4.5.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 (109) 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 +28 -23
  9. data/app/controllers/foreman_remote_execution/concerns/api/v2/registration_commands_controller_extensions.rb +19 -0
  10. data/app/controllers/job_templates_controller.rb +4 -4
  11. data/app/controllers/ui_job_wizard_controller.rb +30 -0
  12. data/app/helpers/job_invocations_helper.rb +2 -2
  13. data/app/helpers/remote_execution_helper.rb +35 -8
  14. data/app/lib/actions/remote_execution/run_host_job.rb +68 -5
  15. data/app/lib/foreman_remote_execution/provider_input.rb +29 -0
  16. data/app/lib/foreman_remote_execution/renderer/scope/input.rb +1 -0
  17. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +5 -5
  18. data/app/models/host_status/execution_status.rb +12 -5
  19. data/app/models/invocation_provider_input_value.rb +12 -0
  20. data/app/models/job_invocation.rb +28 -8
  21. data/app/models/job_invocation_composer.rb +72 -17
  22. data/app/models/remote_execution_provider.rb +17 -2
  23. data/app/models/setting/remote_execution.rb +10 -0
  24. data/app/models/ssh_execution_provider.rb +4 -4
  25. data/app/models/template_invocation.rb +2 -0
  26. data/app/overrides/execution_interface.rb +8 -8
  27. data/app/overrides/subnet_proxies.rb +6 -6
  28. data/app/services/renderer_methods.rb +12 -0
  29. data/app/views/job_invocations/_form.html.erb +8 -0
  30. data/app/views/template_invocations/show.html.erb +30 -23
  31. data/config/routes.rb +5 -0
  32. data/db/migrate/20180110104432_rename_template_invocation_permission.rb +1 -1
  33. data/db/migrate/20190111153330_remove_remote_execution_without_proxy_setting.rb +4 -4
  34. data/db/migrate/20210312074713_add_provider_inputs.rb +10 -0
  35. data/extra/cockpit/foreman-cockpit-session +6 -6
  36. data/foreman_remote_execution.gemspec +1 -2
  37. data/lib/foreman_remote_execution/engine.rb +22 -7
  38. data/lib/foreman_remote_execution/version.rb +1 -1
  39. data/lib/tasks/foreman_remote_execution_tasks.rake +1 -18
  40. data/locale/action_names.rb +1 -0
  41. data/locale/de/foreman_remote_execution.po +77 -27
  42. data/locale/en/foreman_remote_execution.po +77 -27
  43. data/locale/en_GB/foreman_remote_execution.po +77 -27
  44. data/locale/es/foreman_remote_execution.po +77 -27
  45. data/locale/foreman_remote_execution.pot +241 -163
  46. data/locale/fr/foreman_remote_execution.po +77 -27
  47. data/locale/ja/foreman_remote_execution.po +77 -27
  48. data/locale/ko/foreman_remote_execution.po +77 -27
  49. data/locale/pt_BR/foreman_remote_execution.po +77 -27
  50. data/locale/ru/foreman_remote_execution.po +77 -27
  51. data/locale/zh_CN/foreman_remote_execution.po +77 -27
  52. data/locale/zh_TW/foreman_remote_execution.po +77 -27
  53. data/package.json +4 -2
  54. data/test/functional/api/v2/job_invocations_controller_test.rb +38 -5
  55. data/test/functional/api/v2/registration_controller_test.rb +4 -13
  56. data/test/functional/ui_job_wizard_controller_test.rb +16 -0
  57. data/test/helpers/remote_execution_helper_test.rb +16 -0
  58. data/test/unit/job_invocation_composer_test.rb +86 -2
  59. data/test/unit/job_invocation_report_template_test.rb +57 -0
  60. data/webpack/JobWizard/JobWizard.js +96 -0
  61. data/webpack/JobWizard/JobWizard.scss +14 -0
  62. data/webpack/JobWizard/JobWizardConstants.js +6 -0
  63. data/webpack/JobWizard/JobWizardSelectors.js +38 -0
  64. data/webpack/JobWizard/__tests__/JobWizard.test.js +13 -0
  65. data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +32 -0
  66. data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +43 -0
  67. data/webpack/JobWizard/__tests__/fixtures.js +26 -0
  68. data/webpack/JobWizard/__tests__/integration.test.js +156 -0
  69. data/webpack/JobWizard/index.js +32 -0
  70. data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +93 -0
  71. data/webpack/JobWizard/steps/AdvancedFields/Fields.js +181 -0
  72. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +25 -0
  73. data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +249 -0
  74. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +109 -0
  75. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +52 -0
  76. data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +113 -0
  77. data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +94 -0
  78. data/webpack/JobWizard/steps/form/FormHelpers.js +19 -0
  79. data/webpack/JobWizard/steps/form/GroupedSelectField.js +91 -0
  80. data/webpack/JobWizard/steps/form/SelectField.js +48 -0
  81. data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +38 -0
  82. data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +23 -0
  83. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +37 -0
  84. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +23 -0
  85. data/webpack/Routes/routes.js +12 -0
  86. data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
  87. data/webpack/__mocks__/foremanReact/history.js +1 -0
  88. data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +21 -2
  89. data/webpack/__mocks__/foremanReact/redux/API/index.js +5 -0
  90. data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +10 -0
  91. data/webpack/global_index.js +10 -0
  92. data/webpack/index.js +3 -4
  93. data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +83 -0
  94. data/webpack/react_app/components/RecentJobsCard/constants.js +1 -0
  95. data/webpack/react_app/components/RecentJobsCard/index.js +1 -0
  96. data/webpack/react_app/components/RecentJobsCard/styles.css +15 -0
  97. data/webpack/react_app/components/RegistrationExtension/RexInterface.js +50 -0
  98. data/webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js +9 -0
  99. data/webpack/react_app/components/RegistrationExtension/__tests__/__snapshots__/RexInterface.test.js.snap +35 -0
  100. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +1 -1
  101. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss +0 -3
  102. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +8 -3
  103. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -1
  104. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +7 -2
  105. data/webpack/react_app/extend/fillRecentJobsCard.js +11 -0
  106. data/webpack/react_app/extend/fillregistrationAdvanced.js +11 -0
  107. data/webpack/react_app/extend/reducers.js +5 -0
  108. metadata +58 -20
  109. data/app/views/api/v2/registration/_form.html.erb +0 -12
@@ -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
 
@@ -57,15 +64,15 @@ class HostStatus::ExecutionStatus < HostStatus::Status
57
64
 
58
65
  case status
59
66
  when OK
60
- [ "state = 'stopped' AND result = 'success'" ]
67
+ [ "foreman_tasks_tasks.state = 'stopped' AND result = 'success'" ]
61
68
  when CANCELLED
62
- [ "state = 'stopped' AND result = 'cancelled'" ]
69
+ [ "foreman_tasks_tasks.state = 'stopped' AND result = 'cancelled'" ]
63
70
  when ERROR
64
- [ "state = 'stopped' AND (result = 'error' OR result = 'warning')" ]
71
+ [ "foreman_tasks_tasks.state = 'stopped' AND (result = 'error' OR result = 'warning')" ]
65
72
  when QUEUED
66
- [ "state = 'scheduled' OR state IS NULL" ]
73
+ [ "foreman_tasks_tasks.state = 'scheduled' OR foreman_tasks_tasks.state IS NULL" ]
67
74
  when RUNNING
68
- [ "state <> 'stopped'" ]
75
+ [ "foreman_tasks_tasks.state <> 'stopped'" ]
69
76
  else
70
77
  [ '1 = 0' ]
71
78
  end
@@ -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
@@ -15,8 +15,9 @@ class JobInvocation < ApplicationRecord
15
15
 
16
16
  belongs_to :targeting, :dependent => :destroy
17
17
  has_many :all_template_invocations, :inverse_of => :job_invocation, :dependent => :destroy, :class_name => 'TemplateInvocation'
18
- has_many :template_invocations, -> { where('host_id IS NOT NULL') }, :inverse_of => :job_invocation
19
- has_many :pattern_template_invocations, -> { where('host_id IS NULL') }, :inverse_of => :job_invocation, :class_name => 'TemplateInvocation'
18
+ has_many :template_invocations, -> { where('template_invocations.host_id IS NOT NULL') }, :inverse_of => :job_invocation
19
+ has_many :pattern_template_invocations, -> { where('template_invocations.host_id IS NULL') }, :inverse_of => :job_invocation, :class_name => 'TemplateInvocation'
20
+ has_many :pattern_templates, :through => :pattern_template_invocations, :source => :template
20
21
 
21
22
  validates :targeting, :presence => true
22
23
  validates :job_category, :presence => true
@@ -44,26 +45,33 @@ class JobInvocation < ApplicationRecord
44
45
  :source => 'run_host_job_task'
45
46
  has_one :user, through: :task
46
47
  scoped_search relation: :user, on: :login, rename: 'user', complete_value: true,
47
- value_translation: ->(value) { value == 'current_user' ? User.current.login : value },
48
- special_values: [:current_user], aliases: ['owner'], :only_explicit => true
48
+ value_translation: ->(value) { value == 'current_user' ? User.current.login : value },
49
+ special_values: [:current_user], aliases: ['owner'], :only_explicit => true
49
50
  scoped_search :relation => :task, :on => :started_at, :rename => 'started_at', :complete_value => true
50
51
  scoped_search :relation => :task, :on => :start_at, :rename => 'start_at', :complete_value => true
51
52
  scoped_search :relation => :task, :on => :ended_at, :rename => 'ended_at', :complete_value => true
52
53
  scoped_search :relation => :task, :on => :state, :rename => 'status', :ext_method => :search_by_status,
53
- :only_explicit => true, :complete_value => Hash[HostStatus::ExecutionStatus::STATUS_NAMES.values.map { |v| [v, v] }]
54
+ :only_explicit => true, :complete_value => Hash[HostStatus::ExecutionStatus::STATUS_NAMES.values.map { |v| [v, v] }]
54
55
 
55
56
  belongs_to :triggering, :class_name => 'ForemanTasks::Triggering'
56
57
  has_one :recurring_logic, :through => :triggering, :class_name => 'ForemanTasks::RecurringLogic'
57
58
 
58
59
  belongs_to :remote_execution_feature
59
60
 
61
+ has_many :targeted_hosts, :through => :targeting, :source => :hosts
62
+ scoped_search :on => 'targeted_host_id', :rename => 'targeted_host_id', :operators => ['= '],
63
+ :complete_value => false, :only_explicit => true, :ext_method => :search_by_targeted_host
64
+
65
+ scoped_search :on => 'pattern_template_name', :rename => 'pattern_template_name', :operators => ['= '],
66
+ :complete_value => false, :only_explicit => true, :ext_method => :search_by_pattern_template
67
+
60
68
  scope :with_task, -> { references(:task) }
61
69
 
62
70
  scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
63
71
 
64
72
  scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring',
65
- :ext_method => :search_by_recurring_logic, :only_explicit => true,
66
- :complete_value => { :true => true, :false => false }
73
+ :ext_method => :search_by_recurring_logic, :only_explicit => true,
74
+ :complete_value => { :true => true, :false => false }
67
75
 
68
76
  default_scope -> { order('job_invocations.id DESC') }
69
77
 
@@ -76,6 +84,18 @@ class JobInvocation < ApplicationRecord
76
84
 
77
85
  encrypts :password, :key_passphrase, :effective_user_password
78
86
 
87
+ class Jail < Safemode::Jail
88
+ allow :sub_task_for_host, :template_invocations_hosts
89
+ end
90
+
91
+ def self.search_by_targeted_host(key, operator, value)
92
+ { :conditions => sanitize_sql_for_conditions(["hosts.id = ?", value]), :joins => :targeted_hosts }
93
+ end
94
+
95
+ def self.search_by_pattern_template(key, operator, value)
96
+ { :conditions => sanitize_sql_for_conditions(["templates.name = ?", value]), :joins => :pattern_templates }
97
+ end
98
+
79
99
  def self.search_by_status(key, operator, value)
80
100
  conditions = HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.sql_conditions_for(value)
81
101
  conditions[0] = "NOT (#{conditions[0]})" if operator == '<>'
@@ -241,7 +261,7 @@ class JobInvocation < ApplicationRecord
241
261
  end
242
262
 
243
263
  def finished?
244
- !task.pending?
264
+ !(task.nil? || task.pending?)
245
265
  end
246
266
 
247
267
  def missing_hosts_count
@@ -1,4 +1,7 @@
1
1
  class JobInvocationComposer
2
+ class JobTemplateNotFound < StandardError; end
3
+
4
+ class FeatureNotFound < StandardError; end
2
5
 
3
6
  class UiParams
4
7
  attr_reader :ui_params
@@ -64,6 +67,10 @@ class JobInvocationComposer
64
67
  values.merge(:template_input_id => id)
65
68
  end
66
69
 
70
+ provider_values_params = template_base.fetch(:provider_input_values, {})
71
+ template_base[:provider_input_values] = provider_values_params.map do |key, hash|
72
+ { :name => key, :value => hash[:value] }
73
+ end
67
74
  template_base
68
75
  end
69
76
  end
@@ -96,6 +103,17 @@ class JobInvocationComposer
96
103
 
97
104
  def initialize(api_params)
98
105
  @api_params = api_params
106
+
107
+ if api_params[:feature]
108
+ # set a default targeting type for backward compatibility
109
+ # when `for_feature` was used by the API it automatically set a default
110
+ api_params[:targeting_type] = Targeting::STATIC_TYPE
111
+ end
112
+
113
+ if api_params[:search_query].blank? && api_params[:host_ids].present?
114
+ translator = HostIdsTranslator.new(api_params[:host_ids])
115
+ api_params[:search_query] = translator.scoped_search
116
+ end
99
117
  end
100
118
 
101
119
  def params
@@ -103,12 +121,16 @@ class JobInvocationComposer
103
121
  :targeting => targeting_params,
104
122
  :triggering => triggering_params,
105
123
  :description_format => api_params[:description_format],
106
- :remote_execution_feature_id => api_params[:remote_execution_feature_id],
124
+ :remote_execution_feature_id => remote_execution_feature_id,
107
125
  :concurrency_control => concurrency_control_params,
108
126
  :execution_timeout_interval => api_params[:execution_timeout_interval] || template.execution_timeout_interval,
109
127
  :template_invocations => template_invocations_params }.with_indifferent_access
110
128
  end
111
129
 
130
+ def remote_execution_feature_id
131
+ feature&.id || api_params[:remote_execution_feature_id]
132
+ end
133
+
112
134
  def targeting_params
113
135
  raise ::Foreman::Exception, _('Cannot specify both bookmark_id and search_query') if api_params[:bookmark_id] && api_params[:search_query]
114
136
 
@@ -147,6 +169,7 @@ class JobInvocationComposer
147
169
 
148
170
  def template_invocations_params
149
171
  template_invocation_params = { :template_id => template.id, :effective_user => api_params[:effective_user] }
172
+ template_invocation_params[:provider_input_values] = filter_provider_inputs api_params
150
173
  template_invocation_params[:input_values] = api_params.fetch(:inputs, {}).to_h.map do |name, value|
151
174
  input = template.template_inputs_with_foreign.find { |i| i.name == name }
152
175
  unless input
@@ -158,8 +181,27 @@ class JobInvocationComposer
158
181
  [template_invocation_params]
159
182
  end
160
183
 
184
+ def filter_provider_inputs(api_params)
185
+ return [] if template.provider.provider_input_namespace.empty?
186
+ inputs = api_params[template.provider.provider_input_namespace].to_h
187
+ provider_input_names = template.provider.provider_inputs.map(&:name)
188
+ inputs.select { |key, value| provider_input_names.include? key }.map { |key, value| { :name => key, :value => value } }
189
+ end
190
+
191
+ def feature
192
+ @feature ||= RemoteExecutionFeature.feature(api_params[:feature]) if api_params[:feature]
193
+ rescue => e
194
+ raise(FeatureNotFound, e.message)
195
+ end
196
+
197
+ def job_template_id
198
+ feature&.job_template_id || api_params[:job_template_id]
199
+ end
200
+
161
201
  def template
162
- @template ||= JobTemplate.authorized(:view_job_templates).find(api_params[:job_template_id])
202
+ @template ||= JobTemplate.authorized(:view_job_templates).find(job_template_id)
203
+ rescue ActiveRecord::RecordNotFound
204
+ raise(JobTemplateNotFound, _("Template with id '%{id}' was not found") % { id: job_template_id })
163
205
  end
164
206
 
165
207
  private
@@ -227,25 +269,38 @@ class JobInvocationComposer
227
269
  end
228
270
  end
229
271
 
272
+ class HostIdsTranslator
273
+ attr_reader :bookmark, :hosts, :scoped_search, :host_ids
274
+
275
+ def initialize(input)
276
+ case input
277
+ when Bookmark
278
+ @bookmark = input
279
+ when Host::Base
280
+ @hosts = [input]
281
+ when Array
282
+ @hosts = input.map do |id|
283
+ Host::Managed.authorized.friendly.find(id)
284
+ end
285
+ when String
286
+ @scoped_search = input
287
+ else
288
+ @hosts = input
289
+ end
290
+
291
+ @scoped_search ||= Targeting.build_query_from_hosts(hosts.map(&:id)) if @hosts
292
+ end
293
+ end
294
+
230
295
  class ParamsForFeature
231
296
  attr_reader :feature_label, :feature, :provided_inputs
232
297
 
233
298
  def initialize(feature_label, hosts, provided_inputs = {})
234
299
  @feature = RemoteExecutionFeature.feature(feature_label)
235
300
  @provided_inputs = provided_inputs
236
- if hosts.is_a? Bookmark
237
- @host_bookmark = hosts
238
- elsif hosts.is_a? Host::Base
239
- @host_objects = [hosts]
240
- elsif hosts.is_a? Array
241
- @host_objects = hosts.map do |id|
242
- Host::Managed.authorized.friendly.find(id)
243
- end
244
- elsif hosts.is_a? String
245
- @host_scoped_search = hosts
246
- else
247
- @host_objects = hosts
248
- end
301
+ translator = HostIdsTranslator.new(hosts)
302
+ @host_bookmark = translator.bookmark
303
+ @host_scoped_search = translator.scoped_search
249
304
  end
250
305
 
251
306
  def params
@@ -263,7 +318,6 @@ class JobInvocationComposer
263
318
  ret = {}
264
319
  ret['targeting_type'] = Targeting::STATIC_TYPE
265
320
  ret['search_query'] = @host_scoped_search if @host_scoped_search
266
- ret['search_query'] = Targeting.build_query_from_hosts(@host_objects) if @host_objects
267
321
  ret['bookmark_id'] = @host_bookmark.id if @host_bookmark
268
322
  ret['user_id'] = User.current.id
269
323
  ret
@@ -503,6 +557,7 @@ class JobInvocationComposer
503
557
  input = template_invocation.template.template_inputs_with_foreign.find { |i| i.id.to_s == attributes[:template_input_id].to_s }
504
558
  input ? input.template_invocation_input_values.build(attributes) : nil
505
559
  end.compact
560
+ template_invocation.provider_input_values.build job_template_base.fetch('provider_input_values', [])
506
561
  end
507
562
 
508
563
  def build_targeting
@@ -540,7 +595,7 @@ class JobInvocationComposer
540
595
 
541
596
  params[:template_invocations].select { |t| valid_template_ids.include?(t[:template_id].to_i) }.map do |template_invocation_params|
542
597
  template_invocation = job_invocation.pattern_template_invocations.build(:template_id => template_invocation_params[:template_id],
543
- :effective_user => build_effective_user(template_invocation_params))
598
+ :effective_user => build_effective_user(template_invocation_params))
544
599
  build_input_values_for(template_invocation, template_invocation_params)
545
600
  template_invocation
546
601
  end
@@ -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
@@ -2,10 +2,10 @@ class SSHExecutionProvider < RemoteExecutionProvider
2
2
  class << self
3
3
  def proxy_command_options(template_invocation, host)
4
4
  super.merge(:ssh_user => ssh_user(host),
5
- :effective_user => effective_user(template_invocation),
6
- :effective_user_method => effective_user_method(host),
7
- :cleanup_working_dirs => cleanup_working_dirs?(host),
8
- :ssh_port => ssh_port(host))
5
+ :effective_user => effective_user(template_invocation),
6
+ :effective_user_method => effective_user_method(host),
7
+ :cleanup_working_dirs => cleanup_working_dirs?(host),
8
+ :ssh_port => ssh_port(host))
9
9
  end
10
10
 
11
11
  def humanized_name
@@ -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
 
@@ -1,9 +1,9 @@
1
- Deface::Override.new(:virtual_path => 'hosts/_form',
2
- :name => 'add_execution_interface_js',
3
- :insert_before => 'div#primary',
4
- :text => '<%= javascript "foreman_remote_execution/execution_interface" %>')
1
+ Deface::Override.new(:virtual_path => 'hosts/_form',
2
+ :name => 'add_execution_interface_js',
3
+ :insert_before => 'div#primary',
4
+ :text => '<%= javascript "foreman_remote_execution/execution_interface" %>')
5
5
 
6
- Deface::Override.new(:virtual_path => 'nic/_base_form',
7
- :name => 'add_execution_interface',
8
- :insert_after => 'erb[loud]:contains("interface_provision")',
9
- :partial => 'overrides/nics/execution_interface')
6
+ Deface::Override.new(:virtual_path => 'nic/_base_form',
7
+ :name => 'add_execution_interface',
8
+ :insert_after => 'erb[loud]:contains("interface_provision")',
9
+ :partial => 'overrides/nics/execution_interface')
@@ -1,9 +1,9 @@
1
1
  Deface::Override.new(:virtual_path => 'subnets/_form',
2
- :name => 'add_remote_execution_proxies_tab',
3
- :insert_after => 'li.active',
4
- :partial => 'overrides/subnets/rex_tab')
2
+ :name => 'add_remote_execution_proxies_tab',
3
+ :insert_after => 'li.active',
4
+ :partial => 'overrides/subnets/rex_tab')
5
5
 
6
6
  Deface::Override.new(:virtual_path => 'subnets/_form',
7
- :name => 'add_remote_execution_proxies_tab_pane',
8
- :insert_after => 'div#proxies',
9
- :partial => 'overrides/subnets/rex_tab_pane')
7
+ :name => 'add_remote_execution_proxies_tab_pane',
8
+ :insert_after => 'div#proxies',
9
+ :partial => 'overrides/subnets/rex_tab_pane')
@@ -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 %>