foreman_remote_execution 4.5.6 → 4.6.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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/job_invocations_controller.rb +1 -1
  3. data/app/controllers/ui_job_wizard_controller.rb +0 -7
  4. data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +1 -5
  5. data/app/helpers/remote_execution_helper.rb +3 -9
  6. data/app/lib/actions/remote_execution/run_host_job.rb +1 -5
  7. data/app/lib/actions/remote_execution/run_hosts_job.rb +1 -1
  8. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +0 -2
  9. data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +70 -0
  10. data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +0 -6
  11. data/app/models/host_status/execution_status.rb +3 -3
  12. data/app/models/job_invocation.rb +6 -9
  13. data/app/models/job_invocation_composer.rb +4 -4
  14. data/app/models/remote_execution_feature.rb +1 -5
  15. data/app/models/remote_execution_provider.rb +2 -3
  16. data/app/models/setting/remote_execution.rb +2 -2
  17. data/app/models/targeting.rb +1 -5
  18. data/app/views/job_invocations/index.html.erb +1 -1
  19. data/app/views/templates/ssh/module_action.erb +0 -1
  20. data/app/views/templates/ssh/power_action.erb +0 -2
  21. data/app/views/templates/ssh/puppet_run_once.erb +0 -1
  22. data/foreman_remote_execution.gemspec +0 -1
  23. data/lib/foreman_remote_execution/engine.rb +2 -0
  24. data/lib/foreman_remote_execution/version.rb +1 -1
  25. data/test/models/orchestration/ssh_test.rb +56 -0
  26. data/test/unit/job_invocation_composer_test.rb +1 -14
  27. data/test/unit/job_invocation_test.rb +1 -1
  28. data/test/unit/remote_execution_provider_test.rb +0 -12
  29. data/webpack/JobWizard/JobWizard.js +16 -59
  30. data/webpack/JobWizard/JobWizard.scss +0 -58
  31. data/webpack/JobWizard/JobWizardConstants.js +0 -18
  32. data/webpack/JobWizard/JobWizardSelectors.js +0 -9
  33. data/webpack/JobWizard/__tests__/JobWizard.test.js +13 -0
  34. data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +32 -0
  35. data/webpack/JobWizard/__tests__/fixtures.js +2 -112
  36. data/webpack/JobWizard/__tests__/integration.test.js +92 -16
  37. data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +9 -37
  38. data/webpack/JobWizard/steps/AdvancedFields/Fields.js +55 -116
  39. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +16 -150
  40. data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +249 -0
  41. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +2 -4
  42. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +51 -123
  43. data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +113 -0
  44. data/webpack/JobWizard/steps/form/FormHelpers.js +0 -1
  45. data/webpack/JobWizard/steps/form/SelectField.js +2 -14
  46. data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +38 -0
  47. data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +23 -0
  48. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +37 -0
  49. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +23 -0
  50. data/webpack/__mocks__/foremanReact/components/SearchBar.js +1 -18
  51. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +0 -1
  52. metadata +13 -35
  53. data/app/models/host_proxy_invocation.rb +0 -4
  54. data/db/migrate/2021051713291621250977_add_host_proxy_invocations.rb +0 -12
  55. data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +0 -67
  56. data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +0 -23
  57. data/webpack/JobWizard/steps/HostsAndInputs/SelectedChips.js +0 -25
  58. data/webpack/JobWizard/steps/HostsAndInputs/TemplateInputs.js +0 -23
  59. data/webpack/JobWizard/steps/HostsAndInputs/__tests__/SelectedChips.test.js +0 -37
  60. data/webpack/JobWizard/steps/HostsAndInputs/__tests__/TemplateInputs.test.js +0 -50
  61. data/webpack/JobWizard/steps/HostsAndInputs/index.js +0 -66
  62. data/webpack/JobWizard/steps/Schedule/QueryType.js +0 -48
  63. data/webpack/JobWizard/steps/Schedule/RepeatOn.js +0 -61
  64. data/webpack/JobWizard/steps/Schedule/ScheduleType.js +0 -25
  65. data/webpack/JobWizard/steps/Schedule/StartEndDates.js +0 -51
  66. data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +0 -22
  67. data/webpack/JobWizard/steps/Schedule/index.js +0 -44
  68. data/webpack/JobWizard/steps/form/Formatter.js +0 -150
  69. data/webpack/JobWizard/steps/form/NumberInput.js +0 -35
  70. data/webpack/JobWizard/steps/form/WizardTitle.js +0 -14
  71. data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +0 -76
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f14506c337ce696f794a63b0c30b59f6d301edc4d55b9d654e2b5360215beb9
4
- data.tar.gz: 0b9d86b4a422b84d507dfd977dea5dd9f2dc8b30e464b3e4383c8ea421e0615b
3
+ metadata.gz: 14dc89216ec220750d38149b86e0ecded26e13d99678505302a5a157b0c7c9e2
4
+ data.tar.gz: b2091af78be3ed0fec0d5acea056a49e601e6d1ef96954c49976fd1c0da39ecb
5
5
  SHA512:
6
- metadata.gz: 250ec14568078576a42664e40e2d96b974282765e6cfcffca8ec58bdff9e72c8948f2e28e7ba5fdafe36fb45457e2f38c03bd3c39842ba0bebaa303970ea5f09
7
- data.tar.gz: 4f6469e5ad8535b7996d1a8c78d266edbba4c03e1e3eb9b52e0588c653351e153b87b4d6daaf082e039d7af47207fdf5e3e6267920cde4d2a233c4e11ac5ca57
6
+ metadata.gz: 10d901fec16519db44d05d9b9ecb2b15923e1a2e439f1e0032778b9272ed4c48a446436b4ef7375bb5113f8067d5596035b5a7fa593a522fc37e56a472d51c31
7
+ data.tar.gz: cb7686bf72b55bf65bbd35cee82910925f12b9dd29ec573dc96bb340fb1cc629833a80239e96317dcef2163d17d00ba93db92f5f50a10c5a4f4363d77647f729
@@ -67,7 +67,7 @@ class JobInvocationsController < ApplicationController
67
67
  end
68
68
 
69
69
  def index
70
- @job_invocations = resource_base_search_and_page.preload(:task, :targeting).order('job_invocations.id DESC')
70
+ @job_invocations = resource_base_search_and_page.with_task.order('job_invocations.id DESC')
71
71
  end
72
72
 
73
73
  # refreshes the form
@@ -10,12 +10,9 @@ class UiJobWizardController < ::Api::V2::BaseController
10
10
 
11
11
  def template
12
12
  job_template = JobTemplate.authorized.find(params[:id])
13
- advanced_template_inputs, template_inputs = map_template_inputs(job_template.template_inputs_with_foreign).partition { |x| x["advanced"] }
14
13
  render :json => {
15
14
  :job_template => job_template,
16
15
  :effective_user => job_template.effective_user,
17
- :template_inputs => template_inputs,
18
- :advanced_template_inputs => advanced_template_inputs,
19
16
  }
20
17
  end
21
18
 
@@ -23,10 +20,6 @@ class UiJobWizardController < ::Api::V2::BaseController
23
20
  nested_resource || 'job_template'
24
21
  end
25
22
 
26
- def map_template_inputs(template_inputs_with_foreign)
27
- template_inputs_with_foreign.map { |input| input.attributes.merge({:resource_type => input.resource_type&.tableize }) }
28
- end
29
-
30
23
  def resource_class
31
24
  JobTemplate
32
25
  end
@@ -7,9 +7,7 @@ module ForemanRemoteExecution
7
7
  end
8
8
 
9
9
  def multiple_actions
10
- res = super
11
- res += [ [_('Schedule Remote Job'), new_job_invocation_path, false] ] if authorized_for(controller: :job_invocations, action: :new)
12
- res
10
+ super + [ [_('Schedule Remote Job'), new_job_invocation_path, false] ]
13
11
  end
14
12
 
15
13
  def schedule_job_multi_button(*args)
@@ -23,14 +21,12 @@ module ForemanRemoteExecution
23
21
  end
24
22
 
25
23
  def rex_host_features(*args)
26
- return unless authorized_for(controller: :job_invocations, action: :create)
27
24
  RemoteExecutionFeature.with_host_action_button.order(:label).map do |feature|
