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
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,24 +64,31 @@ 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
 
70
80
  api :POST, '/job_invocations/', N_('Create a job invocation')
71
81
  param_group :job_invocation, :as => :create
72
82
  def create
73
- if job_invocation_params[:feature].present?
74
- composer = composer_for_feature
75
- else
76
- validate_template
77
- composer = JobInvocationComposer.from_api_params(
78
- job_invocation_params
79
- )
80
- end
83
+ composer = JobInvocationComposer.from_api_params(
84
+ job_invocation_params
85
+ )
81
86
  composer.trigger!
82
87
  @job_invocation = composer.job_invocation
83
88
  @hosts = @job_invocation.targeting.hosts
84
89
  process_response @job_invocation
90
+ rescue JobInvocationComposer::JobTemplateNotFound, JobInvocationComposer::FeatureNotFound => e
91
+ not_found(error: { message: e.message })
85
92
  end
86
93
 
87
94
  api :GET, '/job_invocations/:id/hosts/:host_id', N_('Get output for a host')
@@ -118,7 +125,7 @@ module Api
118
125
  render :json => { :cancelled => result, :id => @job_invocation.id }
119
126
  else
120
127
  render :json => { :message => _('The job could not be cancelled.') },
121
- :status => :unprocessable_entity
128
+ :status => :unprocessable_entity
122
129
  end
123
130
  end
124
131
 
@@ -133,7 +140,7 @@ module Api
133
140
  process_response @job_invocation
134
141
  else
135
142
  render :json => { :error => _('Could not rerun job %{id} because its template could not be found') % { :id => composer.reruns } },
136
- :status => :not_found
143
+ :status => :not_found
137
144
  end
138
145
  end
139
146
 
@@ -180,30 +187,28 @@ module Api
180
187
  not_found({ :error => { :message => (_("Host with id '%{id}' was not found") % { :id => params['host_id'] }) } })
181
188
  end
182
189
 
183
- def validate_template
184
- JobTemplate.authorized(:view_job_templates).find(job_invocation_params['job_template_id'])
185
- rescue ActiveRecord::RecordNotFound
186
- not_found({ :error => { :message => (_("Template with id '%{id}' was not found") % { :id => job_invocation_params['job_template_id'] }) } })
187
- end
188
-
189
190
  def job_invocation_params
190
191
  return @job_invocation_params if @job_invocation_params.present?
191
192
 
192
193
  job_invocation_params = params.fetch(:job_invocation, {}).dup
194
+
195
+ if job_invocation_params[:feature].present? && job_invocation_params[:job_template_id].present?
196
+ raise _("Only one of feature or job_template_id can be specified")
197
+ end
198
+
193
199
  if job_invocation_params.key?(:ssh)
194
200
  job_invocation_params.merge!(job_invocation_params.delete(:ssh).permit(:effective_user))
195
201
  end
202
+
196
203
  job_invocation_params[:inputs] ||= {}
197
204
  job_invocation_params[:inputs].permit!
205
+ permit_provider_inputs job_invocation_params
198
206
  @job_invocation_params = job_invocation_params
199
207
  end
200
208
 
201
- def composer_for_feature
202
- JobInvocationComposer.for_feature(
203
- job_invocation_params[:feature],
204
- job_invocation_params[:host_ids],
205
- job_invocation_params[:inputs].to_hash
206
- )
209
+ def permit_provider_inputs(invocation_params)
210
+ providers = RemoteExecutionProvider.providers.values.reject { |provider| !provider.respond_to?(:provider_input_namespace) || provider.provider_input_namespace.empty? }
211
+ providers.each { |provider| invocation_params[provider.provider_input_namespace]&.permit! }
207
212
  end
208
213
 
209
214
  def output_lines_since(task, time)
@@ -214,7 +219,7 @@ module Api
214
219
  end
215
220
 
216
221
  def host_output(job_invocation, host, default: nil, since: nil, raw: false)
217
- refresh = true
222
+ refresh = !job_invocation.finished?
218
223
 
219
224
  if (task = job_invocation.sub_task_for_host(host))
220
225
  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
@@ -24,10 +24,10 @@ class JobTemplatesController < ::TemplatesController
24
24
  render :plain => output
25
25
  else
26
26
  render status: :not_acceptable,
27
- plain: _(
28
- 'Problem with previewing the template: %{error}. Note that you must save template input changes before you try to preview it.' %
29
- {:error => renderer.error_message}
30
- )
27
+ plain: _(
28
+ 'Problem with previewing the template: %{error}. Note that you must save template input changes before you try to preview it.' %
29
+ {:error => renderer.error_message}
30
+ )
31
31
  end
32
32
  end
33
33
 
