foreman-tasks 0.13.0 → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0111beaab4e9971f1039c81e0ccf31ad5cbb79e4
4
- data.tar.gz: 0077b9efb0e93957f99a37fe4d08469b6f6b62d2
2
+ SHA256:
3
+ metadata.gz: 464f9df51dc38a0d63e3e573e462f9f57bc5d091f0bdc0090327229d6b8ecffb
4
+ data.tar.gz: 5d2f0b357ce12025975c5cd62d2d31637d5cbcff20349e3af89edb213e8c553a
5
5
  SHA512:
6
- metadata.gz: 0aff56c13e49c3cca9802a8ae63f0f4c024ba69a4b6bb07d9453e236de2571d284895906192c8b52cc2e8ed8300b083e3643d47bf8894fab8fbde55da5bc3364
7
- data.tar.gz: 55255ff6e02f84e834b2ea8927364985830f13a0b456c64af503af56b0a76f286e1a94e5ec2fef04b3ee4da48fe905836a242b9baae4efd90f54afe04ee03944
6
+ metadata.gz: df01ce6f12aec5edd07cfe1ba355d86ad130df011be72c559dc100f92e2dabcfae356aa636bc73454393acd52aa02d569fe3addefe6d4940aa2d9ec2289a0a6d
7
+ data.tar.gz: 2e6da699d36a27a4f5727475e998ee52e07861e0a2eecd8a879a9b17f6b27121a225edb34f66338939c11e9110bb3fd53f0edc090b113fedb983db5bdd8798f6
data/.rubocop_todo.yml CHANGED
@@ -1,74 +1,147 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2016-12-21 14:29:23 +0200 using RuboCop version 0.46.0.
3
+ # on 2018-04-02 18:15:37 +0200 using RuboCop version 0.54.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- Lint/EmptyWhen:
9
+ # Offense count: 3
10
+ # Cop supports --auto-correct.
11
+ # Configuration parameters: Include, TreatCommentsAsGroupSeparators.
12
+ # Include: **/*.gemspec
13
+ Gemspec/OrderedDependencies:
10
14
  Exclude:
11
- - 'app/lib/actions/proxy_action.rb'
15
+ - 'foreman-tasks.gemspec'
16
+
17
+ # Offense count: 1
18
+ # Cop supports --auto-correct.
19
+ # Configuration parameters: EnforcedStyle.
20
+ # SupportedStyles: final_newline, final_blank_line
21
+ Layout/TrailingBlankLines:
22
+ Exclude:
23
+ - 'locale/action_names.rb'
12
24
 
13
- Lint/ShadowingOuterLocalVariable:
25
+ # Offense count: 1
26
+ Lint/EmptyWhen:
14
27
  Exclude:
15
- - 'bin/dynflow-executor'
28
+ - 'app/lib/actions/proxy_action.rb'
16
29
 
30
+ # Offense count: 2
17
31
  Lint/UselessAssignment:
18
32
  Exclude:
19
33
  - 'lib/foreman_tasks/tasks/export_tasks.rake'
20
34
 
35
+ # Offense count: 32
21
36
  Metrics/AbcSize:
22
37
  Max: 41
23
38
 
24
- # Configuration parameters: CountComments.
39
+ # Offense count: 2
40
+ # Configuration parameters: CountComments, ExcludedMethods.
25
41
  Metrics/BlockLength:
26
- Max: 105
42
+ Max: 32
27
43
 
44
+ # Offense count: 13
28
45
  # Configuration parameters: CountComments.
29
46
  Metrics/ClassLength:
30
47
  Max: 230
31
48
 
49
+ # Offense count: 9
32
50
  Metrics/CyclomaticComplexity:
33
- Max: 10
51
+ Max: 9
34
52
 
53
+ # Offense count: 482
35
54
  # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
36
55
  # URISchemes: http, https
37
56
  Metrics/LineLength:
38
- Max: 244
57
+ Max: 211
39
58
 
59
+ # Offense count: 54
40
60
  # Configuration parameters: CountComments.
41
61
  Metrics/MethodLength:
42
- Max: 37
62
+ Max: 29
43
63
 
64
+ # Offense count: 2
44
65
  # Configuration parameters: CountComments.
45
66
  Metrics/ModuleLength:
46
- Max: 170
67
+ Max: 167
47
68
 
69
+ # Offense count: 1
48
70
  # Configuration parameters: CountKeywordArgs.
49
71
  Metrics/ParameterLists:
50
72
  Max: 6
51
73
 
74
+ # Offense count: 4
52
75
  Metrics/PerceivedComplexity:
