foreman-tasks 5.2.3 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/foreman_tasks/tasks_controller.rb +18 -18
  3. data/app/lib/actions/base.rb +1 -0
  4. data/app/lib/actions/foreman/host/import_facts.rb +1 -1
  5. data/app/lib/actions/middleware/watch_delegated_proxy_sub_tasks.rb +18 -11
  6. data/app/lib/actions/proxy_action.rb +1 -1
  7. data/app/lib/actions/task_synchronization.rb +65 -0
  8. data/app/lib/actions/trigger_proxy_batch.rb +2 -3
  9. data/app/models/foreman_tasks/recurring_logic.rb +1 -1
  10. data/app/models/foreman_tasks/remote_task.rb +13 -7
  11. data/app/models/foreman_tasks/task/dynflow_task.rb +20 -21
  12. data/app/views/foreman_tasks/api/locks/show.json.rabl +4 -0
  13. data/config/routes.rb +1 -1
  14. data/db/migrate/20210708123832_add_parent_task_id_to_remote_tasks.foreman_tasks.rb +5 -0
  15. data/db/migrate/20211123170430_tasks_settings_to_dsl_category.rb +5 -0
  16. data/extra/foreman-tasks-cleanup.sh +2 -19
  17. data/foreman-tasks.gemspec +1 -1
  18. data/lib/foreman_tasks/dynflow/configuration.rb +0 -5
  19. data/lib/foreman_tasks/engine.rb +56 -9
  20. data/lib/foreman_tasks/tasks/export_tasks.rake +19 -27
  21. data/lib/foreman_tasks/test_helpers.rb +1 -1
  22. data/lib/foreman_tasks/version.rb +1 -1
  23. data/test/factories/task_factory.rb +1 -1
  24. data/test/lib/actions/middleware/keep_current_taxonomies_test.rb +9 -1
  25. data/test/support/dummy_dynflow_action.rb +1 -1
  26. data/test/unit/actions/trigger_proxy_batch_test.rb +0 -1
  27. data/test/unit/remote_task_test.rb +0 -26
  28. data/test/unit/troubleshooting_help_generator_test.rb +0 -1
  29. data/test/unit/ui_notifications_test.rb +0 -1
  30. data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.js +1 -1
  31. data/webpack/ForemanTasks/Components/TaskActions/index.js +1 -1
  32. data/webpack/ForemanTasks/Components/TaskDetails/Components/Locks.js +28 -14
  33. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Locks.test.js.snap +50 -42
  34. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsActions.js +1 -1
  35. data/webpack/ForemanTasks/Components/TasksTable/TasksBulkActions.js +1 -1
  36. data/webpack/__mocks__/foremanReact/{redux/actions/toasts.js → components/ToastsList/index.js} +0 -0
  37. metadata +9 -8
  38. data/app/models/setting/foreman_tasks.rb +0 -29
  39. data/lib/foreman_tasks/dynflow/persistence.rb +0 -46
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f1c154c83d919ac42b7d12edd03cff55e67f11a1367a67250a9a090759cd099
4
- data.tar.gz: 519d85e8e975aab38b2e2314eb5761cb2bb3fe2d07b291822f3c667e6c0feab4
3
+ metadata.gz: 6f5ae3bb36274977790149d11f722477d6db0254b251ffa1dc79ec850a206183
4
+ data.tar.gz: bfd76d0fcc43a78e34c1831a392042e5770bd09276d95d126043f3a14db174b7
5
5
  SHA512:
6
- metadata.gz: 28a96b1b6c28bb5697607d69950167100815c667ecbebbc3f9f446f7e44899e7459e79a4f8efe8f53d798ee7ece6ebaad6d0becd610b11ab499cd32a4e2b5b53
7
- data.tar.gz: 16c30b57fc2b2f82be9baf9d2dcf4c1b9f7a47df3013393d274fb6268ea04b6a066d3464bdeb1d911b998d7d5e68faa19ea9551193a25a07fb1cc12ddb45ee66
6
+ metadata.gz: b2bde1cb649304ba13019b544c8db8f7fa72b2e0539d47b2db07d6e945364d08b4e5fa0b100b701aa7f080e8be308ad7a6af404eaee2865e7b13fd423f9a9300
7
+ data.tar.gz: 00a77aaf192bbfc12a70dc034aa75bfd728c18956b1b9377bbef3d7fce7491c68cfa520d9fd04de4789c721fcc2d2b2b605b63a9165601092841e44184219dfc
@@ -4,8 +4,6 @@ module ForemanTasks
4
4
  include Foreman::Controller::CsvResponder
5
5
  include ForemanTasks::FindTasksCommon
6
6
 
7
- before_action :find_dynflow_task, only: [:unlock, :force_unlock, :cancel, :cancel_step, :resume]
8
-
9
7
  def show
10
8
  @task = resource_base.find(params[:id])
11
9
  render :layout => !request.xhr?
@@ -33,7 +31,8 @@ module ForemanTasks
33
31
  end
34
32
 
35
33
  def cancel_step
36
- result = ForemanTasks.dynflow.world.event(@dynflow_task.external_id, params[:step_id].to_i, ::Dynflow::Action::Cancellable::Cancel).wait
34
+ task = find_dynflow_task
35
+ result = ForemanTasks.dynflow.world.event(task.external_id, params[:step_id].to_i, ::Dynflow::Action::Cancellable::Cancel).wait
37
36
  if result.rejected?
38
37
  render json: { error: result.reason }, status: :bad_request
39
38
  else
@@ -42,7 +41,8 @@ module ForemanTasks
42
41
  end
43
42
 
44
43
  def cancel
45
- if @dynflow_task.cancel
44
+ task = find_dynflow_task
45
+ if task.cancel
46
46
  render json: { statusText: 'OK' }
47
47
  else
48
48
  render json: {}, status: :bad_request
@@ -50,17 +50,19 @@ module ForemanTasks
50
50
  end
51
51
 
52
52
  def abort
53
- if @dynflow_task.abort
53
+ task = find_dynflow_task
54
+ if task.abort
54
55
  flash[:info] = _('Trying to abort the task')
55
56
  else
56
57
  flash[:warning] = _('The task cannot be aborted at the moment.')
57
58
  end
58
- redirect_back(:fallback_location => foreman_tasks_task_path(@dynflow_task))
59
+ redirect_back(:fallback_location => foreman_tasks_task_path(task))
59
60
  end
60
61
 
61
62
  def resume
62
- if @dynflow_task.resumable?
63
- ForemanTasks.dynflow.world.execute(@dynflow_task.execution_plan.id)
63
+ task = find_dynflow_task
64
+ if task.resumable?
65
+ ForemanTasks.dynflow.world.execute(task.execution_plan.id)
64
66
  render json: { statusText: 'OK' }
65
67
  else
66
68
  render json: {}, status: :bad_request
@@ -68,8 +70,10 @@ module ForemanTasks
68
70
  end
69
71
 
70
72
  def unlock
71
- if @dynflow_task.paused?
72
- unlock_task(@dynflow_task)
73
+ task = find_dynflow_task
74
+ if task.paused?
75
+ task.state = :stopped
76
+ task.save!
73
77
  render json: { statusText: 'OK' }
