foreman-tasks 4.1.6 → 5.0.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby_tests.yml +0 -1
  3. data/app/controllers/foreman_tasks/api/tasks_controller.rb +2 -2
  4. data/app/controllers/foreman_tasks/tasks_controller.rb +7 -6
  5. data/app/graphql/types/recurring_logic.rb +18 -0
  6. data/app/graphql/types/task.rb +25 -0
  7. data/app/graphql/types/triggering.rb +16 -0
  8. data/app/lib/actions/helpers/with_continuous_output.rb +1 -1
  9. data/app/lib/actions/middleware/watch_delegated_proxy_sub_tasks.rb +6 -2
  10. data/app/models/foreman_tasks/recurring_logic.rb +2 -0
  11. data/app/models/foreman_tasks/task/dynflow_task.rb +3 -8
  12. data/app/models/foreman_tasks/task.rb +28 -0
  13. data/app/models/foreman_tasks/triggering.rb +2 -0
  14. data/app/services/foreman_tasks/dashboard_table_filter.rb +56 -0
  15. data/foreman-tasks.gemspec +0 -1
  16. data/lib/foreman_tasks/continuous_output.rb +50 -0
  17. data/lib/foreman_tasks/engine.rb +6 -15
  18. data/lib/foreman_tasks/tasks/export_tasks.rake +46 -90
  19. data/lib/foreman_tasks/version.rb +1 -1
  20. data/lib/foreman_tasks.rb +2 -5
  21. data/test/controllers/api/tasks_controller_test.rb +0 -11
  22. data/test/graphql/queries/recurring_logic_test.rb +28 -0
  23. data/test/graphql/queries/recurring_logics_query_test.rb +30 -0
  24. data/test/graphql/queries/task_query_test.rb +33 -0
  25. data/test/graphql/queries/tasks_query_test.rb +31 -0
  26. data/test/unit/dashboard_table_filter_test.rb +77 -0
  27. data/test/unit/task_test.rb +39 -8
  28. data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.js +4 -11
  29. data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.test.js +5 -27
  30. data/webpack/ForemanTasks/Components/TasksTable/TasksTable.js +0 -8
  31. data/webpack/ForemanTasks/Components/TasksTable/TasksTableActions.js +1 -6
  32. data/webpack/ForemanTasks/Components/TasksTable/TasksTableHelpers.js +1 -2
  33. data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +11 -22
  34. data/webpack/ForemanTasks/Components/TasksTable/TasksTableReducer.js +16 -17
  35. data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +0 -3
  36. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableHelpers.test.js +1 -1
  37. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableReducer.test.js +1 -3
  38. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +2 -12
  39. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableReducer.test.js.snap +0 -5
  40. data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/selectionHeaderCellFormatter.test.js.snap +0 -1
  41. data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/selectionHeaderCellFormatter.test.js +1 -1
  42. data/webpack/ForemanTasks/Components/TasksTable/formatters/selectionHeaderCellFormatter.js +0 -1
  43. data/webpack/ForemanTasks/Components/TasksTable/index.js +0 -2
  44. metadata +18 -27
  45. data/test/core/unit/dispatcher_test.rb +0 -43
  46. data/test/core/unit/runner_test.rb +0 -116
  47. data/test/core/unit/task_launcher_test.rb +0 -56
  48. data/test/foreman_tasks_core_test_helper.rb +0 -4
  49. data/test/unit/otp_manager_test.rb +0 -77
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 113adfd7bfb8197852703d6bb8ca03e724b355a55723b8a0146c016607bb0024
4
- data.tar.gz: 9117207fc628934e82528eb2238f0bcb010e3f92654b621675485ca4f1a59f9e
3
+ metadata.gz: 3e48966ae6049eb85b3051151b7d15d48beb91a55b3580525fe114a3e045ad0f
4
+ data.tar.gz: 93da446f348f1a27600a79223ba4daab0e5e83417db711919392cfab2eedf57a
5
5
  SHA512:
6
- metadata.gz: 0b8228f8d1ea657cf35106884d1d1b537f0ac910af82c1d74f43a2eb28d2337c6a76cd4f89d927a7731af222453acd6439110b92281a9acbadf1696d1dd55ff4
7
- data.tar.gz: 365c53bf9ce1c3e3e15a9632828f867a83d74ecad2fdd7c1c7ce1b3ecd4d6d6f6775adef6f1e02fb6eff79cf6a6c437663e0a4c8e1cd773c9dded48445138cee
6
+ metadata.gz: 740ff700f75e43154068d3838611dc7c7306d7f5da91e3d2d41a6fce5ea0ccde84bd5f027545f24874b8fb112d9848505ae2a535386ee2f356852695dacd61a2
7
+ data.tar.gz: 8f61c3c868af4b7c3d3173b728858537c72ccf695e60b63971ec19f8a675a6ad9c19ecdd3dedb2549e2eb27b7cde62a2be67ead4d737bb43b774301abb303228
@@ -72,5 +72,4 @@ jobs:
72
72
  - name: Run plugin tests
73
73
  run: |
74
74
  bundle exec rake test:foreman_tasks
75
- bundle exec rake test:foreman_tasks_core
76
75
  bundle exec rake test TEST="test/unit/foreman/access_permissions_test.rb"
@@ -192,7 +192,7 @@ module ForemanTasks
192
192
  params[:order] = "#{ordering_params[:sort_by]} #{ordering_params[:sort_order]}"
193
193
  end
194
194
  params[:order] ||= 'started_at DESC'
195
- @tasks = resource_scope_for_index.order(params[:order].to_s)
195
+ @tasks = DashboardTableFilter.new(resource_scope_for_index, params).scope.order(params[:order].to_s)
196
196
  end
197
197
 
198
198
  def search_options
@@ -260,7 +260,7 @@ module ForemanTasks
260
260
  raise BadRequest,
261
261
  _('Resource search_params requires resource_type and resource_id to be specified')
262
262
  end
