foreman_remote_execution 1.5.4 → 1.5.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d911acff83175160207a45f2ca0ba34827beff57
4
- data.tar.gz: 7ed92c4c5653b5317d17aa6c470ba361e7ee26b1
2
+ SHA256:
3
+ metadata.gz: 9494abeb23995efb0f837561fd1b1bd84fbd7ef37060081ea573d2dde16bd74b
4
+ data.tar.gz: afbf23769a88fecbbcb7197249b2261a046b568be38699c35a12210ab6cb13e5
5
5
  SHA512:
6
- metadata.gz: ba51f41db0f75a572099ab7bf4d69f926df0e942f5ded5b21f8111caa14e2ed4f2d1d7d5119089c4c888ca00d079113f15575933552d60d13a8daf8633ad8b39
7
- data.tar.gz: f0bb2bdb7f28f155c2846442e7b7661e834c15beaf0f49aebe405ed39ab217d65db8c825d58fc11974394212e6362a67a17ccf272cd03054643e23f06b6b9248
6
+ metadata.gz: 28dd96a2c75c9e94f71a6613fac13ecbf7938d46fdfc0d5208833073149a3e60c84de6a7ee357ca22318abae29a7deb85afb77377ff462ee71aade08cf7ec0ca
7
+ data.tar.gz: 5d012baa162db923230e7d7c0f0dc03a315075ce79f3bc64b8cc1236c328061c2ff839f97c56849157c303fc5e758376ecab15d65137cf206aabe6159d7025cf
@@ -82,17 +82,15 @@ module Api
82
82
  return
83
83
  end
84
84
  task = @nested_obj.sub_task_for_host(@host)
85
- refresh = task.pending?
86
- since = params[:since].to_f if params[:since].present?
85
+ refresh = true
86
+ line_sets = []
87
87
 
88
- line_sets = task.main_action.live_output
89
- line_sets = line_sets.drop_while { |o| o['timestamp'].to_f <= since } if since
90
-
91
- if line_sets.blank?
92
- render :json => {:refresh => refresh, :output => []}
93
- else
94
- render :json => {:refresh => refresh, :output => line_sets }
88
+ if task
89
+ refresh = task.pending?
90
+ line_sets = output_lines_since task, params[:since]
95
91
  end
92
+
93
+ render :json => { :refresh => refresh, :output => line_sets || [] }
96
94
  end
97
95
 
98
96
  api :POST, '/job_invocations/:id/cancel', N_('Cancel job invocation')
@@ -113,9 +111,14 @@ module Api
113
111
  param :failed_only, :bool
114
112
  def rerun
115
113
  composer = JobInvocationComposer.from_job_invocation(@job_invocation, params)
116
- composer.trigger!
117
- @job_invocation = composer.job_invocation
118
- process_response @job_invocation
114
+ if composer.rerun_possible?
115
+ composer.trigger!
116
+ @job_invocation = composer.job_invocation
117
+ process_response @job_invocation
118
+ else
119
+ render :json => { :error => _('Could not rerun job %{id} because its template could not be found') % { :id => composer.reruns } },
120
+ :status => 404
121
+ end
119
122
  end
120
123
 
121
124
  private
@@ -163,6 +166,13 @@ module Api
163
166
  job_invocation_params[:inputs].to_hash
164
167
  )
165
168
  end
169
+
170
+ def output_lines_since(task, time)
171
+ since = time.to_f if time.present?
172
+ line_sets = task.main_action.live_output
173
+ line_sets = line_sets.drop_while { |o| o['timestamp'].to_f <= since } if since
174
+ line_sets
175
+ end
166
176
  end
167
177
  end
168
178
  end
@@ -19,10 +19,15 @@ class JobTemplatesController < ::TemplatesController
19
19
  host = params[:preview_host_id].present? ? base.find(params[:preview_host_id]) : base.first
20
20
  @template.template = params[:template]
21
21
  renderer = InputTemplateRenderer.new(@template, host)
22
- if (output = renderer.preview)
22
+ output = renderer.preview
23
+ if output
23
24
  render :plain => output
24
25
  else
25
- render :status => 406, :text => _('Problem with previewing the template: %{error}. Note that you must save template input changes before you try to preview it.' % {:error => renderer.error_message})
26
+ render status: :not_acceptable,
27
+ plain: _(
28
+ 'Problem with previewing the template: %{error}. Note that you must save template input changes before you try to preview it.' %
29
+ {:error => renderer.error_message}
30
+ )
26
31
  end
