foreman-tasks 0.15.1 → 0.15.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.babelrc +9 -1
  3. data/.eslintrc +6 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +6 -0
  6. data/.storybook/webpack.config.js +51 -54
  7. data/app/controllers/foreman_tasks/api/tasks_controller.rb +0 -2
  8. data/app/controllers/foreman_tasks/tasks_controller.rb +6 -1
  9. data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +8 -0
  10. data/app/lib/actions/base.rb +7 -0
  11. data/app/lib/actions/helpers/lifecycle_logging.rb +21 -0
  12. data/app/lib/actions/proxy_action.rb +4 -2
  13. data/app/models/foreman_tasks/task.rb +23 -0
  14. data/app/models/foreman_tasks/task/summarizer.rb +96 -6
  15. data/app/models/foreman_tasks/triggering.rb +2 -2
  16. data/app/models/setting/foreman_tasks.rb +8 -1
  17. data/app/services/foreman_tasks/dashboard_table_filter.rb +47 -0
  18. data/app/services/foreman_tasks/troubleshooting_help_generator.rb +92 -0
  19. data/app/services/ui_notifications/tasks.rb +20 -0
  20. data/app/services/ui_notifications/tasks/task_paused_admin.rb +43 -0
  21. data/app/services/ui_notifications/tasks/task_paused_owner.rb +30 -0
  22. data/app/views/foreman_tasks/tasks/_details.html.erb +5 -3
  23. data/app/views/foreman_tasks/tasks/index.html.erb +13 -0
  24. data/config/routes.rb +1 -0
  25. data/db/migrate/20190318153925_add_task_state_updated_at.foreman_tasks.rb +5 -0
  26. data/db/migrate/20190404132157_add_implicit_varchar_uuid_cast.rb +25 -0
  27. data/db/seeds.d/30-notification_blueprints.rb +33 -0
  28. data/foreman-tasks.gemspec +1 -1
  29. data/lib/foreman_tasks/cleaner.rb +5 -4
  30. data/lib/foreman_tasks/engine.rb +1 -1
  31. data/lib/foreman_tasks/test_helpers.rb +10 -0
  32. data/lib/foreman_tasks/version.rb +1 -1
  33. data/package.json +14 -11
  34. data/test/controllers/tasks_controller_test.rb +10 -0
  35. data/test/foreman_tasks_test_helper.rb +4 -0
  36. data/test/support/dummy_dynflow_action.rb +29 -0
  37. data/test/support/history_tasks_builder.rb +42 -0
  38. data/test/unit/actions/action_with_sub_plans_test.rb +4 -1
  39. data/test/unit/actions/bulk_action_test.rb +2 -0
  40. data/test/unit/cleaner_test.rb +15 -0
  41. data/test/unit/dashboard_table_filter_test.rb +65 -0
  42. data/test/unit/summarizer_test.rb +39 -0
  43. data/test/unit/task_test.rb +14 -0
  44. data/test/unit/troubleshooting_help_generator_test.rb +71 -0
  45. data/test/unit/ui_notifications_test.rb +86 -0
  46. data/webpack/ForemanTasks/Components/Chart/Chart.js +128 -0
  47. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.js +20 -0
  48. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.stories.js +51 -0
  49. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.test.js +11 -0
  50. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/__snapshots__/PausedTasksCard.test.js.snap +36 -0
  51. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.js +20 -0
  52. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.stories.js +51 -0
  53. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.test.js +11 -0
  54. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/__snapshots__/RunningTasksCard.test.js.snap +36 -0
  55. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.js +64 -0
  56. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.scss +25 -0
  57. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.stories.js +28 -0
  58. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.test.js +18 -0
  59. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/__snapshots__/ScheduledTasksCard.test.js.snap +94 -0
  60. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.js +89 -0
  61. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.scss +46 -0
  62. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.stories.js +72 -0
  63. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.test.js +48 -0
  64. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCardHelper.js +63 -0
  65. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/__snapshots__/StoppedTasksCard.test.js.snap +973 -0
  66. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.js +96 -0
  67. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.scss +17 -0
  68. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.stories.js +46 -0
  69. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.test.js +43 -0
  70. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/__snapshots__/TasksDonutCard.test.js.snap +183 -0
  71. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.js +166 -0
  72. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.scss +24 -0
  73. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.stories.js +25 -0
  74. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.test.js +40 -0
  75. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartConstants.js +13 -0
  76. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartHelper.js +94 -0
  77. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChartHelper.test.js +152 -0
  78. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/__snapshots__/TasksDonutChart.test.js.snap +302 -0
  79. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/__snapshots__/TasksDonutChartHelper.test.js.snap +21 -0
  80. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.fixtures.js +25 -0
  81. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.js +72 -0
  82. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.stories.js +52 -0
  83. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/TasksCardsGrid.test.js +21 -0
  84. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/__snapshots__/TasksCardsGrid.test.js.snap +223 -0
  85. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.js +57 -0
  86. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.scss +26 -0
  87. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.stories.js +22 -0
  88. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.test.js +57 -0
  89. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/__snapshots__/TasksLabelsRow.test.js.snap +47 -0
  90. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.js +51 -0
  91. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.stories.js +23 -0
  92. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/TimeDropDown.test.js +19 -0
  93. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/Components/TimeDropDown/__snapshots__/TimeDropDown.test.js.snap +85 -0
  94. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.js +33 -0
  95. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.scss +11 -0
  96. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.stories.js +22 -0
  97. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/TasksTimeRow.test.js +15 -0
  98. data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksTimeRow/__snapshots__/TasksTimeRow.test.js.snap +41 -0
  99. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.js +77 -0
  100. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.scss +6 -0
  101. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardActions.js +62 -0
  102. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardConstants.js +94 -0
  103. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardHelper.js +78 -0
  104. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardPropTypes.js +13 -0
  105. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardReducer.js +50 -0
  106. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardSelectors.js +44 -0
  107. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboard.test.js +13 -0
  108. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardActions.test.js +37 -0
  109. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardHelper.test.js +36 -0
  110. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardReducer.test.js +58 -0
  111. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardSelectors.test..js +59 -0
  112. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboard.test.js.snap +51 -0
  113. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardActions.test.js.snap +61 -0
  114. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardReducer.test.js.snap +280 -0
  115. data/webpack/ForemanTasks/Components/TasksDashboard/index.js +25 -0
  116. data/webpack/ForemanTasks/ForemanTasksReducers.js +10 -0
  117. data/webpack/ForemanTasks/ForemanTasksSelectors.js +1 -0
  118. data/webpack/__mocks__/foremanReact/API.js +7 -0
  119. data/webpack/__mocks__/foremanReact/common/I18n.js +5 -0
  120. data/webpack/__mocks__/foremanReact/common/helpers.js +3 -0
  121. data/webpack/index.js +13 -1
  122. data/webpack/stories/decorators/index.js +1 -0
  123. data/webpack/stories/decorators/withCardsDecorator.js +14 -0
  124. data/webpack/stories/index.js +1 -3
  125. data/webpack/stories/index.scss +6 -0
  126. metadata +101 -8
  127. data/webpack/ForemanTasks/components/Hello/Hello.stories.js +0 -5
  128. data/webpack/ForemanTasks/components/Hello/__tests__/Hello.test.js +0 -11
  129. data/webpack/ForemanTasks/components/Hello/__tests__/__snapshots__/Hello.test.js.snap +0 -7
  130. data/webpack/ForemanTasks/components/Hello/index.js +0 -5