263
- scope.joins(:links).where(foreman_tasks_links:
263
+ scope.joins(:locks).where(foreman_tasks_locks:
264
264
  { resource_type: search_params[:resource_type],
265
265
  resource_id: search_params[:resource_id] })
266
266
  when 'task'
@@ -72,15 +72,17 @@ module ForemanTasks
72
72
  def unlock
73
73
  task = find_dynflow_task
74
74
  if task.paused?
75
- force_unlock(task)
75
+ task.state = :stopped
76
+ task.save!
77
+ render json: { statusText: 'OK' }
76
78
  else
77
79
  render json: {}, status: :bad_request
78
80
  end
79
81
  end
80
82
 
81
- def force_unlock(task = find_dynflow_task)
83
+ def force_unlock
84
+ task = find_dynflow_task
82
85
  task.state = :stopped
83
- task.locks.destroy_all
84
86
  task.save!
85
87
  render json: { statusText: 'OK' }
86
88
  end
@@ -125,9 +127,8 @@ module ForemanTasks
125
127
  end
126
128
 
127
129
  def filter(scope, paginate: true)
128
- search = current_taxonomy_search
129
- search = [search, params[:search]].select(&:present?).join(' AND ')
130
- scope = scope.search_for(search, order: params[:order])
130
+ scope = DashboardTableFilter.new(scope, params).scope
131
+ scope = scope.search_for(search_query, order: params[:order])
131
132
  scope = scope.paginate(page: params[:page], per_page: params[:per_page]) if paginate
132
133
  scope.distinct
133
134
  end
@@ -0,0 +1,18 @@
1
+ module Types
2
+ class RecurringLogic < Types::BaseObject
3
+ description 'A Recurring Logic'
4
+ model_class ::ForemanTasks::RecurringLogic
5
+
6
+ global_id_field :id
7
+ field :cron_line, String
8
+ field :end_time, GraphQL::Types::ISO8601DateTime
9
+ field :max_iteration, Integer
10
+ field :iteration, Integer
11
+ field :state, String
12
+ belongs_to :triggering, Types::Triggering
13
+
14
+ def self.graphql_definition
15
+ super.tap { |type| type.instance_variable_set(:@name, 'ForemanTasks::RecurringLogic') }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ module Types
2
+ class Task < Types::BaseObject
3
+ description 'A Task'
4
+ model_class ::ForemanTasks::Task
5
+
6
+ global_id_field :id
7
+ field :type, String
8
+ field :label, String
9
+ field :started_at, GraphQL::Types::ISO8601DateTime
10
+ field :ended_at, GraphQL::Types::ISO8601DateTime
11
+ field :state, String
12
+ field :result, String
13
+ field :external_id, String
14
+ field :parent_task_id, String
15
+ field :start_at, GraphQL::Types::ISO8601DateTime
16
+ field :start_before, GraphQL::Types::ISO8601DateTime
17
+ field :action, String
18
+ field :user_id, Integer
19
+ field :state_updated_at, GraphQL::Types::ISO8601DateTime
20
+
21
+ def self.graphql_definition
22
+ super.tap { |type| type.instance_variable_set(:@name, 'ForemanTasks::Task') }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ module Types
2
+ class Triggering < Types::BaseObject
3
+ description 'A Task Triggering'
4
+ model_class ::ForemanTasks::Triggering
5
+
6
+ global_id_field :id
7
+ field :mode, String
8
+ field :start_at, GraphQL::Types::ISO8601DateTime
9
+ field :start_before, GraphQL::Types::ISO8601DateTime
10
+ field :recurring_logic, Types::RecurringLogic
11
+
12
+ def self.graphql_definition
13
+ super.tap { |type| type.instance_variable_set(:@name, 'ForemanTasks::Triggering') }
14
+ end
15
+ end
16
+ end
@@ -8,7 +8,7 @@ module Actions
8
8
  end
9
9
 
10
10
  def continuous_output
11
- continuous_output = ::ForemanTasksCore::ContinuousOutput.new
11
+ continuous_output = ::ForemanTasks::ContinuousOutput.new
12
12
  continuous_output_providers.each do |continous_output_provider|
13
13
  continous_output_provider.fill_continuous_output(continuous_output)
14
14
  end
@@ -20,7 +20,9 @@ module Actions
20
20
  private
21
21
 
22
22
  def set_clock
23
- action.plan_event(CheckOnProxyActions, POLL_INTERVAL, optional: true)
23
+ action.world.clock.ping action.send(:suspended_action),
24
+ POLL_INTERVAL,
25
+ CheckOnProxyActions
24
26
  end
25
27
 
26
28
  def check_triggered
@@ -42,7 +44,9 @@ module Actions
42
44
 
43
45
  def notify(event, tasks)
44
46
  tasks.each do |task|
45
- action.plan_event(event, execution_plan_id: task.execution_plan_id, step_id: task.step_id)
47
+ action.world.event task.execution_plan_id,
48
+ task.step_id,
49
+ event
46
50
  end
47
51
  end
48
52
 
@@ -4,6 +4,8 @@ module ForemanTasks
4
4
  class RecurringLogic < ApplicationRecord
5
5
  include Authorizable
6
6
 
7
+ graphql_type '::Types::RecurringLogic'
8
+
7
9
  belongs_to :task_group
8
10
  belongs_to :triggering
9
11
 
@@ -140,17 +140,12 @@ module ForemanTasks
140
140
 
141
141
  def main_action
142
142
  return @main_action if defined?(@main_action)
143
-
144
- @main_action = execution_plan && execution_plan.root_plan_step.try(:action, execution_plan)
145
143
  if active_job?
146
144
  job_data = active_job_data
147
- begin
148
- @main_action = active_job_action(job_data['job_class'], job_data['arguments'])
149
- rescue => e
150
- Foreman::Logging.exception("Failed to load ActiveJob for task #{id}", e, :logger => 'foreman-tasks')
151
- end
145
+ @main_action = active_job_action(job_data['job_class'], job_data['arguments'])
146
+ else
147
+ @main_action = execution_plan && execution_plan.root_plan_step.try(:action, execution_plan)
152
148
  end
153
- @main_action
154
149
  end
155
150
 
156
151
  # The class for ActiveJob jobs in Dynflow, JobWrapper is not expected to
@@ -5,6 +5,8 @@ module ForemanTasks
5
5
  include Authorizable
6
6
  extend Search
7
7
 
8
+ graphql_type '::Types::Task'
9
+
8
10
  def check_permissions_after_save
9
11
  # there's no create_tasks permission, tasks are created as a result of internal actions, in such case we
10
12
  # don't do authorization, that should have been performed on wrapping action level
@@ -255,6 +257,32 @@ module ForemanTasks
255
257
  main_action.continuous_output.raw_outputs
256
258
  end
257
259
 
260
+ def self.latest_tasks_by_resource_ids(label, resource_type, resource_ids)
261
+ tasks = arel_table
262
+ links = ForemanTasks::Link.arel_table
263
+ started_at = tasks[:started_at]
264
+ resource_id = links[:resource_id]
265
+
266
+ base_combined_table = tasks
267
+ .join(links).on(tasks[:id].eq(links[:task_id]))
268
+ .where(tasks[:label].eq(label)
269
+ .and(links[:resource_type].eq(resource_type))
270
+ .and(links[:resource_id].in(resource_ids)))
271
+
272
+ grouped = base_combined_table.project(
273
+ started_at.maximum.as('started_at_max'),
274
+ resource_id
275
+ ).group(resource_id).order(resource_id).as('grouped')
276
+
277
+ max_per_resource_id = tasks
278
+ .join(links).on(tasks[:id].eq(links[:task_id]))
279
+ .join(grouped).on(grouped[:started_at_max].eq(started_at).and(grouped[:resource_id].eq(resource_id)))
280
+ .distinct
281
+ .project(tasks[Arel.star], grouped[:resource_id])
282
+
283
+ find_by_sql(max_per_resource_id.to_sql).index_by(&:resource_id)
284
+ end
285
+
258
286
  protected
259
287
 
260
288
  def generate_id
@@ -5,6 +5,8 @@ module ForemanTasks
5
5
  :end_time].freeze