28
25
  link_to(_('%s') % feature.name, job_invocations_path(:host_ids => [args.first.id], :feature => feature.label), :method => :post)
29
26
  end
30
27
  end
31
28
 
32
29
  def schedule_job_button(*args)
33
- return unless authorized_for(controller: :job_invocations, action: :new)
34
30
  link_to(_('Schedule Remote Job'), new_job_invocation_path(:host_ids => [args.first.id]), :id => :run_button, :class => 'btn btn-default')
35
31
  end
36
32
 
@@ -7,10 +7,6 @@ module RemoteExecutionHelper
7
7
  @job_hosts_authorizer ||= Authorizer.new(User.current, :collection => @hosts)
8
8
  end
9
9
 
10
- def host_tasks_authorizer
11
- @host_tasks_authorizer ||= Authorizer.new(User.current, :collection => @job_invocation.sub_tasks)
12
- end
13
-
14
10
  def host_counter(label, count)
15
11
  content_tag(:div, :class => 'host_counter') do
16
12
  content_tag(:div, label, :class => 'header') + content_tag(:div, count.to_s, :class => 'count')
@@ -40,7 +36,7 @@ module RemoteExecutionHelper
40
36
  'data-method': 'get', id: "#{host.name}-actions-rerun" } }
41
37
  end
42
38
 
43
- if host_task.present? && authorized_for(hash_for_foreman_tasks_task_path(host_task).merge(auth_object: host_task, permission: :view_foreman_tasks, authorizer: host_tasks_authorizer))
39
+ if host_task.present? && authorized_for(hash_for_foreman_tasks_task_path(host_task).merge(auth_object: host_task, permission: :view_foreman_tasks))
44
40
  links << { title: _('Host task'),
45
41
  action: { href: foreman_tasks_task_path(host_task),
46
42
  'data-method': 'get', id: "#{host.name}-actions-task" } }
@@ -113,14 +109,12 @@ module RemoteExecutionHelper
113
109
  :class => 'btn btn-danger',
114
110
  :title => _('Try to cancel the job on a host'),
115
111
  :disabled => !task.cancellable?,
116
- :method => :post,
117
- :remote => true)
112
+ :method => :post)
118
113
  buttons << link_to(_('Abort Job'), abort_foreman_tasks_task_path(task),
119
114
  :class => 'btn btn-danger',
120
115
  :title => _('Try to abort the job on a host without waiting for its result'),
121
116
  :disabled => !task.cancellable?,
122
- :method => :post,
123
- :remote => true)
117
+ :method => :post)
124
118
  end
125
119
  buttons
126
120
  end
@@ -47,16 +47,12 @@ module Actions
47
47
  script = renderer.render
48
48
  raise _('Failed rendering template: %s') % renderer.error_message unless script
49
49
 
50
- first_execution = host.executed_through_proxies.where(:id => proxy.id).none?
51
- host.executed_through_proxies << proxy if first_execution
52
-
53
50
  additional_options = { :hostname => provider.find_ip_or_hostname(host),
54
51
  :script => script,
55
52
  :execution_timeout_interval => job_invocation.execution_timeout_interval,
56
53
  :secrets => secrets(host, job_invocation, provider),
57
54
  :use_batch_triggering => true,
58
- :use_concurrency_control => options[:use_concurrency_control],
59
- :first_execution => first_execution }
55
+ :use_concurrency_control => options[:use_concurrency_control]}
60
56
  action_options = provider.proxy_command_options(template_invocation, host)
61
57
  .merge(additional_options)
62
58
 
@@ -72,7 +72,7 @@ module Actions
72
72
 
73
73
  def total_count
74
74
  # For compatibility with already existing tasks
75
- return output[:total_count] || hosts.count unless output.has_key?(:host_count) || task.pending?
75
+ return output[:total_count] unless output.has_key?(:host_count) || task.pending?
76
76
 
77
77
  output[:host_count] || hosts.count
78
78
  end
@@ -6,8 +6,6 @@ module ForemanRemoteExecution
6
6
  has_many :template_invocations, :dependent => :destroy, :foreign_key => 'host_id'