@@ -18,8 +18,8 @@ module ForemanTasks
18
18
  ALLOWED_INPUT_TYPES = [:cronline, :monthly, :weekly, :daily, :hourly].freeze
19
19
 
20
20
  TIME_FORMAT = '%Y-%m-%d %H:%M'.freeze
21
- TIME_REGEXP = /\A\d{4}-\d{2}-\d{2} \d{2}:\d{2}\Z/
22
- DAYS_REGEXP = /\A(\s*\d{1,2}\s*)(,\s*\d{1,2}\s*)*\Z/
21
+ TIME_REGEXP = /\A\d{4}-\d{2}-\d{2} \d{2}:\d{2}\Z/.freeze
22
+ DAYS_REGEXP = /\A(\s*\d{1,2}\s*)(,\s*\d{1,2}\s*)*\Z/.freeze
23
23
 
24
24
  has_one :recurring_logic, :foreign_key => :triggering_id, :dependent => :nullify
25
25
 
@@ -3,6 +3,8 @@ class Setting::ForemanTasks < Setting
3
3
  # Check the table exists
4
4
  return unless super
5
5
 
6
+ Setting::BLANK_ATTRS.push('foreman_tasks_troubleshooting_url')
7
+
6
8
  transaction do
7
9
  [
8
10
  set('foreman_tasks_sync_task_timeout', N_('Number of seconds to wait for synchronous task to finish.'), 120),
@@ -12,7 +14,12 @@ class Setting::ForemanTasks < Setting
12
14
  set('foreman_tasks_proxy_action_retry_count', N_('Number of attempts to start a task on the smart proxy before failing'), 4),
13
15
  set('foreman_tasks_proxy_action_retry_interval', N_('Time in seconds between retries'), 15),
14
16
  set('foreman_tasks_proxy_batch_trigger', N_('Allow triggering tasks on the smart proxy in batches'), true),
15
- 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'), 1000)
17
+ 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),
18
+ set('foreman_tasks_troubleshooting_url',
19
+ N_('Url pointing to the task troubleshooting documentation. '\
20
+ 'It should contain %{label} placeholder, that will be replaced with normalized task label '\
21
+ '(restricted to only alphanumeric characters)). %{version} placeholder is also available.'),
22
+ nil)
16
23
  ].each { |s| create! s.update(:category => 'Setting::ForemanTasks') }
