foreman-tasks 4.1.5 → 5.2.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby_tests.yml +0 -1
  3. data/.rubocop.yml +0 -4
  4. data/.rubocop_todo.yml +0 -2
  5. data/README.md +8 -6
  6. data/app/assets/javascripts/foreman_tasks/trigger_form.js +7 -0
  7. data/app/controllers/foreman_tasks/api/tasks_controller.rb +2 -2
  8. data/app/controllers/foreman_tasks/tasks_controller.rb +2 -2
  9. data/app/graphql/mutations/recurring_logics/cancel.rb +27 -0
  10. data/app/graphql/types/recurring_logic.rb +21 -0
  11. data/app/graphql/types/task.rb +25 -0
  12. data/app/graphql/types/triggering.rb +16 -0
  13. data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +4 -1
  14. data/app/lib/actions/helpers/with_continuous_output.rb +1 -1
  15. data/app/lib/actions/proxy_action.rb +2 -12
  16. data/app/lib/actions/trigger_proxy_batch.rb +79 -0
  17. data/app/models/foreman_tasks/recurring_logic.rb +10 -0
  18. data/app/models/foreman_tasks/remote_task.rb +3 -19
  19. data/app/models/foreman_tasks/task.rb +29 -0
  20. data/app/models/foreman_tasks/triggering.rb +14 -4
  21. data/app/views/foreman_tasks/api/tasks/show.json.rabl +1 -1
  22. data/app/views/foreman_tasks/layouts/react.html.erb +0 -1
  23. data/app/views/foreman_tasks/recurring_logics/index.html.erb +4 -2
  24. data/app/views/foreman_tasks/task_groups/recurring_logic_task_groups/_recurring_logic_task_group.html.erb +8 -0
  25. data/db/migrate/20210720115251_add_purpose_to_recurring_logic.rb +6 -0
  26. data/extra/foreman-tasks-cleanup.sh +127 -0
  27. data/extra/foreman-tasks-export.sh +121 -0
  28. data/foreman-tasks.gemspec +1 -4
  29. data/lib/foreman_tasks/continuous_output.rb +50 -0
  30. data/lib/foreman_tasks/engine.rb +8 -15
  31. data/lib/foreman_tasks/tasks/export_tasks.rake +29 -8
  32. data/lib/foreman_tasks/version.rb +1 -1
  33. data/lib/foreman_tasks.rb +2 -5
  34. data/locale/fr/LC_MESSAGES/foreman_tasks.mo +0 -0
  35. data/locale/ja/LC_MESSAGES/foreman_tasks.mo +0 -0
  36. data/locale/zh_CN/LC_MESSAGES/foreman_tasks.mo +0 -0
  37. data/package.json +7 -9
  38. data/test/controllers/api/tasks_controller_test.rb +19 -1
  39. data/test/controllers/tasks_controller_test.rb +19 -0
  40. data/test/factories/recurring_logic_factory.rb +7 -1
  41. data/test/graphql/mutations/recurring_logics/cancel_mutation_test.rb +66 -0
  42. data/test/graphql/queries/recurring_logic_test.rb +28 -0
  43. data/test/graphql/queries/recurring_logics_query_test.rb +30 -0
  44. data/test/graphql/queries/task_query_test.rb +33 -0
  45. data/test/graphql/queries/tasks_query_test.rb +31 -0
  46. data/test/support/dummy_proxy_action.rb +6 -0
  47. data/test/unit/actions/proxy_action_test.rb +11 -11
  48. data/test/unit/actions/trigger_proxy_batch_test.rb +59 -0
  49. data/test/unit/remote_task_test.rb +0 -8
  50. data/test/unit/task_test.rb +39 -8
  51. data/test/unit/triggering_test.rb +22 -0
  52. metadata +23 -26
  53. data/test/core/unit/dispatcher_test.rb +0 -43
  54. data/test/core/unit/runner_test.rb +0 -116
  55. data/test/core/unit/task_launcher_test.rb +0 -56
  56. data/test/foreman_tasks_core_test_helper.rb +0 -4
  57. data/test/unit/otp_manager_test.rb +0 -77