7
7
  has_one :execution_status_object, :class_name => 'HostStatus::ExecutionStatus', :foreign_key => 'host_id', :dependent => :destroy
8
8
  has_many :run_host_job_tasks, :through => :template_invocations
9
- has_many :host_proxy_invocations, :foreign_key => 'host_id', :dependent => :destroy
10
- has_many :executed_through_proxies, :through => :host_proxy_invocations, :source => 'smart_proxy'
11
9
 
12
10
  scoped_search :relation => :run_host_job_tasks, :on => :result, :rename => 'job_invocation.result',
13
11
  :ext_method => :search_by_job_invocation,
@@ -0,0 +1,70 @@
1
+ module ForemanRemoteExecution
2
+ module Orchestration::SSH
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_destroy :ssh_destroy
7
+ after_validation :queue_ssh_destroy
8
+ register_rebuild(:queue_ssh_destroy, N_("SSH_#{self.to_s.split('::').first}"))
9
+ end
10
+
11
+ def drop_from_known_hosts(proxy_id)
12
+ _, _, target = host_kind_target
13
+ return true if target.nil?
14
+
15
+ proxy = ::SmartProxy.find(proxy_id)
16
+ begin
17
+ proxy.drop_host_from_known_hosts(target)
18
+ rescue ::ProxyAPI::ProxyException => e
19
+ if e.wrapped_exception.is_a?(RestClient::NotFound)
20
+ # ignore 404 when known_hosts entry is missing or the module was not enabled
21
+ Foreman::Logging.exception "Proxy failed to delete SSH known_hosts for #{name}, #{ip}", e, :level => :error
22
+ else
23
+ raise e
24
+ end
25
+ rescue => e
26
+ Rails.logger.warn e.message
27
+ return false
28
+ end
29
+ true
30
+ end
31
+
32
+ def ssh_destroy
33
+ logger.debug "Scheduling SSH known_hosts cleanup"
34
+
35
+ host, _kind, _target = host_kind_target
36
+ # #remote_execution_proxies may not be defined on the host object in some case
37
+ # for example Host::Discovered does not have it defined, even though these hosts
38
+ # have Nic::Managed interfaces associated with them
39
+ proxies = (host.try(:remote_execution_proxies, 'SSH') || {}).values
40
+ proxies.flatten.uniq.each do |proxy|
41
+ queue.create(id: queue_id(proxy.id), name: _("Remove SSH known hosts for %s") % self,
42
+ priority: 200, action: [self, :drop_from_known_hosts, proxy.id])
43
+ end
44
+ end
45
+
46
+ def queue_ssh_destroy
47
+ should_drop_from_known_hosts? && ssh_destroy
48
+ end
49
+
50
+ def should_drop_from_known_hosts?
51
+ host, = host_kind_target
52
+ host && !host.new_record? && host.build && host.changes.key?('build')
53
+ end
54
+
55
+ private
56
+
57
+ def host_kind_target
58
+ if self.is_a?(::Host::Base)
59
+ [self, 'host', name]
60
+ else
61
+ [self.host, 'interface', ip]
62
+ end
63
+ end
64
+
65
+ def queue_id(proxy_id)
66
+ _, kind, id = host_kind_target
67
+ "ssh_remove_known_hosts_#{kind}_#{id}_#{proxy_id}"
68
+ end
69
+ end
70
+ end
@@ -1,11 +1,5 @@
1
1
  module ForemanRemoteExecution
2
2
  module SmartProxyExtensions
3
- def self.prepended(base)
4
- base.instance_eval do
5
- has_many :host_proxy_invocations, :dependent => :destroy
6
- end
7
- end
8
-
9
3
  def pubkey
10
4
  self[:pubkey] || update_pubkey
11
5
  end
@@ -64,11 +64,11 @@ class HostStatus::ExecutionStatus < HostStatus::Status
64
64
 
65
65
  case status
66
66
  when OK
67
- [ "foreman_tasks_tasks.state = 'stopped' AND foreman_tasks_tasks.result = 'success'" ]
67
+ [ "foreman_tasks_tasks.state = 'stopped' AND result = 'success'" ]
68
68
  when CANCELLED