6
6
  attr_accessor(*PARAMS)
7
7
 
8
+ graphql_type '::Types::Triggering'
9
+
8
10
  before_save do
9
11
  if future?
10
12
  parse_start_at!
@@ -0,0 +1,56 @@
1
+ module ForemanTasks
2
+ # narrows the scope for the tasks table based on params coming from tasks dashboard
3
+ #
4
+ # Supported filters:
5
+ #
6
+ # * :result
7
+ # * :state
8
+ # * :time_horizon - expected format of Hxy, where the xy is the time horizon in hours we're interested in
9
+ # :time_mode can be set to 'recent' to filter the recent tasks, or 'older' (default) to filter earlier ones
10
+ class DashboardTableFilter
11
+ def initialize(scope, params)
12
+ @scope = scope
13
+ @params = params
14
+ end
15
+
16
+ def scope
17
+ @new_scope = @scope
18
+ scope_by(:result)
19
+ scope_by(:state)
20
+ scope_by_time
21
+ @new_scope
22
+ end
23
+
24
+ private
25
+
26
+ def scope_by(field)
27
+ if (field == :result) && (@params[field] == 'other')
28
+ @new_scope = @new_scope.where(:result => ['cancelled', 'pending'])
29
+ elsif @params[field].present?
30
+ @new_scope = @new_scope.where(field => @params[field])
31
+ end
32
+ end
33
+
34
+ def scope_by_time
35
+ return if @params[:time_horizon].blank?
36
+ hours = if @params[:time_horizon].casecmp('week') == 0
37
+ 24 * 7
38
+ else
39
+ @params[:time_horizon][/\AH(\d{1,2})$/i, 1]
40
+ end
41
+
42
+ unless hours
43
+ raise Foreman::Exception, 'Unexpected format of time: should be in form of "H24" or equal to "week"'
44
+ end
45
+ timestamp = Time.now.utc - hours.to_i.hours
46
+ case @params[:time_mode]
47
+ when 'recent'
48
+ operator = '>'
49
+ else
50
+ operator = '<'
51
+ search_suffix = 'OR state_updated_at IS NULL'
52
+ end
53
+ @new_scope = @new_scope.where("state_updated_at #{operator} ? #{search_suffix}", timestamp)
54
+ end
55
+ end
56
+ end
@@ -29,7 +29,6 @@ same resource. It also optionally provides Dynflow infrastructure for using it f
29
29
  s.extra_rdoc_files = Dir['README*', 'LICENSE']
30
30
 
31
31
  s.add_dependency "dynflow", '>= 1.2.3'
32
- s.add_dependency "foreman-tasks-core"
33
32
  s.add_dependency "get_process_mem" # for memory polling
34
33
  s.add_dependency "parse-cron", '~> 0.1.4'
35
34
  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,11 @@ 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
+
69
73
  logger :dynflow, :enabled => true
70
74
  logger :action, :enabled => true
71
75
 
@@ -116,19 +120,6 @@ module ForemanTasks
116
120
  world.middleware.use Actions::Middleware::KeepCurrentRequestID
117
121
  world.middleware.use ::Actions::Middleware::LoadSettingValues if Gem::Version.new(::SETTINGS[:version]) >= Gem::Version.new('2.5')
118
122
  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
123
  end
