foreman_remote_execution 1.4.5 → 1.4.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|