foreman_remote_execution 1.3.3 → 1.3.4

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -4
  3. data/app/controllers/api/v2/foreign_input_sets_controller.rb +2 -2
  4. data/app/controllers/api/v2/job_invocations_controller.rb +5 -5
  5. data/app/controllers/api/v2/job_templates_controller.rb +3 -3
  6. data/app/controllers/api/v2/remote_execution_features_controller.rb +1 -1
  7. data/app/controllers/api/v2/template_inputs_controller.rb +3 -3
  8. data/app/controllers/job_templates_controller.rb +1 -1
  9. data/app/controllers/remote_execution_features_controller.rb +1 -1
  10. data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +4 -11
  11. data/app/helpers/concerns/foreman_remote_execution/job_templates_extensions.rb +2 -8
  12. data/app/helpers/remote_execution_helper.rb +10 -0
  13. data/app/lib/actions/remote_execution/run_host_job.rb +11 -2
  14. data/app/lib/actions/remote_execution/run_hosts_job.rb +8 -3
  15. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +40 -46
  16. data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +2 -8
  17. data/app/models/concerns/foreman_remote_execution/taxonomy_extensions.rb +5 -4
  18. data/app/models/job_invocation.rb +13 -8
  19. data/app/models/job_invocation_composer.rb +1 -1
  20. data/app/models/template_invocation.rb +4 -4
  21. data/app/views/job_invocations/_tab_overview.html.erb +1 -1
  22. data/db/migrate/20150616080015_create_template_input.rb +1 -1
  23. data/db/migrate/20150708133241_add_targeting.rb +1 -1
  24. data/foreman_remote_execution.gemspec +2 -2
  25. data/lib/foreman_remote_execution/engine.rb +7 -5
  26. data/lib/foreman_remote_execution/version.rb +1 -1
  27. data/test/unit/actions/run_hosts_job_test.rb +22 -17
  28. data/test/unit/job_invocation_composer_test.rb +11 -6
  29. data/test/unit/job_invocation_test.rb +13 -0
  30. metadata +7 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bb2c9a95f22cf5c9002035f47adfc90eee494da5
4
- data.tar.gz: 75e9a15b54a3832e112c5b8d2706a3a353f48e86
3
+ metadata.gz: 0511db7a2c04a4ddd08d45ebfe0d21e03bd8a919
4
+ data.tar.gz: 121d18818212309d66f9c20f347c6436b4773c0a
5
5
  SHA512:
6
- metadata.gz: af011f877cc95f1bf64cb3dd5c8633de7d0fd2aa6a445d15925c78950a50e636acdd298660b0c6970d2c4f17b53a492a51282004011a0fe5ac030ebed57bae3d
7
- data.tar.gz: 4d8e436256f46a252dc95987de9e1749c41852baf2e33f837dc6465bcf802d84781ac6e5eac307b2717d2bbbd18b594cac8a9fecc1a0a6adb741edf7dfd1965f
6
+ metadata.gz: 947a9c319b0cfe8082cd6fc69da05a5d4ffae40b2e5501f5c5aaf8ab2bc4f9b2b354473d4bff53b7599fffeb182395ed4af5b2d39b2934866bccae9b5edc8f79
7
+ data.tar.gz: 3168906a4cc1bebf31465ef7b0a786ab093cfa29ef2f1fa6902564c1cfc273856663a6bcbacf3974317abb92f5bbfe574b95fb7d02a2a75f8050c0472c76ccae
data/.rubocop.yml CHANGED
@@ -18,10 +18,6 @@ Rails/HttpPositionalArguments:
18
18
  Style/Documentation:
19
19
  Enabled: false
20
20
 
21
- # Force before_filter until upgrade to Rails 4
22
- Rails/ActionFilter:
23
- EnforcedStyle: filter
24
-
25
21
  Metrics/MethodLength:
26
22
  Max: 40
27
23
 
@@ -5,8 +5,8 @@ module Api
5
5
  include ::Foreman::Renderer
6
6
  include ::Foreman::Controller::Parameters::ForeignInputSet
7
7
 
8
- before_filter :find_required_nested_object
9
- before_filter :find_resource, :only => %w{show update destroy}
8
+ before_action :find_required_nested_object
9
+ before_action :find_resource, :only => %w{show update destroy}
10
10
 
11
11
  api :GET, '/templates/:template_id/foreign_input_sets', N_('List foreign input sets')
12
12
  param :template_id, :identifier, :required => true
@@ -5,10 +5,10 @@ module Api
5
5
  include ::Api::TaxonomyScope
6
6
  include ::Foreman::Renderer
7
7
 
8
- before_filter :find_optional_nested_object
9
- before_filter :find_host, :only => %w{output}
10
- before_filter :find_resource, :only => %w{show update destroy clone}
11
- before_filter :validate_template, :only => :create
8
+ before_action :find_optional_nested_object
9
+ before_action :find_host, :only => %w{output}
10
+ before_action :find_resource, :only => %w{show update destroy clone}
11
+ before_action :validate_template, :only => :create
12
12
 