17
24
  end
18
25
 
@@ -0,0 +1,47 @@
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
+ @new_scope = @new_scope.where(field => @params[field]) if @params[field].present?
28
+ end
29
+
30
+ def scope_by_time
31
+ return if @params[:time_horizon].blank?
32
+ hours = @params[:time_horizon][/\AH(\d{1,2})$/i, 1]
33
+ unless hours
34
+ raise Foreman::Exception, 'Unexpected format of time: should be in form of "H24"'
35
+ end
36
+ timestamp = Time.now.utc - hours.to_i.hours
37
+ case @params[:time_mode]
38
+ when 'recent'
39
+ operator = '>'
40
+ else
41
+ operator = '<'
42
+ search_suffix = 'OR state_updated_at IS NULL'
43
+ end
44
+ @new_scope = @new_scope.where("state_updated_at #{operator} ? #{search_suffix}", timestamp)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,92 @@
1
+ module ForemanTasks
2
+ class TroubleshootingHelpGenerator
3
+ class Link
4
+ attr_accessor :name, :title, :description, :href
5
+
6
+ def initialize(name:, title:, description:, href:)
7
+ @name = name
8
+ @title = title
9
+ @description = description
10
+ @href = href
11
+ end
12
+
13
+ def to_h(capitalize_title: false)
14
+ title = capitalize_title ? self.title.titlecase : self.title
15
+ { name: name, title: title, description: description, href: href, external: true }
16
+ end
17
+ end
18
+
19
+ class Info
20
+ attr_reader :links, :description_lines
21
+
22
+ def initialize
23
+ @description_lines = []
24
+ @links = []
25
+ end
26
+
27
+ def add_line(line)
28
+ @description_lines << line
29
+ end
30
+
31
+ def add_link(link)
32
+ @links << link
33
+ end
34
+ end
35
+
36
+ def initialize(action)
37
+ @action = action
38
+ @custom_info = action.troubleshooting_info if action.respond_to?(:troubleshooting_info)
39
+ end
40
+
41
+ def generate_html
42
+ # rubocop:disable Rails/OutputSafety
43
+ (description + link_descriptions_html).join('<br/>').html_safe
44
+ # rubocop:enable Rails/OutputSafety
45
+ end
46
+
47
+ def link_descriptions_html
48
+ links.map do |link|
49
+ link.description % { link: %(<a href="%{href}">%{title}</a>) % link.to_h }
50
+ end
51
+ end
52
+
53
+ def description
54
+ ret = generic_info.description_lines
55
+ ret += @custom_info.description_lines if @custom_info
56
+ ret
57
+ end
58
+
59
+ def links
60
+ links = generic_info.links
61
+ links += @custom_info.links if @custom_info
62
+ links
63
+ end
64
+
65
+ def generic_info
66
+ @generic_info ||= Info.new.tap do |i|
67
+ i.add_line _('A paused task represents a process that has not finished properly. '\
68
+ 'Any task in paused state can lead to potential inconsistency '\
69
+ 'and needs to be resolved.')
70
+ i.add_line _("The recommended approach is to investigate the error messages below and in 'errors' tab, "\
71
+ 'address the primary cause of the issue and resume the task.')
72
+ if (link = troubleshooting_link)
73
+ i.add_link(link)
74
+ end
75
+ end
76
+ end
77
+
78
+ def troubleshooting_link(generic_only: false)
79
+ url_template = Setting[:foreman_tasks_troubleshooting_url]
80
+ return if url_template.blank?
81
+ url = url_template % { label: generic_only ? '' : link_anchor, version: SETTINGS[:version].short }
82
+ Link.new(name: :troubleshooting,
83
+ title: _('troubleshooting documentation'),
84
+ description: _('See %{link} for more details on how to resolve the issue'),
85
+ href: url)
86
+ end
87
+
88
+ def link_anchor
89
+ @action.label.to_s
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,20 @@
1
+ module UINotifications
2
+ module Tasks
3
+ class Base < ::UINotifications::Base
4
+ def initialize(task)
5
+ @subject = @task = task
6
+ end
7
+
8
+ def initiator
9
+ User.anonymous_admin
10
+ end
11
+
12
+ def troubleshooting_help_generator
13
+ return @troubleshooting_help_generator if defined? @troubleshooting_help_generator
14
+ @troubleshooting_help_generator = if @task.main_action
15
+ ForemanTasks::TroubleshootingHelpGenerator.new(@task.main_action)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,43 @@
1
+ module UINotifications
2
+ module Tasks
3
+ class TaskPausedAdmin < Tasks::Base
4
+ def deliver!
5
+ # delete previous notifications about paused tasks first
6
+ Notification.where(notification_blueprint_id: blueprint.id).each(&:destroy)
7
+ notification = ::Notification.new(
8
+ :audience => Notification::AUDIENCE_ADMIN,
9
+ :notification_blueprint => blueprint,
10
+ :initiator => initiator,
11
+ :subject => subject,
12
+ :message => message
13
+ )
14
+
15
+ notification.send(:set_custom_attributes) # to add links from blueprint
16
+ notification.actions['links'] ||= []
17
+ if troubleshooting_help_generator
18
+ troubleshooting_link = troubleshooting_help_generator.troubleshooting_link(generic_only: true)
19
+ notification.actions['links'] << troubleshooting_link.to_h(capitalize_title: true) if troubleshooting_link
20
+ end
21
+ notification.save!
22
+ notification
23
+ end
24
+
25
+ def initiator
26
+ User.anonymous_admin
27
+ end
28
+
29
+ def blueprint
30
+ @blueprint ||= NotificationBlueprint.unscoped.find_by(:name => 'tasks_paused_admin')
31
+ end
32
+
33
+ def message
34
+ return @message if @message
35
+
36
+ tasks_count = ForemanTasks::Task.where(state: 'paused').count
37
+ @message = n_('There is %{count} paused task in the system that need attention',
38
+ 'There are %{count} paused tasks in the system that need attention',
39
+ tasks_count) % { count: tasks_count }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ module UINotifications
2
+ module Tasks
3
+ class TaskPausedOwner < Tasks::Base
4
+ def deliver!
5
+ notification = ::Notification.new(
6
+ :audience => Notification::AUDIENCE_USER,
7
+ :notification_blueprint => blueprint,
8
+ :initiator => initiator,
9
+ :message => message,
10
+ :subject => subject
11
+ )
12
+ notification.send(:set_custom_attributes) # to add links from blueprint
13
+ notification.actions['links'] ||= []
14
+ if troubleshooting_help_generator
15
+ notification.actions['links'].concat(troubleshooting_help_generator.links.map { |l| l.to_h(capitalize_title: true) })
16
+ end
17
+ notification.save!
18
+ notification
19
+ end
20
+
21
+ def blueprint
22
+ @blueprint ||= NotificationBlueprint.unscoped.find_by(:name => 'tasks_paused_owner')
23
+ end
24
+
25
+ def message
26
+ StringParser.new(blueprint.message, subject: subject.action).to_s
27
+ end
28
+ end
29
+ end
30
+ end
@@ -174,6 +174,8 @@
174
174
  </div>