53
- Max: 10
76
+ Max: 9
77
+
78
+ # Offense count: 3
79
+ Naming/MemoizedInstanceVariableName:
80
+ Exclude:
81
+ - 'app/controllers/foreman_tasks/recurring_logics_controller.rb'
82
+ - 'app/lib/actions/recurring_action.rb'
83
+ - 'lib/foreman_tasks_core/otp_manager.rb'
84
+
85
+ # Offense count: 1
86
+ # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist, MethodDefinitionMacros.
87
+ # NamePrefix: is_, has_, have_
88
+ # NamePrefixBlacklist: is_, has_, have_
89
+ # NameWhitelist: is_a?
90
+ # MethodDefinitionMacros: define_method, define_singleton_method
91
+ Naming/PredicateName:
92
+ Exclude:
93
+ - 'spec/**/*'
94
+ - 'app/models/foreman_tasks/task/status_explicator.rb'
95
+
96
+ # Offense count: 12
97
+ # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
98
+ # AllowedNames: io, id, to
99
+ Naming/UncommunicativeMethodParamName:
100
+ Exclude:
101
+ - 'app/helpers/foreman_tasks/foreman_tasks_helper.rb'
102
+ - 'app/models/foreman_tasks/recurring_logic.rb'
54
103
 
104
+ # Offense count: 3
105
+ # Cop supports --auto-correct.
106
+ Rails/ActiveRecordAliases:
107
+ Exclude:
108
+ - 'app/models/foreman_tasks/task/dynflow_task.rb'
109
+ - 'test/factories/task_factory.rb'
110
+
111
+ # Offense count: 6
55
112
  # Configuration parameters: Include.
56
- # Include: app/**/*.rb, config/**/*.rb, lib/**/*.rb
57
- Rails/Exit:
113
+ # Include: db/migrate/*.rb
114
+ Rails/CreateTableWithTimestamps:
115
+ Exclude:
116
+ - 'db/migrate/20131205204140_create_foreman_tasks.rb'
117
+ - 'db/migrate/20131209122644_create_foreman_tasks_locks.rb'
118
+ - 'db/migrate/20150907124936_create_recurring_logic.rb'
119
+ - 'db/migrate/20150907131503_create_task_groups.rb'
120
+ - 'db/migrate/20151112152108_create_triggerings.rb'
121
+
122
+ # Offense count: 1
123
+ # Cop supports --auto-correct.
124
+ # Configuration parameters: EnforcedStyle.
125
+ # SupportedStyles: numeric, symbolic
126
+ Rails/HttpStatus:
58
127
  Exclude:
59
- - 'lib/**/*.rake'
128
+ - 'app/controllers/foreman_tasks/concerns/hosts_controller_extension.rb'
60
129
 
130
+ # Offense count: 1
61
131
  # Configuration parameters: Include.
62
132
  # Include: app/**/*.rb, config/**/*.rb, db/**/*.rb, lib/**/*.rb
63
133
  Rails/Output:
64
134
  Exclude:
65
135
  - 'lib/foreman_tasks/cleaner.rb'
66
136
 
137
+ # Offense count: 5
67
138
  Rails/OutputSafety:
68
139
  Exclude:
69
140
  - 'app/helpers/foreman_tasks/foreman_tasks_helper.rb'
70
141
 
71
- # Configuration parameters: EnforcedStyle, SupportedStyles.
142
+ # Offense count: 11
143
+ # Cop supports --auto-correct.
144
+ # Configuration parameters: AutoCorrect, EnforcedStyle.
72
145
  # SupportedStyles: nested, compact
73
146
  Style/ClassAndModuleChildren:
74
147
  Exclude:
@@ -83,51 +156,37 @@ Style/ClassAndModuleChildren:
83
156
  - 'lib/foreman_tasks/dynflow/persistence.rb'
84
157
  - 'test/controllers/api/recurring_logics_controller_test.rb'
85
158
  - 'test/controllers/api/tasks_controller_test.rb'
86
- - 'test/unit/actions/action_with_sub_plans_test.rb'
87
159
 
160
+ # Offense count: 2
88
161
  Style/DoubleNegation:
89
162
  Exclude:
90
163
  - 'app/models/foreman_tasks/lock.rb'
91
164
  - 'app/models/foreman_tasks/recurring_logic.rb'
92
165
 
93
- # Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts.
94
- Naming/FileName:
166
+ # Offense count: 2
167
+ # Cop supports --auto-correct.
168
+ Style/Encoding:
95
169
  Exclude:
96
- - 'db/seeds.d/20-foreman_tasks_permissions.rb'
97
- - 'db/seeds.d/60-dynflow_proxy_feature.rb'
98
- - 'db/seeds.d/61-foreman_tasks_bookmarks.rb'
99
- - 'lib/foreman-tasks.rb'
170
+ - 'foreman-tasks-core.gemspec'
171
+ - 'foreman-tasks.gemspec'
100
172
 