@@ -0,0 +1,127 @@
1
+ #!/bin/bash
2
+
3
+ PROGNAME="$0"
4
+ EXECUTE=0
5
+ RAKE_COMMAND="${RAKE_COMMAND:-"foreman-rake"}"
6
+
7
+ function die() {
8
+ local code="$1"
9
+ local message="$2"
10
+ echo "$message" >&2
11
+ exit $code
12
+ }
13
+
14
+ function build_rake() {
15
+ echo -n "$RAKE_COMMAND "
16
+ echo -n 'foreman_tasks:cleanup '
17
+ for env in AFTER TASK_BACKUP BATCH_SIZE NOOP TASK_SEARCH STATES VERBOSE; do
18
+ local value="${!env}"
19
+ [ -n "${value}" ] && echo -n "${env}=$(printf '%q' "$value") "
20
+ done
21
+ echo
22
+ }
23
+
24
+ function usage() {
25
+ cat << EOF
26
+ Usage: $PROGNAME [script_options...] [options...]
27
+
28
+ An interface script for setting environment variables properly
29
+ for foreman-tasks:cleanup rake task. By default only prints
30
+ the command to run, with -E|--execute flag performs the cleanup.
31
+
32
+ Environment variables:
33
+ RAKE_COMMAND: can be used to redefine path to rake, by default foreman-rake
34
+
35
+ Script options:
36
+ EOF
37
+ cat <<EOF | column -s\& -t
38
+ -E|--execute & execute the created rake command
39
+ -h|--help & show this output
40
+ EOF
41
+
42
+ echo
43
+ echo Cleanup options:
44
+ cat <<EOF | column -s\& -t
45
+ -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
48
+ -b|--backup & backup deleted tasks
49
+ -n|--noop & do a dry run, print what would be done
50
+ -s|--search QUERY & use QUERY in scoped search format to match tasks to delete
51
+ -v|--verbose & be verbose
52
+ EOF
53
+ }
54
+
55
+ SHORTOPTS="a:bB:Ehns:S:v"
56
+ LONGOPTS="after:,backup,batch-size:,execute,help,noop,search:,states:,verbose"
57
+
58
+ ARGS=$(getopt -s bash \
59
+ --options $SHORTOPTS \
60
+ --longoptions $LONGOPTS \
61
+ --name $PROGNAME \
62
+ -- "$@" )
63
+
64
+ if [ $? -gt 0 ]; then
65
+ die 1 "getopt failed"
66
+ fi
67
+
68
+ eval set -- "$ARGS"
69
+
70
+ while true; do
71
+ case $1 in
72
+ -a|--after)
73
+ shift
74
+ AFTER="$1"
75
+ ;;
76
+ -b|--backup)
77
+ TASK_BACKUP=true
78
+ ;;
79
+ -B|--batch-size)
80
+ shift
81
+ BATCH_SIZE="$1"
82
+ ;;
83
+ -n|--noop)
84
+ NOOP=1
85
+ ;;
86
+ -s|--search)
87
+ shift
88
+ TASK_SEARCH="$1"
89
+ ;;
90
+ -S|--states)
91
+ shift
92
+ if [ "$1" == "all" ]; then
93
+ STATES=","
94
+ else
95
+ STATES="$1"
96
+ fi
97
+ ;;
98
+ -v|--verbose)
99
+ VERBOSE=1
100
+ ;;
101
+ -h|--help)
102
+ usage
103
+ exit 0
104
+ ;;
105
+ -E|--execute)
106
+ EXECUTE=1
107
+ ;;
108
+ \?)
109
+ die 1 "Invalid option: -$OPTARG"
110
+ ;;
111
+ --)
112
+ ;;
113
+ *)
114
+ [ -n "$1" ] || break
115
+ die 1 "Unaccepted parameter: $1"
116
+ shift
117
+ ;;
118
+ esac
119
+ shift
120
+ done
121
+
122
+ if [ "$EXECUTE" -eq 1 ]; then
123
+ build_rake | sh
124
+ else
125
+ build_rake
126
+ fi
127
+
@@ -0,0 +1,121 @@
1
+ #!/bin/bash
2
+
3
+ PROGNAME="$0"
4
+ EXECUTE=0
5
+ RAKE_COMMAND="${RAKE_COMMAND:-"foreman-rake"}"
6
+
7
+ function die() {
8
+ local code="$1"
9
+ local message="$2"
10
+ echo "$message" >&2
11
+ exit $code
12
+ }
13
+
14
+ function build_rake() {
15
+ echo -n "$RAKE_COMMAND "
16
+ echo -n 'foreman_tasks:export_tasks '
17
+ for env in TASK_SEARCH TASK_FILE TASK_FORMAT TASK_DAYS SKIP_FAILED; do
18
+ local value="${!env}"
19
+ [ -n "${value}" ] && echo -n "${env}=$(printf '%q' "$value") "
20
+ done
21
+ echo
22
+ }
23
+
24
+ function usage() {
25
+ cat << EOF
26
+ Usage: $PROGNAME [options]
27
+
28
+ An interface script for setting environment variables properly
29
+ for foreman-tasks:export_tasks rake task. By default only prints
30
+ the command to run, with -E|--execute flag performs the export.
31
+
32
+ Environment variables:
33
+ RAKE_COMMAND: can be used to redefine path to rake, by default foreman-rake
34
+
35
+ Script options:
36
+ EOF
37
+ cat <<EOF | column -s\& -t
38
+ -E|--execute & execute the created rake command
39
+ -h|--help & show this output
40
+ EOF
41
+
42
+ echo
43
+ echo Export options:
44
+ cat <<EOF | column -s\& -t
45
+ -d|--days DAYS & export only tasks started within the last DAYS days
46
+ -f|--format FORMAT & export tasks in FORMAT, one of html, html-dir, csv
47
+ -o|--output FILE & export tasks into FILE, a random file will be used if not provided
48
+ -s|--search QUERY & use QUERY in scoped search format to match tasks to export
49
+ -S|--skip-failed & skip tasks that fail to export
50
+ EOF
51
+ }
52
+
53
+ SHORTOPTS="d:Ehs:o:f:S"
54
+ LONGOPTS="days:,execute,help,search:,output:,format:,skip-failed"
55
+
56
+ ARGS=$(getopt -s bash \
57
+ --options $SHORTOPTS \
58
+ --longoptions $LONGOPTS \
59
+ --name $PROGNAME \
60
+ -- "$@" )
61
+
62
+ if [ $? -gt 0 ]; then
63
+ die 1 "getopt failed"
64
+ fi
65
+
66
+ eval set -- "$ARGS"
67
+
68
+ while true; do
69
+ case $1 in
70
+ -d|--days)
71
+ shift
72
+ TASK_DAYS="$1"
73
+ ;;
74
+ -s|--search)
75
+ shift
76
+ TASK_SEARCH="$1"
77
+ ;;
78
+ -o|--output)
79
+ shift
80
+ TASK_FILE="$1"
81
+ ;;
82
+ -f|--format)
83
+ case "$2" in
84
+ "html" | "html-dir" | "csv")
85
+ shift
86
+ TASK_FORMAT="$1"
87
+ ;;
88
+ *)
89
+ die 1 "Value for $1 must be one of html, csv. Given $2"
90
+ ;;
91
+ esac
92
+ ;;
93
+ -h|--help)
94
+ usage
95
+ exit 0
96
+ ;;
97
+ -E|--execute)
98
+ EXECUTE=1
99
+ ;;
100
+ -S|--skip-failed)
101
+ SKIP_FAILED=1
102
+ ;;
103
+ \?)
104
+ die 1 "Invalid option: -$OPTARG"
105
+ ;;
106
+ --)
107
+ ;;
108
+ *)
109
+ [ -n "$1" ] || break
110
+ die 1 "Unaccepted parameter: $1"
111
+ shift
112
+ ;;
113
+ esac
114
+ shift
115
+ done
116
+
117
+ if [ "$EXECUTE" -eq 1 ]; then
118
+ build_rake | sh
119
+ else
120
+ build_rake
121
+ fi
@@ -20,16 +20,13 @@ same resource. It also optionally provides Dynflow infrastructure for using it f
20
20
  DESC