@@ -0,0 +1,30 @@
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 template
12
+ job_template = JobTemplate.authorized.find(params[:id])
13
+ render :json => {
14
+ :job_template => job_template,
15
+ :effective_user => job_template.effective_user,
16
+ }
17
+ end
18
+
19
+ def resource_name(nested_resource = nil)
20
+ nested_resource || 'job_template'
21
+ end
22
+
23
+ def resource_class
24
+ JobTemplate
25
+ end
26
+
27
+ def action_permission
28
+ :view_job_templates
29
+ end
30
+ end
@@ -34,8 +34,8 @@ module JobInvocationsHelper
34
34
  hosts.map do |host|
35
35
  collapsed_preview(host) +
36
36
  render(:partial => 'job_invocations/user_input',
37
- :locals => { :template_invocation => template_invocation,
38
- :target => host })
37
+ :locals => { :template_invocation => template_invocation,
38
+ :target => host })
39
39
  end.reduce(:+)
40
40
  end
41
41
 
@@ -27,19 +27,19 @@ module RemoteExecutionHelper
27
27
 
28
28
  if authorized_for(hash_for_host_path(host).merge(auth_object: host, permission: :view_hosts, authorizer: job_hosts_authorizer))
29
29
  links << { title: _('Host detail'),
30
- action: { href: host_path(host), 'data-method': 'get', id: "#{host.name}-actions-detail" } }
30
+ action: { href: host_path(host), 'data-method': 'get', id: "#{host.name}-actions-detail" } }
31
31
  end
32
32
 
33
33
  if authorized_for(hash_for_rerun_job_invocation_path(id: job_invocation, host_ids: [ host.id ], authorizer: job_hosts_authorizer))
34
34
  links << { title: (_('Rerun on %s') % host.name),
35
- action: { href: rerun_job_invocation_path(job_invocation, host_ids: [ host.id ]),
36
- 'data-method': 'get', id: "#{host.name}-actions-rerun" } }
35
+ action: { href: rerun_job_invocation_path(job_invocation, host_ids: [ host.id ]),
36
+ 'data-method': 'get', id: "#{host.name}-actions-rerun" } }
37
37
  end
38
38
 
39
39
  if host_task.present? && authorized_for(hash_for_foreman_tasks_task_path(host_task).merge(auth_object: host_task, permission: :view_foreman_tasks))
40
40
  links << { title: _('Host task'),
41
- action: { href: foreman_tasks_task_path(host_task),
42
- 'data-method': 'get', id: "#{host.name}-actions-task" } }
41
+ action: { href: foreman_tasks_task_path(host_task),
42
+ 'data-method': 'get', id: "#{host.name}-actions-task" } }
43
43
  end
44
44
 
45
45
  links
@@ -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',
@@ -153,11 +159,11 @@ module RemoteExecutionHelper
153
159
  content_tag :pre, preview
154
160
  elsif target.nil?
155
161
  alert :text => _('Could not render the preview because no host matches the search query.'),
156
- :class => 'alert alert-block alert-warning base',
157
- :close => false
162
+ :class => 'alert alert-block alert-warning base',
163
+ :close => false
158
164
  else
159
165
  alert :class => 'alert-block alert-danger base in fade has-error',
160
- :text => renderer.error_message.html_safe # rubocop:disable Rails/OutputSafety
166
+ :text => renderer.error_message.html_safe # rubocop:disable Rails/OutputSafety
161
167
  end
162
168
  end
163
169
 
@@ -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,9 @@ module Actions
3
3
  class RunHostJob < Actions::EntryAction
4
4
  include ::Actions::Helpers::WithContinuousOutput
5
5
  include ::Actions::Helpers::WithDelegatedAction
6
+ include ::Actions::ObservableAction
7
+
8
+ execution_plan_hooks.use :emit_feature_event, :on => :success
6
9
 
7
10
  middleware.do_not_use Dynflow::Middleware::Common::Transaction
8
11
  middleware.use Actions::Middleware::HideSecrets
@@ -16,7 +19,12 @@ module Actions
16
19
  end
17
20
 
18
21
  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)
22
+ features = template_invocation.template.remote_execution_features.pluck(:label).uniq
23
+ action_subject(host,
24
+ :job_category => job_invocation.job_category,
25
+ :description => job_invocation.description,
26
+ :job_invocation_id => job_invocation.id,
27
+ :job_features => features)
20
28
 
21
29
  template_invocation.host_id = host.id
22
30
  template_invocation.run_host_job_task_id = task.id
@@ -57,6 +65,22 @@ module Actions
57
65
  check_exit_status
58
66
  end
59
67
 
68
+ def self.feature_job_event_name(label, suffix = :success)
69
+ ::Foreman::Observable.event_name_for("#{::Actions::RemoteExecution::RunHostJob.event_name_base}_#{label}_#{::Actions::RemoteExecution::RunHostJob.event_name_suffix(suffix)}")
70
+ end
71
+
72
+ def emit_feature_event(execution_plan, hook = :success)
73
+ return unless root_action?
74
+
75
+ payload = event_payload(execution_plan)
76
+ if input["job_features"]&.any?
77
+ input['job_features'].each do |feature|
78
+ name = "#{self.class.event_name_base}_#{feature}_#{self.class.event_name_suffix(hook)}"
79
+ trigger_hook name, payload: payload
80
+ end
81
+ end
82
+ end
83
+
60
84
  def secrets(host, job_invocation, provider)