101
- # Configuration parameters: EnforcedStyle, SupportedStyles.
102
- # SupportedStyles: format, sprintf, percent
103
- Style/FormatString:
173
+ # Offense count: 6
174
+ # Cop supports --auto-correct.
175
+ Style/ExpandPathArguments:
104
176
  Exclude:
105
- - 'app/models/foreman_tasks/task/dynflow_task.rb'
106
- - 'lib/foreman_tasks/tasks/export_tasks.rake'
177
+ - 'foreman-tasks-core.gemspec'
178
+ - 'foreman-tasks.gemspec'
179
+ - 'lib/foreman_tasks/engine.rb'
180
+ - 'script/rails'
107
181
 
182
+ # Offense count: 32
108
183
  # Configuration parameters: MinBodyLength.
109
184
  Style/GuardClause:
110
185
  Enabled: false
111
186
 
112
- # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
113
- # NamePrefix: is_, has_, have_
114
- # NamePrefixBlacklist: is_, has_, have_
115
- # NameWhitelist: is_a?
116
- Naming/PredicateName:
117
- Exclude:
118
- - 'spec/**/*'
119
- - 'app/models/foreman_tasks/task/status_explicator.rb'
120
-
187
+ # Offense count: 1
121
188
  # Cop supports --auto-correct.
122
- # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
123
- # SupportedStyles: slashes, percent_r, mixed
124
- Style/RegexpLiteral:
189
+ # Configuration parameters: AllowMultipleReturnValues.
190
+ Style/RedundantReturn:
125
191
  Exclude:
126
- - 'lib/foreman_tasks/dynflow/console_authorizer.rb'
127
-
128
- # Configuration parameters: Methods.
129
- # Methods: {"reduce"=>["acc", "elem"]}, {"inject"=>["acc", "elem"]}
130
- Style/SingleLineBlockParams:
131
- Exclude:
132
- - 'app/models/foreman_tasks/concerns/action_subject.rb'
133
- - 'app/models/foreman_tasks/lock.rb'
192
+ - 'app/models/foreman_tasks/concerns/action_triggering.rb'
data/README.md CHANGED
@@ -24,7 +24,7 @@ Please see the Foreman manual for appropriate instructions:
24
24
 
25
25
  Set up the repo as explained in the link above, then run
26
26
 
27
- # yum install ruby193-rubygem-foreman-tasks
27
+ # yum install tfm-rubygem-foreman-tasks
28
28
 
29
29
  ### Bundle (gem)
30
30
 
@@ -131,7 +131,7 @@ The executor process needs to be executed before the web server. You
131
131
  can run it by:
132
132
 
133
133
  ```
134
- foreman-rake foreman_tasks:dynflow:executor
134
+ foreman-rake dynflow:executor
135
135
  ```
136
136
 
137
137
  Also, there is a possibility to run the executor in daemonized mode
@@ -1,13 +1,7 @@
1
1
  module ForemanTasks
2
2
  module Concerns
3
3
  module HostsControllerExtension
