foreman-tasks 5.2.0 → 5.2.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa2189368cc093a6ca613ca1b91add99bc3689b2cb22cd0c04a3718821b5f27f
4
- data.tar.gz: f7e82294bbbb76fe6576b6c2de9e55c16342962f711a8e5c9070787be304a87c
3
+ metadata.gz: 9f1c154c83d919ac42b7d12edd03cff55e67f11a1367a67250a9a090759cd099
4
+ data.tar.gz: 519d85e8e975aab38b2e2314eb5761cb2bb3fe2d07b291822f3c667e6c0feab4
5
5
  SHA512:
6
- metadata.gz: f20e5fba4dbc2401a4fed1f5eb68961ccc7095071fc309316ad14392b9209141e96aa2372eb7bb5db0e7f829ef08727ff2e232e53de86baa8385183ddb722252
7
- data.tar.gz: 534ac74d1c8010ac25330562a6bdb713beacc413d2c681ee72f865727741cfb17e04d9dda2c0ea8ab7bde2f7f2e08f54fa4266e25686ca9d1f38c5309b24b94c
6
+ metadata.gz: 28a96b1b6c28bb5697607d69950167100815c667ecbebbc3f9f446f7e44899e7459e79a4f8efe8f53d798ee7ece6ebaad6d0becd610b11ab499cd32a4e2b5b53
7
+ data.tar.gz: 16c30b57fc2b2f82be9baf9d2dcf4c1b9f7a47df3013393d274fb6268ea04b6a066d3464bdeb1d911b998d7d5e68faa19ea9551193a25a07fb1cc12ddb45ee66
@@ -4,6 +4,8 @@ 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
+
7
9
  def show
8
10
  @task = resource_base.find(params[:id])
9
11
  render :layout => !request.xhr?
@@ -31,8 +33,7 @@ module ForemanTasks
31
33
  end
32
34
 
33
35
  def cancel_step
34
- task = find_dynflow_task
35
- result = ForemanTasks.dynflow.world.event(task.external_id, params[:step_id].to_i, ::Dynflow::Action::Cancellable::Cancel).wait
36
+ result = ForemanTasks.dynflow.world.event(@dynflow_task.external_id, params[:step_id].to_i, ::Dynflow::Action::Cancellable::Cancel).wait
36
37
  if result.rejected?
37
38
  render json: { error: result.reason }, status: :bad_request
38
39
  else
@@ -41,8 +42,7 @@ module ForemanTasks
41
42
  end
42
43
 
43
44
  def cancel
44
- task = find_dynflow_task
45
- if task.cancel
45
+ if @dynflow_task.cancel
46
46
  render json: { statusText: 'OK' }
47
47
  else
48
48
  render json: {}, status: :bad_request
@@ -50,19 +50,17 @@ module ForemanTasks
50
50
  end
51
51
 
52
52
  def abort
53
- task = find_dynflow_task
54
- if task.abort
53
+ if @dynflow_task.abort
55
54
  flash[:info] = _('Trying to abort the task')
56
55
  else
57
56
  flash[:warning] = _('The task cannot be aborted at the moment.')
58
57
  end
59
- redirect_back(:fallback_location => foreman_tasks_task_path(task))
58
+ redirect_back(:fallback_location => foreman_tasks_task_path(@dynflow_task))
60
59
  end
61
60
 
62
61
  def resume
63
- task = find_dynflow_task
64
- if task.resumable?
65
- ForemanTasks.dynflow.world.execute(task.execution_plan.id)
62
+ if @dynflow_task.resumable?
63
+ ForemanTasks.dynflow.world.execute(@dynflow_task.execution_plan.id)
66
64
  render json: { statusText: 'OK' }
67
65
  else
68
66
  render json: {}, status: :bad_request
@@ -70,10 +68,8 @@ module ForemanTasks
70
68
  end
71
69
 
72
70
  def unlock