21
21
 
22
22
  s.files = `git ls-files`.split("\n").reject do |file|
23
- file.end_with?("test.rake") ||
24
- file.start_with?('lib/foreman_tasks_core') ||
25
- file == 'foreman-tasks-core.gemspec'
23
+ file.end_with?("test.rake")
26
24
  end
27
25
 
28
26
  s.test_files = `git ls-files test`.split("\n")
29
27
  s.extra_rdoc_files = Dir['README*', 'LICENSE']
30
28
 
31
29
  s.add_dependency "dynflow", '>= 1.2.3'
32
- s.add_dependency "foreman-tasks-core"
33
30
  s.add_dependency "get_process_mem" # for memory polling
34
31
  s.add_dependency "parse-cron", '~> 0.1.4'
35
32
  s.add_dependency "sinatra" # for Dynflow web console
@@ -0,0 +1,50 @@
1
+ module ForemanTasks
2
+ class ContinuousOutput
3
+ attr_accessor :raw_outputs
4
+
5
+ def initialize(raw_outputs = [])
6
+ @raw_outputs = []
7
+ raw_outputs.each { |raw_output| add_raw_output(raw_output) }
8
+ end
9
+
10
+ def add_raw_output(raw_output)
11
+ missing_args = %w[output_type output timestamp] - raw_output.keys
12
+ unless missing_args.empty?
13
+ raise ArgumentError, "Missing args for raw output: #{missing_args.inspect}"
14
+ end
15
+ @raw_outputs << raw_output
16
+ end
17
+
18
+ def empty?
19
+ @raw_outputs.empty?
20
+ end
21
+
22
+ def last_timestamp
23
+ return if @raw_outputs.empty?
24
+ @raw_outputs.last.fetch('timestamp')
25
+ end
26
+
27
+ def sort!
28
+ @raw_outputs.sort_by! { |record| record['timestamp'].to_f }
29
+ end
30
+
31
+ def humanize
32
+ sort!
33
+ raw_outputs.map { |output| output['output'] }.join("\n")
34
+ end
35
+
36
+ def add_exception(context, exception, timestamp = Time.now.getlocal)
37
+ add_output(context + ": #{exception.class} - #{exception.message}", 'debug', timestamp)
38
+ end
39
+
40
+ def add_output(*args)
41
+ add_raw_output(self.class.format_output(*args))
42
+ end
43
+
44
+ def self.format_output(message, type = 'debug', timestamp = Time.now.getlocal)
45
+ { 'output_type' => type,
46
+ 'output' => message,
47
+ 'timestamp' => timestamp.to_f }
48
+ end
49
+ end
50
+ end
@@ -1,4 +1,3 @@
1
- require 'foreman_tasks_core'
2
1
  require 'fast_gettext'