69
- [ "foreman_tasks_tasks.state = 'stopped' AND foreman_tasks_tasks.result = 'cancelled'" ]
69
+ [ "foreman_tasks_tasks.state = 'stopped' AND result = 'cancelled'" ]
70
70
  when ERROR
71
- [ "foreman_tasks_tasks.state = 'stopped' AND (foreman_tasks_tasks.result = 'error' OR foreman_tasks_tasks.result = 'warning')" ]
71
+ [ "foreman_tasks_tasks.state = 'stopped' AND (result = 'error' OR result = 'warning')" ]
72
72
  when QUEUED
73
73
  [ "foreman_tasks_tasks.state = 'scheduled' OR foreman_tasks_tasks.state IS NULL" ]
74
74
  when RUNNING
@@ -65,6 +65,8 @@ class JobInvocation < ApplicationRecord
65
65
  scoped_search :on => 'pattern_template_name', :rename => 'pattern_template_name', :operators => ['= '],
66
66
  :complete_value => false, :only_explicit => true, :ext_method => :search_by_pattern_template
67
67
 
68
+ scope :with_task, -> { references(:task) }
69
+
68
70
  scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
69
71
 
70
72
  scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring',
@@ -97,7 +99,7 @@ class JobInvocation < ApplicationRecord
97
99
  def self.search_by_status(key, operator, value)
98
100
  conditions = HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.sql_conditions_for(value)
99
101
  conditions[0] = "NOT (#{conditions[0]})" if operator == '<>'
100
- { :conditions => sanitize_sql_for_conditions(conditions), :joins => :task }
102
+ { :conditions => sanitize_sql_for_conditions(conditions), :include => :task }
101
103
  end
102
104
 
103
105
  def self.search_by_recurring_logic(key, operator, value)
@@ -191,16 +193,11 @@ class JobInvocation < ApplicationRecord
191
193
  end
192
194
 
193
195
  def total_hosts_count
194
- count = _('N/A')
195
-
196
196
  if targeting.resolved?
197
- count = if task&.main_action.respond_to?(:total_count)
198
- task.main_action.total_count
199
- else
200
- targeting.hosts.count
201
- end
197
+ task&.main_action&.total_count || targeting.hosts.count
198
+ else
199
+ _('N/A')
202
200
  end
203
- count
204
201
  end
205
202
 
206
203
  def pattern_template_invocation_for_host(host)
@@ -209,7 +209,7 @@ class JobInvocationComposer
209
209
  def format_datetime(datetime)
210
210
  return datetime if datetime.blank?
211
211
 
212
- Time.parse(datetime).utc.strftime('%Y-%m-%d %H:%M')
212
+ Time.parse(datetime).strftime('%Y-%m-%d %H:%M')
213
213
  end
214
214
  end
215
215
 
@@ -296,7 +296,7 @@ class JobInvocationComposer
296
296
  attr_reader :feature_label, :feature, :provided_inputs
297
297
 
298
298
  def initialize(feature_label, hosts, provided_inputs = {})
299
- @feature = RemoteExecutionFeature.feature!(feature_label)
299
+ @feature = RemoteExecutionFeature.feature(feature_label)
300
300
  @provided_inputs = provided_inputs
301
301
  translator = HostIdsTranslator.new(hosts)
302
302
  @host_bookmark = translator.bookmark
@@ -405,7 +405,7 @@ class JobInvocationComposer
405
405
  job_invocation.effective_user_password = params[:effective_user_password]
406
406
 
407
407
  if @reruns && job_invocation.targeting.static?
408
- job_invocation.targeting.assign_host_ids(JobInvocation.find(@reruns).targeting.host_ids)
408
+ job_invocation.targeting.host_ids = JobInvocation.find(@reruns).targeting.host_ids
409
409
  job_invocation.targeting.mark_resolved!
410
410
  end
411
411
 
@@ -636,7 +636,7 @@ class JobInvocationComposer
636
636
  setting_value = Setting['remote_execution_form_job_template']
637
637
  return default_value unless setting_value
638
638
 