13
13
  wrap_parameters JobInvocation, :include => (JobInvocation.attribute_names + [:ssh])
14
14
 
@@ -97,7 +97,7 @@ module Api
97
97
  end
98
98
 
99
99
  def find_host
100
- @host = Host::Base.authorized(:view_hosts).find(params['host_id'])
100
+ @host = Host.authorized(:view_hosts).find(params['host_id'])
101
101
  rescue ActiveRecord::RecordNotFound
102
102
  not_found({ :error => { :message => (_("Host with id '%{id}' was not found") % { :id => params['host_id'] }) } })
103
103
  end
@@ -7,10 +7,10 @@ module Api
7
7
  include ::Foreman::Controller::ProvisioningTemplates
8
8
  include ::Foreman::Controller::Parameters::JobTemplate
9
9
 
10
- before_filter :find_optional_nested_object
11
- before_filter :find_resource, :only => %w{show update destroy clone export}
10
+ before_action :find_optional_nested_object
11
+ before_action :find_resource, :only => %w{show update destroy clone export}
12
12
 
13
- before_filter :handle_template_upload, :only => [:create, :update]
13
+ before_action :handle_template_upload, :only => [:create, :update]
14
14
 
15
15
  wrap_parameters JobTemplate, :include => (JobTemplate.attribute_names + [:ssh])
16
16
 
@@ -4,7 +4,7 @@ module Api
4
4
  include ::Api::Version2
5
5
  include ::Foreman::Controller::Parameters::RemoteExecutionFeature
6
6
 
7
- before_filter :find_resource, :only => %w{show update}
7
+ before_action :find_resource, :only => %w{show update}
8
8
 
9
9
  api :GET, '/remote_execution_features/', N_('List remote execution features')
10
10
  def index
@@ -5,9 +5,9 @@ module Api
5
5
  include ::Foreman::Renderer
6
6
  include ::Foreman::Controller::Parameters::TemplateInput
7
7
 
8
- before_filter :find_required_nested_object
9
- before_filter :find_resource, :only => %w{show update destroy}
10
- before_filter :normalize_options, :only => %w{create update}
8
+ before_action :find_required_nested_object
9
+ before_action :find_resource, :only => %w{show update destroy}
10
+ before_action :normalize_options, :only => %w{create update}
11
11
 
12
12
  api :GET, '/templates/:template_id/template_inputs', N_('List template inputs')
13
13
  param :template_id, :identifier, :required => true
@@ -20,7 +20,7 @@ class JobTemplatesController < ::TemplatesController
20
20
  @template.template = params[:template]
21
21
  renderer = InputTemplateRenderer.new(@template, host)
22
22
  if (output = renderer.preview)
23
- render :text => output
23
+ render :plain => output
24
24
  else
25
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
26
  end
@@ -1,5 +1,5 @@
1
1
  class RemoteExecutionFeaturesController < ::ApplicationController
2
- before_filter :find_resource, :only => [:show, :update]
2
+ before_action :find_resource, :only => [:show, :update]
3
3
  include ::Foreman::Controller::Parameters::RemoteExecutionFeature
4
4
 
5
5
  def index
@@ -1,14 +1,7 @@
1
1
  module ForemanRemoteExecution
2
2
  module HostsHelperExtensions
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- alias_method_chain(:host_title_actions, :run_button)
7
- alias_method_chain :multiple_actions, :remote_execution
8
- end
9
-
10
- def multiple_actions_with_remote_execution
11
- multiple_actions_without_remote_execution + [ [_('Schedule Remote Job'), new_job_invocation_path, false] ]
3
+ def multiple_actions
4
+ super + [ [_('Schedule Remote Job'), new_job_invocation_path, false] ]
12
5
  end
13
6
 
14
7
  def schedule_job_multi_button(*args)
@@ -27,9 +20,9 @@ module ForemanRemoteExecution
27
20
  link_to(_('Schedule Remote Job'), new_job_invocation_path(:host_ids => [args.first.id]), :id => :run_button, :class => 'btn btn-default')
28
21
  end
29
22
 
30
- def host_title_actions_with_run_button(*args)
23
+ def host_title_actions(*args)
31
24
  title_actions(button_group(schedule_job_multi_button(*args)))
32
- host_title_actions_without_run_button(*args)
25
+ super(*args)
33
26
  end
34
27
  end
35
28
  end
@@ -1,13 +1,7 @@
1
1
  module ForemanRemoteExecution
2
2
  module JobTemplatesExtensions
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- alias_method_chain :permitted_actions, :run_button
7
- end
8
-
9
- def permitted_actions_with_run_button(template)
10
- original = permitted_actions_without_run_button(template)
3
+ def permitted_actions(template)
4
+ original = super(template)
11
5
 
12
6
  if template.is_a?(JobTemplate)
13
7
  original.unshift(display_link_if_authorized(_('Run'), hash_for_new_job_invocation_path(:template_id => template.id).merge(:authorizer => authorizer))) unless template.snippet
@@ -122,6 +122,11 @@ module RemoteExecutionHelper
122
122
  :title => _('Try to cancel the job'),