4
- extend ActiveSupport::Concern
5
-
6
- included do
7
- alias_method_chain :facts, :dynflow
8
- end
9
-
10
- def facts_with_dynflow
4
+ def facts
11
5
  task = ForemanTasks.async_task(::Actions::Foreman::Host::ImportFacts,
12
6
  detect_host_type,
13
7
  params[:name],
@@ -17,7 +17,12 @@ module Actions
17
17
 
18
18
  def fill_planning_errors_to_continuous_output(continuous_output)
19
19
  execution_plan.errors.map do |e|
20
- continuous_output.add_exception(_('Failed to initialize'), e, task.started_at)
20
+ case e.exception
21
+ when ::Actions::ProxyAction::ProxyActionMissing
22
+ continuous_output.add_output(e.message, 'debug', task.started_at)
23
+ else
24
+ continuous_output.add_exception(_('Failed to initialize'), e.exception, task.started_at)
25
+ end
21
26
  end
22
27
  end
23
28
  end
@@ -0,0 +1,73 @@
1
+ module Actions
2
+ module Middleware
3
+ class WatchDelegatedProxySubTasks < ::Dynflow::Middleware
4
+ class CheckOnProxyActions; end
5
+ # Poll the proxy every 10 minutes
6
+ POLL_INTERVAL = 600
7
+ BATCH_SIZE = 1000
8
+
9
+ def run(event = nil)
10
+ if event.nil?
11
+ set_clock
12
+ elsif event == CheckOnProxyActions
13
+ check_triggered
14
+ set_clock
15
+ action.send(:suspend)
16
+ end
17
+ pass event
18
+ end
19
+
20
+ private
21
+
22
+ def set_clock
23
+ action.world.clock.ping action.send(:suspended_action),
24
+ POLL_INTERVAL,
25
+ CheckOnProxyActions
26
+ end
27
+
28
+ def check_triggered
29
+ # Sort by proxy_url so there are less requests per proxy
30
+ source = remote_tasks.triggered.order(:proxy_url, :id)
31
+ source.find_in_batches(:batch_size => BATCH_SIZE) do |batch|
32
+ tasks = batch.group_by(&:proxy_url)
33
+ .map { |(url, tasks)| poll_proxy_tasks(url, tasks) }
34
+ .flatten
35
+ process_task_results tasks
36
+ end
37
+ end
38
+
39
+ def process_task_results(tasks)
40
+ missing, present = tasks.partition { |task| task.result.nil? }
41
+ notify ::Actions::ProxyAction::ProxyActionMissing.new, missing if missing.any?
42
+
43
+ stopped = present.select { |task| %w[stopped paused].include? task.result['state'] }
44
+ notify ::Actions::ProxyAction::ProxyActionStopped.new, stopped if stopped.any?
45
+ end
46
+
47
+ def notify(event, tasks)
48
+ tasks.each do |task|
49
+ action.world.event task.execution_plan_id,
50
+ task.step_id,
51
+ event
52
+ end
53
+ end
54
+
55
+ def remote_tasks
56
+ action.task.remote_sub_tasks
57
+ end
58
+
59
+ def poll_proxy_tasks(url, tasks)
60
+ proxy = ProxyAPI::ForemanDynflow::DynflowProxy.new(:url => url)
61
+ results = proxy.task_states(tasks.map(&:remote_task_id))
62
+ tasks.map do |task|
63
+ task.result = results[task.remote_task_id]
64
+ task
65
+ end
66
+ rescue => e
67
+ # We could not reach the remote task, we assume it's gone
68
+ action.action_logger.warn(_('Failed to check on tasks on proxy at %{url}: %{exception}') % { :url => url, :exception => e.message })
69
+ tasks
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,10 +1,12 @@
1
1
  module Actions
2
2
  class ProxyAction < Base
3
3
  include ::Dynflow::Action::Cancellable
4
- include ::Dynflow::Action::Timeouts
5
4
 
6
5
  middleware.use ::Actions::Middleware::HideSecrets
7
6
 
7
+ execution_plan_hooks.use :clean_remote_task, :on => :stopped
8
+ execution_plan_hooks.use :wipe_secrets!, :on => :stopped
9
+
8
10
  class CallbackData
9
11
  attr_reader :data
10
12
 
@@ -13,6 +15,14 @@ module Actions
13
15
  end
14
16
  end
15
17
 
18
+ class ProxyActionMissing < RuntimeError
19
+ def backtrace
20
+ []
21
+ end
22
+ end
23
+
24
+ class ProxyActionStopped; end
25
+
16
26
  def plan(proxy, klass, options)
17
27
  options[:connection_options] ||= {}
18
28
  default_connection_options.each { |key, value| options[:connection_options][key] ||= value }
@@ -23,7 +33,7 @@ module Actions
23
33
  with_connection_error_handling(event) do |event|
24
34
  case event
25
35
  when nil
26
- if output[:proxy_task_id]
36
+ if remote_task
27
37
  on_resume
28
38
  else
29
39
  trigger_proxy_task
@@ -37,38 +47,41 @@ module Actions
37
47
  abort_proxy_task
38
48
  when CallbackData
39
49
  on_data(event.data)
40
- when ::Dynflow::Action::Timeouts::Timeout
41
- check_task_status
50
+ when ProxyActionMissing
51
+ on_proxy_action_missing
52
+ when ProxyActionStopped
53
+ on_proxy_action_stopped
42
54
  else
43
55
  raise "Unexpected event #{event.inspect}"
44
56
  end
45
57
  end
46
58
  end
47
59
 
60
+ def remote_task
61
+ @remote_task ||= ForemanTasks::RemoteTask.find_by(:execution_plan_id => execution_plan_id, :step_id => run_step_id)
62
+ end
63
+
48
64
  def trigger_proxy_task
49
65
  suspend do |_suspended_action|
50
- set_timeout! unless timeout_set?
51
66
  response = proxy.trigger_task(proxy_action_name,
52
67
  input.merge(:callback => { :task_id => task.id,
53
68
  :step_id => run_step_id }))
69
+ ::ForemanTasks::RemoteTask.new(:remote_task_id => response['task_id'], :execution_plan_id => execution_plan_id,
70
+ :state => 'triggered', :proxy_url => input[:proxy_url], :step_id => run_step_id).save!
54
71
  output[:proxy_task_id] = response['task_id']
55
72
  end
56
73
  end
57
74
 
58
75
  def check_task_status
59
- if output[:proxy_task_id]
60
- response = proxy.status_of_task(output[:proxy_task_id])
61
- if %w[stopped paused].include? response['state']
62
- if response['result'] == 'error'
63
- raise ::Foreman::Exception, _('The smart proxy task %s failed.') % output[:proxy_task_id]
64
- else
65
- on_data(response['actions'].find { |block_action| block_action['class'] == proxy_action_name }['output'])
66
- end
76
+ response = proxy.status_of_task(output[:proxy_task_id])
77
+ if %w[stopped paused].include? response['state']
78
+ if response['result'] == 'error'
79
+ raise ::Foreman::Exception, _('The smart proxy task %s failed.') % output[:proxy_task_id]
67
80
  else
68
- suspend
81
+ on_data(response['actions'].find { |block_action| block_action['class'] == proxy_action_name }['output'])
69
82
  end
70
83
  else
71
- process_timeout
84
+ suspend
72
85
  end
73
86
  end
74
87
 
@@ -95,12 +108,21 @@ module Actions
95
108
  # @override to put custom logic on event handling
96
109
  def on_data(data)
97
110
  output[:proxy_output] = data
98
- wipe_secrets!
99
111
  end
100
112
 
101
- def wipe_secrets!
113
+ # Removes the :secrets key from the action's input and output and saves the action
114
+ def wipe_secrets!(_execution_plan)
102
115
  input.delete(:secrets)
103
116
  output.delete(:secrets)
117
+ world.persistence.save_action(execution_plan_id, self)
118
+ end
119
+
120
+ def on_proxy_action_missing
121
+ error! ProxyActionMissing.new(_('Proxy task gone missing from the smart proxy'))
122
+ end
123
+
124
+ def on_proxy_action_stopped
125
+ check_task_status
104
126
  end
105
127
 
106
128
  # @override String name of an action to be triggered on server
@@ -113,8 +135,8 @@ module Actions
113
135
  end
114
136
 
115
137
  def proxy_output(live = false)
116
- if output.key?(:proxy_output)
117
- output.fetch(:proxy_output) || {}
138
+ if output.key?(:proxy_output) || state == :error
139
+ output.fetch(:proxy_output, {})
118
140
  elsif live && output[:proxy_task_id]
119
141
  proxy_data = proxy.status_of_task(output[:proxy_task_id])['actions'].detect { |action| action['class'] == proxy_action_name }
120
142
  proxy_data.fetch('output', {})
@@ -146,22 +168,15 @@ module Actions
146
168
  output[:metadata] = thing
147
169
  end
148
170
 
149
- def timeout_set?
150
- !metadata[:timeout].nil?
151
- end
152
-
153
- def set_timeout!
154
- time = Time.zone.now + input[:connection_options][:timeout]
155
- schedule_timeout(time)
156
- metadata[:timeout] = time.to_s
157
- end
158
-
159
171
  def default_connection_options
160
172
  # Fails if the plan is not finished within 60 seconds from the first task trigger attempt on the smart proxy
161
173
  # If the triggering fails, it retries 3 more times with 15 second delays
162
174
  { :retry_interval => Setting['foreman_tasks_proxy_action_retry_interval'] || 15,
163
- :retry_count => Setting['foreman_tasks_proxy_action_retry_count'] || 4,
164
- :timeout => Setting['foreman_tasks_proxy_action_start_timeout'] || 60 }
175
+ :retry_count => Setting['foreman_tasks_proxy_action_retry_count'] || 4 }
176
+ end
177
+
178
+ def clean_remote_task(*_args)
179
+ remote_task.destroy! if remote_task
165
180
  end