27
32
  end
28
33
 
@@ -161,7 +161,7 @@ module RemoteExecutionHelper
161
161
 
162
162
  def preview_box(template_invocation, target)
163
163
  renderer = InputTemplateRenderer.new(template_invocation.template, target, template_invocation)
164
- if (preview = renderer.preview)
164
+ if (preview = (load_template_from_task(template_invocation, target) || renderer.preview))
165
165
  content_tag :pre, preview
166
166
  elsif target.nil?
167
167
  alert :text => _('Could not render the preview because no host matches the search query.'),
@@ -234,4 +234,10 @@ module RemoteExecutionHelper
234
234
  end
235
235
  end
236
236
  end
237
+
238
+ def load_template_from_task(template_invocation, target)
239
+ task = template_invocation.job_invocation.sub_task_for_host(target)
240
+ return if task.nil?
241
+ task.execution_plan.actions[1].try(:input) { |input| input['script'] }
242
+ end
237
243
  end
@@ -58,7 +58,7 @@ class InputTemplateRenderer
58
58
  def render_template(template_name, input_values = {}, options = {})
59
59
  options.assert_valid_keys(:with_foreign_input_set)
60
60
  with_foreign_input_set = options.fetch(:with_foreign_input_set, true)
61
- template = @template.class.authorized("view_#{@template.class.to_s.underscore}").find_by(:name => template_name)
61
+ template = @template.class.authorized("view_#{@template.class.to_s.underscore.pluralize}").find_by(:name => template_name)
62
62
  unless template
63
63
  self.error_message = _('included template \'%s\' not found') % template_name
64
64
  raise error_message
@@ -186,7 +186,8 @@ class JobInvocationComposer
186
186
  :concurrency_control => concurrency_control_params,
187
187
  :execution_timeout_interval => job_invocation.execution_timeout_interval,
188
188
  :remote_execution_feature_id => job_invocation.remote_execution_feature_id,
189
- :template_invocations => template_invocations_params }.with_indifferent_access
189
+ :template_invocations => template_invocations_params,
190
+ :reruns => job_invocation.id }.with_indifferent_access
190
191
  end
191
192
 
192
193
  private
@@ -297,6 +298,7 @@ class JobInvocationComposer
297
298
  end
298
299
 
299
300
  attr_accessor :params, :job_invocation, :host_ids, :search_query
301
+ attr_reader :reruns
300
302
  delegate :job_category, :remote_execution_feature_id, :pattern_template_invocations, :template_invocations, :targeting, :triggering, :to => :job_invocation
301
303
 
302
304
  def initialize(params, set_defaults = false)
@@ -304,6 +306,7 @@ class JobInvocationComposer
304
306
  @set_defaults = set_defaults
305
307
  @job_invocation = JobInvocation.new
306
308
  @job_invocation.task_group = JobInvocationTaskGroup.new
309
+ @reruns = params[:reruns]
307
310
  compose
308
311
 
309
312
  @host_ids = validate_host_ids(params[:host_ids])
@@ -342,6 +345,8 @@ class JobInvocationComposer
342
345
  job_invocation.key_passphrase = params[:key_passphrase]
343
346
  job_invocation.sudo_password = params[:sudo_password]
344
347
 
348
+ job_invocation.job_category = nil unless rerun_possible?
349
+
345
350
  self
346
351
  end
347
352
  # rubocop:enable Metrics/AbcSize
@@ -470,6 +475,10 @@ class JobInvocationComposer
470
475
  job_invocation.pattern_template_invocations.map(&:template_id)
471
476
  end
472
477
 
478
+ def rerun_possible?
479
+ !(reruns && job_invocation.pattern_template_invocations.empty?)
480
+ end
481
+
473
482
  private
474
483
 
475
484
  # builds input values for a given templates id based on params
@@ -1,12 +1,14 @@
1
1
  class JobTemplate < ::Template
2
2
  audited
3
3
  include ::Exportable
4
+ include ::TemplateTax
4
5
 
5
6
  class NonUniqueInputsError < Foreman::Exception
6
7
  end
7
8
 
8
9
  attr_exportable :job_category, :description_format, :template_inputs,