175
175
  </div>
176
176
 
177
+ <%= troubleshooting_info %>
178
+
177
179
  <% unless @task.humanized[:output].blank? %>
178
180
  <div>
179
181
  <span class="param-name list-group-item-heading"><%= _("Output") %>:</span>
@@ -184,10 +186,10 @@
184
186
  <% end %>
185
187
 
186
188
  <% unless @task.humanized[:errors].blank? %>
187
- <div>
188
- <span class="param-name"><%= _("Errors") %>:</span>
189
+ <p>
190
+ <span class="param-name"><b><%= _("Errors") %>:</b></span>
189
191
  <span class="param-value">
190
192
  <pre><%=Array(@task.humanized[:errors]).join("\n") %></pre>
191
193
  </span>
192
- </div>
194
+ </p>
193
195
  <% end %>
@@ -1,3 +1,16 @@
1
+ <% content_for(:javascripts) do %>
2
+ <%= webpacked_plugins_js_for :'foreman-tasks' %>
3
+ <% end %>
4
+ <% content_for(:stylesheets) do %>
5
+ <%= webpacked_plugins_css_for :'foreman-tasks' %>
6
+ <% end %>
7
+ <% content_for(:before_search_bar) do %>
8
+ <div id="tasks-dashboard"></div>
9
+ <%= mount_react_component('TasksDashboard', '#tasks-dashboard', {
10
+ yo: 'ya'
11
+ }.to_json) %>
12
+ <% end %>
13
+
1
14
  <% title _("Tasks") %>
