foreman_remote_execution 1.4.5 → 1.4.6
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 +5 -5
- data/.hound.yml +13 -0
- data/.rubocop.yml +9 -0
- data/app/controllers/api/v2/job_invocations_controller.rb +49 -6
- data/app/controllers/job_invocations_controller.rb +29 -1
- data/app/helpers/remote_execution_helper.rb +5 -5
- data/app/lib/actions/remote_execution/run_host_job.rb +32 -20
- data/app/lib/actions/remote_execution/run_hosts_job.rb +13 -8
- data/app/models/foreign_input_set.rb +1 -1
- data/app/models/job_invocation.rb +23 -1
- data/app/models/job_invocation_composer.rb +25 -3
- data/app/models/job_template.rb +2 -2
- data/app/models/remote_execution_feature.rb +22 -10
- data/app/models/setting/remote_execution.rb +34 -10
- data/app/models/ssh_execution_provider.rb +8 -0
- data/app/models/template_input.rb +1 -1
- data/app/views/api/v2/job_templates/base.json.rabl +1 -1
- data/app/views/job_invocations/_form.html.erb +11 -5
- data/app/views/job_invocations/_tab_hosts.html.erb +1 -1
- data/app/views/job_invocations/index.html.erb +1 -1
- data/app/views/job_templates/index.html.erb +1 -1
- data/app/views/remote_execution_features/index.html.erb +1 -1
- data/app/views/template_inputs/_invocation_form.html.erb +2 -2
- data/app/views/template_invocations/show.html.erb +1 -1
- data/config/routes.rb +5 -0
- data/db/migrate/20160113162007_expand_all_template_invocations.rb +1 -1
- data/db/migrate/20171129103615_add_secrets_to_job_invocations.rb +6 -0
- data/db/migrate/20180202072115_add_notification_builder_to_remote_execution_feature.rb +5 -0
- data/db/migrate/20180202123215_add_feature_id_to_job_invocation.rb +6 -0
- data/db/migrate/20180226095631_change_task_id_to_uuid.rb +31 -0
- data/foreman_remote_execution.gemspec +1 -1
- data/lib/foreman_remote_execution/engine.rb +3 -1
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/factories/foreman_remote_execution_factories.rb +6 -0
- data/test/functional/api/v2/job_invocations_controller_test.rb +139 -32
- data/test/functional/job_invocations_controller_test.rb +49 -0
- data/test/unit/actions/run_hosts_job_test.rb +10 -6
- data/test/unit/concerns/foreman_tasks_cleaner_extensions_test.rb +1 -1
- data/test/unit/job_invocation_composer_test.rb +43 -1
- metadata +14 -10
- data/app/models/concerns/foreman_remote_execution/exportable.rb +0 -71
- data/test/unit/concerns/exportable_test.rb +0 -88
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3a0c3ef241880bf26fcf76f004b430ad451324b4d544302a423f92f61ed354c9
|
4
|
+
data.tar.gz: 3fd717b03faf2baa425c23150cba6aee3ecd0025d39a1233eab8b27307141d03
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65801bb36a9f54ae4c01bab9b4926e28066f7b5f0e608bfeff0f858262d6d432d9c380c5c2b982ea2d5375649fdcfe8d8fce485b820af6091b01b30323120f07
|
7
|
+
data.tar.gz: e37a034355a6967cc5e760e37299de3ba0afed9845715ae4628e83665606b1e21f558fb677392cee93b396036a420d48fbaaee1b6f5397d75a3a645dd6a39c34
|
data/.hound.yml
ADDED
data/.rubocop.yml
CHANGED
@@ -91,5 +91,14 @@ Naming/HeredocDelimiterNaming:
|
|
91
91
|
Style/RescueStandardError:
|
92
92
|
Enabled: false
|
93
93
|
|
94
|
+
Style/SafeNavigation:
|
95
|
+
Enabled: false
|
96
|
+
|
94
97
|
Lint/BooleanSymbol:
|
95
98
|
Enabled: false
|
99
|
+
|
100
|
+
Metrics/PerceivedComplexity:
|
101
|
+
Max: 8
|
102
|
+
|
103
|
+
Metrics/AbcSize:
|
104
|
+
Max: 45
|
@@ -6,8 +6,7 @@ module Api
|
|
6
6
|
|
7
7
|
before_action :find_optional_nested_object
|
8
8
|
before_action :find_host, :only => %w{output}
|
9
|
-
before_action :find_resource, :only => %w{show update destroy clone}
|
10
|
-
before_action :validate_template, :only => :create
|
9
|
+
before_action :find_resource, :only => %w{show update destroy clone cancel rerun}
|
11
10
|
|
12
11
|
wrap_parameters JobInvocation, :include => (JobInvocation.attribute_names + [:ssh])
|
13
12
|
|
@@ -24,7 +23,7 @@ module Api
|
|
24
23
|
# rubocop:disable Metrics/BlockLength
|
25
24
|
def_param_group :job_invocation do
|
26
25
|
param :job_invocation, Hash, :required => true, :action_aware => true do
|
27
|
-
param :job_template_id, String, :required =>
|
26
|
+
param :job_template_id, String, :required => false, :desc => N_('The job template to use, parameter is required unless feature was specified')
|
28
27
|
param :targeting_type, String, :required => true, :desc => N_('Invocation type, one of %s') % Targeting::TYPES
|
29
28
|
param :inputs, Hash, :required => false, :desc => N_('Inputs to use')
|
30
29
|
param :ssh, Hash, :desc => N_('SSH provider specific options') do
|
@@ -50,16 +49,24 @@ module Api
|
|
50
49
|
end
|
51
50
|
|
52
51
|
param :bookmark_id, Integer, :required => false
|
53
|
-
param :search_query,
|
52
|
+
param :search_query, String, :required => false
|
54
53
|
param :description_format, String, :required => false, :desc => N_('Override the description format from the template for this invocation only')
|
55
54
|
param :execution_timeout_interval, Integer, :required => false, :desc => N_('Override the timeout interval from the template for this invocation only')
|
55
|
+
param :feature, String, :required => false, :desc => N_('Remote execution feature label that should be triggered, job template assigned to this feature will be used')
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
59
|
api :POST, '/job_invocations/', N_('Create a job invocation')
|
60
60
|
param_group :job_invocation, :as => :create
|
61
61
|
def create
|
62
|
-
|
62
|
+
if job_invocation_params[:feature].present?
|
63
|
+
composer = composer_for_feature
|
64
|
+
else
|
65
|
+
validate_template
|
66
|
+
composer = JobInvocationComposer.from_api_params(
|
67
|
+
job_invocation_params
|
68
|
+
)
|
69
|
+
end
|
63
70
|
composer.trigger!
|
64
71
|
@job_invocation = composer.job_invocation
|
65
72
|
process_response @job_invocation
|
@@ -88,12 +95,39 @@ module Api
|
|
88
95
|
end
|
89
96
|
end
|
90
97
|
|
98
|
+
api :POST, '/job_invocations/:id/cancel', N_('Cancel job invocation')
|
99
|
+
param :id, :identifier, :required => true
|
100
|
+
param :force, :bool
|
101
|
+
def cancel
|
102
|
+
if @job_invocation.task.cancellable?
|
103
|
+
result = @job_invocation.cancel(params.fetch('force', false))
|
104
|
+
render :json => { :cancelled => result, :id => @job_invocation.id }
|
105
|
+
else
|
106
|
+
render :json => { :message => _('The job could not be cancelled.') },
|
107
|
+
:status => 422
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
api :POST, '/job_invocations/:id/rerun', N_('Rerun job on failed hosts')
|
112
|
+
param :id, :identifier, :required => true
|
113
|
+
param :failed_only, :bool
|
114
|
+
def rerun
|
115
|
+
composer = JobInvocationComposer.from_job_invocation(@job_invocation, params)
|
116
|
+
composer.trigger!
|
117
|
+
@job_invocation = composer.job_invocation
|
118
|
+
process_response @job_invocation
|
119
|
+
end
|
120
|
+
|
91
121
|
private
|
92
122
|
|
93
123
|
def action_permission
|
94
124
|
case params[:action]
|
95
125
|
when 'output'
|
96
126
|
:view
|
127
|
+
when 'cancel'
|
128
|
+
:cancel
|
129
|
+
when 'rerun'
|
130
|
+
:create
|
97
131
|
else
|
98
132
|
super
|
99
133
|
end
|
@@ -112,11 +146,20 @@ module Api
|
|
112
146
|
end
|
113
147
|
|
114
148
|
def job_invocation_params
|
149
|
+
return @job_invocation_params if @job_invocation_params.present?
|
115
150
|
job_invocation_params = params.fetch(:job_invocation, {}).dup
|
116
151
|
job_invocation_params.merge!(job_invocation_params.delete(:ssh)) if job_invocation_params.key?(:ssh)
|
117
152
|
job_invocation_params[:inputs] ||= {}
|
118
153
|
job_invocation_params[:inputs].permit!
|
119
|
-
job_invocation_params
|
154
|
+
@job_invocation_params = job_invocation_params
|
155
|
+
end
|
156
|
+
|
157
|
+
def composer_for_feature
|
158
|
+
JobInvocationComposer.for_feature(
|
159
|
+
job_invocation_params[:feature],
|
160
|
+
job_invocation_params[:host_ids],
|
161
|
+
job_invocation_params[:inputs].to_hash
|
162
|
+
)
|
120
163
|
end
|
121
164
|
end
|
122
165
|
end
|
@@ -3,6 +3,7 @@ class JobInvocationsController < ApplicationController
|
|
3
3
|
include ::ForemanTasks::Concerns::Parameters::Triggering
|
4
4
|
|
5
5
|
def new
|
6
|
+
return @composer = prepare_composer if params[:feature].present?
|
6
7
|
ui_params = {
|
7
8
|
:host_ids => params[:host_ids],
|
8
9
|
:targeting => {
|
@@ -72,6 +73,26 @@ class JobInvocationsController < ApplicationController
|
|
72
73
|
render :partial => 'job_invocations/preview_hosts_list'
|
73
74
|
end
|
74
75
|
|
76
|
+
def cancel
|
77
|
+
@job_invocation = resource_base.find(params[:id])
|
78
|
+
result = @job_invocation.cancel(params[:force])
|
79
|
+
|
80
|
+
if result
|
81
|
+
flash[:notice] = if params[:force]
|
82
|
+
_('Trying to abort the job')
|
83
|
+
else
|
84
|
+
_('Trying to cancel the job')
|
85
|
+
end
|
86
|
+
else
|
87
|
+
flash[:warning] = if params[:force]
|
88
|
+
_('The job cannot be aborted at the moment.')
|
89
|
+
else
|
90
|
+
_('The job cannot be cancelled at the moment.')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
redirect_back(:fallback_location => job_invocation_path(@job_invocation))
|
94
|
+
end
|
95
|
+
|
75
96
|
private
|
76
97
|
|
77
98
|
def action_permission
|
@@ -80,6 +101,8 @@ class JobInvocationsController < ApplicationController
|
|
80
101
|
'create'
|
81
102
|
when 'preview_hosts'
|
82
103
|
'create'
|
104
|
+
when 'cancel'
|
105
|
+
'cancel'
|
83
106
|
else
|
84
107
|
super
|
85
108
|
end
|
@@ -87,7 +110,12 @@ class JobInvocationsController < ApplicationController
|
|
87
110
|
|
88
111
|
def prepare_composer
|
89
112
|
if params[:feature].present?
|
90
|
-
|
113
|
+
inputs = params[:inputs].permit!.to_hash if params.include?(:inputs)
|
114
|
+
JobInvocationComposer.for_feature(
|
115
|
+
params[:feature],
|
116
|
+
params[:host_ids],
|
117
|
+
inputs
|
118
|
+
)
|
91
119
|
else
|
92
120
|
# triggering_params is a Hash
|
93
121
|
# when a hash is merged into ActionController::Parameters,
|
@@ -116,13 +116,13 @@ module RemoteExecutionHelper
|
|
116
116
|
:class => 'btn btn-default',
|
117
117
|
:title => _('See the last task details'))
|
118
118
|
end
|
119
|
-
if authorized_for(:permission => :
|
120
|
-
buttons << link_to(_('Cancel Job'),
|
119
|
+
if authorized_for(:permission => :cancel_job_invocations, :auth_object => job_invocation)
|
120
|
+
buttons << link_to(_('Cancel Job'), cancel_job_invocation_path(job_invocation),
|
121
121
|
:class => 'btn btn-danger',
|
122
122
|
:title => _('Try to cancel the job'),
|
123
123
|
:disabled => !task.cancellable?,
|
124
124
|
:method => :post)
|
125
|
-
buttons << link_to(_('Abort Job'),
|
125
|
+
buttons << link_to(_('Abort Job'), cancel_job_invocation_path(job_invocation, :force => true),
|
126
126
|
:class => 'btn btn-danger',
|
127
127
|
:title => _('Try to abort the job without waiting for the results from the remote hosts'),
|
128
128
|
:disabled => !task.cancellable?,
|
@@ -132,14 +132,14 @@ module RemoteExecutionHelper
|
|
132
132
|
end
|
133
133
|
# rubocop:enable Metrics/AbcSize
|
134
134
|
|
135
|
-
def template_invocation_task_buttons(task)
|
135
|
+
def template_invocation_task_buttons(task, invocation)
|
136
136
|
buttons = []
|
137
137
|
if authorized_for(:permission => :view_foreman_tasks, :auth_object => task)
|
138
138
|
buttons << link_to(_('Task Details'), foreman_tasks_task_path(task),
|
139
139
|
:class => 'btn btn-default',
|
140
140
|
:title => _('See the task details'))
|
141
141
|
end
|
142
|
-
if authorized_for(:permission => :
|
142
|
+
if authorized_for(:permission => :cancel_job_invocations, :auth_object => invocation)
|
143
143
|
buttons << link_to(_('Cancel Job'), cancel_foreman_tasks_task_path(task),
|
144
144
|
:class => 'btn btn-danger',
|
145
145
|
:title => _('Try to cancel the job on a host'),
|
@@ -5,12 +5,12 @@ module Actions
|
|
5
5
|
include ::Actions::Helpers::WithDelegatedAction
|
6
6
|
|
7
7
|
middleware.do_not_use Dynflow::Middleware::Common::Transaction
|
8
|
+
middleware.use Actions::Middleware::HideSecrets
|
8
9
|
|
9
10
|
def resource_locks
|
10
11
|
:link
|
11
12
|
end
|
12
13
|
|
13
|
-
# rubocop:disable Metrics/AbcSize
|
14
14
|
def plan(job_invocation, host, template_invocation, proxy_selector = ::RemoteExecutionProxySelector.new, options = {})
|
15
15
|
action_subject(host, :job_category => job_invocation.job_category, :description => job_invocation.description)
|
16
16
|
|
@@ -25,32 +25,25 @@ module Actions
|
|
25
25
|
|
26
26
|
raise _('Could not use any template used in the job invocation') if template_invocation.blank?
|
27
27
|
|
28
|
-
|
29
|
-
proxy =
|
30
|
-
if proxy == :not_available
|
31
|
-
offline_proxies = proxy_selector.offline
|
32
|
-
settings = { :count => offline_proxies.count, :proxy_names => offline_proxies.map(&:name).join(', ') }
|
33
|
-
raise n_('The only applicable proxy %{proxy_names} is down',
|
34
|
-
'All %{count} applicable proxies are down. Tried %{proxy_names}',
|
35
|
-
offline_proxies.count) % settings
|
36
|
-
elsif proxy == :not_defined && !Setting['remote_execution_without_proxy']
|
37
|
-
settings = { :global_proxy => 'remote_execution_global_proxy',
|
38
|
-
:fallback_proxy => 'remote_execution_fallback_proxy',
|
39
|
-
:no_proxy => 'remote_execution_no_proxy' }
|
40
|
-
|
41
|
-
raise _('Could not use any proxy. Consider configuring %{global_proxy}, ' +
|
42
|
-
'%{fallback_proxy} or %{no_proxy} in settings') % settings
|
43
|
-
end
|
28
|
+
provider_type = template_invocation.template.provider_type.to_s
|
29
|
+
proxy = determine_proxy!(proxy_selector, provider_type, host)
|
44
30
|
|
45
31
|
renderer = InputTemplateRenderer.new(template_invocation.template, host, template_invocation)
|
46
32
|
script = renderer.render
|
47
33
|
raise _('Failed rendering template: %s') % renderer.error_message unless script
|
48
34
|
|
49
35
|
provider = template_invocation.template.provider
|
50
|
-
|
36
|
+
|
37
|
+
secrets = { :ssh_password => job_invocation.password || provider.ssh_password(host),
|
38
|
+
:key_passphrase => job_invocation.key_passphrase || provider.ssh_key_passphrase(host) }
|
39
|
+
|
40
|
+
additional_options = { :hostname => provider.find_ip_or_hostname(host),
|
41
|
+
:script => script,
|
42
|
+
:execution_timeout_interval => job_invocation.execution_timeout_interval,
|
43
|
+
:secrets => secrets }
|
51
44
|
action_options = provider.proxy_command_options(template_invocation, host)
|
52
|
-
.merge(
|
53
|
-
|
45
|
+
.merge(additional_options)
|
46
|
+
|
54
47
|
plan_delegated_action(proxy, ForemanRemoteExecutionCore::Actions::RunScript, action_options)
|
55
48
|
plan_self
|
56
49
|
end
|
@@ -144,6 +137,25 @@ module Actions
|
|
144
137
|
|
145
138
|
true
|
146
139
|
end
|
140
|
+
|
141
|
+
def determine_proxy!(proxy_selector, provider, host)
|
142
|
+
proxy = proxy_selector.determine_proxy(host, provider)
|
143
|
+
if proxy == :not_available
|
144
|
+
offline_proxies = proxy_selector.offline
|
145
|
+
settings = { :count => offline_proxies.count, :proxy_names => offline_proxies.map(&:name).join(', ') }
|
146
|
+
raise n_('The only applicable proxy %{proxy_names} is down',
|
147
|
+
'All %{count} applicable proxies are down. Tried %{proxy_names}',
|
148
|
+
offline_proxies.count) % settings
|
149
|
+
elsif proxy == :not_defined && !Setting['remote_execution_without_proxy']
|
150
|
+
settings = { :global_proxy => 'remote_execution_global_proxy',
|
151
|
+
:fallback_proxy => 'remote_execution_fallback_proxy',
|
152
|
+
:no_proxy => 'remote_execution_no_proxy' }
|
153
|
+
|
154
|
+
raise _('Could not use any proxy. Consider configuring %{global_proxy}, ' +
|
155
|
+
'%{fallback_proxy} or %{no_proxy} in settings') % settings
|
156
|
+
end
|
157
|
+
proxy
|
158
|
+
end
|
147
159
|
end
|
148
160
|
end
|
149
161
|
end
|
@@ -26,7 +26,6 @@ module Actions
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def create_sub_plans
|
29
|
-
job_invocation = JobInvocation.find(input[:job_invocation_id])
|
30
29
|
proxy_selector = RemoteExecutionProxySelector.new
|
31
30
|
|
32
31
|
current_batch.map do |host|
|
@@ -38,6 +37,18 @@ module Actions
|
|
38
37
|
end
|
39
38
|
end
|
40
39
|
|
40
|
+
def finalize
|
41
|
+
job_invocation.password = job_invocation.key_passphrase = nil
|
42
|
+
job_invocation.save!
|
43
|
+
|
44
|
+
# creating the success notification should be the very last thing this tasks do
|
45
|
+
job_invocation.build_notification.deliver!
|
46
|
+
end
|
47
|
+
|
48
|
+
def job_invocation
|
49
|
+
@job_invocation ||= JobInvocation.find(input[:job_invocation_id])
|
50
|
+
end
|
51
|
+
|
41
52
|
def batch(from, size)
|
42
53
|
hosts.offset(from).limit(size)
|
43
54
|
end
|
@@ -47,7 +58,7 @@ module Actions
|
|
47
58
|
end
|
48
59
|
|
49
60
|
def hosts
|
50
|
-
|
61
|
+
job_invocation.targeting.hosts.order(:name, :id)
|
51
62
|
end
|
52
63
|
|
53
64
|
def set_up_concurrency_control(invocation)
|
@@ -66,12 +77,6 @@ module Actions
|
|
66
77
|
super unless event == Dynflow::Action::Skip
|
67
78
|
end
|
68
79
|
|
69
|
-
def finalize
|
70
|
-
# creating the success notification should be the very last thing this tasks do
|
71
|
-
job_invocation = JobInvocation.find(input[:job_invocation_id])
|
72
|
-
job_invocation.build_notification.deliver!
|
73
|
-
end
|
74
|
-
|
75
80
|
def humanized_input
|
76
81
|
input.fetch(:job_invocation, {}).fetch(:description, '')
|
77
82
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
class JobInvocation < ApplicationRecord
|
2
2
|
include Authorizable
|
3
|
+
include Encryptable
|
4
|
+
|
3
5
|
audited :except => [ :task_id, :targeting_id, :task_group_id, :triggering_id ]
|
4
6
|
|
5
7
|
include ForemanRemoteExecution::ErrorsFlattener
|
@@ -47,6 +49,8 @@ class JobInvocation < ApplicationRecord
|
|
47
49
|
belongs_to :triggering, :class_name => 'ForemanTasks::Triggering'
|
48
50
|
has_one :recurring_logic, :through => :triggering, :class_name => 'ForemanTasks::RecurringLogic'
|
49
51
|
|
52
|
+
belongs_to :remote_execution_feature
|
53
|
+
|
50
54
|
scope :with_task, -> { references(:task) }
|
51
55
|
|
52
56
|
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
|
@@ -64,6 +68,8 @@ class JobInvocation < ApplicationRecord
|
|
64
68
|
|
65
69
|
delegate :start_at, :to => :task, :allow_nil => true
|
66
70
|
|
71
|
+
encrypts :password, :key_passphrase
|
72
|
+
|
67
73
|
def self.search_by_status(key, operator, value)
|
68
74
|
conditions = HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.sql_conditions_for(value)
|
69
75
|
conditions[0] = "NOT (#{conditions[0]})" if operator == '<>'
|
@@ -83,7 +89,16 @@ class JobInvocation < ApplicationRecord
|
|
83
89
|
end
|
84
90
|
|
85
91
|
def build_notification
|
86
|
-
|
92
|
+
klass = nil
|
93
|
+
if self.remote_execution_feature && self.remote_execution_feature.notification_builder.present?
|
94
|
+
begin
|
95
|
+
klass = remote_execution_feature.notification_builder.constantize
|
96
|
+
rescue NameError => e
|
97
|
+
logger.exception "REX feature defines unknown notification builder class", e
|
98
|
+
end
|
99
|
+
end
|
100
|
+
klass ||= UINotifications::RemoteExecutionJobs::BaseJobFinish
|
101
|
+
klass.new(self)
|
87
102
|
end
|
88
103
|
|
89
104
|
def status
|
@@ -120,6 +135,8 @@ class JobInvocation < ApplicationRecord
|
|
120
135
|
invocation.description_format = self.description_format
|
121
136
|
invocation.description = self.description
|
122
137
|
invocation.pattern_template_invocations = self.pattern_template_invocations.map(&:deep_clone)
|
138
|
+
invocation.password = self.password
|
139
|
+
invocation.key_passphrase = self.key_passphrase
|
123
140
|
end
|
124
141
|
end
|
125
142
|
|
@@ -208,6 +225,11 @@ class JobInvocation < ApplicationRecord
|
|
208
225
|
end
|
209
226
|
end
|
210
227
|
|
228
|
+
def cancel(force = false)
|
229
|
+
method = force ? :abort : :cancel
|
230
|
+
task.send(method)
|
231
|
+
end
|
232
|
+
|
211
233
|
private
|
212
234
|
|
213
235
|
def failed_template_invocations
|
@@ -11,7 +11,10 @@ class JobInvocationComposer
|
|
11
11
|
:targeting => ui_params.fetch(:targeting, {}).merge(:user_id => User.current.id),
|
12
12
|
:triggering => triggering,
|
13
13
|
:host_ids => ui_params[:host_ids],
|
14
|
+
:remote_execution_feature_id => ui_params[:remote_execution_feature_id],
|
14
15
|
:description_format => job_invocation_base[:description_format],
|
16
|
+
:password => blank_to_nil(job_invocation_base[:password]),
|
17
|
+
:key_passphrase => blank_to_nil(job_invocation_base[:key_passphrase]),
|
15
18
|
:concurrency_control => concurrency_control_params,
|
16
19
|
:execution_timeout_interval => execution_timeout_interval,
|
17
20
|
:template_invocations => template_invocations_params }.with_indifferent_access
|
@@ -32,6 +35,10 @@ class JobInvocationComposer
|
|
32
35
|
end.first
|
33
36
|
end
|
34
37
|
|
38
|
+
def blank_to_nil(thing)
|
39
|
+
thing.blank? ? nil : thing
|
40
|
+
end
|
41
|
+
|
35
42
|
# TODO: Fix this comment
|
36
43
|
# parses params to get job templates in form of id => attributes for selected job templates, e.g.
|
37
44
|
# {
|
@@ -88,6 +95,7 @@ class JobInvocationComposer
|
|
88
95
|
:targeting => targeting_params,
|
89
96
|
:triggering => triggering_params,
|
90
97
|
:description_format => api_params[:description_format],
|
98
|
+
:remote_execution_feature_id => api_params[:remote_execution_feature_id],
|
91
99
|
:concurrency_control => concurrency_control_params,
|
92
100
|
:execution_timeout_interval => api_params[:execution_timeout_interval] || template.execution_timeout_interval,
|
93
101
|
:template_invocations => template_invocations_params }.with_indifferent_access
|
@@ -172,6 +180,7 @@ class JobInvocationComposer
|
|
172
180
|
:description_format => job_invocation.description_format,
|
173
181
|
:concurrency_control => concurrency_control_params,
|
174
182
|
:execution_timeout_interval => job_invocation.execution_timeout_interval,
|
183
|
+
:remote_execution_feature_id => job_invocation.remote_execution_feature_id,
|
175
184
|
:template_invocations => template_invocations_params }.with_indifferent_access
|
176
185
|
end
|
177
186
|
|
@@ -185,10 +194,12 @@ class JobInvocationComposer
|
|
185
194
|
end
|
186
195
|
|
187
196
|
def targeting_params
|
197
|
+
base = { :user_id => User.current.id }
|
188
198
|
if @host_ids
|
189
|
-
|
199
|
+
search_query = @host_ids.empty? ? 'name ^ ()' : Targeting.build_query_from_hosts(@host_ids)
|
200
|
+
base.merge(:search_query => search_query, :targeting_type => job_invocation.targeting.targeting_type)
|
190
201
|
else
|
191
|
-
job_invocation.targeting.attributes.slice('search_query', 'bookmark_id', '
|
202
|
+
base.merge job_invocation.targeting.attributes.slice('search_query', 'bookmark_id', 'targeting_type')
|
192
203
|
end
|
193
204
|
end
|
194
205
|
|
@@ -215,6 +226,10 @@ class JobInvocationComposer
|
|
215
226
|
@host_bookmark = hosts
|
216
227
|
elsif hosts.is_a? Host::Base
|
217
228
|
@host_objects = [hosts]
|
229
|
+
elsif hosts.is_a? Array
|
230
|
+
@host_objects = hosts.map do |id|
|
231
|
+
Host::Managed.authorized.friendly.find(id)
|
232
|
+
end
|
218
233
|
elsif hosts.is_a? String
|
219
234
|
@host_scoped_search = hosts
|
220
235
|
else
|
@@ -227,6 +242,7 @@ class JobInvocationComposer
|
|
227
242
|
:targeting => targeting_params,
|
228
243
|
:triggering => {},
|
229
244
|
:concurrency_control => {},
|
245
|
+
:remote_execution_feature_id => @feature.id,
|
230
246
|
:template_invocations => template_invocations_params }.with_indifferent_access
|
231
247
|
end
|
232
248
|
|
@@ -248,6 +264,7 @@ class JobInvocationComposer
|
|
248
264
|
end
|
249
265
|
|
250
266
|
def input_values_params
|
267
|
+
return {} if @provided_inputs.blank?
|
251
268
|
@provided_inputs.map do |key, value|
|
252
269
|
input = job_template.template_inputs_with_foreign.find { |i| i.name == key.to_s }
|
253
270
|
unless input
|
@@ -275,7 +292,7 @@ class JobInvocationComposer
|
|
275
292
|
end
|
276
293
|
|
277
294
|
attr_accessor :params, :job_invocation, :host_ids, :search_query
|
278
|
-
delegate :job_category, :pattern_template_invocations, :template_invocations, :targeting, :triggering, :to => :job_invocation
|
295
|
+
delegate :job_category, :remote_execution_feature_id, :pattern_template_invocations, :template_invocations, :targeting, :triggering, :to => :job_invocation
|
279
296
|
|
280
297
|
def initialize(params, set_defaults = false)
|
281
298
|
@params = params
|
@@ -304,9 +321,11 @@ class JobInvocationComposer
|
|
304
321
|
self.new(ParamsForFeature.new(feature_label, hosts, provided_inputs).params)
|
305
322
|
end
|
306
323
|
|
324
|
+
# rubocop:disable Metrics/AbcSize
|
307
325
|
def compose
|
308
326
|
job_invocation.job_category = validate_job_category(params[:job_category])
|
309
327
|
job_invocation.job_category ||= available_job_categories.first if @set_defaults
|
328
|
+
job_invocation.remote_execution_feature_id = params[:remote_execution_feature_id]
|
310
329
|
job_invocation.targeting = build_targeting
|
311
330
|
job_invocation.triggering = build_triggering
|
312
331
|
job_invocation.pattern_template_invocations = build_template_invocations
|
@@ -314,9 +333,12 @@ class JobInvocationComposer
|
|
314
333
|
job_invocation.time_span = params[:concurrency_control][:time_span].to_i if params[:concurrency_control][:time_span].present?
|
315
334
|
job_invocation.concurrency_level = params[:concurrency_control][:level].to_i if params[:concurrency_control][:level].present?
|
316
335
|
job_invocation.execution_timeout_interval = params[:execution_timeout_interval]
|
336
|
+
job_invocation.password = params[:password]
|
337
|
+
job_invocation.key_passphrase = params[:key_passphrase]
|
317
338
|
|
318
339
|
self
|
319
340
|
end
|
341
|
+
# rubocop:enable Metrics/AbcSize
|
320
342
|
|
321
343
|
def trigger(raise_on_error = false)
|
322
344
|
generate_description
|