61
85
  job_secrets = { :ssh_password => job_invocation.password,
62
86
  :key_passphrase => job_invocation.key_passphrase,
@@ -77,7 +101,7 @@ module Actions
77
101
 
78
102
  def humanized_input
79
103
  N_('%{description} on %{host}') % { :host => input[:host].try(:[], :name),
80
- :description => input[:description].try(:capitalize) || input[:job_category] }
104
+ :description => input[:description].try(:capitalize) || input[:job_category] }
81
105
  end
82
106
 
83
107
  def humanized_name
@@ -121,6 +145,26 @@ module Actions
121
145
  delegated_output[:exit_status]
122
146
  end
123
147
 
148
+ def host_id
149
+ input['host']['id']
150
+ end
151
+
152
+ def host_name
153
+ input['host']['name']
154
+ end
155
+
156
+ def job_invocation_id
157
+ input['job_invocation_id']
158
+ end
159
+
160
+ def job_invocation
161
+ @job_invocation ||= ::JobInvocation.authorized.find(job_invocation_id)
162
+ end
163
+
164
+ def host
165
+ @host ||= ::Host.authorized.find(host_id)
166
+ end
167
+
124
168
  private
125
169
 
126
170
  def update_host_status
@@ -165,14 +209,33 @@ module Actions
165
209
  'All %{count} applicable proxies are down. Tried %{proxy_names}',
166
210
  offline_proxies.count) % settings
167
211
  elsif proxy == :not_defined
168
- settings = { :global_proxy => 'remote_execution_global_proxy',
169
- :fallback_proxy => 'remote_execution_fallback_proxy' }
212
+ settings = {
213
+ global_proxy: 'remote_execution_global_proxy',
214
+ fallback_proxy: 'remote_execution_fallback_proxy',
215
+ provider: provider,
216
+ }
170
217
 
171
- raise _('Could not use any proxy. Consider configuring %{global_proxy}, ' +
218
+ raise _('Could not use any proxy for the %{provider} job. Consider configuring %{global_proxy}, ' +
172
219
  '%{fallback_proxy} in settings') % settings
173
220
  end
174
221
  proxy
175
222
  end
223
+
224
+ extend ApipieDSL::Class
225
+ apipie :class, "An action representing execution of a job against a host" do
226
+ name 'Actions::RemoteExecution::RunHostJob'
227
+ refs 'Actions::RemoteExecution::RunHostJob'
228
+ sections only: %w[all webhooks]
229
+ property :task, object_of: 'Task', desc: 'Returns the task to which this action belongs'
230
+ property :host_name, String, desc: "Returns the name of the host"
231
+ property :host_id, Integer, desc: "Returns the id of the host"
232
+ property :host, object_of: 'Host', desc: "Returns the host"
233
+ property :job_invocation_id, Integer, desc: "Returns the id of the job invocation"
234
+ property :job_invocation, object_of: 'JobInvocation', desc: "Returns the job invocation"
235
+ end
236
+ class Jail < ::Actions::ObservableAction::Jail
237
+ allow :host_name, :host_id, :host, :job_invocation_id, :job_invocation
238
+ end
176
239
  end
177
240
  end
178
241
  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
@@ -8,15 +8,15 @@ module ForemanRemoteExecution
8
8
  has_many :run_host_job_tasks, :through => :template_invocations
9
9
 
10
10
  scoped_search :relation => :run_host_job_tasks, :on => :result, :rename => 'job_invocation.result',
11
- :ext_method => :search_by_job_invocation,
12
- :only_explicit => true,
13
- :complete_value => TemplateInvocation::TaskResultMap::REVERSE_MAP
11
+ :ext_method => :search_by_job_invocation,
12
+ :only_explicit => true,
13
+ :complete_value => TemplateInvocation::TaskResultMap::REVERSE_MAP
14
14
 
15
15
  scoped_search :relation => :template_invocations, :on => :job_invocation_id,
16
- :rename => 'job_invocation.id', :only_explicit => true, :ext_method => :search_by_job_invocation
16
+ :rename => 'job_invocation.id', :only_explicit => true, :ext_method => :search_by_job_invocation
17
17
 
18
18
  scoped_search :relation => :execution_status_object, :on => :status, :rename => :execution_status,
19
- :complete_value => { :ok => HostStatus::ExecutionStatus::OK, :error => HostStatus::ExecutionStatus::ERROR }
19
+ :complete_value => { :ok => HostStatus::ExecutionStatus::OK, :error => HostStatus::ExecutionStatus::ERROR }
20
20
 
21
21
  def search_by_job_invocation(key, operator, value)
22
22
  if key == 'job_invocation.result'