foreman_remote_execution 4.3.1 → 4.5.2
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.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/job_invocations_controller.rb +27 -22
- data/app/controllers/foreman_remote_execution/concerns/api/v2/registration_commands_controller_extensions.rb +19 -0
- data/app/controllers/job_invocations_controller.rb +1 -1
- data/app/controllers/job_templates_controller.rb +4 -4
- data/app/controllers/ui_job_wizard_controller.rb +19 -0
- data/app/helpers/job_invocations_helper.rb +2 -2
- data/app/helpers/remote_execution_helper.rb +40 -9
- data/app/lib/actions/remote_execution/run_host_job.rb +36 -6
- data/app/lib/foreman_remote_execution/provider_input.rb +29 -0
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +7 -5
- data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +6 -0
- data/app/models/host_proxy_invocation.rb +4 -0
- data/app/models/host_status/execution_status.rb +5 -5
- data/app/models/invocation_provider_input_value.rb +12 -0
- data/app/models/job_invocation.rb +35 -12
- data/app/models/job_invocation_composer.rb +74 -19
- data/app/models/remote_execution_provider.rb +18 -3
- data/app/models/setting/remote_execution.rb +11 -1
- data/app/models/ssh_execution_provider.rb +4 -4
- data/app/models/targeting.rb +5 -1
- data/app/models/template_invocation.rb +2 -0
- data/app/overrides/execution_interface.rb +8 -8
- data/app/overrides/subnet_proxies.rb +6 -6
- data/app/services/renderer_methods.rb +12 -0
- data/app/views/job_invocations/_form.html.erb +8 -0
- data/app/views/job_invocations/index.html.erb +1 -1
- data/app/views/templates/ssh/module_action.erb +1 -0
- data/app/views/templates/ssh/puppet_run_once.erb +1 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20180110104432_rename_template_invocation_permission.rb +1 -1
- data/db/migrate/20190111153330_remove_remote_execution_without_proxy_setting.rb +4 -4
- data/db/migrate/20210312074713_add_provider_inputs.rb +10 -0
- data/db/migrate/2021051713291621250977_add_host_proxy_invocations.rb +12 -0
- data/extra/cockpit/foreman-cockpit-session +6 -6
- data/foreman_remote_execution.gemspec +1 -1
- data/lib/foreman_remote_execution/engine.rb +14 -12
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/action_names.rb +1 -0
- data/locale/de/foreman_remote_execution.po +77 -27
- data/locale/en/foreman_remote_execution.po +77 -27
- data/locale/en_GB/foreman_remote_execution.po +77 -27
- data/locale/es/foreman_remote_execution.po +77 -27
- data/locale/foreman_remote_execution.pot +241 -163
- data/locale/fr/foreman_remote_execution.po +77 -27
- data/locale/ja/foreman_remote_execution.po +77 -27
- data/locale/ko/foreman_remote_execution.po +77 -27
- data/locale/pt_BR/foreman_remote_execution.po +77 -27
- data/locale/ru/foreman_remote_execution.po +77 -27
- data/locale/zh_CN/foreman_remote_execution.po +77 -27
- data/locale/zh_TW/foreman_remote_execution.po +77 -27
- data/package.json +4 -2
- data/test/functional/api/v2/job_invocations_controller_test.rb +14 -1
- data/test/helpers/remote_execution_helper_test.rb +16 -0
- data/test/unit/job_invocation_composer_test.rb +100 -3
- data/test/unit/job_invocation_report_template_test.rb +57 -0
- data/test/unit/job_invocation_test.rb +1 -1
- data/webpack/JobWizard/JobWizard.js +95 -11
- data/webpack/JobWizard/JobWizard.scss +53 -0
- data/webpack/JobWizard/JobWizardConstants.js +16 -0
- data/webpack/JobWizard/JobWizardSelectors.js +47 -0
- data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +43 -0
- data/webpack/JobWizard/__tests__/fixtures.js +128 -0
- data/webpack/JobWizard/__tests__/integration.test.js +84 -0
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +110 -0
- data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +67 -0
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +195 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +144 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +23 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +109 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +123 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +94 -0
- data/webpack/JobWizard/steps/Schedule/QueryType.js +48 -0
- data/webpack/JobWizard/steps/Schedule/RepeatOn.js +61 -0
- data/webpack/JobWizard/steps/Schedule/ScheduleType.js +25 -0
- data/webpack/JobWizard/steps/Schedule/StartEndDates.js +51 -0
- data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +22 -0
- data/webpack/JobWizard/steps/Schedule/index.js +41 -0
- data/webpack/JobWizard/steps/form/FormHelpers.js +20 -0
- data/webpack/JobWizard/steps/form/Formatter.js +149 -0
- data/webpack/JobWizard/steps/form/GroupedSelectField.js +91 -0
- data/webpack/JobWizard/steps/form/NumberInput.js +33 -0
- data/webpack/JobWizard/steps/form/SelectField.js +60 -0
- data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +76 -0
- data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
- data/webpack/__mocks__/foremanReact/components/SearchBar.js +18 -1
- data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +21 -2
- data/webpack/__mocks__/foremanReact/redux/API/index.js +5 -0
- data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +10 -0
- data/webpack/global_index.js +6 -0
- data/webpack/index.js +3 -4
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +83 -0
- data/webpack/react_app/components/RecentJobsCard/constants.js +1 -0
- data/webpack/react_app/components/RecentJobsCard/index.js +1 -0
- data/webpack/react_app/components/RecentJobsCard/styles.css +15 -0
- data/webpack/react_app/components/RegistrationExtension/RexInterface.js +50 -0
- data/webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js +9 -0
- data/webpack/react_app/components/RegistrationExtension/__tests__/__snapshots__/RexInterface.test.js.snap +35 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +8 -3
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +7 -2
- data/webpack/react_app/extend/fillRecentJobsCard.js +11 -0
- data/webpack/react_app/extend/fillregistrationAdvanced.js +11 -0
- data/webpack/react_app/extend/reducers.js +5 -0
- metadata +52 -8
- data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +0 -70
- data/app/views/api/v2/registration/_form.html.erb +0 -12
- data/test/models/orchestration/ssh_test.rb +0 -56
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8c01aa4397a7c931f0cda22e96e238bded98642267efbd24d450fc27bb656d0a
|
|
4
|
+
data.tar.gz: 4a07bd69223c941761dc3ad16001eeef969612e3f5b648bd168f141e6f087781
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 56a50ffebf8864ec4851fc9e74c479a84b3af194502c5945e9c58cacc0068c283aab2d8cbc282a837aad686ebfe137477989fa572dce8e5d9914ae5c06b72736
|
|
7
|
+
data.tar.gz: c70b0c20e50b576841f96e8050b8be366d345758749b3c3d144af8e4364a65796d72a0874c382e3b13c5e73e36ef0d6266a1fa5544bf6b34805cb79de113c572
|
|
@@ -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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
202
|
-
|
|
203
|
-
|
|
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)
|
|
@@ -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
|
|
@@ -67,7 +67,7 @@ class JobInvocationsController < ApplicationController
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def index
|
|
70
|
-
@job_invocations = resource_base_search_and_page.
|
|
70
|
+
@job_invocations = resource_base_search_and_page.preload(:task, :targeting).order('job_invocations.id DESC')
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
# refreshes the form
|
|
@@ -24,10 +24,10 @@ class JobTemplatesController < ::TemplatesController
|
|
|
24
24
|
render :plain => output
|
|
25
25
|
else
|
|
26
26
|
render status: :not_acceptable,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
|
@@ -8,6 +8,25 @@ class UiJobWizardController < ::Api::V2::BaseController
|
|
|
8
8
|
render :json => {:job_categories =>job_categories}
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def template
|
|
12
|
+
job_template = JobTemplate.authorized.find(params[:id])
|
|
13
|
+
advanced_template_inputs, template_inputs = map_template_inputs(job_template.template_inputs_with_foreign).partition { |x| x["advanced"] }
|
|
14
|
+
render :json => {
|
|
15
|
+
:job_template => job_template,
|
|
16
|
+
:effective_user => job_template.effective_user,
|
|
17
|
+
:template_inputs => template_inputs,
|
|
18
|
+
:advanced_template_inputs => advanced_template_inputs,
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def resource_name(nested_resource = nil)
|
|
23
|
+
nested_resource || 'job_template'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def map_template_inputs(template_inputs_with_foreign)
|
|
27
|
+
template_inputs_with_foreign.map { |input| input.attributes.merge({:resource_type => input.resource_type&.tableize }) }
|
|
28
|
+
end
|
|
29
|
+
|
|
11
30
|
def resource_class
|
|
12
31
|
JobTemplate
|
|
13
32
|
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
|
-
|
|
38
|
-
|
|
37
|
+
:locals => { :template_invocation => template_invocation,
|
|
38
|
+
:target => host })
|
|
39
39
|
end.reduce(:+)
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -7,6 +7,10 @@ module RemoteExecutionHelper
|
|
|
7
7
|
@job_hosts_authorizer ||= Authorizer.new(User.current, :collection => @hosts)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
def host_tasks_authorizer
|
|
11
|
+
@host_tasks_authorizer ||= Authorizer.new(User.current, :collection => @job_invocation.sub_tasks)
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
def host_counter(label, count)
|
|
11
15
|
content_tag(:div, :class => 'host_counter') do
|
|
12
16
|
content_tag(:div, label, :class => 'header') + content_tag(:div, count.to_s, :class => 'count')
|
|
@@ -27,19 +31,19 @@ module RemoteExecutionHelper
|
|
|
27
31
|
|
|
28
32
|
if authorized_for(hash_for_host_path(host).merge(auth_object: host, permission: :view_hosts, authorizer: job_hosts_authorizer))
|
|
29
33
|
links << { title: _('Host detail'),
|
|
30
|
-
|
|
34
|
+
action: { href: host_path(host), 'data-method': 'get', id: "#{host.name}-actions-detail" } }
|
|
31
35
|
end
|
|
32
36
|
|
|
33
37
|
if authorized_for(hash_for_rerun_job_invocation_path(id: job_invocation, host_ids: [ host.id ], authorizer: job_hosts_authorizer))
|
|
34
38
|
links << { title: (_('Rerun on %s') % host.name),
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
action: { href: rerun_job_invocation_path(job_invocation, host_ids: [ host.id ]),
|
|
40
|
+
'data-method': 'get', id: "#{host.name}-actions-rerun" } }
|
|
37
41
|
end
|
|
38
42
|
|
|
39
|
-
if host_task.present? && authorized_for(hash_for_foreman_tasks_task_path(host_task).merge(auth_object: host_task, permission: :view_foreman_tasks))
|
|
43
|
+
if host_task.present? && authorized_for(hash_for_foreman_tasks_task_path(host_task).merge(auth_object: host_task, permission: :view_foreman_tasks, authorizer: host_tasks_authorizer))
|
|
40
44
|
links << { title: _('Host task'),
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
action: { href: foreman_tasks_task_path(host_task),
|
|
46
|
+
'data-method': 'get', id: "#{host.name}-actions-task" } }
|
|
43
47
|
end
|
|
44
48
|
|
|
45
49
|
links
|
|
@@ -60,6 +64,12 @@ module RemoteExecutionHelper
|
|
|
60
64
|
job_invocation = task.task_groups.find { |group| group.class == JobInvocationTaskGroup }.job_invocation
|
|
61
65
|
task_authorizer = Authorizer.new(User.current, :collection => [task])
|
|
62
66
|
buttons = []
|
|
67
|
+
if (template = job_report_template) && authorized_for(controller: :report_templates, action: :generate)
|
|
68
|
+
buttons << link_to(_('Create Report'), generate_report_template_path(template, job_report_template_parameters(job_invocation, template)),
|
|
69
|
+
class: 'btn btn-default',
|
|
70
|
+
title: _('Create report for this job'),
|
|
71
|
+
disabled: task.pending?)
|
|
72
|
+
end
|
|
63
73
|
if authorized_for(hash_for_new_job_invocation_path)
|
|
64
74
|
buttons << link_to(_('Rerun'), rerun_job_invocation_path(:id => job_invocation.id),
|
|
65
75
|
:class => 'btn btn-default',
|
|
@@ -153,11 +163,11 @@ module RemoteExecutionHelper
|
|
|
153
163
|
content_tag :pre, preview
|
|
154
164
|
elsif target.nil?
|
|
155
165
|
alert :text => _('Could not render the preview because no host matches the search query.'),
|
|
156
|
-
|
|
157
|
-
|
|
166
|
+
:class => 'alert alert-block alert-warning base',
|
|
167
|
+
:close => false
|
|
158
168
|
else
|
|
159
169
|
alert :class => 'alert-block alert-danger base in fade has-error',
|
|
160
|
-
|
|
170
|
+
:text => renderer.error_message.html_safe # rubocop:disable Rails/OutputSafety
|
|
161
171
|
end
|
|
162
172
|
end
|
|
163
173
|
|
|
@@ -230,6 +240,27 @@ module RemoteExecutionHelper
|
|
|
230
240
|
task.execution_plan.actions[1].try(:input).try(:[], 'script')
|
|
231
241
|
end
|
|
232
242
|
|
|
243
|
+
def job_report_template
|
|
244
|
+
template = ReportTemplate.where(name: Setting['remote_execution_job_invocation_report_template']).first
|
|
245
|
+
|
|
246
|
+
template if template.template_inputs.where(name: 'job_id').exists?
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def job_report_template_parameters(job_invocation, template)
|
|
250
|
+
template_input = template.template_inputs.where(name: 'job_id').first
|
|
251
|
+
raise "#job_report_template_parameters need template that has 'job_id' input" unless template_input
|
|
252
|
+
|
|
253
|
+
{
|
|
254
|
+
report_template_report: {
|
|
255
|
+
input_values: {
|
|
256
|
+
"#{template_input.id}": {
|
|
257
|
+
value: job_invocation.id,
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
}
|
|
262
|
+
end
|
|
263
|
+
|
|
233
264
|
def targeting_hosts(job_invocation, hosts)
|
|
234
265
|
hosts.map do |host|
|
|
235
266
|
template_invocation = job_invocation.template_invocations.find { |template_inv| template_inv.host_id == host.id }
|
|
@@ -5,6 +5,8 @@ module Actions
|
|
|
5
5
|
include ::Actions::Helpers::WithDelegatedAction
|
|
6
6
|
include ::Actions::ObservableAction
|
|
7
7
|
|
|
8
|
+
execution_plan_hooks.use :emit_feature_event, :on => :success
|
|
9
|
+
|
|
8
10
|
middleware.do_not_use Dynflow::Middleware::Common::Transaction
|
|
9
11
|
middleware.use Actions::Middleware::HideSecrets
|
|
10
12
|
|
|
@@ -17,7 +19,12 @@ module Actions
|
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def plan(job_invocation, host, template_invocation, proxy_selector = ::RemoteExecutionProxySelector.new, options = {})
|
|
20
|
-
|
|
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)
|
|
21
28
|
|
|
22
29
|
template_invocation.host_id = host.id
|
|
23
30
|
template_invocation.run_host_job_task_id = task.id
|
|
@@ -40,12 +47,16 @@ module Actions
|
|
|
40
47
|
script = renderer.render
|
|
41
48
|
raise _('Failed rendering template: %s') % renderer.error_message unless script
|
|
42
49
|
|
|
50
|
+
first_execution = host.executed_through_proxies.where(:id => proxy.id).none?
|
|
51
|
+
host.executed_through_proxies << proxy if first_execution
|
|
52
|
+
|
|
43
53
|
additional_options = { :hostname => provider.find_ip_or_hostname(host),
|
|
44
54
|
:script => script,
|
|
45
55
|
:execution_timeout_interval => job_invocation.execution_timeout_interval,
|
|
46
56
|
:secrets => secrets(host, job_invocation, provider),
|
|
47
57
|
:use_batch_triggering => true,
|
|
48
|
-
:use_concurrency_control => options[:use_concurrency_control]
|
|
58
|
+
:use_concurrency_control => options[:use_concurrency_control],
|
|
59
|
+
:first_execution => first_execution }
|
|
49
60
|
action_options = provider.proxy_command_options(template_invocation, host)
|
|
50
61
|
.merge(additional_options)
|
|
51
62
|
|
|
@@ -58,6 +69,22 @@ module Actions
|
|
|
58
69
|
check_exit_status
|
|
59
70
|
end
|
|
60
71
|
|
|
72
|
+
def self.feature_job_event_name(label, suffix = :success)
|
|
73
|
+
::Foreman::Observable.event_name_for("#{::Actions::RemoteExecution::RunHostJob.event_name_base}_#{label}_#{::Actions::RemoteExecution::RunHostJob.event_name_suffix(suffix)}")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def emit_feature_event(execution_plan, hook = :success)
|
|
77
|
+
return unless root_action?
|
|
78
|
+
|
|
79
|
+
payload = event_payload(execution_plan)
|
|
80
|
+
if input["job_features"]&.any?
|
|
81
|
+
input['job_features'].each do |feature|
|
|
82
|
+
name = "#{self.class.event_name_base}_#{feature}_#{self.class.event_name_suffix(hook)}"
|
|
83
|
+
trigger_hook name, payload: payload
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
61
88
|
def secrets(host, job_invocation, provider)
|
|
62
89
|
job_secrets = { :ssh_password => job_invocation.password,
|
|
63
90
|
:key_passphrase => job_invocation.key_passphrase,
|
|
@@ -78,7 +105,7 @@ module Actions
|
|
|
78
105
|
|
|
79
106
|
def humanized_input
|
|
80
107
|
N_('%{description} on %{host}') % { :host => input[:host].try(:[], :name),
|
|
81
|
-
|
|
108
|
+
:description => input[:description].try(:capitalize) || input[:job_category] }
|
|
82
109
|
end
|
|
83
110
|
|
|
84
111
|
def humanized_name
|
|
@@ -186,10 +213,13 @@ module Actions
|
|
|
186
213
|
'All %{count} applicable proxies are down. Tried %{proxy_names}',
|
|
187
214
|
offline_proxies.count) % settings
|
|
188
215
|
elsif proxy == :not_defined
|
|
189
|
-
settings = {
|
|
190
|
-
|
|
216
|
+
settings = {
|
|
217
|
+
global_proxy: 'remote_execution_global_proxy',
|
|
218
|
+
fallback_proxy: 'remote_execution_fallback_proxy',
|
|
219
|
+
provider: provider,
|
|
220
|
+
}
|
|
191
221
|
|
|
192
|
-
raise _('Could not use any proxy. Consider configuring %{global_proxy}, ' +
|
|
222
|
+
raise _('Could not use any proxy for the %{provider} job. Consider configuring %{global_proxy}, ' +
|
|
193
223
|
'%{fallback_proxy} in settings') % settings
|
|
194
224
|
end
|
|
195
225
|
proxy
|
|
@@ -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,17 +6,19 @@ module ForemanRemoteExecution
|
|
|
6
6
|
has_many :template_invocations, :dependent => :destroy, :foreign_key => 'host_id'
|
|
7
7
|
has_one :execution_status_object, :class_name => 'HostStatus::ExecutionStatus', :foreign_key => 'host_id', :dependent => :destroy
|
|
8
8
|
has_many :run_host_job_tasks, :through => :template_invocations
|
|
9
|
+
has_many :host_proxy_invocations, :foreign_key => 'host_id', :dependent => :destroy
|
|
10
|
+
has_many :executed_through_proxies, :through => :host_proxy_invocations, :source => 'smart_proxy'
|
|
9
11
|
|
|
10
12
|
scoped_search :relation => :run_host_job_tasks, :on => :result, :rename => 'job_invocation.result',
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
:ext_method => :search_by_job_invocation,
|
|
14
|
+
:only_explicit => true,
|
|
15
|
+
:complete_value => TemplateInvocation::TaskResultMap::REVERSE_MAP
|
|
14
16
|
|
|
15
17
|
scoped_search :relation => :template_invocations, :on => :job_invocation_id,
|
|
16
|
-
|
|
18
|
+
:rename => 'job_invocation.id', :only_explicit => true, :ext_method => :search_by_job_invocation
|
|
17
19
|
|
|
18
20
|
scoped_search :relation => :execution_status_object, :on => :status, :rename => :execution_status,
|
|
19
|
-
|
|
21
|
+
:complete_value => { :ok => HostStatus::ExecutionStatus::OK, :error => HostStatus::ExecutionStatus::ERROR }
|
|
20
22
|
|
|
21
23
|
def search_by_job_invocation(key, operator, value)
|
|
22
24
|
if key == 'job_invocation.result'
|