foreman-tasks 3.0.6 → 4.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.
- checksums.yaml +4 -4
- 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/task.rb +20 -7
- data/app/models/foreman_tasks/task/dynflow_task.rb +3 -8
- data/app/models/foreman_tasks/task/search.rb +6 -6
- 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/tasks/_lock_card.html.erb +10 -0
- 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/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/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/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 +5 -1
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +3 -0
- data/webpack/ForemanTasks/Components/TaskDetails/index.js +2 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.scss +4 -3
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +2 -2
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/actionNameCellFormatter.test.js.snap +2 -3
- data/webpack/ForemanTasks/Components/TasksTable/formatters/actionNameCellFormatter.js +2 -3
- metadata +12 -4
- data/test/unit/lock_test.rb +0 -22
@@ -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
|
@@ -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
|
-
|
148
|
-
|
149
|
-
|
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,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,12 @@ 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
24
|
value = value.split(',') if operator.index(/IN/i)
|
25
25
|
{ :conditions => sanitize_sql_for_conditions([sql, value]), :joins => joins }
|
26
26
|
end
|
@@ -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'] }
|
@@ -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>
|
@@ -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
|
@@ -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",
|
@@ -10,7 +10,7 @@ module ForemanTasks
|
|
10
10
|
# rubocop:enable Naming/AccessorMethodName
|
11
11
|
|
12
12
|
def linked_task(resource)
|
13
|
-
FactoryBot.create(:some_task).tap { |t| ForemanTasks::
|
13
|
+
FactoryBot.create(:some_task).tap { |t| ForemanTasks::Link.link!(resource, t) }
|
14
14
|
end
|
15
15
|
|
16
16
|
def in_taxonomy_scope(organization, location = nil)
|
@@ -34,8 +34,9 @@ module ForemanTasks
|
|
34
34
|
describe Actions::ActionWithSubPlans do
|
35
35
|
include ForemanTasks::TestHelpers::WithInThreadExecutor
|
36
36
|
|
37
|
+
let(:user) { FactoryBot.create(:user) }
|
38
|
+
|
37
39
|
let(:task) do
|
38
|
-
user = FactoryBot.create(:user)
|
39
40
|
triggered = ForemanTasks.trigger(ParentAction, user)
|
40
41
|
raise triggered.error if triggered.respond_to?(:error)
|
41
42
|
triggered.finished.wait(30)
|
@@ -49,7 +50,9 @@ module ForemanTasks
|
|
49
50
|
|
50
51
|
specify "the locks of the sub-plan don't colide with the locks of its parent" do
|
51
52
|
child_task = task.sub_tasks.first
|
52
|
-
|
53
|
+
assert_not(child_task.locks.any?, "the lock is ensured by the parent")
|
54
|
+
found = ForemanTasks::Link.for_resource(user).where(:task_id => child_task.id).any?
|
55
|
+
assert(found, "the action is linked properly")
|
53
56
|
end
|
54
57
|
end
|
55
58
|
end
|
data/test/unit/cleaner_test.rb
CHANGED
@@ -60,18 +60,18 @@ class TasksTest < ActiveSupport::TestCase
|
|
60
60
|
task.started_at = task.ended_at = Time.zone.now
|
61
61
|
task.save
|
62
62
|
end]
|
63
|
-
|
63
|
+
link_to_delete = tasks_to_delete.first.links.create(:resource => User.current)
|
64
64
|
|
65
65
|
tasks_to_keep = [FactoryBot.create(:dynflow_task, :product_create_task)]
|
66
|
-
|
66
|
+
link_to_keep = tasks_to_keep.first.links.create(:resource => User.current)
|
67
67
|
|
68
68
|
cleaner.expects(:tasks_to_csv)
|
69
69
|
cleaner.delete
|
70
70
|
_(ForemanTasks::Task.where(id: tasks_to_delete)).must_be_empty
|
71
71
|
_(ForemanTasks::Task.where(id: tasks_to_keep)).must_equal tasks_to_keep
|
72
72
|
|
73
|
-
_(ForemanTasks::
|
74
|
-
_(ForemanTasks::
|
73
|
+
_(ForemanTasks::Link.find_by(id: link_to_delete.id)).must_be_nil
|
74
|
+
_(ForemanTasks::Link.find_by(id: link_to_keep.id)).wont_be_nil
|
75
75
|
end
|
76
76
|
|
77
77
|
it 'supports passing empty filter (just delete all)' do
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'foreman_tasks_test_helper'
|
3
|
+
|
4
|
+
module ForemanTasks
|
5
|
+
class LockTest < ::ActiveSupport::TestCase
|
6
|
+
describe ::ForemanTasks::Lock::LockConflict do
|
7
|
+
class FakeLockConflict < ForemanTasks::Lock::LockConflict
|
8
|
+
def _(val)
|
9
|
+
val.freeze
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'does not modify frozen strings' do
|
14
|
+
required_lock = OpenStruct.new(:name => 'my_lock')
|
15
|
+
# Before #21770 the next line would raise
|
16
|
+
# RuntimeError: can't modify frozen String
|
17
|
+
conflict = FakeLockConflict.new(required_lock, [])
|
18
|
+
assert conflict._('this should be frozen').frozen?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'locking and linking' do
|
23
|
+
before { [Lock, Link].each(&:destroy_all) }
|
24
|
+
let(:task1) { FactoryBot.create(:some_task) }
|
25
|
+
let(:task2) { FactoryBot.create(:some_task) }
|
26
|
+
let(:resource) { FactoryBot.create(:user) }
|
27
|
+
|
28
|
+
describe Lock do
|
29
|
+
it 'can lock a resource for a single task' do
|
30
|
+
Lock.lock!(resource, task1)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'can lock a resource for a single task only once' do
|
34
|
+
Lock.lock!(resource, task1)
|
35
|
+
_(Lock.for_resource(resource).count).must_equal 1
|
36
|
+
Lock.lock!(resource, task1)
|
37
|
+
_(Lock.for_resource(resource).count).must_equal 1
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'cannot lock a resource for multiple tasks' do
|
41
|
+
lock = Lock.lock!(resource, task1)
|
42
|
+
_(Lock.colliding_locks(resource, task2)).must_equal [lock]
|
43
|
+
assert_raises Lock::LockConflict do
|
44
|
+
Lock.lock!(resource, task2)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'raises LockConflict when enforced by db' do
|
49
|
+
lock = Lock.lock!(resource, task1)
|
50
|
+
Lock.any_instance
|
51
|
+
.expects(:colliding_locks).twice.returns([], [lock])
|
52
|
+
exception = assert_raises Lock::LockConflict do
|
53
|
+
Lock.lock!(resource, task2)
|
54
|
+
end
|
55
|
+
_(exception.message).must_match(/#{lock.task_id}/)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'creates a link when creating a lock for a resource' do
|
59
|
+
Lock.lock!(resource, task1)
|
60
|
+
link = Link.for_resource(resource).first
|
61
|
+
_(link.task_id).must_equal task1.id
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe Link do
|
66
|
+
it 'can link a resource to a single task' do
|
67
|
+
Link.link!(resource, task1)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'can link a resource for a single task only once' do
|
71
|
+
Link.link!(resource, task1)
|
72
|
+
_(Link.for_resource(resource).count).must_equal 1
|
73
|
+
Link.link!(resource, task1)
|
74
|
+
_(Link.for_resource(resource).count).must_equal 1
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'can link a resource to multiple tasks' do
|
78
|
+
Link.link!(resource, task1)
|
79
|
+
Link.link!(resource, task2)
|
80
|
+
_(Link.for_resource(resource).count).must_equal 2
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|