639
- form_template = JobTemplate.authorized(:view_job_templates).find_by :name => setting_value
639
+ form_template = JobTemplate.find_by :name => setting_value
640
640
  return default_value unless form_template
641
641
 
642
642
  if block_given?
@@ -20,11 +20,7 @@ class RemoteExecutionFeature < ApplicationRecord
20
20
  end
21
21
 
22
22
  def self.feature(label)
23
- self.find_by(label: label)
24
- end
25
-
26
- def self.feature!(label)
27
- feature(label) || raise(::Foreman::Exception.new(N_('Unknown remote execution feature %s'), label))
23
+ self.find_by(label: label) || raise(::Foreman::Exception.new(N_('Unknown remote execution feature %s'), label))
28
24
  end
29
25
 
30
26
  def self.register(label, name, options = {})
@@ -89,8 +89,7 @@ class RemoteExecutionProvider
89
89
  end
90
90
 
91
91
  def host_setting(host, setting)
92
- param_value = host.host_param(setting.to_s)
93
- param_value.nil? ? Setting[setting] : param_value
92
+ host.host_param(setting.to_s) || Setting[setting]
94
93
  end
95
94
 
96
95
  def ssh_password(_host)
@@ -112,7 +111,7 @@ class RemoteExecutionProvider
112
111
  end
113
112
 
114
113
  def proxy_action_class
115
- 'ForemanRemoteExecutionCore::Actions::RunScript'
114
+ 'Proxy::RemoteExecution::Ssh::Actions::RunScript'
116
115
  end
117
116
 
118
117
  # Return a specific proxy selector to use for running a given template
@@ -70,13 +70,13 @@ class Setting::RemoteExecution < Setting
70
70
  self.set('remote_execution_form_job_template',
71
71
  N_('Choose a job template that is pre-selected in job invocation form'),
72
72
  'Run Command - SSH Default',
73
- N_('Form Job Template'),
73
+ _('Form Job Template'),
74
74
  nil,
75
75
  { :collection => proc { Hash[JobTemplate.unscoped.map { |template| [template.name, template.name] }] } }),
76
76
  self.set('remote_execution_job_invocation_report_template',
77
77
  N_('Select a report template used for generating a report for a particular remote execution job'),
78
78
  'Jobs - Invocation report template',
79
- N_('Job Invocation Report Template'),
79
+ _('Job Invocation Report Template'),
80
80
  nil,
81
81
  { :collection => proc { self.job_invocation_report_templates_select } }),
82
82
  ]
@@ -46,13 +46,9 @@ class Targeting < ApplicationRecord
46
46
  # pluck(:id) returns duplicate results for HostCollections
47
47
  host_ids = User.as(user.login) { Host.authorized(RESOLVE_PERMISSION, Host).search_for(search_query).order(:name, :id).pluck(:id).uniq }
48
48
  host_ids.shuffle!(random: Random.new) if randomized_ordering
49
- self.assign_host_ids(host_ids)
50
- self.save(:validate => false)
51
- end
52
-
53
- def assign_host_ids(host_ids)
54
49
  # this can be optimized even more, by introducing bulk insert
55
50
  self.targeting_hosts.build(host_ids.map { |id| { :host_id => id } })
51
+ self.save(:validate => false)
56
52
  end
57
53
 
58
54
  def dynamic?
@@ -21,7 +21,7 @@
21
21
  <% @job_invocations.each do |invocation| %>
22
22
  <tr>
23
23
  <td class="text_warp"><%= link_to_if_authorized invocation_description(invocation), hash_for_job_invocation_path(invocation).merge(:auth_object => invocation, :permission => :view_job_invocations, :authorizer => authorizer) %></td>
24
- <td><%= trunc_with_tooltip(invocation.targeting.search_query, 15) %></td>
24
+ <td><%= trunc_with_tooltip(invocation&.targeting&.search_query, 15) %></td>
25
25
  <td><%= link_to_invocation_task_if_authorized(invocation) %></td>
26
26
  <td><%= invocation_result(invocation, :success_count) %></td>
27
27
  <td><%= invocation_result(invocation, :failed_count) %></td>
@@ -29,7 +29,6 @@ template_inputs:
29
29
  input_type: user
30
30
  required: false