73
- task = find_dynflow_task
74
- if task.paused?
75
- task.state = :stopped
76
- task.save!
71
+ if @dynflow_task.paused?
72
+ unlock_task(@dynflow_task)
77
73
  render json: { statusText: 'OK' }
78
74
  else
79
75
  render json: {}, status: :bad_request
@@ -81,9 +77,7 @@ module ForemanTasks
81
77
  end
82
78
 
83
79
  def force_unlock
84
- task = find_dynflow_task
85
- task.state = :stopped
86
- task.save!
80
+ unlock_task(@dynflow_task)
87
81
  render json: { statusText: 'OK' }
88
82
  end
89
83
 
@@ -98,6 +92,12 @@ module ForemanTasks
98
92
 
99
93
  private
100
94
 
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
- resource_scope.where(:type => 'ForemanTasks::Task::DynflowTask').find(params[:id])
126
+ @dynflow_task = resource_scope.where(:type => 'ForemanTasks::Task::DynflowTask').find(params[:id])
127
127
  end
128
128
 
129
129
  def filter(scope, paginate: true)
@@ -40,7 +40,8 @@ module Actions
40
40
  end
41
41
  output[:planned_count] += batch.size
42
42
  rescue => e
43
- action_logger.warn "Could not trigger task on the smart proxy: #{e.message}"
43
+ action_logger.warn "Could not trigger task on the smart proxy"
44
+ action_logger.warn e
44
45
  batch.each { |remote_task| remote_task.update_from_batch_trigger({}) }
45
46
  output[:failed_count] += batch.size
46
47
  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))
93
+ before_next = @parser.next(@parser.last(time.in_time_zone))
94
94
  return before_next if before_next >= time && tasks.count == 0
95
95
  @parser.next(time)
96
96
  end
@@ -18,7 +18,8 @@ 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: #{e.message}"
21
+ logger.warn "Could not trigger task on the smart proxy"
22
+ logger.warn e
22
23
  {}
23
24
  end
24
25
  update_from_batch_trigger(response)
@@ -26,13 +27,13 @@ module ForemanTasks
26
27
  end
27
28
 
28
29
  def self.batch_trigger(operation, remote_tasks)
29
- remote_tasks.group_by(&:proxy_url).values.map do |group|
30
+ remote_tasks.group_by(&:proxy_url).each_value do |group|
30
31
  input_hash = group.reduce({}) do |acc, remote_task|
31
32
  acc.merge(remote_task.execution_plan_id => { :action_input => remote_task.proxy_input,
32
33
  :action_class => remote_task.proxy_action_name })
33
34
  end
34
- results = remote_tasks.first.proxy.launch_tasks(operation, input_hash)
35
- remote_tasks.each { |remote_task| remote_task.update_from_batch_trigger results[remote_task.execution_plan_id] }
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] }
36
37
  end
37
38
  remote_tasks
38
39
  end
@@ -21,6 +21,21 @@ 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
+
24
39
  function usage() {
25
40
  cat << EOF
26
41
  Usage: $PROGNAME [script_options...] [options...]
@@ -43,8 +58,8 @@ EOF
43
58
  echo Cleanup options:
44
59
  cat <<EOF | column -s\& -t
45
60
  -B|--batch-size BATCH_SIZE & process tasks in batches of BATCH_SIZE, 1000 by default
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
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
48
63
  -b|--backup & backup deleted tasks
49
64
  -n|--noop & do a dry run, print what would be done
50
65
  -s|--search QUERY & use QUERY in scoped search format to match tasks to delete
@@ -119,6 +134,8 @@ while true; do
119
134
  shift
120
135
  done
121
136
 
137
+ validate_options!
138
+
122
139
  if [ "$EXECUTE" -eq 1 ]; then
123
140
  build_rake | sh
124
141
  else
@@ -241,35 +241,42 @@ namespace :foreman_tasks do
241
241
  end
242
242
  end
243
243
 
244
- def csv_export(export_filename, tasks)
244
+ def csv_export(export_filename, id_scope, task_scope)
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
- 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]
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
251
253
  end