133
124
 
134
125
  # to enable async Foreman operations using Dynflow
@@ -12,7 +12,7 @@ namespace :foreman_tasks do
12
12
 
13
13
  * TASK_SEARCH : scoped search filter (example: 'label = "Actions::Foreman::Host::ImportFacts"')
14
14
  * TASK_FILE : file to export to
15
- * TASK_FORMAT : format to use for the export (either html, html-dir or csv)
15
+ * TASK_FORMAT : format to use for the export (either html or csv)
16
16
  * TASK_DAYS : number of days to go back
17
17
 
18
18
  If TASK_SEARCH is not defined, it defaults to all tasks in the past 7 days and
@@ -185,27 +185,23 @@ namespace :foreman_tasks do
185
185
  end
186
186
 
187
187
  class PageHelper
188
- def self.pagify(io, template = nil)
189
- io.write <<~HTML
190
- <html>
191
- <head>
192
- <title>Dynflow Console</title>
193
- <script src="jquery.js"></script>
194
- <link rel="stylesheet" type="text/css" href="bootstrap.css">
195
- <link rel="stylesheet" type="text/css" href="application.css">
196
- <script src="bootstrap.js"></script>
197
- <script src="run_prettify.js"></script>
198
- <script src="application.js"></script>
199
- </head>
200
- <body>
188
+ def self.pagify(template)
189
+ pre = <<-HTML
190
+ <html>
191
+ <head>
192
+ <title>Dynflow Console</title>
193
+ <script src="jquery.js"></script>
194
+ <link rel="stylesheet" type="text/css" href="bootstrap.css">
195
+ <link rel="stylesheet" type="text/css" href="application.css">
196
+ <script src="bootstrap.js"></script>
197
+ <script src="run_prettify.js"></script>
198
+ <script src="application.js"></script>
199
+ </head>
200
+ <body>
201
+ #{template}
202
+ <body>
203
+ </html>
201
204
  HTML
202
- if block_given?
203
- yield io
204
- else
205
- io.write template
206
- end
207
- ensure
208
- io.write '</body></html>'
209
205
  end
210
206
 
211
207
  def self.copy_assets(tmp_dir)
@@ -220,64 +216,13 @@ namespace :foreman_tasks do
220
216
  end
221
217
  end
222
218
 
223
- def self.generate_with_index(io)
224
- io.write '<div><table class="table">'
225
- yield io
226
- ensure
227
- io.write '</table></div>'
228
- end
229
-
230
- def self.generate_index_entry(io, task)
231
- io << <<~HTML
232
- <tr>
233
- <td><a href=\"#{task.id}.html\">#{task.label}</a></td>
234
- <td>#{task.started_at}</td>
235
- <td>#{task.state}</td>
236
- <td>#{task.result}</td>
237
- </tr>
238
- HTML
239
- end
240
- end
241
-
242
- def csv_export(export_filename, tasks)
243
- CSV.open(export_filename, 'wb') do |csv|
244
- csv << %w[id state type label result parent_task_id started_at ended_at]
245
- 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]
219
+ def self.generate_index(tasks)
220
+ html = '<div><table class="table">'
221
+ tasks.order('started_at desc').all.each do |task|
222
+ html << "<tr><td><a href=\"#{task.id}.html\">#{task.label}</a></td><td>#{task.started_at}</td>\
223
+ <td>#{task.state}</td><td>#{task.result}</td></tr>"
248
224
  end
249
- end
250
- end
251
-
252
- def html_export(workdir, tasks)
253
- PageHelper.copy_assets(workdir)
254
-
255
- renderer = TaskRender.new
256
- total = tasks.count
257
- index = File.open(File.join(workdir, 'index.html'), 'w')
258
-
259
- File.open(File.join(workdir, 'index.html'), 'w') do |index|
260
- PageHelper.pagify(index) do |io|
261
- PageHelper.generate_with_index(io) do |index|
262
- 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)
265
- puts "#{count + 1}/#{total}"
266
- end
267
- end
268
- end
269
- end
270
- end
271
-
272
- def generate_filename(format)
273
- base = "/tmp/task-export-#{Time.now.to_i}"
274
- case format
275
- when 'html'
276
- base + '.tar.gz'
277
- when 'csv'
278
- base + '.csv'
279
- when 'html-dir'
280
- base
225
+ html << '</table></div>'
281
226
  end
282
227
  end