31
31
  advanced: true
32
- feature: katello_module_stream_action
33
32
  %>
34
33
 
35
34
  <%
@@ -13,8 +13,6 @@ template_inputs:
13
13
  required: true
14
14
  %>
15
15
 
16
- PATH="$PATH:/usr/sbin:/sbin"
17
-
18
16
  echo <%= input('action') %> host && sleep 3
19
17
  <%= case input('action')
20
18
  when 'restart'
@@ -10,7 +10,6 @@ template_inputs:
10
10
  description: Additional options to pass to puppet
11
11
  input_type: user
12
12
  required: false
13
- feature: puppet_run_host
14
13
  %>
15
14
  <% if @host.operatingsystem.family == 'Debian' -%>
16
15
  export PATH=/opt/puppetlabs/bin:$PATH
@@ -25,7 +25,6 @@ Gem::Specification.new do |s|
25
25
 
26
26
  s.add_dependency 'deface'
27
27
  s.add_dependency 'dynflow', '>= 1.0.2', '< 2.0.0'
28
- s.add_dependency 'foreman_remote_execution_core'
29
28
  s.add_dependency 'foreman-tasks', '>= 4.1.0'
30
29
 
31
30
  s.add_development_dependency 'factory_bot_rails', '~> 4.8.0'
@@ -201,10 +201,12 @@ module ForemanRemoteExecution
201
201
 
202
202
  Host::Managed.prepend ForemanRemoteExecution::HostExtensions
203
203
  Host::Managed.include ForemanTasks::Concerns::HostActionSubject
204
+ Host::Managed.include ForemanRemoteExecution::Orchestration::SSH
204
205
 
205
206
  (Nic::Base.descendants + [Nic::Base]).each do |klass|
206
207
  klass.send(:include, ForemanRemoteExecution::NicExtensions)
207
208
  end
209
+ Nic::Managed.include ForemanRemoteExecution::Orchestration::SSH
208
210
 
209
211
  Bookmark.include ForemanRemoteExecution::BookmarkExtensions
210
212
  HostsHelper.prepend ForemanRemoteExecution::HostsHelperExtensions
@@ -1,3 +1,3 @@
1
1
  module ForemanRemoteExecution
2
- VERSION = '4.5.6'.freeze
2
+ VERSION = '4.6.0'.freeze
3
3
  end
@@ -0,0 +1,56 @@
1
+ require 'test_plugin_helper'
2
+
3
+ class SSHOrchestrationTest < ActiveSupport::TestCase
4
+ let(:host) { FactoryBot.create(:host, :managed, :with_subnet) }
5
+ let(:proxy) { FactoryBot.create(:smart_proxy, :ssh) }
6
+ let(:interface) { host.interfaces.first }
7
+
8
+ before { interface.subnet.remote_execution_proxies = [proxy] }
9
+
10
+ it 'attempts to drop IP address and hostname from smart proxies on destroy' do
11
+ host.stubs(:skip_orchestration?).returns false
12
+ SmartProxy.any_instance.expects(:drop_host_from_known_hosts).with(interface.ip)
13
+ SmartProxy.any_instance.expects(:drop_host_from_known_hosts).with(host.name)
14
+ host.destroy
15
+ end
16
+
17
+ it 'attempts to drop IP address and hostname from smart proxies on rebuild' do
18
+ host.stubs(:skip_orchestration?).returns false
19
+ SmartProxy.any_instance.expects(:drop_host_from_known_hosts).with(interface.ip)
20
+ SmartProxy.any_instance.expects(:drop_host_from_known_hosts).with(host.name)
21
+
22
+ host.build = true
23
+ host.save!
24
+
25
+ ids = ["ssh_remove_known_hosts_interface_#{interface.ip}_#{proxy.id}",
26
+ "ssh_remove_known_hosts_host_#{host.name}_#{proxy.id}"]
27
+ _(host.queue.task_ids).must_equal ids
28
+ _(host.queue.items.map(&:status)).must_equal %w(completed completed)
29
+ end
30
+
31
+ it 'does not fail on 404 from the smart proxy' do
32
+ host.stubs(:skip_orchestration?).returns false
33
+ ::ProxyAPI::RemoteExecutionSSH.any_instance.expects(:delete).raises(RestClient::ResourceNotFound).twice
34
+ host.build = true
35
+ host.save!
36
+ ids = ["ssh_remove_known_hosts_interface_#{interface.ip}_#{proxy.id}",
37
+ "ssh_remove_known_hosts_host_#{host.name}_#{proxy.id}"]
38
+ _(host.queue.task_ids).must_equal ids
39
+ _(host.queue.items.map(&:status)).must_equal %w(completed completed)
40
+ end
41
+
42
+ it 'does not trigger the removal when creating a new host' do
43
+ SmartProxy.any_instance.expects(:drop_host_from_known_hosts).never
44
+ host = Host::Managed.new(:name => 'test', :ip => '127.0.0.1')
45
+ host.stubs(:skip_orchestration?).returns false
46
+ _(host.queue.task_ids).must_equal []
47
+ end
48
+
49
+ it 'does not call to the proxy when target is nil' do
50
+ host.stubs(:skip_orchestration?).returns false
51
+ SmartProxy.any_instance.expects(:drop_host_from_known_hosts).with(host.name)
52
+ host.interfaces.first.stubs(:ip)
53
+ host.destroy
54
+ _(host.queue.items.map(&:status)).must_equal %w(completed completed)
55
+ end
56
+ end
@@ -864,9 +864,7 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
864
864
  it 'marks targeting as resolved if static' do