123
123
  :disabled => !task.cancellable?,
124
124
  :method => :post)
125
+ buttons << link_to(_('Abort Job'), abort_foreman_tasks_task_path(task),
126
+ :class => 'btn btn-danger',
127
+ :title => _('Try to abort the job without waiting for the results from the remote hosts'),
128
+ :disabled => !task.cancellable?,
129
+ :method => :post)
125
130
  end
126
131
  return buttons
127
132
  end
@@ -140,6 +145,11 @@ module RemoteExecutionHelper
140
145
  :title => _('Try to cancel the job on a host'),
141
146
  :disabled => !task.cancellable?,
142
147
  :method => :post)
148
+ buttons << link_to(_('Abort Job'), abort_foreman_tasks_task_path(task),
149
+ :class => 'btn btn-danger',
150
+ :title => _('Try to abort the job on a host without waiting for its result'),
151
+ :disabled => !task.cancellable?,
152
+ :method => :post)
143
153
  end
144
154
  return buttons
145
155
  end
@@ -60,6 +60,7 @@ module Actions
60
60
  end
61
61
 
62
62
  def check_exit_status
63
+ error! ForemanTasks::Task::TaskCancelledException.new(_('Task cancelled')) if delegated_action && delegated_action.output[:cancel_sent]
63
64
  error! _('Job execution failed') if exit_status.to_s != '0'
64
65
  end
65
66
 
@@ -89,20 +90,28 @@ module Actions
89
90
  super << self
90
91
  end
91
92
 
93
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
92
94
  def fill_continuous_output(continuous_output)
93
- fill_planning_errors_to_continuous_output(continuous_output) unless exit_status
94
95
  delegated_output.fetch('result', []).each do |raw_output|
95
96
  continuous_output.add_raw_output(raw_output)
96
97
  end
98
+
97
99
  final_timestamp = (continuous_output.last_timestamp || task.ended_at).to_f + 1
100
+
101
+ if task.state == 'stopped' && task.result == 'cancelled'
102
+ continuous_output.add_output(_('Job cancelled by user'), 'debug', final_timestamp)
103
+ else
104
+ fill_planning_errors_to_continuous_output(continuous_output) unless exit_status
105
+ end
98
106
  if exit_status
99
107
  continuous_output.add_output(_('Exit status: %s') % exit_status, 'stdout', final_timestamp)
100
108
  elsif run_step && run_step.error
101
- continuous_output.add_ouput(_('Job finished with error') + ": #{run_step.error.exception_class} - #{run_step.error.message}", 'debug', final_timestamp)
109
+ continuous_output.add_output(_('Job finished with error') + ": #{run_step.error.exception_class} - #{run_step.error.message}", 'debug', final_timestamp)
102
110
  end
103
111
  rescue => e
104
112
  continuous_output.add_exception(_('Error loading data from proxy'), e)
105
113
  end
114
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
106
115
 
107
116
  def exit_status
108
117
  delegated_output[:exit_status]
@@ -3,6 +3,7 @@ module Actions
3
3
  class RunHostsJob < Actions::ActionWithSubPlans
4
4
 
5
5
  include Dynflow::Action::WithBulkSubPlans
6
+ include Dynflow::Action::WithPollingSubPlans
6
7
 
7
8
  middleware.use Actions::Middleware::BindJobInvocation
8
9
  middleware.use Actions::Middleware::RecurringLogic
@@ -10,15 +11,16 @@ module Actions
10
11
  def delay(delay_options, job_invocation)
11
12
  task.add_missing_task_groups(job_invocation.task_group)
12
13
  job_invocation.targeting.resolve_hosts! if job_invocation.targeting.static? && !job_invocation.targeting.resolved?
14
+ input.update :job_invocation => job_invocation.to_action_input
13
15
  super delay_options, job_invocation
14
16
  end
15
17
 
16
18
  def plan(job_invocation)
17
- set_up_concurrency_control job_invocation
18
19
  job_invocation.task_group.save! if job_invocation.task_group.try(:new_record?)
19
20
  task.add_missing_task_groups(job_invocation.task_group) if job_invocation.task_group
20
21
  action_subject(job_invocation)
21
22
  job_invocation.targeting.resolve_hosts! if job_invocation.targeting.dynamic? || !job_invocation.targeting.resolved?
23
+ set_up_concurrency_control job_invocation
22
24
  input.update(:job_category => job_invocation.job_category)
23
25
  plan_self(:job_invocation_id => job_invocation.id)
24
26
  end
@@ -50,7 +52,10 @@ module Actions
50
52
 
51
53
  def set_up_concurrency_control(invocation)
52
54
  limit_concurrency_level invocation.concurrency_level unless invocation.concurrency_level.nil?
53
- distribute_over_time invocation.time_span unless invocation.time_span.nil?
55
+ unless invocation.time_span.nil?
56
+ distribute_over_time(invocation.time_span,
57
+ invocation.targeting.hosts.count)
58
+ end
54
59
  end
55
60
 
56
61
  def rescue_strategy