74
78
  else
75
79
  render json: {}, status: :bad_request
@@ -77,7 +81,9 @@ module ForemanTasks
77
81
  end
78
82
 
79
83
  def force_unlock
80
- unlock_task(@dynflow_task)
84
+ task = find_dynflow_task
85
+ task.state = :stopped
86
+ task.save!
81
87
  render json: { statusText: 'OK' }
82
88
  end
83
89
 
@@ -92,12 +98,6 @@ module ForemanTasks
92
98
 
93
99
  private
94
100
 
95
- def unlock_task(task)
96
- task.state = :stopped
97
- task.locks.destroy_all
98
- task.save!
99
- end
100
-
101
101
  def respond_with_tasks(scope)
102
102
  @tasks = filter(scope, paginate: false).with_duration
103
103
  csv_response(@tasks, [:id, :action, :state, :result, 'started_at.in_time_zone', 'ended_at.in_time_zone', :duration, :username], ['Id', 'Action', 'State', 'Result', 'Started At', 'Ended At', 'Duration', 'User'])
@@ -123,7 +123,7 @@ module ForemanTasks
123
123
  end
124
124
 
125
125
  def find_dynflow_task
126
- @dynflow_task = resource_scope.where(:type => 'ForemanTasks::Task::DynflowTask').find(params[:id])
126
+ resource_scope.where(:type => 'ForemanTasks::Task::DynflowTask').find(params[:id])
127
127
  end
128
128
 
129
129
  def filter(scope, paginate: true)
@@ -2,6 +2,7 @@ module Actions
2
2
  class Base < Dynflow::Action
3
3
  middleware.use ::Actions::Middleware::RailsExecutorWrap
4
4
  include Actions::Helpers::LifecycleLogging
5
+ include Actions::TaskSynchronization
5
6
 
6
7
  execution_plan_hooks.use :notify_paused, :on => [:paused]
7
8
 
@@ -26,7 +26,7 @@ module Actions
26
26
  def run
27
27
  ::User.as :admin do
28
28
  host = ::Host.find(input[:host][:id])
29
- state = host.import_facts(input[:facts], proxy)
29
+ state = ::HostFactImporter.new(host).import_facts(input[:facts], proxy)
30
30
  output[:state] = state
31
31
  end
32
32
  rescue ::Foreman::Exception => e
@@ -26,17 +26,26 @@ module Actions
26
26
  def check_triggered
27
27
  in_remote_task_batches(remote_tasks.triggered) do |batch|
28
28
  batch.group_by(&:proxy_url).each do |(url, tasks)|
29
- tasks = poll_proxy_tasks(url, tasks).flatten
30
- process_task_results tasks
29
+ results = poll_proxy_tasks(url, tasks)
30
+ process_task_results tasks, results
31
31
  end
32
32
  end
33
33
  end
34
34
 
35
- def process_task_results(tasks)
36
- missing, present = tasks.partition { |task| task.result.nil? }
35
+ def process_task_results(tasks, results)
36
+ possibly_missing, present = tasks.partition { |task| !results.key?(task.remote_task_id) }
37
+ missing = possibly_missing.select do |task|
38
+ # Really missing are tasks which are missing and:
39
+ # don't have a remote parent
40
+ # had a remote parent but the proxy doesn't have the remote parent anymore
41
+ # has a remote parent, proxy has the remote parent but it is stopped or paused
42
+ task.parent_task_id.nil? ||
43
+ !results.key?(task.parent_task_id) ||
44
+ %(stopped paused).include?(results[task.parent_task_id]['state'])
45
+ end
37
46
  notify ::Actions::ProxyAction::ProxyActionMissing.new, missing if missing.any?
38
47
 
39
- stopped = present.select { |task| %w[stopped paused].include? task.result['state'] }
48
+ stopped = present.select { |task| %w[stopped paused].include? results.dig(task.remote_task_id, 'state') }
40
49
  notify ::Actions::ProxyAction::ProxyActionStopped.new, stopped if stopped.any?
41
50
  end
42
51
 
@@ -52,15 +61,13 @@ module Actions
52
61
 
53
62
  def poll_proxy_tasks(url, tasks)
54
63
  proxy = ProxyAPI::ForemanDynflow::DynflowProxy.new(:url => url)
55
- results = proxy.task_states(tasks.map(&:remote_task_id))
56
- tasks.map do |task|
57
- task.result = results[task.remote_task_id]
58
- task
59
- end
64
+ # Get statuses of tasks and their optional parent tasks
65
+ ids = (tasks.map(&:remote_task_id) + tasks.map(&:parent_task_id)).uniq
66
+ proxy.task_states(ids)
60
67
  rescue => e
61
68
  # We could not reach the remote task, we'll try again next time
62
69
  action.action_logger.warn(_('Failed to check on tasks on proxy at %{url}: %{exception}') % { :url => url, :exception => e.message })
63
- []
70
+ {}
64
71
  end
65
72
 
66
73
  def in_remote_task_batches(scope)
@@ -263,7 +263,7 @@ module Actions
263
263
  end
264
264
 
265
265
  def proxy_task_id
266
- output[:proxy_task_id] ||= remote_task.try(:remote_task_id)
266
+ output[:proxy_task_id] || remote_task.try(:remote_task_id) || @execution_plan_id
267
267
  end
268
268
  end
269
269
  end
@@ -0,0 +1,65 @@
1
+ module Actions
2
+ # Examples:
3
+
4
+ # # Action A which emits an event when it successfully finishes.
5
+ # class A
6
+ # include ::Actions::ObservableAction
7
+ # # ... rest ...
8
+ # end
9
+
10
+ # # Action B which emits an event when it successfully finishes or fails.
11
+ # class B
12
+ # include ::Actions::ObservableAction
13
+ #
14
+ # execution_plan_hooks.use :emit_event_failure, :on => [:failure]
15
+ #
16
+ # def self.event_names
17
+ # super + [event_name_base + '_' + event_name_suffix(:failure)]
18
+ # end
19
+ #
20
+ # def emit_event_failure(plan)
21
+ # emit_event(plan, :failure)
22
+ # end
23
+ # # ... rest ...
24
+ # end
25
+ module TaskSynchronization
26
+ def self.included(base)
27
+ base.execution_plan_hooks.use :sync_execution_plan_to_task, on: ::Dynflow::ExecutionPlan.states
28
+ end
29
+
30
+ def sync_execution_plan_to_task(plan)
31
+ return unless root_action?
32
+ on_execution_plan_save(plan)
33
+ rescue => e
34
+ ::Foreman::Logging.exception('Error on on_execution_plan_save event', e,
35
+ :logger => 'dynflow')
36
+ end
37
+
38
+ private
39
+
40
+ def on_execution_plan_save(execution_plan)
41
+ # We can load the data unless the execution plan was properly planned and saved
42
+ # including its steps
43
+ case execution_plan.state
44
+ when :pending
45
+ task = ForemanTasks::Task::DynflowTask.new_for_execution_plan(execution_plan)
46
+ task.start_at ||= Time.zone.now
47
+ task.save!
48
+ when :scheduled
49
+ delayed_plan = world.persistence.load_delayed_plan(execution_plan.id)
50
+ raise ::Foreman::Exception, 'Plan is delayed but the delay record is missing' if delayed_plan.nil?
51
+ task = ::ForemanTasks::Task::DynflowTask.find_by!(:external_id => execution_plan.id)
52
+ task.update_from_dynflow(execution_plan, delayed_plan)
53
+ when :planning
54
+ task = ::ForemanTasks::Task::DynflowTask.where(:external_id => execution_plan.id).first
55
+ task.update_from_dynflow(execution_plan)
56
+ else
57
+ if (task = ::ForemanTasks::Task::DynflowTask.where(:external_id => execution_plan.id).first)
58
+ unless task.state.to_s == execution_plan.state.to_s
59
+ task.update_from_dynflow(execution_plan)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -37,11 +37,10 @@ module Actions
37
37
  # Group the tasks by operation, in theory there should be only one operation