166
181
 
167
182
  private
@@ -173,7 +188,7 @@ module Actions
173
188
  def with_connection_error_handling(event = nil)
174
189
  yield event
175
190
  rescue ::RestClient::Exception, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ETIMEDOUT => e
176
- if event.class == CallbackData || event == ::Dynflow::Action::Timeouts::Timeout
191
+ if event.class == CallbackData
177
192
  raise e
178
193
  else
179
194
  handle_connection_exception(e, event)
@@ -34,6 +34,11 @@ module ProxyAPI
34
34
  def tasks_count(state)
35
35
  MultiJson.load(Task.new(@args).send(:get, "count?state=#{state}"))['count'].to_i
36
36
  end
37
+
38
+ def task_states(ids)
39
+ payload = MultiJson.dump(:task_ids => ids)
40
+ MultiJson.load(Task.new(@args).send(:post, payload, 'status'))
41
+ end
37
42
  end
38
43
  end
39
44
  end
@@ -0,0 +1,12 @@
1
+ module ForemanTasks
2
+ class RemoteTask < ApplicationRecord
3
+ attr_accessor :result
4
+
5
+ belongs_to :task, :class_name => 'ForemanTasks::Task',
6
+ :primary_key => :external_id,
7
+ :foreign_key => :execution_plan_id,
8
+ :inverse_of => :remote_tasks
9
+
10
+ scope :triggered, -> { where(:state => 'triggered') }
11
+ end
12
+ end
@@ -20,6 +20,10 @@ module ForemanTasks
20
20
  has_many :sub_tasks, :class_name => 'ForemanTasks::Task', :foreign_key => :parent_task_id, :dependent => :nullify