@@ -62,7 +67,7 @@ module Actions
62
67
  end
63
68
 
64
69
  def humanized_input
65
- input[:job_invocation][:description]
70
+ input.fetch(:job_invocation, {}).fetch(:description, '')
66
71
  end
67
72
 
68
73
  def humanized_name
@@ -1,47 +1,41 @@
1
1
  module ForemanRemoteExecution
2
2
  module HostExtensions
3
- extend ActiveSupport::Concern
4
-
5
3
  # rubocop:disable Metrics/BlockLength
6
- included do
7
- alias_method_chain :build_required_interfaces, :remote_execution
8
- alias_method_chain :reload, :remote_execution
9
- alias_method_chain :becomes, :remote_execution
10
- alias_method_chain :host_params, :remote_execution
11
-
12
- has_many :targeting_hosts, :dependent => :destroy, :foreign_key => 'host_id'
13
- has_many :template_invocations, :dependent => :destroy, :foreign_key => 'host_id'
14
- has_one :execution_status_object, :class_name => 'HostStatus::ExecutionStatus', :foreign_key => 'host_id'
15
- has_many :run_host_job_tasks, :through => :template_invocations
16
-
17
- scoped_search :relation => :run_host_job_tasks, :on => :result, :rename => 'job_invocation.result',
18
- :ext_method => :search_by_job_invocation,
19
- :only_explicit => true,
20
- :complete_value => TemplateInvocation::TaskResultMap::REVERSE_MAP
21
-
22
- scoped_search :relation => :template_invocations, :on => :job_invocation_id,
23
- :rename => 'job_invocation.id', :only_explicit => true, :ext_method => :search_by_job_invocation
24
-
25
- scoped_search :in => :execution_status_object, :on => :status, :rename => :execution_status,
26
- :complete_value => { :ok => HostStatus::ExecutionStatus::OK, :error => HostStatus::ExecutionStatus::ERROR }
27
-
28
- def self.search_by_job_invocation(key, operator, value)
29
- if key == 'job_invocation.result'
30
- operator = operator == '=' ? 'IN' : 'NOT IN'
31
- value = TemplateInvocation::TaskResultMap.status_to_task_result(value)
4
+ def self.prepended(base)
5
+ base.instance_eval do
6
+ has_many :targeting_hosts, :dependent => :destroy, :foreign_key => 'host_id'
7
+ has_many :template_invocations, :dependent => :destroy, :foreign_key => 'host_id'
8
+ has_one :execution_status_object, :class_name => 'HostStatus::ExecutionStatus', :foreign_key => 'host_id'
9
+ has_many :run_host_job_tasks, :through => :template_invocations
10
+
11
+ scoped_search :relation => :run_host_job_tasks, :on => :result, :rename => 'job_invocation.result',
12
+ :ext_method => :search_by_job_invocation,
13
+ :only_explicit => true,
14
+ :complete_value => TemplateInvocation::TaskResultMap::REVERSE_MAP
15
+
16
+ scoped_search :relation => :template_invocations, :on => :job_invocation_id,
17
+ :rename => 'job_invocation.id', :only_explicit => true, :ext_method => :search_by_job_invocation
18
+
19
+ scoped_search :relation => :execution_status_object, :on => :status, :rename => :execution_status,
20
+ :complete_value => { :ok => HostStatus::ExecutionStatus::OK, :error => HostStatus::ExecutionStatus::ERROR }
21
+
22
+ def search_by_job_invocation(key, operator, value)
23
+ if key == 'job_invocation.result'
24
+ operator = operator == '=' ? 'IN' : 'NOT IN'
25
+ value = TemplateInvocation::TaskResultMap.status_to_task_result(value)
26
+ end
27
+
28
+ mapping = {
29
+ 'job_invocation.id' => %(#{TemplateInvocation.table_name}.job_invocation_id #{operator} ?),
30
+ 'job_invocation.result' => %(#{ForemanTasks::Task.table_name}.result #{operator} (?))
31
+ }
32
+ {
33
+ :conditions => sanitize_sql_for_conditions([mapping[key], value_to_sql(operator, value)]),
34
+ :joins => { :template_invocations => [:run_host_job_task] }
35
+ }
32
36
  end
33
-
34
- mapping = {
35
- 'job_invocation.id' => %(#{TemplateInvocation.table_name}.job_invocation_id #{operator} ?),
36
- 'job_invocation.result' => %(#{ForemanTasks::Task.table_name}.result #{operator} (?))
37
- }
38
- {
39
- :conditions => sanitize_sql_for_conditions([mapping[key], value_to_sql(operator, value)]),
40
- :joins => { :template_invocations => [:run_host_job_task] }
41
- }
42
37
  end
43
38
  end
44
- # rubocop:enable Metrics/BlockLength
45
39
 
46
40
  def execution_status(options = {})
47
41
  @execution_status ||= get_status(HostStatus::ExecutionStatus).to_status(options)
@@ -51,8 +45,8 @@ module ForemanRemoteExecution
51
45
  @execution_status_label ||= get_status(HostStatus::ExecutionStatus).to_label(options)
52
46
  end
53
47
 
54
- def host_params_with_remote_execution
55
- params = host_params_without_remote_execution
48
+ def host_params
49
+ params = super
56
50
  keys = remote_execution_ssh_keys
57
51
  params['remote_execution_ssh_keys'] = keys if keys.present?
58
52
  [:remote_execution_ssh_user, :remote_execution_effective_user_method,
@@ -94,21 +88,21 @@ module ForemanRemoteExecution
94
88
  @execution_interface = nil
95
89
  end
96
90
 
97
- def becomes_with_remote_execution(*args)
98
- became = becomes_without_remote_execution(*args)
91
+ def becomes(*args)
92
+ became = super(*args)
99
93
  became.drop_execution_interface_cache if became.respond_to? :drop_execution_interface_cache
100
94
  became
101
95
  end
102
96
 
103
- def reload_with_remote_execution(*args)
97
+ def reload(*args)
104
98
  drop_execution_interface_cache
105
- reload_without_remote_execution(*args)
99
+ super(*args)
106
100
  end
107
101
 
108
102
  private
109
103
 
110
- def build_required_interfaces_with_remote_execution(attrs = {})
111
- build_required_interfaces_without_remote_execution(attrs)
104
+ def build_required_interfaces(attrs = {})
105
+ super(attrs)
112
106
  self.primary_interface.execution = true if self.execution_interface.nil?
113
107
  end
114
108
  end
@@ -1,11 +1,5 @@
1
1
  module ForemanRemoteExecution
2
2
  module SmartProxyExtensions
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- alias_method_chain :refresh, :remote_execution
7
- end
8
-
9
3
  def pubkey
10
4
  self[:pubkey] || update_pubkey
11
5
  end
@@ -17,8 +11,8 @@ module ForemanRemoteExecution
17
11
  key
18
12
  end
19
13
 
20
- def refresh_with_remote_execution
21
- errors = refresh_without_remote_execution
14
+ def refresh
15
+ errors = super
22
16
  update_pubkey
23
17
  errors
24
18
  end
@@ -5,11 +5,12 @@ module ForemanRemoteExecution
5
5
  included do
6
6
  has_many :job_templates, :through => :taxable_taxonomies, :source => :taxable, :source_type => 'JobTemplate'
7
7
 
8
- # FIXME - workaround for #11805 - use before_create for setting
8
+ # TODO: on foreman_version_bump
9
+ # workaround for #11805 - use before_create for setting
9
10
  # the default templates, remove after it's fixed in upstream
10
- # (https://github.com/theforeman/foreman/pull/2723) and we don't
11
- # support Foreman 1.9
12
- skip_callback :create, :after, :assign_default_templates
11
+ # (https://github.com/theforeman/foreman/pull/4890) and gets
12
+ # into a 1.17 release
13
+ skip_callback :create, :after, :assign_default_templates, :raise => false
13
14
  before_create :assign_default_templates
14
15
  end
15
16
  end
@@ -1,5 +1,6 @@
1
1
  class JobInvocation < ActiveRecord::Base
2
2
  include Authorizable
3
+ audited :except => [ :task_id, :targeting_id, :task_group_id, :triggering_id ]
3
4
 
4
5
  include ForemanRemoteExecution::ErrorsFlattener
5
6
  FLATTENED_ERRORS_MAPPING = {
@@ -21,7 +22,7 @@ class JobInvocation < ActiveRecord::Base
21
22
  scoped_search :on => :description # FIXME No auto complete because of https://github.com/wvanbergen/scoped_search/issues/138
22
23
 
23
24
  has_many :template_invocations_hosts, :through => :template_invocations, :source => :host
24
- scoped_search :in => :template_invocations_hosts, :on => :name, :rename => 'host', :complete_value => true
25
+ scoped_search :relation => :template_invocations_hosts, :on => :name, :rename => 'host', :complete_value => true
25
26
 
26
27
  delegate :bookmark, :resolved?, :to => :targeting, :allow_nil => true
27
28
 
@@ -37,10 +38,10 @@ class JobInvocation < ActiveRecord::Base
37
38
  :class_name => 'ForemanTasks::Task',
38
39
  :source => 'run_host_job_task'
39
40
 
40
- scoped_search :in => :task, :on => :started_at, :rename => 'started_at', :complete_value => true
41
- scoped_search :in => :task, :on => :start_at, :rename => 'start_at', :complete_value => true
42
- scoped_search :in => :task, :on => :ended_at, :rename => 'ended_at', :complete_value => true
43
- scoped_search :in => :task, :on => :state, :rename => 'status', :ext_method => :search_by_status,
41
+ scoped_search :relation => :task, :on => :started_at, :rename => 'started_at', :complete_value => true
42
+ scoped_search :relation => :task, :on => :start_at, :rename => 'start_at', :complete_value => true
43
+ scoped_search :relation => :task, :on => :ended_at, :rename => 'ended_at', :complete_value => true
44
+ scoped_search :relation => :task, :on => :state, :rename => 'status', :ext_method => :search_by_status,
44
45
  :only_explicit => true, :complete_value => Hash[HostStatus::ExecutionStatus::STATUS_NAMES.values.map { |v| [v, v] }]
45
46
 
46
47
  belongs_to :triggering, :class_name => 'ForemanTasks::Triggering'
@@ -48,9 +49,9 @@ class JobInvocation < ActiveRecord::Base
48
49
 
49
50
  scope :with_task, -> { references(:task) }
50
51
 
51
- scoped_search :in => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
52
+ scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
52
53
 
53
- scoped_search :in => :recurring_logic, :on => 'id', :rename => 'recurring',
54
+ scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring',
54
55
  :ext_method => :search_by_recurring_logic, :only_explicit => true,
55
56
  :complete_value => { :true => true, :false => false }
56
57
 
@@ -85,6 +86,10 @@ class JobInvocation < ActiveRecord::Base
85
86
  HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.new(task).status_label
86
87
  end
87
88
 
89
+ def to_label
90
+ description
91
+ end
92
+
88
93
  # returns progress in percents
89
94
  def progress(total = nil, done = nil)
90
95
  if queued? || !targeting.resolved? || done == 0
@@ -183,7 +188,7 @@ class JobInvocation < ActiveRecord::Base
183
188
  def progress_report
184
189
  map = TemplateInvocation::TaskResultMap
185
190
  all_keys = (map.results | map.statuses | [:progress, :total])
186
- if queued? || !targeting.resolved?
191
+ if queued? || (task && task.started_at.nil?) || !targeting.resolved?
187
192
  all_keys.reduce({}) do |acc, key|
188
193
  acc.merge(key => 0)
189
194
  end
@@ -186,7 +186,7 @@ class JobInvocationComposer
186
186
 
187
187
  def targeting_params
188
188
  if @host_ids
189
- { :search_query => Targeting.build_query_from_hosts(@host_ids) }
189
+ { :search_query => Targeting.build_query_from_hosts(@host_ids), :targeting_type => job_invocation.targeting.targeting_type }
190
190
  else
191
191
  job_invocation.targeting.attributes.slice('search_query', 'bookmark_id', 'user_id', 'targeting_type')
192
192
  end
@@ -20,10 +20,10 @@ class TemplateInvocation < ActiveRecord::Base
20
20
  validate :provides_required_input_values
21
21
  before_validation :set_effective_user
22
22
 
23
- scoped_search :in => :host, :on => :name, :rename => 'host.name', :complete_value => true
24
- scoped_search :in => :host_group, :on => :name, :rename => 'host_group.name', :complete_value => true
25
- scoped_search :in => :template, :on => :job_category, :complete_value => true
26
- scoped_search :in => :template, :on => :name, :complete_value => true
23
+ scoped_search :relation => :host, :on => :name, :rename => 'host.name', :complete_value => true
24
+ scoped_search :relation => :host_group, :on => :name, :rename => 'host_group.name', :complete_value => true
25
+ scoped_search :relation => :template, :on => :job_category, :complete_value => true
26
+ scoped_search :relation => :template, :on => :name, :complete_value => true
27
27
 
28
28
  class TaskResultMap
29
29
  MAP = {
@@ -47,7 +47,7 @@
47
47
  <b><%= _("Concurrency level limited to") %></b>: <%= job_invocation.concurrency_level %> <%= _('tasks at a time') %><br>
48
48
  <% end %>
49
49
  <% if job_invocation.time_span %>
50
- <b><%= _("Set to distribute over") %></b>: <%= job_invocation.concurrency_level %> <%= _('minutes') %><br>
50
+ <b><%= _("Set to distribute over") %></b>: <%= job_invocation.time_span %> <%= _('seconds') %><br>
51
51
  <% end %>
52
52
  <% if job_invocation.task && (job_invocation.task.delayed? || job_invocation.task.recurring?) %>
53
53
  <b><%= _("Scheduled to start at") %></b>: <%= job_invocation.task.start_at.try(:in_time_zone) %><br>
@@ -11,7 +11,7 @@ class CreateTemplateInput < ActiveRecord::Migration
11
11
  t.text :description
12
12
  t.integer :template_id
13
13
 
14
- t.timestamps
14
+ t.timestamps :null => true
15
15
  end
16
16
 
17
17
  add_foreign_key :template_inputs, :templates, :name => 'templates_template_id_fk', :column => 'template_id'
@@ -5,7 +5,7 @@ class AddTargeting < ActiveRecord::Migration
5
5
  t.references :bookmark
6
6
  t.references :user
7
7
  t.string :targeting_type, :null => false, :limit => 255
8
- t.timestamps
8
+ t.timestamps :null => true
9
9
  end
10
10
 
11
11
  add_index :targetings, [:bookmark_id], :name => 'targetings_bookmark_id'
@@ -24,9 +24,9 @@ Gem::Specification.new do |s|
24
24
  s.extra_rdoc_files = `git ls-files doc`.split("\n") + Dir['README*', 'LICENSE']
25
25
 
26
26
  s.add_dependency 'deface'
27
- s.add_dependency 'dynflow', '~> 0.8.10'
27
+ s.add_dependency 'dynflow', '~> 0.8.26'
28
28
  s.add_dependency 'foreman_remote_execution_core'
29
- s.add_dependency 'foreman-tasks', '>= 0.9'
29
+ s.add_dependency 'foreman-tasks', '>= 0.9.5'
30
30
 
31
31
  s.add_development_dependency 'rubocop'
32
32
  s.add_development_dependency 'rdoc'
@@ -109,7 +109,9 @@ module ForemanRemoteExecution
109
109
  # widget 'foreman_remote_execution_widget', name: N_('Foreman plugin template widget'), sizex: 4, sizey: 1
110
110
 
111
111
  parameter_filter Subnet, :remote_execution_proxies, :remote_execution_proxy_ids => []
112
- parameter_filter Nic, :execution
112
+ parameter_filter Nic::Interface do |ctx|
113
+ ctx.permit :execution
114
+ end
113
115
  end
114
116
  end
115
117
 
@@ -146,7 +148,7 @@ module ForemanRemoteExecution
146
148
 
147
149
  User.send(:include, ForemanRemoteExecution::UserExtensions)
148
150
 
149
- Host::Managed.send(:include, ForemanRemoteExecution::HostExtensions)
151
+ Host::Managed.send(:prepend, ForemanRemoteExecution::HostExtensions)
150
152
  Host::Managed.send(:include, ForemanTasks::Concerns::HostActionSubject)
151
153
 
152
154
  (Nic::Base.descendants + [Nic::Base]).each do |klass|
@@ -154,10 +156,10 @@ module ForemanRemoteExecution
154
156
  end
155
157
 
156
158
  Bookmark.send(:include, ForemanRemoteExecution::BookmarkExtensions)
157
- HostsHelper.send(:include, ForemanRemoteExecution::HostsHelperExtensions)
158
- ProvisioningTemplatesHelper.send(:include, ForemanRemoteExecution::JobTemplatesExtensions)
159
+ HostsHelper.send(:prepend, ForemanRemoteExecution::HostsHelperExtensions)
160
+ ProvisioningTemplatesHelper.send(:prepend, ForemanRemoteExecution::JobTemplatesExtensions)
159
161
 
160
- SmartProxy.send(:include, ForemanRemoteExecution::SmartProxyExtensions)
162
+ SmartProxy.send(:prepend, ForemanRemoteExecution::SmartProxyExtensions)
161
163
  Subnet.send(:include, ForemanRemoteExecution::SubnetExtensions)
162
164
 
163
165
  # We need to explicitly force to load the Task model due to Rails loader
@@ -1,3 +1,3 @@
1
1
  module ForemanRemoteExecution
2
- VERSION = '1.3.3'.freeze
2
+ VERSION = '1.3.4'.freeze
3
3
  end
@@ -11,6 +11,7 @@ module ForemanRemoteExecution
11
11
  let(:job_invocation) do
12
12
  FactoryGirl.build(:job_invocation, :with_template).tap do |invocation|
13
13
  invocation.targeting = targeting
14
+ invocation.description = 'Some short description'
14
15
  invocation.save
15
16
  end
16
17
  end
@@ -31,6 +32,11 @@ module ForemanRemoteExecution
31
32
  plan_action action, job_invocation
32
33
  end
33
34
 
35
+ let(:delayed) do
36
+ action.delay({ :start_at => Time.now.getlocal }, job_invocation)
37
+ action
38
+ end
39
+
34
40
  before do
35
41
  ProxyAPI::ForemanDynflow::DynflowProxy.any_instance.stubs(:tasks_count).returns(0)
36
42
  User.current = users :admin
@@ -38,11 +44,6 @@ module ForemanRemoteExecution
38
44
  end
39
45
 
40
46
  context 'targeting resolving' do
41
- let(:delayed) do
42
- action.delay({ :start_at => Time.now.getlocal }, job_invocation)
43
- action
44
- end
45
-
46
47
  it 'resolves dynamic targeting in plan' do
47
48
  targeting.targeting_type = 'dynamic_query'
48
49
  refute targeting.resolved?
@@ -78,6 +79,15 @@ module ForemanRemoteExecution
78
79
  job_invocation.task_id.must_equal '123'
79
80
  end
80
81
 
82
+ # In plan phase this is handled by #action_subject
83
+ # which is expected in tests
84
+ it 'sets input in delay phase when delayed' do
85
+ delayed.input[:job_invocation]['id'].must_equal job_invocation.id
86
+ delayed.input[:job_invocation]['name'].must_equal job_invocation.job_category
87
+ delayed.input[:job_invocation]['description'].must_equal job_invocation.description
88
+ planned # To make the expectations happy
89
+ end
90
+
81
91
  describe 'concurrency control' do
82
92
  let(:level) { 5 }
83
93
  let(:span) { 60 }
@@ -85,33 +95,28 @@ module ForemanRemoteExecution
85
95
  it 'can be disabled' do
86
96
  job_invocation.expects(:concurrency_level)
87
97
  job_invocation.expects(:time_span)
88
- action.expects(:limit_concurrency_level).never
89
- action.expects(:distribute_over_time).never
90
- planned
98
+ planned.input.key?(:concurrency_control).must_equal false
91
99
  end
92
100
 
93
101
  it 'can limit concurrency level' do
94
102
  job_invocation.expects(:concurrency_level).returns(level).twice
95
103
  job_invocation.expects(:time_span)
96
- action.expects(:limit_concurrency_level).with(level)
97
- action.expects(:distribute_over_time).never
98
- planned
104
+ planned.input[:concurrency_control][:level].wont_be_empty
105
+ planned.input[:concurrency_control].key?(:time).must_equal false
99
106
  end
100
107
 
101
108
  it 'can distribute tasks over time' do
102
109
  job_invocation.expects(:time_span).returns(span).twice
103
110
  job_invocation.expects(:concurrency_level)
104
- action.expects(:distribute_over_time).with(span)
105
- action.expects(:distribute_over_time).never
106
- planned
111
+ planned.input[:concurrency_control][:time].wont_be_empty
112
+ planned.input[:concurrency_control].key?(:level).must_equal false
107
113
  end
108
114
 
109
115
  it 'can use both' do
110
116
  job_invocation.expects(:time_span).returns(span).twice
111
- action.expects(:distribute_over_time).with(span)
112
117
  job_invocation.expects(:concurrency_level).returns(level).twice
113
- action.expects(:limit_concurrency_level).with(level)
114
- planned
118
+ planned.input[:concurrency_control][:time].wont_be_empty
119
+ planned.input[:concurrency_control][:level].wont_be_empty
115
120
  end
116
121
  end
117
122
  end
@@ -4,9 +4,15 @@ RemoteExecutionProvider.register(:Mcollective, OpenStruct)
4
4
 
5
5
  class JobInvocationComposerTest < ActiveSupport::TestCase
6
6
  before do
7
- setup_user('view', 'job_templates', 'name ~ trying*')
8
- setup_user('view', 'bookmarks')
9
- setup_user('view', 'hosts')
7
+ setup_user('view', 'job_templates', 'name ~ trying*')
8
+ setup_user('create', 'job_templates', 'name ~ trying*')
9
+ setup_user('view', 'job_invocations')
10
+ setup_user('create', 'job_invocations')
11
+ setup_user('view', 'bookmarks')
12
+ setup_user('create', 'bookmarks')
13
+ setup_user('edit', 'bookmarks')
14
+ setup_user('view', 'hosts')
15
+ setup_user('create', 'hosts')
10
16
  end
11
17
 
12
18
  let(:trying_job_template_1) { FactoryGirl.create(:job_template, :job_category => 'trying_job_template_1', :name => 'trying1', :provider_type => 'SSH') }
@@ -500,8 +506,7 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
500
506
  }
501
507
  }.with_indifferent_access
502
508
  end
503
- let(:existing) { composer.job_invocation.reload }
504
- let(:new_job_invocation) { JobInvocation.new }
509
+ let(:existing) { composer.job_invocation }
505
510
  let(:new_composer) { JobInvocationComposer.from_job_invocation(composer.job_invocation) }
506
511
 
507
512
  before do
@@ -590,7 +595,7 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
590
595
 
591
596
  it 'finds the inputs by name' do
592
597
  assert composer.save!
593
- assert_equal 1, composer.pattern_template_invocations.first.input_values.count
598
+ assert_equal 1, composer.pattern_template_invocations.first.input_values.collect.count
594
599
  end
595
600
  end
596
601
 
@@ -127,6 +127,19 @@ class JobInvocationTest < ActiveSupport::TestCase
127
127
  specify { job_invocation.progress_report.must_equal progress_report_without_sub_tasks }
128
128
  end
129
129
 
130
+ context 'with cancelled task' do
131
+ before do
132
+ task.state = 'stopped'
133
+ task.result = 'error'
134
+ end
135
+
136
+ it 'calculates the progress correctly' do
137
+ job_invocation.targeting.stubs(:resolved?).returns(true)
138
+ task.expects(:sub_tasks_counts).never
139
+ job_invocation.progress_report.must_equal progress_report_without_sub_tasks
140
+ end
141
+ end
142
+
130
143
  context 'with succeeded task' do
131
144
  before do
132
145
  task.state = 'stopped'
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.3.3
4
+ version: 1.3.4
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: 2017-07-20 00:00:00.000000000 Z
11
+ date: 2017-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deface
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.8.10
33
+ version: 0.8.26
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.8.10
40
+ version: 0.8.26
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: foreman_remote_execution_core
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '0.9'
61
+ version: 0.9.5
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '0.9'
68
+ version: 0.9.5
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rubocop
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -431,7 +431,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
431
431
  version: '0'
432
432
  requirements: []
433
433
  rubyforge_project:
434
- rubygems_version: 2.4.5
434
+ rubygems_version: 2.6.12
435
435
  signing_key:
436
436
  specification_version: 4
437
437
  summary: A plugin bringing remote execution to the Foreman, completing the config