foreman-tasks 4.1.5 → 5.2.0

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