21
21
  has_many :locks, :dependent => :destroy
22
22
 
23
+ has_many :remote_sub_tasks, :class_name => 'ForemanTasks::RemoteTask', :through => :sub_tasks, :source => :remote_tasks
24
+
25
+ has_many :remote_tasks, :class_name => 'ForemanTasks::RemoteTask', :primary_key => :external_id, :foreign_key => :execution_plan_id, :dependent => :destroy
26
+
23
27
  has_many :task_group_members, :dependent => :destroy
24
28
  has_many :task_groups, :through => :task_group_members
25
29
  if Rails::VERSION::MAJOR < 4
@@ -210,7 +214,8 @@ module ForemanTasks
210
214
  end
211
215
 
212
216
  def action
213
- super || to_label
217
+ return to_label if super.blank?
218
+ super
214
219
  end
215
220
 
216
221
  def to_label
@@ -15,11 +15,11 @@ module ForemanTasks
15
15
  self.start_at = string_to_time(utc_zone, data[:start_at]) if data[:start_at]
16
16
  self.start_before = string_to_time(utc_zone, data[:start_before]) if data[:start_before]
17
17
  self.parent_task_id ||= begin
18
- if main_action.caller_execution_plan_id
18
+ if main_action.present? && main_action.caller_execution_plan_id
19
19
  DynflowTask.where(:external_id => main_action.caller_execution_plan_id).first!.id
20
20
  end
21
21
  end
22
- self.label ||= main_action && main_action.class.name
22
+ self[:label] ||= label
23
23
  changes = self.changes
24
24
  save!
25
25
  changes
@@ -69,12 +69,19 @@ module ForemanTasks
69
69
  execution_plan(false)
70
70
  end
71
71
 
72
+ def label
73
+ return execution_plan_action.input['job_class'] if active_job?
74
+ return main_action.class.name if main_action.present?
75
+ self[:label]
76
+ end
77
+
72
78
  def input
73
- main_action.respond_to?(:task_input) && main_action.task_input
79
+ return execution_plan_action.input['job_arguments'] if active_job?
80
+ main_action.task_input if main_action.respond_to?(:task_input)
74
81
  end
75
82
 
76
83
  def output
77
- main_action.respond_to?(:task_output) && main_action.task_output
84
+ main_action.task_output if main_action.respond_to?(:task_output)
78
85
  end
79
86
 
80
87
  def failed_steps
@@ -98,17 +105,47 @@ module ForemanTasks
98
105
 
99
106
  def main_action
100
107
  return @main_action if defined?(@main_action)
101
- @main_action = execution_plan && execution_plan.root_plan_step.try(:action, execution_plan)
108
+ if active_job?
109
+ action = execution_plan_action
110
+ active_job_action(action.input['job_class'], action.input['job_arguments'])
111
+ else
112
+ @main_action = execution_plan && execution_plan.root_plan_step.try(:action, execution_plan)
113
+ end
114
+ end
115
+
116
+ # The class for ActiveJob jobs in Dynflow, JobWrapper is not expected to
117
+ # implement any humanized actions. Individual jobs are expected to implement
118
+ # humanized_* methods for foreman-tasks integration.
119
+ def active_job_action(klass, args)
120
+ return if klass.blank?
121
+ klass.constantize.new(*args)
122
+ end
123
+
124
+ def active_job?
125
+ return false unless execution_plan.present? &&
126
+ execution_plan.root_plan_step.present?
127
+ execution_plan_action.class == ::Dynflow::ActiveJob::QueueAdapters::JobWrapper
128
+ end
129
+
130
+ def execution_plan_action
131
+ execution_plan.root_plan_step.action(execution_plan)
132
+ end
133
+
134
+ def execution_scheduled?
135
+ main_action.nil? || main_action.respond_to?(:execution_plan) &&
136
+ main_action.execution_plan.state == :scheduled ||
137
+ execution_plan.state == :scheduled
102
138
  end