2
15
  <% title_actions csv_link, help_button %>
3
16
  <% stylesheet 'foreman_tasks/tasks' %>
data/config/routes.rb CHANGED
@@ -11,6 +11,7 @@ Foreman::Application.routes.draw do
11
11
  resources :tasks, :only => [:index, :show] do
12
12
  collection do
13
13
  get 'auto_complete_search'
14
+ get '/summary/:recent_timeframe', action: 'summary'
14
15
  end
15
16
  member do
16
17
  get :sub_tasks
@@ -0,0 +1,5 @@
1
+ class AddTaskStateUpdatedAt < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :foreman_tasks_tasks, :state_updated_at, :timestamp, index: true
4
+ end
5
+ end
@@ -0,0 +1,25 @@
1
+ class AddImplicitVarcharUuidCast < ActiveRecord::Migration[5.2]
2
+ def up
3
+ if on_postgresql?
4
+ ActiveRecord::Base.connection.execute <<~SQL
5
+ CREATE CAST (varchar AS uuid)
6
+ WITH INOUT
7
+ AS IMPLICIT
8
+ SQL
9
+ end
10
+ end
11
+
12
+ def down
13
+ if on_postgresql?
14
+ ActiveRecord::Base.connection.execute <<~SQL
15
+ DROP CAST (varchar AS uuid)
16
+ SQL
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def on_postgresql?
23
+ ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ blueprints = [
2
+ {
3
+ group: N_('Tasks'),
4
+ name: 'tasks_paused_admin',
5
+ message: "DYNAMIC",
6
+ level: 'warning',
7
+ actions:
8
+ {
9
+ links:
10
+ [
11
+ href: "/foreman_tasks/tasks?search=#{CGI.escape('state=paused')}",
12
+ title: N_('List of tasks')
13
+ ]
14
+ }
15
+ },
16
+
17
+ {
18
+ group: N_('Tasks'),
19
+ name: 'tasks_paused_owner',
20
+ message: "The task '%{subject}' got paused",
21
+ level: 'warning',
22
+ actions:
23
+ {
24
+ links:
25
+ [
26
+ path_method: :foreman_tasks_task_path,
27
+ title: N_('Task Details')
28
+ ]
29
+ }
30
+ }
31
+ ]
32
+
33
+ blueprints.each { |blueprint| UINotifications::Seed.new(blueprint).configure }
@@ -29,7 +29,7 @@ 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 "foreman-tasks-core"
32
- s.add_dependency "dynflow", '>= 1.2.2'
32
+ s.add_dependency "dynflow", '>= 1.2.3'
33
33
  s.add_dependency "sinatra" # for Dynflow web console