3
2
  require 'gettext_i18n_rails'
4
3
 
@@ -34,7 +33,7 @@ module ForemanTasks
34
33
 
35
34
  initializer 'foreman_tasks.register_plugin', :before => :finisher_hook do |_app|
36
35
  Foreman::Plugin.register :"foreman-tasks" do
37
- requires_foreman '>= 2.4.0'
36
+ requires_foreman '>= 2.6.0'
38
37
  divider :top_menu, :parent => :monitor_menu, :last => true, :caption => N_('Foreman Tasks')
39
38
  menu :top_menu, :tasks,
40
39
  :url_hash => { :controller => 'foreman_tasks/tasks', :action => :index },
@@ -66,6 +65,13 @@ module ForemanTasks
66
65
 
67
66
  add_all_permissions_to_default_roles
68
67
 
68
+ register_graphql_query_field :task, '::Types::Task', :record_field
69
+ register_graphql_query_field :tasks, '::Types::Task', :collection_field
70
+ register_graphql_query_field :recurring_logic, '::Types::RecurringLogic', :record_field
71
+ register_graphql_query_field :recurring_logics, '::Types::RecurringLogic', :collection_field
72
+
73
+ register_graphql_mutation_field :cancel_recurring_logic, ::Mutations::RecurringLogics::Cancel
74
+
69
75
  logger :dynflow, :enabled => true