103
139
 
104
140
  def get_humanized(method)
105
141
  @humanized_cache ||= {}
106
- if [:name, :input, :output, :error].include?(method)
107
- method = "humanized_#{method}".to_sym
108
- end
142
+ method = find_humanize_method_kind(method)
109
143
  Match! method, :humanized_name, :humanized_input, :humanized_output, :humanized_errors
110
- return N_('N/A') if method != :humanized_name && (main_action.nil? || main_action.execution_plan.state == :scheduled)
111
- return N_(label) if method == :humanized_name && main_action.nil?
144
+ if method != :humanized_name && execution_scheduled?
145
+ return N_('N/A')
146
+ elsif method == :humanized_name && main_action.nil?
147
+ return N_(label)
148
+ end
112
149
  @humanized_cache[method] ||= begin
113
150
  if main_action.respond_to? method
114
151
  begin
@@ -120,6 +157,13 @@ module ForemanTasks
120
157
  end
121
158
  end
122
159
 
160
+ def find_humanize_method_kind(method)
161
+ return method if /humanized_.*/ =~ method
162
+ if [:name, :input, :output, :error].include?(method)
163
+ "humanized_#{method}".to_sym
164
+ end
165
+ end
166
+
123
167
  def self.consistency_check
124
168
  fixed_count = 0
125
169
  logger = Foreman::Logging.logger('foreman-tasks')
@@ -9,8 +9,7 @@ class Setting::ForemanTasks < Setting
9
9
  set('dynflow_enable_console', N_('Enable the dynflow console (/foreman_tasks/dynflow) for debugging'), true),
10
10
  set('dynflow_console_require_auth', N_('Require user to be authenticated as user with admin rights when accessing dynflow console'), true),
11
11
  set('foreman_tasks_proxy_action_retry_count', N_('Number of attempts to start a task on the smart proxy before failing'), 4),
12
- set('foreman_tasks_proxy_action_retry_interval', N_('Time in seconds between retries'), 15),
13
- set('foreman_tasks_proxy_action_start_timeout', N_('Time in second during which a task has to be started on the proxy'), 60)
12
+ set('foreman_tasks_proxy_action_retry_interval', N_('Time in seconds between retries'), 15)
14
13
  ].each { |s| create! s.update(:category => 'Setting::ForemanTasks') }
15
14
  end
16
15
 
@@ -0,0 +1,16 @@
1
+ class AddRemoteTasks < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_table :foreman_tasks_remote_tasks do |t|
4
+ t.string :execution_plan_id, :null => false
5
+ t.integer :step_id, :null => false
6
+ t.string :state, :null => false, :default => 'new'
7
+
8
+ t.string :proxy_url, :null => false
9
+ t.string :remote_task_id
10
+
11
+ t.index [:execution_plan_id, :step_id], :name => 'index_foreman_tasks_plan_id_and_step_id'
12
+
13
+ t.datetime :created_at
14
+ end
15
+ end
16
+ end
@@ -100,6 +100,7 @@ module ForemanTasks
100
100
  with_batches(source, name) do |chunk|
101
101
  delete_tasks chunk
102
102
  delete_dynflow_plans chunk
103
+ delete_remote_tasks(chunk)
103
104
  end
104
105
  end
105
106
  delete_orphaned_dynflow_tasks
@@ -115,6 +116,10 @@ module ForemanTasks
115
116
  tasks.delete_all
116
117
  end
117
118
 
119
+ def delete_remote_tasks(chunk)
120
+ ForemanTasks::RemoteTask.where(:execution_plan_id => chunk.map(&:external_id)).delete_all
121
+ end
122
+
118
123
  def tasks_to_csv(dataset, backup_dir, file_name)
119
124
  with_backup_file(backup_dir, file_name) do |csv, appending|
120
125
  csv << ForemanTasks::Task.attribute_names.to_csv unless appending
@@ -133,7 +133,7 @@ module ForemanTasks
133
133
  # to enable async Foreman operations using Dynflow
134
134
  if ENV['FOREMAN_TASKS_MONKEYS'] == 'true'
135
135
  config.to_prepare do
136
- ::Api::V2::HostsController.send :include, ForemanTasks::Concerns::HostsControllerExtension
136
+ ::Api::V2::HostsController.send :prepend, ForemanTasks::Concerns::HostsControllerExtension
137
137
  ::Host::Base.send :include, ForemanTasks::Concerns::HostActionSubject
138
138
  end
139
139
  end
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '0.13.0'.freeze
2
+ VERSION = '0.13.1'.freeze
3
3
  end
@@ -18,6 +18,33 @@ module ForemanTasks
18
18
  assert_response :success
