foreman_remote_execution 1.3.3 → 1.3.4

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