70
76
  logger :action, :enabled => true
71
77
 
@@ -116,19 +122,6 @@ module ForemanTasks
116
122
  world.middleware.use Actions::Middleware::KeepCurrentRequestID
117
123
  world.middleware.use ::Actions::Middleware::LoadSettingValues if Gem::Version.new(::SETTINGS[:version]) >= Gem::Version.new('2.5')
118
124
  end
119
-
120
- ::ForemanTasks.dynflow.config.on_init do |world|
121
- ForemanTasksCore.dynflow_setup(world)
122
- end
123
- end
124
-
125
- initializer 'foreman_tasks.set_core_settings' do
126
- ForemanTasksCore::SettingsLoader.settings_registry.each_key do |settings_keys|
127
- settings = settings_keys.inject({}) do |h, settings_key|
128
- h.merge(SETTINGS[settings_key] || {})
129
- end
130
- ForemanTasksCore::SettingsLoader.setup_settings(settings_keys.first, settings)
131
- end
132
125
  end
133
126
 
134
127
  # to enable async Foreman operations using Dynflow
@@ -14,6 +14,7 @@ namespace :foreman_tasks do
14
14
  * TASK_FILE : file to export to
15
15
  * TASK_FORMAT : format to use for the export (either html, html-dir or csv)
16
16
  * TASK_DAYS : number of days to go back
17
+ * SKIP_FAILED : skip tasks that fail to export (true or false[default])
17
18
 
18
19
  If TASK_SEARCH is not defined, it defaults to all tasks in the past 7 days and
19
20
  all unsuccessful tasks in the past 60 days. The default TASK_FORMAT is html
@@ -232,6 +233,7 @@ namespace :foreman_tasks do
232
233
  <tr>
233
234
  <td><a href=\"#{task.id}.html\">#{task.label}</a></td>
234
235
  <td>#{task.started_at}</td>
236
+ <td>#{task.duration}</td>
235
237
  <td>#{task.state}</td>
236
238
  <td>#{task.result}</td>
237
239
  </tr>
@@ -241,10 +243,12 @@ namespace :foreman_tasks do
241
243
 
242
244
  def csv_export(export_filename, tasks)
243
245
  CSV.open(export_filename, 'wb') do |csv|
244
- csv << %w[id state type label result parent_task_id started_at ended_at]
246
+ csv << %w[id state type label result parent_task_id started_at ended_at duration]
245
247
  tasks.find_each do |task|
246
- csv << [task.id, task.state, task.type, task.label, task.result,
247
- task.parent_task_id, task.started_at, task.ended_at]
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]
251
+ end
248
252
  end
249
253
  end
250
254
  end
@@ -253,15 +257,18 @@ namespace :foreman_tasks do
253
257
  PageHelper.copy_assets(workdir)
254
258
 
255
259
  renderer = TaskRender.new
256
- total = tasks.count
260
+ total = tasks.count(:all)
257
261
  index = File.open(File.join(workdir, 'index.html'), 'w')
258
262
 
259
263
  File.open(File.join(workdir, 'index.html'), 'w') do |index|
260
264
  PageHelper.pagify(index) do |io|