19
19
  assert_template 'api/tasks/show'
20
20
  end
21
+
22
+ test_attributes :pid => 'a2a81ca2-63c4-47f5-9314-5852f5e2617f'
23
+ it 'search for non-existent task' do
24
+ get :show, params: { :id => 'abc123' }
25
+ assert_response :missing
26
+ assert_includes @response.body, 'Resource task not found by id'
27
+ end
28
+ end
29
+
30
+ describe 'GET /api/tasks/summary' do
31
+ class DummyTestSummaryAction < Support::DummyDynflowAction
32
+ # a dummy test action that do nothing
33
+ def run
34
+ # a dummy run method
35
+ end
36
+ end
37
+
38
+ test_attributes :pid => 'bdcab413-a25d-4fe1-9db4-b50b5c31ebce'
39
+ it 'get tasks summary' do
40
+ ForemanTasks.trigger(DummyTestSummaryAction)
41
+ get :summary
42
+ assert_response :success
43
+ response = JSON.parse(@response.body)
44
+ assert_kind_of Array, response
45
+ refute response.empty?
46
+ assert_kind_of Hash, response[0]
47
+ end
21
48
  end
22
49
 
23
50
  describe 'POST /tasks/callback' do
@@ -26,7 +26,7 @@ module ForemanTasks
26
26
  { 'foo' => 'bar',
27
27
  'secrets' => secrets,
28
28
  'connection_options' =>
29
- { 'retry_interval' => 15, 'retry_count' => 4, 'timeout' => 60 },
29
+ { 'retry_interval' => 15, 'retry_count' => 4 },
30
30
  'proxy_url' => 'proxy.example.com',
31
31
  'proxy_action_name' => 'Proxy::DummyAction',
32
32
  'callback' => { 'task_id' => Support::DummyProxyAction.proxy.uuid, 'step_id' => @action.run_step_id } }]
@@ -107,6 +107,13 @@ module ForemanTasks
107
107
  it 'wipes secrets' do
108
108
  @action.input[:secrets].must_equal secrets
109
109
  action = run_action(@action, ::Actions::ProxyAction::CallbackData.new('result' => 'success'))
110
+
111
+ # #wipe_secrets! gets called as a hook, hooks are not triggered when using action testing helpers
112
+ persistence = mock
113
+ persistence.stubs(:save_action)
114
+ action.world.stubs(:persistence).returns(persistence)
115
+ action.wipe_secrets!(nil)
116
+
110
117
  refute action.input.key?(:secrets)
111
118
  end
112
119
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman-tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Nečas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-02 00:00:00.000000000 Z
11
+ date: 2018-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: foreman-tasks-core
@@ -143,6 +143,7 @@ files:
143
143
  - app/lib/actions/middleware/keep_current_user.rb
144
144
  - app/lib/actions/middleware/rails_executor_wrap.rb
145
145
  - app/lib/actions/middleware/recurring_logic.rb
146
+ - app/lib/actions/middleware/watch_delegated_proxy_sub_tasks.rb
146
147
  - app/lib/actions/proxy_action.rb
147
148
  - app/lib/actions/recurring_action.rb
148
149
  - app/lib/actions/serializers/active_record_serializer.rb
@@ -152,6 +153,7 @@ files:
152
153
  - app/models/foreman_tasks/concerns/host_action_subject.rb
153
154
  - app/models/foreman_tasks/lock.rb
154
155
  - app/models/foreman_tasks/recurring_logic.rb
156
+ - app/models/foreman_tasks/remote_task.rb
155
157
  - app/models/foreman_tasks/task.rb
156
158
  - app/models/foreman_tasks/task/dynflow_task.rb
157
159
  - app/models/foreman_tasks/task/status_explicator.rb
@@ -203,6 +205,7 @@ files:
203
205
  - db/migrate/20160924213030_change_tasks_widget_names.rb
204
206
  - db/migrate/20161003091412_add_missing_indexes.rb
205
207
  - db/migrate/20171026082635_add_task_action.foreman_tasks.rb
208
+ - db/migrate/20180207150921_add_remote_tasks.foreman_tasks.rb
206
209
  - db/migrate/20180216092715_use_uuid.rb
207
210
  - db/seeds.d/20-foreman_tasks_permissions.rb
208
211
  - db/seeds.d/60-dynflow_proxy_feature.rb
@@ -283,7 +286,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
283
286
  version: '0'
284
287
  requirements: []
285
288
  rubyforge_project:
286
- rubygems_version: 2.6.8
289
+ rubygems_version: 2.7.3
287
290
  signing_key:
288
291
  specification_version: 4
289
292
  summary: Foreman plugin for showing tasks information for resoruces and users