foreman_remote_execution 4.5.6 → 4.6.0

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