38
38
  batch.group_by(&:operation).each do |operation, group|
39
39
  ForemanTasks::RemoteTask.batch_trigger(operation, group)
40
+ output[:planned_count] += group.size
40
41
  end
41
- output[:planned_count] += batch.size
42
42
  rescue => e
43
- action_logger.warn "Could not trigger task on the smart proxy"
44
- action_logger.warn e
43
+ action_logger.warn "Could not trigger task on the smart proxy: #{e.message}"
45
44
  batch.each { |remote_task| remote_task.update_from_batch_trigger({}) }
46
45
  output[:failed_count] += batch.size
47
46
  end
@@ -90,7 +90,7 @@ module ForemanTasks
90
90
  def next_occurrence_time(time = Time.zone.now)
91
91
  @parser ||= CronParser.new(cron_line, Time.zone)
92
92
  # @parser.next(start_time) is not inclusive of the start_time hence stepping back one run to include checking start_time for the first run.
93
- before_next = @parser.next(@parser.last(time.in_time_zone))
93
+ before_next = @parser.next(@parser.last(time))
94
94
  return before_next if before_next >= time && tasks.count == 0
95
95
  @parser.next(time)
96
96
  end
@@ -18,8 +18,7 @@ module ForemanTasks
18
18
  response = begin
19
19
  proxy.launch_tasks('single', :action_class => proxy_action_name, :action_input => input)
20
20
  rescue RestClient::Exception => e
21
- logger.warn "Could not trigger task on the smart proxy"
22
- logger.warn e
21
+ logger.warn "Could not trigger task on the smart proxy: #{e.message}"
23
22
  {}
24
23
  end
25
24
  update_from_batch_trigger(response)
@@ -27,26 +26,33 @@ module ForemanTasks
27
26
  end
28
27
 
29
28
  def self.batch_trigger(operation, remote_tasks)
30
- remote_tasks.group_by(&:proxy_url).each_value do |group|
29
+ remote_tasks.group_by(&:proxy_url).values.map do |group|
31
30
  input_hash = group.reduce({}) do |acc, remote_task|
32
31
  acc.merge(remote_task.execution_plan_id => { :action_input => remote_task.proxy_input,
33
32
  :action_class => remote_task.proxy_action_name })
34
33
  end
35
- results = group.first.proxy.launch_tasks(operation, input_hash)
36
- group.each { |remote_task| remote_task.update_from_batch_trigger results[remote_task.execution_plan_id] }
34
+ results = remote_tasks.first.proxy.launch_tasks(operation, input_hash)
35
+ remote_tasks.each do |remote_task|
36
+ remote_task.update_from_batch_trigger results.fetch(remote_task.execution_plan_id, {}),
37
+ results.fetch('parent', {})
38
+ end
37
39
  end
38
40
  remote_tasks
39
41
  end
40
42
 
41
- def update_from_batch_trigger(data)
43
+ def update_from_batch_trigger(data, parent = {})
42
44
  if data['result'] == 'success'
43
45
  self.remote_task_id = data['task_id']
44
46
  self.state = 'triggered'
47
+ elsif !parent.empty?
48
+ self.parent_task_id = parent['task_id']
49
+ self.state = 'parent-triggered'
45
50
  else
46
51
  # Tell the action the task on the smart proxy stopped
47
52
  ForemanTasks.dynflow.world.event execution_plan_id,
48
53
  step_id,
49
- ::Actions::ProxyAction::ProxyActionStopped.new
54
+ ::Actions::ProxyAction::ProxyActionStopped.new,
55
+ optional: true
50
56
  end
51
57
  save!
52
58
  end
@@ -5,15 +5,14 @@ module ForemanTasks
5
5
  scope :for_action, ->(action_class) { where(label: action_class.name) }
6
6
  after_validation :set_action_field
7
7
 
8
- def update_from_dynflow(data)
9
- utc_zone = ActiveSupport::TimeZone.new('UTC')
10
- self.external_id = data[:id]
11
- self.result = map_result(data).to_s
12
- self.state = data[:state].to_s
13
- self.started_at = string_to_time(utc_zone, data[:started_at]) unless data[:started_at].nil?
14
- self.ended_at = string_to_time(utc_zone, data[:ended_at]) unless data[:ended_at].nil?
15
- self.start_at = string_to_time(utc_zone, data[:start_at]) if data[:start_at]
16
- self.start_before = string_to_time(utc_zone, data[:start_before]) if data[:start_before]
8
+ def update_from_dynflow(plan, delayed_plan = nil)
9
+ self.external_id = plan.id
10
+ self.result = map_result(plan).to_s
11
+ self.state = plan.state.to_s
12
+ self.started_at = plan.started_at unless plan.started_at.nil?
13
+ self.ended_at = plan.ended_at unless plan.ended_at.nil?
14
+ self.start_at = delayed_plan.start_at if delayed_plan
15
+ self.start_before = delayed_plan.start_before if delayed_plan
17
16
  self.parent_task_id ||= begin
18
17
  if main_action.try(:caller_execution_plan_id)
19
18
  DynflowTask.where(:external_id => main_action.caller_execution_plan_id).first!.id
@@ -221,7 +220,7 @@ module ForemanTasks
221
220
  fixed_count = 0
222
221
  logger = Foreman::Logging.logger('foreman-tasks')
223
222
  running.each do |task|
224
- changes = task.update_from_dynflow(task.execution_plan.to_hash)
223
+ changes = task.update_from_dynflow(task.execution_plan)
225
224
  unless changes.empty?
226
225
  fixed_count += 1
227
226
  logger.warn('Task %s updated at consistency check: %s' % [task.id, changes.inspect])
@@ -236,10 +235,10 @@ module ForemanTasks
236
235
  fixed_count
237
236
  end
238
237
 