261
265
  PageHelper.generate_with_index(io) do |index|
262
266
  tasks.find_each.each_with_index do |task, count|
263
- File.open(File.join(workdir, "#{task.id}.html"), 'w') { |file| PageHelper.pagify(file, renderer.render_task(task)) }
264
- PageHelper.generate_index_entry(index, task)
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) }
271
+ end
265
272
  puts "#{count + 1}/#{total}"
266
273
  end
267
274
  end
@@ -281,6 +288,20 @@ namespace :foreman_tasks do
281
288
  end
282
289
  end
283
290
 
291
+ def with_error_handling(task, what = _('task'))
292
+ yield
293
+ rescue StandardError => e
294
+ resolution = SKIP_ERRORS ? _(', skipping') : ''
295
+ puts _("WARNING: %{what} failed to export%{resolution}. Additional task details below.") % { :what => what, :resolution => resolution }
296
+ puts task.inspect
297
+ unless SKIP_ERRORS
298
+ puts _("Re-run with SKIP_FAILED=true if you want to simply skip any tasks that fail to export.")
299
+ raise e
300
+ end
301
+ end
302
+
303
+ SKIP_ERRORS = ['true', '1', 'y', 'yes'].include? ENV['SKIP_FAILED'].downcase
304
+
284
305
  filter = if ENV['TASK_SEARCH'].nil? && ENV['TASK_DAYS'].nil?
285
306
  "started_at > \"#{7.days.ago.to_s(:db)}\" || " \
286
307
  "(result != success && started_at > \"#{60.days.ago.to_s(:db)})\""
@@ -296,10 +317,10 @@ namespace :foreman_tasks do
296
317
  format = ENV['TASK_FORMAT'] || 'html'
297
318
  export_filename = ENV['TASK_FILE'] || generate_filename(format)
298
319
 
299
- tasks = ForemanTasks::Task.search_for(filter).order(:started_at => :desc)
320
+ tasks = ForemanTasks::Task.search_for(filter).order(:started_at => :desc).with_duration.distinct
300
321
 
301
322
  puts _("Exporting all tasks matching filter #{filter}")
302
- puts _("Gathering #{tasks.count} tasks.")
323
+ puts _("Gathering #{tasks.count(:all)} tasks.")
303
324
  case format
304
325
  when 'html'
305
326
  Dir.mktmpdir('task-export') do |tmp_dir|
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '4.1.5'.freeze
2
+ VERSION = '5.2.0'.freeze
3
3
  end
data/lib/foreman_tasks.rb CHANGED
@@ -6,17 +6,14 @@ require 'foreman_tasks/dynflow/configuration'
6
6
  require 'foreman_tasks/triggers'
7
7
  require 'foreman_tasks/authorizer_ext'
8
8
  require 'foreman_tasks/cleaner'
9
+ require 'foreman_tasks/continuous_output'
9
10
 
10
11
  module ForemanTasks
11
12
  extend Algebrick::TypeCheck
12
13
  extend Algebrick::Matching
13
14
 
14
15
  def self.dynflow
15
- @dynflow ||= begin
16
- world = ForemanTasks::Dynflow.new(nil, ForemanTasks::Dynflow::Configuration.new)
17
- ForemanTasksCore.dynflow_setup(world) if defined?(ForemanTasksCore)
18
- world
19
- end
16
+ @dynflow ||= ForemanTasks::Dynflow.new(nil, ForemanTasks::Dynflow::Configuration.new)
20
17
  end
21
18
 
22
19
  def self.trigger(action, *args, &block)
Binary file
Binary file
data/package.json CHANGED
@@ -23,20 +23,18 @@
23
23
  "url": "http://projects.theforeman.org/projects/foreman-tasks/issues"
24
24
  },
25
25
  "peerDependencies": {
26
- "@theforeman/vendor": ">= 6.0.0"
26
+ "@theforeman/vendor": "^8.15.0"
27
27
  },
