foreman-tasks 3.0.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/js_tests.yml +5 -1
- data/app/controllers/foreman_tasks/api/tasks_controller.rb +19 -5
- data/app/lib/actions/entry_action.rb +8 -4
- data/app/lib/actions/helpers/lock.rb +11 -5
- data/app/lib/actions/middleware/keep_current_request_id.rb +4 -1
- data/app/lib/actions/middleware/keep_current_user.rb +11 -1
- data/app/lib/actions/observable_action.rb +80 -0
- data/app/models/foreman_tasks/concerns/action_subject.rb +0 -6
- data/app/models/foreman_tasks/link.rb +60 -0
- data/app/models/foreman_tasks/lock.rb +30 -128
- data/app/models/foreman_tasks/recurring_logic.rb +3 -3
- data/app/models/foreman_tasks/task.rb +20 -7
- data/app/models/foreman_tasks/task/search.rb +7 -6
- data/app/models/setting/foreman_tasks.rb +8 -8
- data/app/services/foreman_tasks/dashboard_table_filter.rb +5 -1
- data/app/views/foreman_tasks/api/locks/show.json.rabl +4 -0
- data/app/views/foreman_tasks/api/tasks/details.json.rabl +5 -3
- data/app/views/foreman_tasks/layouts/react.html.erb +1 -2
- data/app/views/foreman_tasks/tasks/_lock_card.html.erb +10 -0
- data/app/views/foreman_tasks/tasks/dashboard/_latest_tasks_in_error_warning.html.erb +1 -1
- data/app/views/foreman_tasks/tasks/dashboard/_tasks_status.html.erb +1 -1
- data/app/views/foreman_tasks/tasks/show.html.erb +1 -6
- data/db/migrate/20181206123910_create_foreman_tasks_links.foreman_tasks.rb +26 -0
- data/db/migrate/20181206124952_migrate_non_exclusive_locks_to_links.foreman_tasks.rb +14 -0
- data/db/migrate/20181206131436_drop_old_locks.foreman_tasks.rb +20 -0
- data/db/migrate/20181206131627_make_locks_exclusive.foreman_tasks.rb +25 -0
- data/foreman-tasks.gemspec +1 -0
- data/lib/foreman_tasks/cleaner.rb +10 -0
- data/lib/foreman_tasks/engine.rb +5 -2
- data/lib/foreman_tasks/tasks/export_tasks.rake +2 -2
- data/lib/foreman_tasks/version.rb +1 -1
- data/package.json +6 -6
- data/test/controllers/api/tasks_controller_test.rb +10 -0
- data/test/controllers/tasks_controller_test.rb +1 -1
- data/test/unit/actions/action_with_sub_plans_test.rb +5 -2
- data/test/unit/cleaner_test.rb +4 -4
- data/test/unit/locking_test.rb +85 -0
- data/test/unit/recurring_logic_test.rb +6 -0
- data/test/unit/task_test.rb +10 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/Locks.js +2 -2
- data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskInfo.js +3 -2
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Locks.test.js.snap +4 -4
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskInfo.test.js.snap +2 -0
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.js +4 -1
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.scss +14 -0
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +3 -0
- data/webpack/ForemanTasks/Components/TaskDetails/index.js +2 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.scss +4 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/OtherInfo.js +53 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/OtherInfo.test.js +14 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.js +27 -19
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.scss +14 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.test.js +1 -34
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/{StoppedTasksCardHelper.js → StoppedTasksCardTable.js} +28 -1
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCardTable.test.js +54 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/__snapshots__/OtherInfo.test.js.snap +48 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/__snapshots__/StoppedTasksCard.test.js.snap +60 -1367
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/__snapshots__/StoppedTasksCardTable.test.js.snap +960 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/__snapshots__/TasksCardsGrid.test.js.snap +14 -11
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardConstants.js +2 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardSelectors.js +17 -11
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardSelectors.test.js +26 -14
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboard.test.js.snap +14 -11
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardSelectors.test.js.snap +38 -22
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableHelpers.js +0 -8
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +13 -4
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.scss +4 -13
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +2 -4
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/actionNameCellFormatter.test.js.snap +1 -0
- data/webpack/ForemanTasks/Components/TasksTable/formatters/actionNameCellFormatter.js +5 -1
- metadata +24 -11
- data/app/assets/stylesheets/foreman_tasks/tasks.scss +0 -9
- data/test/unit/lock_test.rb +0 -22
@@ -31,12 +31,12 @@ module ForemanTasks
|
|
31
31
|
if value
|
32
32
|
task.update!(:start_at => next_occurrence_time) if task.start_at < Time.zone.now
|
33
33
|
update(:state => 'active')
|
34
|
-
else
|
35
|
-
update(:state => 'disabled')
|
36
34
|
end
|
37
|
-
|
35
|
+
elsif value
|
38
36
|
raise RecurringLogicCancelledException
|
39
37
|
end
|
38
|
+
|
39
|
+
update(:state => 'disabled') unless value
|
40
40
|
end
|
41
41
|
|
42
42
|
def enabled?
|
@@ -31,6 +31,7 @@ module ForemanTasks
|
|
31
31
|
has_many :recurring_logic_task_groups, -> { where :type => 'ForemanTasks::TaskGroups::RecurringLogicTaskGroup' },
|
32
32
|
:through => :task_group_members, :source => :task_group
|
33
33
|
belongs_to :user
|
34
|
+
has_many :links, :dependent => :destroy
|
34
35
|
|
35
36
|
scoped_search :on => :id, :complete_value => false
|
36
37
|
scoped_search :on => :action, :complete_value => false
|
@@ -46,10 +47,16 @@ module ForemanTasks
|
|
46
47
|
|
47
48
|
# Note: the following searches may return duplicates, this is due to
|
48
49
|
# one task maybe having multiple locks (e.g. read/write) for the same resource_id
|
49
|
-
scoped_search :relation => :locks, :on => :resource_id, :complete_value => false, :rename => '
|
50
|
-
scoped_search :relation => :locks, :on => :resource_id, :complete_value => false, :rename => '
|
51
|
-
scoped_search :relation => :locks, :on => :resource_type, :complete_value => true, :rename => '
|
52
|
-
scoped_search :relation => :locks, :on => :resource_id, :complete_value => false, :rename => '
|
50
|
+
scoped_search :relation => :locks, :on => :resource_id, :complete_value => false, :rename => 'locked_location_id', :ext_method => :search_by_taxonomy, :only_explicit => true
|
51
|
+
scoped_search :relation => :locks, :on => :resource_id, :complete_value => false, :rename => 'locked_organization_id', :ext_method => :search_by_taxonomy, :only_explicit => true
|
52
|
+
scoped_search :relation => :locks, :on => :resource_type, :complete_value => true, :rename => 'locked_resource_type', :ext_method => :search_by_generic_resource, :only_explicit => true
|
53
|
+
scoped_search :relation => :locks, :on => :resource_id, :complete_value => false, :rename => 'locked_resource_id', :ext_method => :search_by_generic_resource, :only_explicit => true
|
54
|
+
|
55
|
+
scoped_search :relation => :links, :on => :resource_id, :complete_value => false, :rename => 'location_id', :ext_method => :search_by_taxonomy, :only_explicit => true
|
56
|
+
scoped_search :relation => :links, :on => :resource_id, :complete_value => false, :rename => 'organization_id', :ext_method => :search_by_taxonomy, :only_explicit => true
|
57
|
+
scoped_search :relation => :links, :on => :resource_type, :complete_value => true, :rename => 'resource_type', :ext_method => :search_by_generic_resource, :only_explicit => true
|
58
|
+
scoped_search :relation => :links, :on => :resource_id, :complete_value => false, :rename => 'resource_id', :ext_method => :search_by_generic_resource, :only_explicit => true
|
59
|
+
|
53
60
|
scoped_search :on => :user_id,
|
54
61
|
:complete_value => true,
|
55
62
|
:rename => 'user.id',
|
@@ -65,8 +72,8 @@ module ForemanTasks
|
|
65
72
|
scope :running, -> { where("foreman_tasks_tasks.state NOT IN ('stopped', 'paused')") }
|
66
73
|
scope :for_resource,
|
67
74
|
(lambda do |resource|
|
68
|
-
joins(:
|
69
|
-
:"
|
75
|
+
joins(:links).where(:"foreman_tasks_links.resource_id" => resource.id,
|
76
|
+
:"foreman_tasks_links.resource_type" => resource.class.name)
|
70
77
|
end)
|
71
78
|
scope :for_action_types, (->(action_types) { where('foreman_tasks_tasks.label IN (?)', Array(action_types)) })
|
72
79
|
|
@@ -82,7 +89,7 @@ module ForemanTasks
|
|
82
89
|
property :ended_at, ActiveSupport::TimeWithZone, desc: 'Returns date with time the task ended at'
|
83
90
|
end
|
84
91
|
class Jail < Safemode::Jail
|
85
|
-
allow :started_at, :ended_at, :result, :state, :label, :main_action
|
92
|
+
allow :started_at, :ended_at, :result, :state, :label, :main_action, :action_output
|
86
93
|
end
|
87
94
|
|
88
95
|
def input
|
@@ -242,6 +249,12 @@ module ForemanTasks
|
|
242
249
|
parts.join(' ').strip
|
243
250
|
end
|
244
251
|
|
252
|
+
def action_output
|
253
|
+
return unless main_action.is_a?(Actions::Helpers::WithContinuousOutput)
|
254
|
+
main_action.continuous_output.sort!
|
255
|
+
main_action.continuous_output.raw_outputs
|
256
|
+
end
|
257
|
+
|
245
258
|
protected
|
246
259
|
|
247
260
|
def generate_id
|
@@ -5,9 +5,9 @@ module ForemanTasks
|
|
5
5
|
key = 'resource_type' if key.blank?
|
6
6
|
key_name = connection.quote_column_name(key.sub(/^.*\./, ''))
|
7
7
|
value = value.split(',') if operator.index(/IN/i)
|
8
|
-
condition = sanitize_sql_for_conditions(["
|
8
|
+
condition = sanitize_sql_for_conditions(["foreman_tasks_links.#{key_name} #{operator} (?)", value])
|
9
9
|
|
10
|
-
{ :conditions => condition, :joins => :
|
10
|
+
{ :conditions => condition, :joins => :links }
|
11
11
|
end
|
12
12
|
|
13
13
|
def search_by_taxonomy(key, operator, value)
|
@@ -15,12 +15,13 @@ module ForemanTasks
|
|
15
15
|
resource_type = key == 'location_id' ? 'Location' : 'Organization'
|
16
16
|
|
17
17
|
joins = <<-SQL
|
18
|
-
LEFT JOIN
|
19
|
-
ON (
|
20
|
-
|
18
|
+
LEFT JOIN foreman_tasks_links AS foreman_tasks_links_taxonomy#{uniq_suffix}
|
19
|
+
ON (foreman_tasks_links_taxonomy#{uniq_suffix}.task_id = foreman_tasks_tasks.id AND
|
20
|
+
foreman_tasks_links_taxonomy#{uniq_suffix}.resource_type = '#{resource_type}')
|
21
21
|
SQL
|
22
22
|
# Select only those tasks which either have the correct taxonomy or are not related to any
|
23
|
-
sql = "
|
23
|
+
sql = "foreman_tasks_links_taxonomy#{uniq_suffix}.resource_id #{operator} (?) OR foreman_tasks_links_taxonomy#{uniq_suffix}.resource_id IS NULL"
|
24
|
+
value = value.split(',') if operator.index(/IN/i)
|
24
25
|
{ :conditions => sanitize_sql_for_conditions([sql, value]), :joins => joins }
|
25
26
|
end
|
26
27
|
|
@@ -1,18 +1,18 @@
|
|
1
1
|
class Setting::ForemanTasks < Setting
|
2
2
|
def self.default_settings
|
3
3
|
[
|
4
|
-
set('foreman_tasks_sync_task_timeout', N_('Number of seconds to wait for synchronous task to finish.'), 120),
|
5
|
-
set('dynflow_enable_console', N_('Enable the dynflow console (/foreman_tasks/dynflow) for debugging'), true),
|
6
|
-
set('dynflow_console_require_auth', N_('Require user to be authenticated as user with admin rights when accessing dynflow console'), true),
|
7
|
-
set('foreman_tasks_proxy_action_retry_count', N_('Number of attempts to start a task on the smart proxy before failing'), 4),
|
8
|
-
set('foreman_tasks_proxy_action_retry_interval', N_('Time in seconds between retries'), 15),
|
9
|
-
set('foreman_tasks_proxy_batch_trigger', N_('Allow triggering tasks on the smart proxy in batches'), true),
|
10
|
-
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),
|
4
|
+
set('foreman_tasks_sync_task_timeout', N_('Number of seconds to wait for synchronous task to finish.'), 120, N_('Sync task timeout')),
|
5
|
+
set('dynflow_enable_console', N_('Enable the dynflow console (/foreman_tasks/dynflow) for debugging'), true, N_('Enable dynflow console')),
|
6
|
+
set('dynflow_console_require_auth', N_('Require user to be authenticated as user with admin rights when accessing dynflow console'), true, N_('Require auth for dynflow console')),
|
7
|
+
set('foreman_tasks_proxy_action_retry_count', N_('Number of attempts to start a task on the smart proxy before failing'), 4, N_('Proxy action retry count')),
|
8
|
+
set('foreman_tasks_proxy_action_retry_interval', N_('Time in seconds between retries'), 15, N_('Proxy action retry interval')),
|
9
|
+
set('foreman_tasks_proxy_batch_trigger', N_('Allow triggering tasks on the smart proxy in batches'), true, N_('Allow proxy batch tasks')),
|
10
|
+
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, N_('Proxy tasks batch size')),
|
11
11
|
set('foreman_tasks_troubleshooting_url',
|
12
12
|
N_('Url pointing to the task troubleshooting documentation. '\
|
13
13
|
'It should contain %{label} placeholder, that will be replaced with normalized task label '\
|
14
14
|
'(restricted to only alphanumeric characters)). %{version} placeholder is also available.'),
|
15
|
-
nil),
|
15
|
+
nil, N_('Tasks troubleshooting URL')),
|
16
16
|
set('foreman_tasks_polling_multiplier',
|
17
17
|
N_('Polling multiplier which is used to multiply the default polling intervals. '\
|
18
18
|
'This can be used to prevent polling too frequently for long running tasks.'),
|
@@ -24,7 +24,11 @@ module ForemanTasks
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def scope_by(field)
|
27
|
-
|
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
|
28
32
|
end
|
29
33
|
|
30
34
|
def scope_by_time
|
@@ -9,10 +9,12 @@ node(:failed_steps) { @task.input_output_failed_steps }
|
|
9
9
|
node(:running_steps) { @task.input_output_running_steps }
|
10
10
|
node(:help) { troubleshooting_info_text }
|
11
11
|
node(:has_sub_tasks) { @task.sub_tasks.any? }
|
12
|
+
|
12
13
|
node(:locks) do
|
13
|
-
@task.locks.map
|
14
|
-
|
15
|
-
|
14
|
+
@task.locks.map { |lock| partial('foreman_tasks/api/locks/show', :object => lock) }
|
15
|
+
end
|
16
|
+
node(:links) do
|
17
|
+
@task.links.map { |link| partial('foreman_tasks/api/locks/show', :object => link, :locals => { :link => true }) }
|
16
18
|
end
|
17
19
|
node(:username_path) { username_link_task(@task.owner, @task.username) }
|
18
20
|
node(:dynflow_enable_console) { Setting['dynflow_enable_console'] }
|
@@ -9,7 +9,6 @@
|
|
9
9
|
<%= notifications %>
|
10
10
|
<div id="organization-id" data-id="<%= Organization.current.id if Organization.current %>" ></div>
|
11
11
|
<div id="user-id" data-id="<%= User.current.id if User.current %>" ></div>
|
12
|
-
|
12
|
+
<%= react_component('ForemanTasks') %>
|
13
13
|
<% end %>
|
14
14
|
<%= render file: "layouts/base" %>
|
15
|
-
<%= mount_react_component('ForemanTasks', '#foremanTasksReactRoot') %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<div class="col-xs-6 col-sm-4 col-md-4">
|
2
|
+
<div class="card-pf card-pf-accented card-pf-aggregate-status">
|
3
|
+
<h2 class="card-pf-title">
|
4
|
+
<span class="fa <%= lock.is_a?(::ForemanTasks::Lock) ? 'fa-lock' : 'fa-unlock-alt' %>"></span> <%= lock.resource_type %>
|
5
|
+
</h2>
|
6
|
+
<div class="card-pf-body">
|
7
|
+
<%= format('id:%s', lock.resource_id) %><br>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
</div>
|
@@ -11,7 +11,7 @@
|
|
11
11
|
<td class="ellipsis"><%= link_to task.humanized[:action], defined?(main_app) ? main_app.foreman_tasks_task_path(task.id) : foreman_tasks_task_path(task.id) %></td>
|
12
12
|
<td><%= task.state %></td>
|
13
13
|
<td><%= task.result %></td>
|
14
|
-
<td><%= task.started_at ?
|
14
|
+
<td><%= task.started_at ? date_time_relative(task.started_at) : _('N/A') %></td>
|
15
15
|
</tr>
|
16
16
|
<% end %>
|
17
17
|
</table>
|
@@ -11,7 +11,7 @@
|
|
11
11
|
<td><%= result.state %></td>
|
12
12
|
<td><%= result.result %></td>
|
13
13
|
<td><%= link_to result.count, main_app.foreman_tasks_tasks_path(:search => "state=#{result.state}&result=#{result.result}") %></td>
|
14
|
-
<td><%= result.started_at ?
|
14
|
+
<td><%= result.started_at ? date_time_relative(result.started_at) : _('N/A') %></td>
|
15
15
|
</tr>
|
16
16
|
<% end %>
|
17
17
|
</table>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class CreateForemanTasksLinks < ActiveRecord::Migration[4.2]
|
2
|
+
def change
|
3
|
+
# rubocop:disable Rails/CreateTableWithTimestamps
|
4
|
+
create_table :foreman_tasks_links do |t|
|
5
|
+
task_id_options = { :index => true, :null => true }
|
6
|
+
if on_postgresql?
|
7
|
+
t.uuid :task_id, task_id_options
|
8
|
+
else
|
9
|
+
t.string :task_id, task_id_options
|
10
|
+
end
|
11
|
+
t.string :resource_type
|
12
|
+
t.integer :resource_id
|
13
|
+
end
|
14
|
+
# rubocop:enable Rails/CreateTableWithTimestamps
|
15
|
+
|
16
|
+
add_index :foreman_tasks_links, [:resource_type, :resource_id]
|
17
|
+
add_index :foreman_tasks_links, [:task_id, :resource_type, :resource_id],
|
18
|
+
:unique => true, :name => 'foreman_tasks_links_unique_index'
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def on_postgresql?
|
24
|
+
ActiveRecord::Base.connection.adapter_name.casecmp('postgresql').zero?
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class MigrateNonExclusiveLocksToLinks < ActiveRecord::Migration[5.0]
|
2
|
+
def up
|
3
|
+
execute <<-SQL
|
4
|
+
INSERT INTO foreman_tasks_links(task_id, resource_type, resource_id)
|
5
|
+
SELECT DISTINCT locks.task_id, locks.resource_type, locks.resource_id
|
6
|
+
FROM foreman_tasks_locks AS locks
|
7
|
+
LEFT JOIN foreman_tasks_links AS links
|
8
|
+
ON links.task_id = locks.task_id
|
9
|
+
AND links.resource_type = locks.resource_type
|
10
|
+
AND links.resource_id = locks.resource_id
|
11
|
+
WHERE locks.exclusive = FALSE AND links.task_id IS NULL;
|
12
|
+
SQL
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class DropOldLocks < ActiveRecord::Migration[5.0]
|
2
|
+
BATCH_SIZE = 10_000
|
3
|
+
|
4
|
+
# Delete all locks which are exclusive or have a stopped task or are orphaned
|
5
|
+
def up
|
6
|
+
scope = ForemanTasks::Lock.left_outer_joins(:task)
|
7
|
+
scope = scope.where(:exclusive => false)
|
8
|
+
.or(scope.where("#{ForemanTasks::Task.table_name}.state" => ['stopped', nil]))
|
9
|
+
scope.limit(BATCH_SIZE).delete_all while scope.any?
|
10
|
+
|
11
|
+
# For each group of locks, where each lock has the same task_id, resource_type and resource_id
|
12
|
+
# return the highest id within the group, if there is more than 1 lock in the group
|
13
|
+
scope = ForemanTasks::Lock.select("MAX(id) as id")
|
14
|
+
.group(:task_id, :resource_type, :resource_id)
|
15
|
+
.having("count(*) > 1")
|
16
|
+
|
17
|
+
# Make sure there is at most one lock per task and resource
|
18
|
+
ForemanTasks::Lock.where(:id => scope.limit(BATCH_SIZE)).delete_all while scope.any?
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class MakeLocksExclusive < ActiveRecord::Migration[5.0]
|
2
|
+
BATCH_SIZE = 2
|
3
|
+
|
4
|
+
def up
|
5
|
+
change_table :foreman_tasks_locks do |t|
|
6
|
+
t.remove :exclusive
|
7
|
+
t.remove :name
|
8
|
+
t.remove_index :name => 'index_foreman_tasks_locks_on_resource_type_and_resource_id'
|
9
|
+
t.index [:resource_type, :resource_id], :unique => true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def down
|
14
|
+
change_table :foreman_tasks_locks do |t|
|
15
|
+
t.boolean :exclusive, index: true
|
16
|
+
t.string :name, index: true
|
17
|
+
t.remove_index :name => 'index_foreman_tasks_locks_on_resource_type_and_resource_id'
|
18
|
+
t.index [:resource_type, :resource_id]
|
19
|
+
end
|
20
|
+
|
21
|
+
scope = ForemanTasks::Lock.where(:name => nil)
|
22
|
+
while scope.limit(BATCH_SIZE).update_all(:name => 'lock') == BATCH_SIZE; end
|
23
|
+
change_column_null(:foreman_tasks_locks, :name, false)
|
24
|
+
end
|
25
|
+
end
|
data/foreman-tasks.gemspec
CHANGED
@@ -7,6 +7,7 @@ require "foreman_tasks/version"
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "foreman-tasks"
|
9
9
|
s.version = ForemanTasks::VERSION
|
10
|
+
s.license = 'GPL-3.0'
|
10
11
|
s.authors = ["Ivan Nečas"]
|
11
12
|
s.email = ["inecas@redhat.com"]
|
12
13
|
s.homepage = "https://github.com/theforeman/foreman-tasks"
|
@@ -103,6 +103,7 @@ module ForemanTasks
|
|
103
103
|
end
|
104
104
|
end
|
105
105
|
delete_orphaned_locks
|
106
|
+
delete_orphaned_links
|
106
107
|
delete_orphaned_dynflow_tasks
|
107
108
|
end
|
108
109
|
|
@@ -156,6 +157,15 @@ module ForemanTasks
|
|
156
157
|
end
|
157
158
|
end
|
158
159
|
|
160
|
+
def delete_orphaned_links
|
161
|
+
orphaned_links = ForemanTasks::Link.left_outer_joins(:task).where(:'foreman_tasks_tasks.id' => nil)
|
162
|
+
with_noop(orphaned_links, 'orphaned task links') do |source, name|
|
163
|
+
with_batches(source, name) do |chunk|
|
164
|
+
ForemanTasks::Link.where(id: chunk.pluck(:id)).delete_all
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
159
169
|
def delete_orphaned_dynflow_tasks
|
160
170
|
with_noop(orphaned_dynflow_tasks, 'orphaned execution plans') do |source, name|
|
161
171
|
with_batches(source, name) do |chunk|
|
data/lib/foreman_tasks/engine.rb
CHANGED
@@ -34,7 +34,7 @@ module ForemanTasks
|
|
34
34
|
|
35
35
|
initializer 'foreman_tasks.register_plugin', :before => :finisher_hook do |_app|
|
36
36
|
Foreman::Plugin.register :"foreman-tasks" do
|
37
|
-
requires_foreman '>= 2.
|
37
|
+
requires_foreman '>= 2.4.0'
|
38
38
|
divider :top_menu, :parent => :monitor_menu, :last => true, :caption => N_('Foreman Tasks')
|
39
39
|
menu :top_menu, :tasks,
|
40
40
|
:url_hash => { :controller => 'foreman_tasks/tasks', :action => :index },
|
@@ -76,6 +76,9 @@ module ForemanTasks
|
|
76
76
|
|
77
77
|
widget 'foreman_tasks/tasks/dashboard/tasks_status', :sizex => 6, :sizey => 1, :name => N_('Task Status')
|
78
78
|
widget 'foreman_tasks/tasks/dashboard/latest_tasks_in_error_warning', :sizex => 6, :sizey => 1, :name => N_('Latest Warning/Error Tasks')
|
79
|
+
|
80
|
+
ForemanTasks.dynflow.eager_load_actions!
|
81
|
+
extend_observable_events(::Dynflow::Action.descendants.select { |klass| klass <= ::Actions::ObservableAction }.map(&:namespaced_event_names))
|
79
82
|
end
|
80
83
|
end
|
81
84
|
|
@@ -108,7 +111,7 @@ module ForemanTasks
|
|
108
111
|
ForemanTasks.dynflow.require!
|
109
112
|
::ForemanTasks.dynflow.config.on_init(false) do |world|
|
110
113
|
world.middleware.use Actions::Middleware::KeepCurrentTaxonomies
|
111
|
-
world.middleware.use Actions::Middleware::KeepCurrentUser
|
114
|
+
world.middleware.use Actions::Middleware::KeepCurrentUser, :before => ::Dynflow::Middleware::Common::Transaction
|
112
115
|
world.middleware.use Actions::Middleware::KeepCurrentTimezone
|
113
116
|
world.middleware.use Actions::Middleware::KeepCurrentRequestID
|
114
117
|
end
|
@@ -252,7 +252,7 @@ namespace :foreman_tasks do
|
|
252
252
|
renderer = TaskRender.new
|
253
253
|
total = tasks.count
|
254
254
|
|
255
|
-
tasks.
|
255
|
+
tasks.find_each.with_index do |task, count|
|
256
256
|
File.open(File.join(tmp_dir, "#{task.id}.html"), 'w') { |file| file.write(PageHelper.pagify(renderer.render_task(task))) }
|
257
257
|
puts "#{count + 1}/#{total}"
|
258
258
|
end
|
@@ -264,7 +264,7 @@ namespace :foreman_tasks do
|
|
264
264
|
elsif format == 'csv'
|
265
265
|
CSV.open(export_filename, 'wb') do |csv|
|
266
266
|
csv << %w[id state type label result parent_task_id started_at ended_at]
|
267
|
-
tasks.
|
267
|
+
tasks.find_each do |task|
|
268
268
|
csv << [task.id, task.state, task.type, task.label, task.result,
|
269
269
|
task.parent_task_id, task.started_at, task.ended_at]
|
270
270
|
end
|
data/package.json
CHANGED
@@ -23,7 +23,7 @@
|
|
23
23
|
"url": "http://projects.theforeman.org/projects/foreman-tasks/issues"
|
24
24
|
},
|
25
25
|
"peerDependencies": {
|
26
|
-
"@theforeman/vendor": ">=
|
26
|
+
"@theforeman/vendor": ">= 6.0.0"
|
27
27
|
},
|
28
28
|
"dependencies": {
|
29
29
|
"c3": "^0.4.11",
|
@@ -32,11 +32,11 @@
|
|
32
32
|
},
|
33
33
|
"devDependencies": {
|
34
34
|
"@babel/core": "^7.7.0",
|
35
|
-
"@theforeman/builder": "^
|
36
|
-
"@theforeman/eslint-plugin-foreman": "
|
37
|
-
"@theforeman/stories": "^
|
38
|
-
"@theforeman/test": "^
|
39
|
-
"@theforeman/vendor-dev": "^
|
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",
|
40
40
|
"babel-eslint": "^10.0.3",
|
41
41
|
"eslint": "^6.7.2",
|
42
42
|
"jed": "^1.1.1",
|
@@ -45,6 +45,16 @@ module ForemanTasks
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
describe 'POST /api/tasks/bulk_search' do
|
49
|
+
it 'searching for a task' do
|
50
|
+
task = FactoryBot.create(:dynflow_task, :user_create_task)
|
51
|
+
post :bulk_search, params: { :searches => [{ :type => "task", :task_id => task.id, :search_id => "1" }] }
|
52
|
+
assert_response :success
|
53
|
+
data = JSON.parse(response.body)
|
54
|
+
_(data[0]['results'][0]['id']).must_equal task.id
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
48
58
|
describe 'GET /api/tasks/show' do
|
49
59
|
it 'searches for task' do
|
50
60
|
task = FactoryBot.create(:dynflow_task, :user_create_task)
|