239
- def self.new_for_execution_plan(execution_plan_id, data)
240
- new(:external_id => execution_plan_id,
241
- :state => data[:state].to_s,
242
- :result => data[:result].to_s,
238
+ def self.new_for_execution_plan(execution_plan)
239
+ new(:external_id => execution_plan.id,
240
+ :state => execution_plan.state.to_s,
241
+ :result => execution_plan.result.to_s,
243
242
  :user_id => User.current.try(:id))
244
243
  end
245
244
 
@@ -258,20 +257,20 @@ module ForemanTasks
258
257
  self.action = to_label
259
258
  end
260
259
 
261
- def map_result(data)
262
- if state_result_transitioned?(%w[planned pending], %w[stopped error], data) ||
263
- (data[:result] == :error && cancelled?)
260
+ def map_result(plan)
261
+ if state_result_transitioned?(%w[planned pending], %w[stopped error], plan) ||
262
+ (plan.result == :error && cancelled?)
264
263
  :cancelled
265
264
  else
266
- data[:result]
265
+ plan.result
267
266
  end
268
267
  end
269
268
 
270
- def state_result_transitioned?(from, to, data)
269
+ def state_result_transitioned?(from, to, plan)
271
270
  oldstate, oldresult = from
272
271
  newstate, newresult = to
273
- state == oldstate && data[:state].to_s == newstate &&
274
- result == oldresult && data[:result].to_s == newresult
272
+ state == oldstate && plan.state.to_s == newstate &&
273
+ result == oldresult && plan.result.to_s == newresult
275
274
  end
276
275
 
277
276
  def cancelled?
@@ -2,3 +2,7 @@ object @lock
2
2
 
3
3
  attributes :name, :resource_type, :resource_id
4
4
  node(:exclusive) { !locals[:link] }
5
+ node(:link) do
6
+ method = "#{@object.resource_type.underscore.split('/').first}_path".to_sym
7
+ public_send(method, @object.resource_id) if defined?(method)
8
+ end
data/config/routes.rb CHANGED
@@ -70,7 +70,7 @@ Foreman::Application.routes.draw do
70
70
  mount ForemanTasks.dynflow.web_console => '/dynflow'
71
71
  if defined? ::Sidekiq
72
72
  require 'sidekiq/web'
73
- redis_url = SETTINGS.dig(:dynflow, :redis_url)
73
+ redis_url = ENV['DYNFLOW_REDIS_URL'] || SETTINGS.dig(:dynflow, :redis_url)
74
74
  Sidekiq.redis = { url: redis_url }
75
75
  Sidekiq::Web.set :sessions, false
76
76
  mount Sidekiq::Web => '/sidekiq', :constraints => ForemanTasks::Dynflow::SidekiqConsoleConstraint.new
@@ -0,0 +1,5 @@
1
+ class AddParentTaskIdToRemoteTasks < ActiveRecord::Migration[5.0]
2
+ def change
3
+ add_column :foreman_tasks_remote_tasks, :parent_task_id, :string
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class TasksSettingsToDslCategory < ActiveRecord::Migration[6.0]
2
+ def up
3
+ Setting.where(category: 'Setting::ForemanTasks').update_all(category: 'Setting')
4
+ end
5
+ end
@@ -21,21 +21,6 @@ function build_rake() {
21
21
  echo
22
22
  }
23
23
 
24
- function incorrect_usage() {
25
- echo "$1" >&2
26
- echo
27
- usage
28
-
29
- exit 1
30
- }
31
-
32
- function validate_options!() {
33
- if [ -z "$TASK_SEARCH" ]; then
34
- [ -n "$AFTER" ] && incorrect_usage "Error: -a|--after cannot be used without -s|--search"
35
- [ -n "$STATES" ] && incorrect_usage "Error: -S|--states cannot be used without -s|--search"
36
- fi
37
- }
38
-
39
24
  function usage() {
40
25
  cat << EOF
41
26
  Usage: $PROGNAME [script_options...] [options...]
@@ -58,8 +43,8 @@ EOF
58
43
  echo Cleanup options:
59
44
  cat <<EOF | column -s\& -t
60
45
  -B|--batch-size BATCH_SIZE & process tasks in batches of BATCH_SIZE, 1000 by default
61
- -S|--states STATES & operate on tasks in STATES, comma separated list of states, set to all to operate on tasks in any state. Has to be used together with -s|--search
62
- -a|--after AGE & operate on tasks older than AGE. Expected format is a number followed by the time unit (s,h,m,y), such as '10d' for 10 days. Has to be used together with -s|--search
46
+ -S|--states STATES & operate on tasks in STATES, comma separated list of states, set to all to operate on tasks in any state
47
+ -a|--after AGE & operate on tasks older than AGE. Expected format is a number followed by the time unit (s,h,m,y), such as '10d' for 10 days
63
48
  -b|--backup & backup deleted tasks
64
49
  -n|--noop & do a dry run, print what would be done
65
50
  -s|--search QUERY & use QUERY in scoped search format to match tasks to delete
@@ -134,8 +119,6 @@ while true; do
134
119
  shift
135
120
  done
136
121
 
137
- validate_options!
138
-
139
122
  if [ "$EXECUTE" -eq 1 ]; then
140
123
  build_rake | sh
141
124
  else
@@ -26,7 +26,7 @@ same resource. It also optionally provides Dynflow infrastructure for using it f
26
26
  s.test_files = `git ls-files test`.split("\n")
27
27
  s.extra_rdoc_files = Dir['README*', 'LICENSE']
28
28
 
29
- s.add_dependency "dynflow", '>= 1.2.3'
29
+ s.add_dependency "dynflow", '>= 1.6.0'
30
30
  s.add_dependency "get_process_mem" # for memory polling
31
31
  s.add_dependency "parse-cron", '~> 0.1.4'
32
32
  s.add_dependency "sinatra" # for Dynflow web console
@@ -1,5 +1,4 @@
1
1
  require File.expand_path('lib/foreman/dynflow/configuration', Rails.root)
2
- require 'foreman_tasks/dynflow/persistence'
3
2
 
4
3
  module ForemanTasks
5
4
  # Import all Dynflow configuration from Foreman, and add our own for Tasks
@@ -34,9 +33,5 @@ module ForemanTasks
34
33
  end
35
34
  options
36
35
  end
37
-
38
- def persistence_class
39
- ForemanTasks::Dynflow::Persistence
40
- end
41
36
  end
42
37
  end
@@ -5,14 +5,6 @@ module ForemanTasks
5
5
  class Engine < ::Rails::Engine
6
6
  engine_name 'foreman_tasks'
7
7
 
8
- initializer 'foreman_tasks.load_default_settings', :before => :load_config_initializers do
9
- require_dependency File.expand_path('../../app/models/setting/foreman_tasks.rb', __dir__) if begin
10
- Setting.table_exists?
11
- rescue
12
- false
13
- end
14
- end
15
-
16
8
  assets_to_precompile = %w[foreman_tasks/foreman_tasks.css
17
9
  foreman_tasks/foreman_tasks.js]
18
10
 
@@ -33,7 +25,7 @@ module ForemanTasks
33
25
 
34
26
  initializer 'foreman_tasks.register_plugin', :before => :finisher_hook do |_app|
35
27
  Foreman::Plugin.register :"foreman-tasks" do
36
- requires_foreman '>= 2.6.0'
28
+ requires_foreman '>= 3.0.0'
37
29
  divider :top_menu, :parent => :monitor_menu, :last => true, :caption => N_('Foreman Tasks')
38
30
  menu :top_menu, :tasks,
39
31
  :url_hash => { :controller => 'foreman_tasks/tasks', :action => :index },
@@ -65,6 +57,60 @@ module ForemanTasks
65
57
 
66
58
  add_all_permissions_to_default_roles
67
59
 
60
+ settings do
61
+ category(:tasks, N_('Tasks')) do
62
+ setting('foreman_tasks_sync_task_timeout',
63
+ type: :integer,
64
+ description: N_('Number of seconds to wait for synchronous task to finish.'),
65
+ default: 120,
66
+ full_name: N_('Sync task timeout'))
67
+ setting('dynflow_enable_console',
68
+ type: :boolean,
69
+ description: N_('Enable the dynflow console (/foreman_tasks/dynflow) for debugging'),
70
+ default: true,
71
+ full_name: N_('Enable dynflow console'))
72
+ setting('dynflow_console_require_auth',
73
+ type: :boolean,
74
+ description: N_('Require user to be authenticated as user with admin rights when accessing dynflow console'),
75
+ default: true,
76
+ full_name: N_('Require auth for dynflow console'))
77
+ setting('foreman_tasks_proxy_action_retry_count',
78
+ type: :integer,
79
+ description: N_('Number of attempts to start a task on the smart proxy before failing'),
80
+ default: 4,
81
+ full_name: N_('Proxy action retry count'))
82
+ setting('foreman_tasks_proxy_action_retry_interval',
83
+ type: :integer,
84
+ description: N_('Time in seconds between retries'),
85
+ default: 15,
86
+ full_name: N_('Proxy action retry interval'))
87
+ setting('foreman_tasks_proxy_batch_trigger',
88
+ type: :boolean,
89
+ description: N_('Allow triggering tasks on the smart proxy in batches'),
90
+ default: true,
91
+ full_name: N_('Allow proxy batch tasks'))
92
+ setting('foreman_tasks_proxy_batch_size',
93
+ type: :integer,
94
+ description: N_('Number of tasks which should be sent to the smart proxy in one request, if foreman_tasks_proxy_batch_trigger is enabled'),
95
+ default: 100,
96
+ full_name: N_('Proxy tasks batch size'))
97
+ setting('foreman_tasks_troubleshooting_url',
98
+ type: :string,
99
+ description: N_('Url pointing to the task troubleshooting documentation. '\
100
+ 'It should contain %{label} placeholder, that will be replaced with normalized task label '\
101
+ '(restricted to only alphanumeric characters)). %{version} placeholder is also available.'),
102
+ default: nil,
103
+ full_name: N_('Tasks troubleshooting URL'))
104
+ setting('foreman_tasks_polling_multiplier',
105
+ type: :integer,
106
+ description: N_('Polling multiplier which is used to multiply the default polling intervals. '\
107
+ 'This can be used to prevent polling too frequently for long running tasks.'),
108
+ default: 1,
109
+ full_name: N_("Polling intervals multiplier"),
110
+ validate: { numericality: { greater_than: 0 } })
111
+ end
112
+ end
113
+
68
114
  register_graphql_query_field :task, '::Types::Task', :record_field
69
115
  register_graphql_query_field :tasks, '::Types::Task', :collection_field
70
116
  register_graphql_query_field :recurring_logic, '::Types::RecurringLogic', :record_field
@@ -138,6 +184,7 @@ module ForemanTasks
138
184
  Authorizer.prepend AuthorizerExt
139
185
  User.include ForemanTasks::Concerns::UserExtensions
140
186
  ::Dynflow::Action::Polling.prepend ForemanTasks::Concerns::PollingActionExtensions
187
+ ::Dynflow::ActiveJob::QueueAdapters::JobWrapper.include Actions::TaskSynchronization
141
188
  end
142
189
 
143
190
  rake_tasks do
@@ -241,42 +241,35 @@ namespace :foreman_tasks do
241
241
  end
242
242
  end
243
243
 
244
- def csv_export(export_filename, id_scope, task_scope)
244
+ def csv_export(export_filename, tasks)
245
245
  CSV.open(export_filename, 'wb') do |csv|
246
246
  csv << %w[id state type label result parent_task_id started_at ended_at duration]
247
- id_scope.pluck(:id).each_slice(1000).each do |ids|
248
- task_scope.where(id: ids).each do |task|
249
- with_error_handling(task) do
250
- csv << [task.id, task.state, task.type, task.label, task.result,
251
- task.parent_task_id, task.started_at, task.ended_at, task.duration]
252
- end
247
+ tasks.find_each do |task|
248
+ with_error_handling(task) do
249
+ csv << [task.id, task.state, task.type, task.label, task.result,
250
+ task.parent_task_id, task.started_at, task.ended_at, task.duration]
253
251
  end
254
252
  end
255
253
  end
256
254
  end
257
255
 
258
- def html_export(workdir, id_scope, task_scope)
256
+ def html_export(workdir, tasks)
259
257
  PageHelper.copy_assets(workdir)
260
258
 
261
- ids = id_scope.pluck(:id)
262
259
  renderer = TaskRender.new
263
- count = 0
264
- total = ids.count
260
+ total = tasks.count(:all)
265
261
  index = File.open(File.join(workdir, 'index.html'), 'w')
266
262
 
267
263
  File.open(File.join(workdir, 'index.html'), 'w') do |index|
268
264
  PageHelper.pagify(index) do |io|
269
265
  PageHelper.generate_with_index(io) do |index|
270
- ids.each_slice(1000).each do |ids|
271
- task_scope.where(id: ids).each do |task|
272
- content = with_error_handling(task) { renderer.render_task(task) }
273
- if content
274
- File.open(File.join(workdir, "#{task.id}.html"), 'w') { |file| PageHelper.pagify(file, content) }
275
- with_error_handling(task, _('task index entry')) { PageHelper.generate_index_entry(index, task) }
276
- end
277
- count += 1
278
- puts "#{count}/#{total}"
266
+ tasks.find_each.each_with_index do |task, count|
267
+ content = with_error_handling(task) { renderer.render_task(task) }
268
+ if content
269
+ File.open(File.join(workdir, "#{task.id}.html"), 'w') { |file| PageHelper.pagify(file, content) }
270
+ with_error_handling(task, _('task index entry')) { PageHelper.generate_index_entry(index, task) }
279
271
  end
272
+ puts "#{count + 1}/#{total}"
280
273
  end
281
274
  end
282
275
  end
@@ -307,7 +300,7 @@ namespace :foreman_tasks do
307
300
  end
308
301
  end
309
302
 
310
- SKIP_ERRORS = ['true', '1', 'y', 'yes'].include? (ENV['SKIP_FAILED'] || '').downcase
303
+ SKIP_ERRORS = ['true', '1', 'y', 'yes'].include? ENV['SKIP_FAILED'].downcase
311
304
 
312
305
  filter = if ENV['TASK_SEARCH'].nil? && ENV['TASK_DAYS'].nil?
313
306
  "started_at > \"#{7.days.ago.to_s(:db)}\" || " \
@@ -324,22 +317,21 @@ namespace :foreman_tasks do
324
317
  format = ENV['TASK_FORMAT'] || 'html'
325
318
  export_filename = ENV['TASK_FILE'] || generate_filename(format)
326
319
 
327
- task_scope = ForemanTasks::Task.search_for(filter).with_duration.order(:started_at => :desc)
328
- id_scope = task_scope.group(:id, :started_at)
320
+ tasks = ForemanTasks::Task.search_for(filter).order(:started_at => :desc).with_duration.distinct
329
321
 
330
322
  puts _("Exporting all tasks matching filter #{filter}")
331
- puts _("Gathering #{id_scope.count(:all).count} tasks.")
323
+ puts _("Gathering #{tasks.count(:all)} tasks.")
332
324
  case format
333
325
  when 'html'
334
326
  Dir.mktmpdir('task-export') do |tmp_dir|
335
- html_export(tmp_dir, id_scope, task_scope)
327
+ html_export(tmp_dir, tasks)
336
328
  system("tar", "czf", export_filename, tmp_dir)
337
329
  end
338
330
  when 'html-dir'
339
331
  FileUtils.mkdir_p(export_filename)
340
- html_export(export_filename, id_scope, task_scope)
332
+ html_export(export_filename, tasks)
341
333
  when 'csv'
342
- csv_export(export_filename, id_scope, task_scope)
334
+ csv_export(export_filename, tasks)
343
335
  else
344
336
  raise "Unkonwn export format '#{format}'"
345
337
  end
@@ -11,7 +11,7 @@ module ForemanTasks
11
11
  world_config = ForemanTasks.dynflow.config.world_config
12
12
  if @use_in_memory_sqlite
13
13
  world_config.persistence_adapter = lambda do |*_args|
14
- ::ForemanTasks::Dynflow::Persistence.new('adapter' => 'sqlite', 'database' => ':memory:')
14
+ ::Dynflow::PersistenceAdapters::Sequel.new('adapter' => 'sqlite', 'database' => ':memory:')
15
15
  end
16
16
  end
17
17
  @test_in_thread_world = ::Dynflow::Testing::InThreadWorld.new(world_config)
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '5.2.3'.freeze
2
+ VERSION = '5.3.0'.freeze
3
3
  end
@@ -24,7 +24,7 @@ FactoryBot.define do
24
24
  ForemanTasks::Task.where(:external_id => execution_plan.id).delete_all
25
25
  task.update!(:external_id => execution_plan.id)
26
26
  if evaluator.sync_with_dynflow
27
- task.update_from_dynflow(execution_plan.to_hash)
27
+ task.update_from_dynflow(execution_plan)
28
28
  end
29
29
  end
30
30
 
@@ -15,7 +15,7 @@ module Actions
15
15
  middleware.use KeepCurrentTaxonomies
16
16
  execution_plan_hooks.use :null_hook, :on => :planning
17
17
 
18
- def null_hook; end
18
+ def null_hook(plan); end
19
19
  end
20
20
 
21
21
  before do
@@ -78,6 +78,14 @@ module Actions
78
78
  Organization.stubs(:current=)
79
79
  Location.stubs(:current=)
80
80
 
81
+ org_finder = mock('organization finder')
82
+ org_finder.stubs(:find).with(@org.id).returns(@org)
83
+ Organization.stubs(:unscoped).returns(org_finder)
84
+
85
+ loc_finder = mock('location finder')
86
+ loc_finder.stubs(:find).with(@loc.id).returns(@loc)
87
+ Location.stubs(:unscoped).returns(loc_finder)
88
+
81
89
  triggered = ForemanTasks.trigger(TestHookAction)
82
90
  task = ForemanTasks::Task.where(:external_id => triggered.id).first
83
91
  wait_for { task.reload.state == 'stopped' }
@@ -1,5 +1,5 @@
1
1
  module Support
2
- class DummyDynflowAction < Dynflow::Action
2
+ class DummyDynflowAction < Actions::EntryAction
3
3
  end
4
4
 
5
5
  class DummyPauseAction < Actions::EntryAction
@@ -46,7 +46,6 @@ module ForemanTasks
46
46
 
47
47
  it 'fetches batch_size of tasks and triggers them' do
48
48
  remote_tasks.expects(:first).with(batch_size).returns(remote_tasks)
49
- remote_tasks.expects(:size).returns(batch_size)
50
49
  triggered.expects(:remote_tasks).returns(remote_tasks)
51
50
  ForemanTasks::RemoteTask.expects(:batch_trigger).with(proxy_operation_name, grouped_remote_batch)
52
51
 
@@ -28,32 +28,6 @@ module ForemanTasks
28
28
  _(remote_task.remote_task_id).must_equal((remote_task.id + 5).to_s)
29
29
  end
30
30
  end
31
-
32
- it 'honors the batches with multiple proxies' do
33
- remote_task = remote_tasks.last
34
- remote_task.proxy_url = 'something else'
35
-
36
- results = remote_tasks.reduce({}) do |acc, cur|
37
- acc.merge(cur.execution_plan_id.to_s => { 'task_id' => cur.id + 5, 'result' => 'success' })
38
- end
39
- other_results = { remote_task.execution_plan_id => results.delete(remote_task.execution_plan_id) }
40
-
41
- fake_proxy = mock
42
- fake_proxy.expects(:launch_tasks).returns(results)
43
-
44
- another_fake_proxy = mock
45
- another_fake_proxy.expects(:launch_tasks).returns(other_results)
46
-
47
- remote_tasks.first.expects(:proxy).returns(fake_proxy)
48
- remote_tasks.last.expects(:proxy).returns(another_fake_proxy)
49
-
50
- RemoteTask.batch_trigger('a_operation', remote_tasks)
51
- remote_tasks.each do |remote_task|
52
- remote_task.reload
53
- _(remote_task.state).must_equal 'triggered'
54
- _(remote_task.remote_task_id).must_equal((remote_task.id + 5).to_s)
55
- end
56
- end
57
31
  end
58
32
  end
59
33
  end
@@ -21,7 +21,6 @@ module ForemanTasks
21
21
  end
22
22
 
23
23
  before do
24
- Setting::ForemanTasks.load_defaults
25
24
  ::ForemanTasks::Task.delete_all
26
25
  @task = trigger_task
27
26
  Setting[:foreman_tasks_troubleshooting_url] = sample_troubleshooting_url
@@ -12,7 +12,6 @@ module ForemanTasks
12
12
  end
13
13
 
14
14
  before do
15
- Setting::ForemanTasks.load_defaults
16
15
  ::ForemanTasks::Task.delete_all
17
16
  load File.join(ForemanTasks::Engine.root, 'db', 'seeds.d', '30-notification_blueprints.rb')
18
17
  @admin_user = FactoryBot.create(:user, :admin)
@@ -1,5 +1,5 @@
1
1
  import { translate as __, sprintf } from 'foremanReact/common/I18n';
2
- import { addToast } from 'foremanReact/redux/actions/toasts';
2
+ import { addToast } from 'foremanReact/components/ToastsList';
3
3
  import { TASKS_DASHBOARD_JS_QUERY_MODES } from '../TasksDashboard/TasksDashboardConstants';
4
4
  import { timeToHoursNumber } from '../TasksDashboard/TasksDashboardHelper';
5
5
  import {
@@ -1,6 +1,6 @@
1
1
  import { sprintf } from 'foremanReact/common/I18n';
2
2
  import { API } from 'foremanReact/redux/API';
3
- import { addToast } from 'foremanReact/redux/actions/toasts';
3
+ import { addToast } from 'foremanReact/components/ToastsList';
4
4
  import {
5
5
  TASKS_RESUME_REQUEST,
6
6
  TASKS_RESUME_SUCCESS,
@@ -3,6 +3,18 @@ import PropTypes from 'prop-types';
3
3
  import { Alert, Card, Row, Col } from 'patternfly-react';
4
4
  import { translate as __ } from 'foremanReact/common/I18n';
5
5
 
6
+ const ConditionalLink = ({ children, link }) =>
7
+ link ? <a href={link}>{children}</a> : children;
8
+
9
+ ConditionalLink.propTypes = {
10
+ children: PropTypes.node.isRequired,
11
+ link: PropTypes.string,
12
+ };
13
+
14
+ ConditionalLink.defaultProps = {
15
+ link: null,
16
+ };
17
+
6
18
  const Locks = ({ locks }) => (
7
19
  <div>
8
20
  <Alert type="info">
@@ -14,20 +26,22 @@ const Locks = ({ locks }) => (
14
26
  <Row>
15
27
  {locks.map((lock, key) => (
16
28
  <Col xs={6} sm={4} md={4} key={key}>
17
- <Card className="card-pf-aggregate-status" accented>
18
- <Card.Title>
19
- <span
20
- className={`fa ${
21
- lock.exclusive ? 'fa-lock' : 'fa-unlock-alt'
22
- }`}
23
- />
24
- {lock.resource_type}
25
- </Card.Title>
26
- <Card.Body>
27
- {`id:${lock.resource_id}`}
28
- <br />
29
- </Card.Body>
30
- </Card>
29
+ <ConditionalLink link={lock.link}>
30
+ <Card className="card-pf-aggregate-status" accented>
31
+ <Card.Title>
32
+ <span
33
+ className={`fa ${
34
+ lock.exclusive ? 'fa-lock' : 'fa-unlock-alt'
35
+ }`}
36
+ />
37
+ {lock.resource_type}
38
+ </Card.Title>
39
+ <Card.Body>
40
+ {`id:${lock.resource_id}`}
41
+ <br />
42
+ </Card.Body>
43
+ </Card>
44
+ </ConditionalLink>
31
45
  </Col>
32
46
  ))}
33
47
  </Row>
@@ -25,29 +25,33 @@ exports[`Locks rendering render with Props 1`] = `
25
25
  sm={4}
26
26
  xs={6}
27
27
  >
28
- <Card
29
- accented={true}
30
- aggregated={false}
31
- aggregatedMini={false}
32
- cardRef={null}
33
- className="card-pf-aggregate-status"
34
- matchHeight={false}
28
+ <ConditionalLink
29
+ link={null}
35
30
  >
36
- <CardTitle
37
- className=""
31
+ <Card
32
+ accented={true}
33
+ aggregated={false}
34
+ aggregatedMini={false}
35
+ cardRef={null}
36
+ className="card-pf-aggregate-status"
37
+ matchHeight={false}
38
38
  >
39
- <span
40
- className="fa fa-unlock-alt"
41
- />
42
- User
43
- </CardTitle>
44
- <CardBody
45
- className=""
46
- >
47
- id:4
48
- <br />
49
- </CardBody>
50
- </Card>
39
+ <CardTitle
40
+ className=""
41
+ >
42
+ <span
43
+ className="fa fa-unlock-alt"
44
+ />
45
+ User
46
+ </CardTitle>
47
+ <CardBody
48
+ className=""
49
+ >
50
+ id:4
51
+ <br />
52
+ </CardBody>
53
+ </Card>
54
+ </ConditionalLink>
51
55
  </Col>
52
56
  <Col
53
57
  bsClass="col"
@@ -57,29 +61,33 @@ exports[`Locks rendering render with Props 1`] = `
57
61
  sm={4}
58
62
  xs={6}
59
63
  >
60
- <Card
61
- accented={true}
62
- aggregated={false}
63
- aggregatedMini={false}
64
- cardRef={null}
65
- className="card-pf-aggregate-status"
66
- matchHeight={false}
64
+ <ConditionalLink
65
+ link={null}
67
66
  >
68
- <CardTitle
69
- className=""
70
- >
71
- <span
72
- className="fa fa-unlock-alt"
73
- />
74
- User
75
- </CardTitle>
76
- <CardBody
77
- className=""
67
+ <Card
68
+ accented={true}
69
+ aggregated={false}
70
+ aggregatedMini={false}
71
+ cardRef={null}
72
+ className="card-pf-aggregate-status"
73
+ matchHeight={false}
78
74
  >
79
- id:2
80
- <br />
81
- </CardBody>
82
- </Card>
75
+ <CardTitle
76
+ className=""
77
+ >
78
+ <span
79
+ className="fa fa-unlock-alt"
80
+ />
81
+ User
82
+ </CardTitle>
83
+ <CardBody
84
+ className=""
85
+ >
86
+ id:2
87
+ <br />
88
+ </CardBody>
89
+ </Card>
90
+ </ConditionalLink>
83
91
  </Col>
84
92
  </Row>
85
93
  </CardGrid>
@@ -1,5 +1,5 @@
1
1
  import { post, get } from 'foremanReact/redux/API';
2
- import { addToast } from 'foremanReact/redux/actions/toasts';
2
+ import { addToast } from 'foremanReact/components/ToastsList';
3
3
  import { translate as __ } from 'foremanReact/common/I18n';
4
4
  import {
5
5
  withInterval,
@@ -1,5 +1,5 @@
1
1
  import { API } from 'foremanReact/redux/API';
2
- import { addToast } from 'foremanReact/redux/actions/toasts';
2
+ import { addToast } from 'foremanReact/components/ToastsList';
3
3
  import { translate as __, sprintf } from 'foremanReact/common/I18n';
4
4
  import {
5
5
  BULK_CANCEL_PATH,
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: 5.2.3
4
+ version: 5.3.0
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: 2022-04-21 00:00:00.000000000 Z
11
+ date: 2021-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dynflow
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.2.3
19
+ version: 1.6.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 1.2.3
26
+ version: 1.6.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: get_process_mem
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -166,6 +166,7 @@ files:
166
166
  - app/lib/actions/proxy_action.rb
167
167
  - app/lib/actions/recurring_action.rb
168
168
  - app/lib/actions/serializers/active_record_serializer.rb
169
+ - app/lib/actions/task_synchronization.rb
169
170
  - app/lib/actions/trigger_proxy_batch.rb
170
171
  - app/lib/foreman_tasks/concerns/polling_action_extensions.rb
171
172
  - app/lib/proxy_api/foreman_dynflow/dynflow_proxy.rb
@@ -188,7 +189,6 @@ files:
188
189
  - app/models/foreman_tasks/task_group_member.rb
189
190
  - app/models/foreman_tasks/task_groups/recurring_logic_task_group.rb
190
191
  - app/models/foreman_tasks/triggering.rb
191
- - app/models/setting/foreman_tasks.rb
192
192
  - app/services/foreman_tasks/proxy_selector.rb
193
193
  - app/services/foreman_tasks/troubleshooting_help_generator.rb
194
194
  - app/services/ui_notifications/tasks.rb
@@ -250,7 +250,9 @@ files:
250
250
  - db/migrate/20200517215015_rename_bookmarks_controller.rb
251
251
  - db/migrate/20200519093217_drop_dynflow_allow_dangerous_actions_setting.foreman_tasks.rb
252
252
  - db/migrate/20200611090846_add_task_lock_index_on_resource_type_and_task_id.rb
253
+ - db/migrate/20210708123832_add_parent_task_id_to_remote_tasks.foreman_tasks.rb
253
254
  - db/migrate/20210720115251_add_purpose_to_recurring_logic.rb
255
+ - db/migrate/20211123170430_tasks_settings_to_dsl_category.rb
254
256
  - db/seeds.d/20-foreman_tasks_permissions.rb
255
257
  - db/seeds.d/30-notification_blueprints.rb
256
258
  - db/seeds.d/60-dynflow_proxy_feature.rb
@@ -271,7 +273,6 @@ files:
271
273
  - lib/foreman_tasks/dynflow.rb
272
274
  - lib/foreman_tasks/dynflow/configuration.rb
273
275
  - lib/foreman_tasks/dynflow/console_authorizer.rb
274
- - lib/foreman_tasks/dynflow/persistence.rb
275
276
  - lib/foreman_tasks/engine.rb
276
277
  - lib/foreman_tasks/task_error.rb
277
278
  - lib/foreman_tasks/tasks/cleanup.rake
@@ -572,6 +573,7 @@ files:
572
573
  - webpack/__mocks__/foremanReact/components/ForemanModal/index.js
573
574
  - webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js
574
575
  - webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js
576
+ - webpack/__mocks__/foremanReact/components/ToastsList/index.js
575
577
  - webpack/__mocks__/foremanReact/components/common/ActionButtons/ActionButtons.js
576
578
  - webpack/__mocks__/foremanReact/components/common/MessageBox.js
577
579
  - webpack/__mocks__/foremanReact/components/common/dates/LongDateTime.js
@@ -581,7 +583,6 @@ files:
581
583
  - webpack/__mocks__/foremanReact/constants.js
582
584
  - webpack/__mocks__/foremanReact/redux/API/APISelectors.js
583
585
  - webpack/__mocks__/foremanReact/redux/API/index.js
584
- - webpack/__mocks__/foremanReact/redux/actions/toasts.js
585
586
  - webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware.js
586
587
  - webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js
587
588
  - webpack/__mocks__/foremanReact/routes/common/PageLayout/components/ExportButton/ExportButton.js
@@ -607,7 +608,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
607
608
  - !ruby/object:Gem::Version
608
609
  version: '0'
609
610
  requirements: []
610
- rubygems_version: 3.1.4
611
+ rubygems_version: 3.1.2
611
612
  signing_key:
612
613
  specification_version: 4
613
614
  summary: Foreman plugin for showing tasks information for resources and users
@@ -1,29 +0,0 @@
1
- class Setting::ForemanTasks < Setting
2
- def self.default_settings
3
- [
4
- set('foreman_tasks_sync_task_timeout', N_('Number of seconds to wait for synchronous task to finish.'), 120, N_('Sync task timeout')),
5
- set('dynflow_enable_console', N_('Enable the dynflow console (/foreman_tasks/dynflow) for debugging'), true, N_('Enable dynflow console')),
6
- set('dynflow_console_require_auth', N_('Require user to be authenticated as user with admin rights when accessing dynflow console'), true, N_('Require auth for dynflow console')),
7
- set('foreman_tasks_proxy_action_retry_count', N_('Number of attempts to start a task on the smart proxy before failing'), 4, N_('Proxy action retry count')),
8
- set('foreman_tasks_proxy_action_retry_interval', N_('Time in seconds between retries'), 15, N_('Proxy action retry interval')),
9
- set('foreman_tasks_proxy_batch_trigger', N_('Allow triggering tasks on the smart proxy in batches'), true, N_('Allow proxy batch tasks')),
10
- set('foreman_tasks_proxy_batch_size', N_('Number of tasks which should be sent to the smart proxy in one request, if foreman_tasks_proxy_batch_trigger is enabled'), 100, N_('Proxy tasks batch size')),
11
- set('foreman_tasks_troubleshooting_url',
12
- N_('Url pointing to the task troubleshooting documentation. '\
13
- 'It should contain %{label} placeholder, that will be replaced with normalized task label '\
14
- '(restricted to only alphanumeric characters)). %{version} placeholder is also available.'),
15
- nil, N_('Tasks troubleshooting URL')),
16
- set('foreman_tasks_polling_multiplier',
17
- N_('Polling multiplier which is used to multiply the default polling intervals. '\
18
- 'This can be used to prevent polling too frequently for long running tasks.'),
19
- 1,
20
- N_("Polling intervals multiplier")),
21
- ]
22
- end
23
-
24
- def self.load_defaults
25
- Setting::BLANK_ATTRS.push('foreman_tasks_troubleshooting_url')
26
- Setting::NONZERO_ATTRS.push('foreman_tasks_polling_multiplier')
27
- super
28
- end
29
- end
@@ -1,46 +0,0 @@
1
- module ForemanTasks
2
- # Wrap the Dynflow persistence to reflect the changes to execution plan
3
- # in the Task model. This is probably a temporary solution and
4
- # Dynflow will probably get more events-based API but it should be enought
5
- # for start, until the requiements on the API are clear enough.
6
- class Dynflow::Persistence < ::Dynflow::PersistenceAdapters::Sequel
7
- def save_execution_plan(execution_plan_id, value)
8
- # clear connection only if not running in some active record transaction already
9
- clear_connections = ActiveRecord::Base.connection.open_transactions.zero?
10
- super.tap do
11
- on_execution_plan_save(execution_plan_id, value)
12
- rescue => e
13
- Foreman::Logging.exception('Error on on_execution_plan_save event', e,
14
- :logger => 'dynflow')
15
- end
16
- ensure
17
- ::ActiveRecord::Base.clear_active_connections! if clear_connections
18
- end
19
-
20
- def on_execution_plan_save(execution_plan_id, data)
21
- # We can load the data unless the execution plan was properly planned and saved
22
- # including its steps
23
- case data[:state]
24
- when :pending
25
- task = ForemanTasks::Task::DynflowTask.new_for_execution_plan(execution_plan_id, data)
26
- task.start_at ||= Time.zone.now
27
- task.save!
28
- when :scheduled
29
- delayed_plan = load_delayed_plan(execution_plan_id)
30
- raise Foreman::Exception, 'Plan is delayed but the delay record is missing' if delayed_plan.nil?
31
- task = ::ForemanTasks::Task::DynflowTask.find_by!(:external_id => execution_plan_id)
32
- task.update_from_dynflow(data.merge(:start_at => delayed_plan[:start_at],
33
- :start_before => delayed_plan[:start_before]))
34
- when :planning
35
- task = ::ForemanTasks::Task::DynflowTask.where(:external_id => execution_plan_id).first
36
- task.update_from_dynflow(data)
37
- else
38
- if (task = ::ForemanTasks::Task::DynflowTask.where(:external_id => execution_plan_id).first)
39
- unless task.state.to_s == data[:state].to_s
40
- task.update_from_dynflow(data)
41
- end
42
- end
43
- end
44
- end
45
- end
46
- end