28
28
  "dependencies": {
29
- "c3": "^0.4.11",
30
- "humanize-duration": "^3.20.1",
31
- "react-intl": "^2.8.0"
29
+ "c3": "^0.4.11"
32
30
  },
33
31
  "devDependencies": {
34
32
  "@babel/core": "^7.7.0",
35
- "@theforeman/builder": "^6.0.0",
36
- "@theforeman/eslint-plugin-foreman": "6.0.0",
37
- "@theforeman/stories": "^6.0.0",
38
- "@theforeman/test": "^6.0.0",
39
- "@theforeman/vendor-dev": "^6.0.0",
33
+ "@theforeman/builder": "^8.15.0",
34
+ "@theforeman/eslint-plugin-foreman": "^8.15.0",
35
+ "@theforeman/stories": "^8.15.0",
36
+ "@theforeman/test": "^8.15.0",
37
+ "@theforeman/vendor-dev": "^8.15.0",
40
38
  "babel-eslint": "^10.0.3",
41
39
  "eslint": "^6.7.2",
42
40
  "jed": "^1.1.1",
@@ -102,6 +102,24 @@ module ForemanTasks
102
102
  session: set_session_user(User.current)
103
103
  assert_response :not_found
104
104
  end
105
+
106
+ it 'shows duration column' do
107
+ task = ForemanTasks::Task.with_duration.find(FactoryBot.create(:dynflow_task).id)
108
+ get :show, params: { id: task.id }, session: set_session_user
109
+ assert_response :success
110
+ data = JSON.parse(response.body)
111
+ _(data['duration']).must_equal task.duration
112
+ end
113
+ end
114
+
115
+ describe 'GET /api/tasks/index' do
116
+ it 'shows duration column' do
117
+ task = ForemanTasks::Task.with_duration.find(FactoryBot.create(:dynflow_task).id)
118
+ get :index, session: set_session_user
119
+ assert_response :success
120
+ data = JSON.parse(response.body)
121
+ _(data['results'][0]['duration']).must_equal task.duration
122
+ end
105
123
  end
106
124
 
107
125
  describe 'GET /api/tasks/summary' do
@@ -174,7 +192,7 @@ module ForemanTasks
174
192
  _(task.state).must_equal 'running'
175
193
  _(task.result).must_equal 'pending'
176
194
 
177
- callback = Support::DummyProxyAction.proxy.log[:trigger_task].first[1][:callback]
195
+ callback = Support::DummyProxyAction.proxy.log[:trigger_task].first[1][:action_input][:callback]
178
196
  post :callback, params: { 'callback' => callback, 'data' => { 'result' => 'success' } }
179
197
  triggered.finished.wait(5)
180
198
 
@@ -91,6 +91,15 @@ module ForemanTasks
91
91
  end
92
92
  end
93
93
 
94
+ describe 'index' do
95
+ it 'shows duration column' do
96
+ task = ForemanTasks::Task.with_duration.find(FactoryBot.create(:some_task).id)
97
+ get(:index, params: {}, session: set_session_user)
98
+ assert_response :success
99
+ assert_include response.body.lines[1], task.duration
100
+ end
101
+ end
102
+
94
103
  describe 'sub_tasks' do
95
104
  it 'does not allow user without permissions to see task details' do
96
105
  setup_user('view', 'foreman_tasks', 'owner.id = current_user')
@@ -109,6 +118,16 @@ module ForemanTasks
109
118
  assert_equal 2, response.body.lines.size
110
119
  assert_include response.body.lines[1], 'Child action'
111
120
  end
121
+
122
+ it 'shows duration column' do
123
+ parent = ForemanTasks::Task.find(FactoryBot.create(:some_task).id)
124
+ child = ForemanTasks::Task.with_duration.find(FactoryBot.create(:some_task).id)
125
+ child.parent_task_id = parent.id
126
+ child.save!
127
+ get(:sub_tasks, params: { id: parent.id }, session: set_session_user)
128
+ assert_response :success
129
+ assert_include response.body.lines[1], child.duration
130
+ end
112
131
  end
113
132
 
114
133
  describe 'taxonomy scoping' do
@@ -1,8 +1,14 @@
1
1
  FactoryBot.define do
2
2
  factory :recurring_logic, :class => ForemanTasks::RecurringLogic do
3
3
  cron_line { '* * * * *' }
4
- after(:build) { |logic| logic.task_group = build(:recurring_logic_task_group) }
4
+ association :task_group
5
5
  end
6
6
 
7
+ factory :task_group, :class => ::ForemanTasks::TaskGroup do
8
+ type { "ForemanTasks::TaskGroups::RecurringLogicTaskGroup" }
9
+ end
7
10
  factory :recurring_logic_task_group, :class => ::ForemanTasks::TaskGroups::RecurringLogicTaskGroup
11
+ factory :task_group_member, :class => ::ForemanTasks::TaskGroupMember do
12
+ association :task_group, :task
13
+ end
8
14
  end
@@ -0,0 +1,66 @@
1
+ require 'foreman_tasks_test_helper'
2
+
3
+ module Mutations
4
+ module RecurringLogics
5
+ class CancelMutationTest < ActiveSupport::TestCase
6
+ setup do
7
+ @task = FactoryBot.create(:dynflow_task, state: 'pending')
8
+ @task_group = FactoryBot.create(:recurring_logic_task_group)
9
+ @task_group_member = FactoryBot.create(:task_group_member, task: @task, task_group: @task_group)
10
+ @recurring_logic = FactoryBot.create(:recurring_logic, task_group: @task_group)
11
+ @id = Foreman::GlobalId.for(@recurring_logic)
12
+ @variables = { id: @id }
13
+ @query =
14
+ <<-GRAPHQL
15
+ mutation CancelRecurringLogic($id:ID!) {
16
+ cancelRecurringLogic(input: { id: $id }){
17
+ recurringLogic {
18
+ id
19
+ state
20
+ cronLine
21
+ }
22
+ errors {
23
+ message
24
+ path
25
+ }
26
+ }
27
+ }
28
+ GRAPHQL
29
+ end
30
+
31
+ context 'as admin' do
32
+ setup do
33
+ @context = { current_user: FactoryBot.create(:user, :admin) }
34
+ end
35
+
36
+ test 'should cancel recurring logic' do
37
+ assert_not_equal 'cancelled', @recurring_logic.state
38
+ result = ForemanGraphqlSchema.execute(@query, variables: @variables, context: @context)
39
+ assert_empty result['errors']
40
+ assert_empty result['data']['cancelRecurringLogic']['errors']
41
+ assert_equal 'cancelled', result['data']['cancelRecurringLogic']['recurringLogic']['state']
42
+ @recurring_logic.reload
43
+ assert_equal 'cancelled', @recurring_logic.state
44
+ end
45
+
46
+ test 'should handle errors on execution plan load failure' do
47
+ invalid_plan = ::Dynflow::ExecutionPlan::InvalidPlan.new(StandardError.new('This is a failure'), 'xyz', 'test-label', 'invalid')
48
+ ::Dynflow::Persistence.any_instance.stubs(:load_execution_plan).returns(invalid_plan)
49
+ result = ForemanGraphqlSchema.execute(@query, variables: @variables, context: @context)
50
+ assert_equal "There has been an error when canceling one of the tasks: This is a failure", result['data']['cancelRecurringLogic']['errors'].first['message']
51
+ end
52
+ end
53
+
54
+ context 'as viewer' do
55
+ setup do
56
+ @context = { current_user: setup_user('view', 'recurring_logics') }
57
+ end
58
+
59
+ test 'should not allow to cancel recurring logic' do
60
+ result = ForemanGraphqlSchema.execute(@query, variables: @variables, context: @context)
61
+ assert_includes result['errors'].first['message'], 'Unauthorized.'
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end