865
865
  created = JobInvocationComposer.from_job_invocation(job_invocation).job_invocation
866
866
  assert created.targeting.resolved?
867
- created.targeting.save
868
- created.targeting.reload
869
- assert_equal job_invocation.template_invocations_host_ids, created.targeting.targeting_hosts.pluck(:host_id)
867
+ assert_equal job_invocation.template_invocations_host_ids, created.targeting.host_ids
870
868
  end
871
869
 
872
870
  it 'takes randomized_ordering from the original job invocation when rerunning failed' do
@@ -876,17 +874,6 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
876
874
  composer = JobInvocationComposer.from_job_invocation(job_invocation, :host_ids => host_ids)
877
875
  assert composer.job_invocation.targeting.randomized_ordering
878
876
  end
879
-
880
- it 'works with invalid hosts' do
881
- host = job_invocation.targeting.hosts.first
882
- ::Host::Managed.any_instance.stubs(:valid?).returns(false)
883
- composer = JobInvocationComposer.from_job_invocation(job_invocation, {})
884
- targeting = composer.compose.job_invocation.targeting
885
- targeting.save!
886
- targeting.reload
887
- assert targeting.valid?
888
- assert_equal targeting.hosts.pluck(:id), [host.id]
889
- end
890
877
  end
891
878
 
892
879
  describe '.for_feature' do
@@ -10,7 +10,7 @@ class JobInvocationTest < ActiveSupport::TestCase
10
10
  end
11
11
 
12
12
  it 'is able to perform search through job invocations' do
13
- found_jobs = JobInvocation.search_for(%{job_category = "#{job_invocation.job_category}"}).paginate(:page => 1).order('job_invocations.id DESC')
13
+ found_jobs = JobInvocation.search_for(%{job_category = "#{job_invocation.job_category}"}).paginate(:page => 1).with_task.order('job_invocations.id DESC')
14
14
  _(found_jobs).must_equal [job_invocation]
15
15
  end
16
16
 
@@ -50,18 +50,6 @@ class RemoteExecutionProviderTest < ActiveSupport::TestCase
50
50
  end
51
51
  end
52
52
 
53
- describe '.host_setting' do
54
- let(:host) { FactoryBot.create(:host) }
55
-
56
- it 'honors falsey values set as a host parameter' do
57
- key = 'remote_execution_connect_by_ip'
58
- Setting[key] = true
59
- host.parameters << HostParameter.new(name: key, value: false)
60
-
61
- refute RemoteExecutionProvider.host_setting(host, key)
62
- end
63
- end
64
-
65
53
  describe SSHExecutionProvider do
66
54
  before { User.current = FactoryBot.build(:user, :admin) }
67
55
  after { User.current = nil }