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.
- checksums.yaml +4 -4
- data/.github/workflows/js_ci.yml +29 -0
- data/.github/workflows/{ci.yml → ruby_ci.yml} +22 -50
- data/.prettierrc +4 -0
- data/.rubocop.yml +13 -49
- data/.rubocop_todo.yml +326 -102
- data/Gemfile +1 -4
- data/app/controllers/api/v2/job_invocations_controller.rb +28 -23
- data/app/controllers/foreman_remote_execution/concerns/api/v2/registration_commands_controller_extensions.rb +19 -0
- data/app/controllers/job_templates_controller.rb +4 -4
- data/app/controllers/ui_job_wizard_controller.rb +30 -0
- data/app/helpers/job_invocations_helper.rb +2 -2
- data/app/helpers/remote_execution_helper.rb +35 -8
- data/app/lib/actions/remote_execution/run_host_job.rb +68 -5
- data/app/lib/foreman_remote_execution/provider_input.rb +29 -0
- data/app/lib/foreman_remote_execution/renderer/scope/input.rb +1 -0
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +5 -5
- data/app/models/host_status/execution_status.rb +12 -5
- data/app/models/invocation_provider_input_value.rb +12 -0
- data/app/models/job_invocation.rb +28 -8
- data/app/models/job_invocation_composer.rb +72 -17
- data/app/models/remote_execution_provider.rb +17 -2
- data/app/models/setting/remote_execution.rb +10 -0
- data/app/models/ssh_execution_provider.rb +4 -4
- 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/template_invocations/show.html.erb +30 -23
- data/config/routes.rb +5 -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/extra/cockpit/foreman-cockpit-session +6 -6
- data/foreman_remote_execution.gemspec +1 -2
- data/lib/foreman_remote_execution/engine.rb +22 -7
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/lib/tasks/foreman_remote_execution_tasks.rake +1 -18
- 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 +38 -5
- data/test/functional/api/v2/registration_controller_test.rb +4 -13
- data/test/functional/ui_job_wizard_controller_test.rb +16 -0
- data/test/helpers/remote_execution_helper_test.rb +16 -0
- data/test/unit/job_invocation_composer_test.rb +86 -2
- data/test/unit/job_invocation_report_template_test.rb +57 -0
- data/webpack/JobWizard/JobWizard.js +96 -0
- data/webpack/JobWizard/JobWizard.scss +14 -0
- data/webpack/JobWizard/JobWizardConstants.js +6 -0
- data/webpack/JobWizard/JobWizardSelectors.js +38 -0
- data/webpack/JobWizard/__tests__/JobWizard.test.js +13 -0
- data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +32 -0
- data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +43 -0
- data/webpack/JobWizard/__tests__/fixtures.js +26 -0
- data/webpack/JobWizard/__tests__/integration.test.js +156 -0
- data/webpack/JobWizard/index.js +32 -0
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +93 -0
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +181 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +25 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +249 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +109 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +52 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +113 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +94 -0
- data/webpack/JobWizard/steps/form/FormHelpers.js +19 -0
- data/webpack/JobWizard/steps/form/GroupedSelectField.js +91 -0
- data/webpack/JobWizard/steps/form/SelectField.js +48 -0
- data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +38 -0
- data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +23 -0
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +37 -0
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +23 -0
- data/webpack/Routes/routes.js +12 -0
- data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
- data/webpack/__mocks__/foremanReact/history.js +1 -0
- 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 +10 -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/TargetingHostsPage.js +1 -1
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss +0 -3
- 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 -1
- 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 +58 -20
- 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
|
-
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
66
|
-
|
|
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 =>
|
|
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(
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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 %>
|