252
254
  end
253
255
  end
254
256
  end
255
257
 
256
- def html_export(workdir, tasks)
258
+ def html_export(workdir, id_scope, task_scope)
257
259
  PageHelper.copy_assets(workdir)
258
260
 
261
+ ids = id_scope.pluck(:id)
259
262
  renderer = TaskRender.new
260
- total = tasks.count(:all)
263
+ count = 0
264
+ total = ids.count
261
265
  index = File.open(File.join(workdir, 'index.html'), 'w')
262
266
 
263
267
  File.open(File.join(workdir, 'index.html'), 'w') do |index|
264
268
  PageHelper.pagify(index) do |io|
265
269
  PageHelper.generate_with_index(io) do |index|
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) }
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}"
271
279
  end
272
- puts "#{count + 1}/#{total}"
273
280
  end
274
281
  end
275
282
  end
@@ -300,7 +307,7 @@ namespace :foreman_tasks do
300
307
  end
301
308
  end
302
309
 
303
- SKIP_ERRORS = ['true', '1', 'y', 'yes'].include? ENV['SKIP_FAILED'].downcase
310
+ SKIP_ERRORS = ['true', '1', 'y', 'yes'].include? (ENV['SKIP_FAILED'] || '').downcase
304
311
 
305
312
  filter = if ENV['TASK_SEARCH'].nil? && ENV['TASK_DAYS'].nil?
306
313
  "started_at > \"#{7.days.ago.to_s(:db)}\" || " \
@@ -317,21 +324,22 @@ namespace :foreman_tasks do
317
324
  format = ENV['TASK_FORMAT'] || 'html'
318
325
  export_filename = ENV['TASK_FILE'] || generate_filename(format)
319
326
 
320
- tasks = ForemanTasks::Task.search_for(filter).order(:started_at => :desc).with_duration.distinct
327
+ task_scope = ForemanTasks::Task.search_for(filter).with_duration.order(:started_at => :desc)
328
+ id_scope = task_scope.group(:id, :started_at)
321
329
 
322
330
  puts _("Exporting all tasks matching filter #{filter}")
323
- puts _("Gathering #{tasks.count(:all)} tasks.")
331
+ puts _("Gathering #{id_scope.count(:all).count} tasks.")
324
332
  case format
325
333
  when 'html'
326
334
  Dir.mktmpdir('task-export') do |tmp_dir|
327
- html_export(tmp_dir, tasks)
335
+ html_export(tmp_dir, id_scope, task_scope)
328
336
  system("tar", "czf", export_filename, tmp_dir)
329
337
  end
330
338
  when 'html-dir'
331
339
  FileUtils.mkdir_p(export_filename)
332
- html_export(export_filename, tasks)
340
+ html_export(export_filename, id_scope, task_scope)
333
341
  when 'csv'
334
- csv_export(export_filename, tasks)
342
+ csv_export(export_filename, id_scope, task_scope)
335
343
  else
336
344
  raise "Unkonwn export format '#{format}'"
337
345
  end
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '5.2.0'.freeze
2
+ VERSION = '5.2.3'.freeze
3
3
  end
@@ -28,6 +28,32 @@ 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
31
57
  end
32
58
  end
33
59
  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: 5.2.0
4
+ version: 5.2.3
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: 2021-11-11 00:00:00.000000000 Z
11
+ date: 2022-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dynflow
@@ -607,7 +607,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
607
607
  - !ruby/object:Gem::Version
608
608
  version: '0'
609
609
  requirements: []
610
- rubygems_version: 3.1.2
610
+ rubygems_version: 3.1.4
611
611
  signing_key:
612
612
  specification_version: 4
613
613
  summary: Foreman plugin for showing tasks information for resources and users