283
228
 
@@ -294,25 +239,36 @@ namespace :foreman_tasks do
294
239
  end
295
240
 
296
241
  format = ENV['TASK_FORMAT'] || 'html'
297
- export_filename = ENV['TASK_FILE'] || generate_filename(format)
242
+ export_filename = ENV['TASK_FILE'] || "/tmp/task-export-#{Time.now.to_i}.#{format == 'csv' ? 'csv' : 'tar.gz'}"
298
243
 
299
- tasks = ForemanTasks::Task.search_for(filter).order(:started_at => :desc)
244
+ tasks = ForemanTasks::Task.search_for(filter)
300
245
 
301
246
  puts _("Exporting all tasks matching filter #{filter}")
302
247
  puts _("Gathering #{tasks.count} tasks.")
303
- case format
304
- when 'html'
248
+ if format == 'html'
305
249
  Dir.mktmpdir('task-export') do |tmp_dir|
306
- html_export(tmp_dir, tasks)
250
+ PageHelper.copy_assets(tmp_dir)
251
+
252
+ renderer = TaskRender.new
253
+ total = tasks.count
254
+
255
+ tasks.find_each.with_index do |task, count|
256
+ File.open(File.join(tmp_dir, "#{task.id}.html"), 'w') { |file| file.write(PageHelper.pagify(renderer.render_task(task))) }
257
+ puts "#{count + 1}/#{total}"
258
+ end
259
+
260
+ File.open(File.join(tmp_dir, 'index.html'), 'w') { |file| file.write(PageHelper.pagify(PageHelper.generate_index(tasks))) }
261
+
307
262
  system("tar", "czf", export_filename, tmp_dir)
308
263
  end
309
- when 'html-dir'
310
- FileUtils.mkdir_p(export_filename)
311
- html_export(export_filename, tasks)
312
- when 'csv'
313
- csv_export(export_filename, tasks)
314
- else
315
- raise "Unkonwn export format '#{format}'"
264
+ elsif format == 'csv'
265
+ CSV.open(export_filename, 'wb') do |csv|
266
+ csv << %w[id state type label result parent_task_id started_at ended_at]
267
+ tasks.find_each do |task|
268
+ csv << [task.id, task.state, task.type, task.label, task.result,
269
+ task.parent_task_id, task.started_at, task.ended_at]
270
+ end
271
+ end
316
272
  end
317
273
 
318
274
  puts "Created #{export_filename}"
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '4.1.6'.freeze
2
+ VERSION = '5.0.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)
@@ -68,17 +68,6 @@ module ForemanTasks
68
68
  data = JSON.parse(response.body)
69
69
  _(data[0]['results'][0]['id']).must_equal task.id
70
70
  end
71
-
72
- it 'can search for a specific resource' do
73
- org = FactoryBot.create(:organization)
74
- task = FactoryBot.create(:task_with_links, resource_id: org.id, resource_type: 'Organization')
75
-
76
- post :bulk_search, params: { :searches => [{ :type => 'resource', :resource_id => org.id, :resource_type => 'Organization' }] }
77
-
78
- assert_response :success
79
- data = JSON.parse(response.body)
80
- _(data[0]['results'][0]['id']).must_equal task.id
81
- end
82
71
  end
83
72
 
84
73
  describe 'GET /api/tasks/show' do
@@ -0,0 +1,28 @@
1
+ require 'foreman_tasks_test_helper'
2
+
3
+ module Queries
4
+ class RecurringLogicTest < GraphQLQueryTestCase
5
+ let(:query) do
6
+ <<-GRAPHQL
7
+ query($id: String!) {
8
+ recurringLogic(id: $id) {
9
+ id
10
+ cronLine
11
+ }
12
+ }
13
+ GRAPHQL
14
+ end
15
+
16
+ let(:cron_line) { '5 4 3 2 1' }
17
+ let(:recurring_logic) { FactoryBot.create(:recurring_logic, :cron_line => cron_line) }
18
+ let(:global_id) { Foreman::GlobalId.for(recurring_logic) }
19
+ let(:variables) { { id: global_id } }
20
+ let(:data) { result['data']['recurringLogic'] }
21
+
22
+ test "should fetch recurring logic" do
23
+ assert_empty result['errors']
24
+ assert_equal global_id, data['id']
25
+ assert_equal cron_line, data['cronLine']
26
+ end
27
+ end
28
+ end