34
34
  s.add_dependency "parse-cron", '~> 0.1.4'
35
35
  s.add_dependency "get_process_mem" # for memory polling
@@ -193,15 +193,16 @@ module ForemanTasks
193
193
 
194
194
  def orphaned_dynflow_tasks
195
195
  db = ForemanTasks.dynflow.world.persistence.adapter.db
196
- uuid_select = Sequel.lit(ForemanTasks::Task.select(:external_id).to_sql)
197
- db[:dynflow_execution_plans].filter(:uuid => [uuid_select]).invert
196
+ db.fetch("select dynflow_execution_plans.uuid from dynflow_execution_plans left join "\
197
+ "foreman_tasks_tasks on (dynflow_execution_plans.uuid = foreman_tasks_tasks.external_id) "\
198
+ "where foreman_tasks_tasks.id IS NULL")
198
199
  end
199
200
 
200
201
  def prepare_filter
201
202
  filter_parts = [filter]
202
203
  filter_parts << %(started_at < "#{after.ago.to_s(:db)}") if after > 0
203
- filter_parts << states.map { |s| "state = #{s}" }.join(' OR ') if states.any?
204
- filter_parts.select(&:present?).join(' AND ')
204
+ filter_parts << "state ^ (#{states.join(',')})" if states.any?
205
+ filter_parts.select(&:present?).map { |segment| "(#{segment})" }.join(' AND ')
205
206
  end
206
207
 
207
208
  def start_tracking_progress(name, total = tasks.size)
@@ -56,7 +56,7 @@ module ForemanTasks
56
56
  :last => true
57
57
 
58
58
  security_block :foreman_tasks do |_map|
59
- permission :view_foreman_tasks, { :'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :show],
59
+ permission :view_foreman_tasks, { :'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :summary, :show],
60
60
  :'foreman_tasks/react' => [:index],
61
61
  :'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary] }, :resource_type => ForemanTasks::Task.name
62
62
  permission :edit_foreman_tasks, { :'foreman_tasks/tasks' => [:resume, :unlock, :force_unlock, :cancel_step, :cancel, :abort],
@@ -1,9 +1,19 @@
1
1
  require 'dynflow/testing'
2
2
  module ForemanTasks
3
3
  module TestHelpers
4
+ def self.use_in_memory_sqlite!
5
+ raise 'the in thread world have already been initialized' if @test_in_thread_world
6
+ @use_in_memory_sqlite = true
7
+ end
8
+
4
9
  def self.test_in_thread_world
5
10
  return @test_in_thread_world if @test_in_thread_world
6
11
  world_config = ForemanTasks.dynflow.config.world_config
12
+ if @use_in_memory_sqlite
13
+ world_config.persistence_adapter = lambda do |*_args|
14
+ ::ForemanTasks::Dynflow::Persistence.new('adapter' => 'sqlite', 'database' => ':memory:')
15
+ end
16
+ end
7
17
  @test_in_thread_world = ::Dynflow::Testing::InThreadWorld.new(world_config)
8
18
  end
9
19