9
- :foreign_input_sets, :provider_type, :kind => ->(template) { template.class.name.underscore }
10
+ :foreign_input_sets, :provider_type,
11
+ { :kind => ->(template) { template.class.name.underscore } }.merge(taxonomy_exportable)
10
12
 
11
13
  include Authorizable
12
14
  extend FriendlyId
@@ -134,7 +136,7 @@ class JobTemplate < ::Template
134
136
 
135
137
  def generate_description_format
136
138
  if description_format.blank?
137
- generated_description = '%{job_category}'
139
+ generated_description = '%{template_name}'
138
140
  unless template_inputs_with_foreign.empty?
139
141
  inputs = template_inputs_with_foreign.map(&:name).map { |name| %{#{name}="%{#{name}}"} }.join(' ')
140
142
  generated_description << " with inputs #{inputs}"
@@ -6,7 +6,15 @@
6
6
 
7
7
  <%= form_for @composer.job_invocation, :html => {'data-refresh-url' => refresh_job_invocations_path, :id => 'job_invocation_form'} do |f| %>
8
8
 
9
- <%= selectable_f f, :job_category, @composer.available_job_categories, {}, :label => _('Job category') %>
9
+ <% unless @composer.rerun_possible? %>
10
+ <%= alert :text => _('Could not rerun job %{id} because its template could not be found') % { :id => @composer.reruns },
11
+ :class => 'alert alert-block alert-danger has-error',
12
+ :close => false %>
13
+ <% end %>
14
+
15
+ <%= selectable_f f, :job_category, @composer.available_job_categories,
16
+ { :selected => @composer.job_invocation.job_category, :include_blank => @composer.job_invocation.job_category.nil? },
17
+ :label => _('Job category') %>
10
18
  <%= f.hidden_field(:remote_execution_feature_id, :value => @composer.remote_execution_feature_id) %>
11
19
 
12
20
  <% selected_templates_per_provider = {} %>
@@ -116,5 +124,5 @@
116
124
 
117
125
  <%= render :partial => 'preview_hosts_modal' %>
118
126
 
119
- <%= submit_or_cancel f, false, :cancel_path => job_invocations_path %>
127
+ <%= submit_or_cancel f, false, :cancel_path => job_invocations_path, :disabled => !@composer.rerun_possible? %>
120
128
  <% end %>
@@ -1,5 +1,5 @@
1
1
  <div class="tab-pane" id="rex_proxies">
2
2
  <%= fields_for :subnet do |f| %>
3
- <%= multiple_selects f, :remote_execution_proxies, SmartProxy.authorized.with_features(*RemoteExecutionProvider.provider_names), @subnet.remote_execution_proxy_ids, {:label => _("Proxies"), :help_inline => _("Select as many remote execution proxies as applicable for this subnet. When multiple proxies with the same provider are added, actions will be load balanced among them.")} %>
3
+ <%= multiple_selects f, :remote_execution_proxies, SmartProxy.authorized.with_features(*RemoteExecutionProvider.provider_names).distinct, @subnet.remote_execution_proxy_ids, {:label => _("Proxies"), :help_inline => _("Select as many remote execution proxies as applicable for this subnet. When multiple proxies with the same provider are added, actions will be load balanced among them.")} %>
4
4
  <% end %>
5
5
  </div>
@@ -1,3 +1,3 @@
1
1
  module ForemanRemoteExecution
2
- VERSION = '1.5.4'.freeze
2
+ VERSION = '1.5.5'.freeze
3
3
  end
@@ -1,14 +1,12 @@
1
1
  FactoryBot.define do
2
-
3
- factory :job_template do |f|
4
- f.sequence(:name) { |n| "Job template #{n}" }
5
- sequence(:job_category) { |n| "job name #{n}" }
6
- f.template 'id'
7
- f.provider_type 'SSH'
2
+ factory :job_template do
3
+ sequence(:name) { |n| "Job template #{n}" }
4
+ sequence(:job_category) { |n| "Job name #{n}" }
5
+ template 'id'
6
+ provider_type 'SSH'
8
7
  organizations { [Organization.find_by(name: 'Organization 1')] } if SETTINGS[:organizations_enabled]
9
8
  locations { [Location.find_by(name: 'Location 1')] } if SETTINGS[:locations_enabled]
10
9
 
11
-
12
10
  trait :with_input do
13
11
  after(:build) do |template, evaluator|
14
12
  template.template_inputs << FactoryBot.build(:template_input)
@@ -6,6 +6,11 @@ module Api
6
6
  setup do
7
7
  @invocation = FactoryBot.create(:job_invocation, :with_template, :with_task)
8
8
  @template = FactoryBot.create(:job_template, :with_input)
9
+
10
+ # Without this the template in template_invocations and in pattern_template_invocations
11
+ # would belong to different job_categories, causing trouble when trying to rerun
12
+ @invocation.job_category = @invocation.pattern_template_invocations.first.template.job_category
13
+ @invocation.save!
9
14
  end
10
15
 
11
16
  test 'should get index' do
@@ -107,6 +112,16 @@ module Api
107
112
  assert_response :success
108
113
  end
109
114
 
115
+ test 'should provide empty output for host which does not have a task yet' do
116
+ host = @invocation.template_invocations_hosts.first
117
+ JobInvocation.any_instance.expects(:sub_task_for_host).returns(nil)
118
+ get :output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
119
+ result = ActiveSupport::JSON.decode(@response.body)
120
+ assert_equal result['refresh'], true
121
+ assert_equal result['output'], []
122
+ assert_response :success
123
+ end
124
+
110
125
  test 'should cancel a job' do
111
126
  @invocation.task.expects(:cancellable?).returns(true)
112
127
  @invocation.task.expects(:cancel).returns(true)
@@ -168,6 +183,8 @@ module Api
168
183
 
169
184
  test 'should rerun failed only' do
170
185
  @invocation = FactoryBot.create(:job_invocation, :with_template, :with_failed_task)
186
+ @invocation.job_category = @invocation.pattern_template_invocations.first.template.job_category
187
+ @invocation.save!
171
188
  JobInvocation.any_instance.expects(:generate_description)
172
189
  JobInvocationComposer.any_instance
173
190
  .expects(:validate_job_category)
@@ -181,6 +198,13 @@ module Api
181
198
  targeting.user_id.must_equal users(:admin).id
182
199
  targeting.search_query.must_equal "name ^ (#{hostnames.join(',')})"
183
200
  end
201
+
202
+ test 'should return 404 if template is not found' do
203
+ @invocation.job_category = 'Missing category'
204
+ @invocation.save!
205
+ post :rerun, params: { :id => @invocation.id }
206
+ assert_response 404
207
+ end
184
208
  end
185
209
  end
186
210
  end
@@ -92,6 +92,7 @@ module Api
92
92
 
93
93
  test 'should export template' do
94
94
  get :export, params: { :id => @template.to_param }
95
+ User.current = users(:admin)
95
96
  assert_equal @response.body, @template.to_erb
96
97
  assert_response :success
97
98
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ class JobTemplatesControllerTest < ActionController::TestCase
6
+ context '#preview' do
7
+ let(:template) { FactoryBot.create(:job_template) }
8
+ let(:host) { FactoryBot.create(:host, :managed) }
9
+
10
+ test 'should render a preview version of a template' do
11
+ post :preview, params: { job_template: template.to_param, template: 'uptime' }, session: set_session_user
12
+ assert_response :success
13
+ end
14
+
15
+ test 'should render a preview version of a template for a specific host' do
16
+ post :preview, params: {
17
+ job_template: template.to_param,
18
+ template: '<%= @host.name %>',
19
+ preview_host_id: host.id
20
+ }, session: set_session_user
21
+ assert_response :success
22
+ assert_equal host.name, @response.body
23
+ end
24
+
25
+ test 'should render a error message when template has errors' do
26
+ InputTemplateRenderer.any_instance.stubs(:render).returns(false)
27
+ post :preview, params: { job_template: template.to_param }, session: set_session_user
28
+ assert_response :not_acceptable
29
+ end
30
+ end
31
+ end
@@ -156,6 +156,24 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
156
156
  end
157
157
  end
158
158
 
159
+ describe '#rerun_possible?' do
160
+ it 'is true when not rerunning' do
161
+ composer.must_be :rerun_possible?
162
+ end
163
+
164
+ it 'is true when rerunning with pattern tempalte invocations' do
165
+ composer.expects(:reruns).returns(1)
166
+ composer.job_invocation.expects(:pattern_template_invocations).returns([1])
167
+ composer.must_be :rerun_possible?
168
+ end
169
+
170
+ it 'is false when rerunning without pattern template invocations' do
171
+ composer.expects(:reruns).returns(1)
172
+ composer.job_invocation.expects(:pattern_template_invocations).returns([])
173
+ composer.wont_be :rerun_possible?
174
+ end
175
+ end
176
+
159
177
  describe '#selected_job_templates' do
160
178
  it 'returns no template if none was selected through params' do
161
179
  composer.selected_job_templates.must_be_empty
@@ -44,14 +44,14 @@ class JobTemplateTest < ActiveSupport::TestCase
44
44
  end
45
45
 
46
46
  it 'uses the job name as description_format if not set or blank and has no inputs' do
47
- minimal_template.generate_description_format.must_equal '%{job_category}'
47
+ minimal_template.generate_description_format.must_equal '%{template_name}'
48
48
  minimal_template.description_format = ''
49
- minimal_template.generate_description_format.must_equal '%{job_category}'
49
+ minimal_template.generate_description_format.must_equal '%{template_name}'
50
50
  end
51
51
 
52
52
  it 'generates the description_format if not set or blank and has inputs' do
53
53
  input_name = template.template_inputs.first.name
54
- expected_result = %(%{job_category} with inputs #{input_name}="%{#{input_name}}")
54
+ expected_result = %(%{template_name} with inputs #{input_name}="%{#{input_name}}")
55
55
  template.generate_description_format.must_equal expected_result
56
56
  template.description_format = ''
57
57
  template.generate_description_format.must_equal expected_result
@@ -267,6 +267,11 @@ class JobTemplateTest < ActiveSupport::TestCase
267
267
  imported.name.must_equal old_name
268
268
  imported.template_inputs.first.to_export.must_equal exportable_template.template_inputs.first.to_export
269
269
  end
270
+
271
+ it 'has taxonomies in metadata' do
272
+ assert_equal 'Organization 1', exportable_template.to_export["organizations"].first
273
+ assert_equal 'Location 1', exportable_template.to_export["locations"].first
274
+ end
270
275
  end
271
276
 
272
277
  context 'there is existing template invocation of a job template' do
@@ -290,4 +295,20 @@ class JobTemplateTest < ActiveSupport::TestCase
290
295
  refute_valid job_template
291
296
  end
292
297
  end
298
+
299
+ context 'rendering' do
300
+ it 'renders nested template as a non-admin user' do
301
+ inner = FactoryBot.create(:job_template)
302
+ template_invocation = FactoryBot.create(:template_invocation)
303
+ template_invocation.template.template = "<wrap><%= render_template('#{inner.name}') %></wrap>"
304
+ template_invocation.template.save!
305
+
306
+ setup_user('view', 'job_templates')
307
+ renderer = InputTemplateRenderer.new template_invocation.template,
308
+ template_invocation.host,
309
+ template_invocation
310
+ result = renderer.render
311
+ result.must_equal "<wrap>#{inner.template}</wrap>"
312
+ end
313
+ end
293
314
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_remote_execution
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.4
4
+ version: 1.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Foreman Remote Execution team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-12 00:00:00.000000000 Z
11
+ date: 2018-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deface
@@ -368,6 +368,7 @@ files:
368
368
  - test/functional/api/v2/template_inputs_controller_test.rb
369
369
  - test/functional/api/v2/template_invocations_controller_test.rb
370
370
  - test/functional/job_invocations_controller_test.rb
371
+ - test/functional/job_templates_controller_test.rb
371
372
  - test/test_plugin_helper.rb
372
373
  - test/unit/actions/run_host_job_test.rb
373
374
  - test/unit/actions/run_hosts_job_test.rb
@@ -416,7 +417,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
416
417
  version: '0'
417
418
  requirements: []
418
419
  rubyforge_project:
419
- rubygems_version: 2.6.12
420
+ rubygems_version: 2.7.3
420
421
  signing_key:
421
422
  specification_version: 4
422
423
  summary: A plugin bringing remote execution to the Foreman, completing the config
@@ -432,6 +433,7 @@ test_files:
432
433
  - test/functional/api/v2/template_inputs_controller_test.rb
433
434
  - test/functional/api/v2/template_invocations_controller_test.rb
434
435
  - test/functional/job_invocations_controller_test.rb
436
+ - test/functional/job_templates_controller_test.rb
435
437
  - test/test_plugin_helper.rb
436
438
  - test/unit/actions/run_host_job_test.rb
437
439
  - test/unit/actions/run_hosts_job_test.rb