foreman-tasks 3.0.5 → 4.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby_tests.yml +5 -3
- data/app/controllers/concerns/foreman_tasks/find_tasks_common.rb +14 -0
- data/app/controllers/foreman_tasks/api/tasks_controller.rb +4 -6
- data/app/controllers/foreman_tasks/tasks_controller.rb +3 -11
- 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/middleware/load_setting_values.rb +35 -0
- data/app/lib/actions/observable_action.rb +80 -0
- data/app/lib/actions/proxy_action.rb +2 -4
- 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/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/20180927120509_add_user_id.foreman_tasks.rb +4 -2
- 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 +6 -2
- data/lib/foreman_tasks/tasks/export_tasks.rake +2 -2
- data/lib/foreman_tasks/version.rb +1 -1
- data/locale/action_names.rb +3 -2
- data/locale/en/foreman_tasks.po +60 -27
- data/locale/foreman_tasks.pot +180 -132
- data/locale/fr/foreman_tasks.po +61 -28
- data/locale/ja/foreman_tasks.po +61 -28
- data/locale/zh_CN/foreman_tasks.po +61 -28
- data/package.json +6 -6
- data/test/controllers/api/tasks_controller_test.rb +16 -1
- data/test/controllers/tasks_controller_test.rb +3 -3
- data/test/core/unit/runner_test.rb +4 -17
- data/test/factories/task_factory.rb +31 -4
- data/test/unit/actions/action_with_sub_plans_test.rb +5 -2
- data/test/unit/actions/proxy_action_test.rb +4 -1
- data/test/unit/cleaner_test.rb +4 -4
- data/test/unit/locking_test.rb +85 -0
- data/test/unit/task_test.rb +15 -15
- data/test/unit/triggering_test.rb +2 -2
- 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 +14 -4
- data/test/unit/lock_test.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 676dae1bc60b1633da496445b980e4b5fb491931bb227a70f2dcec4c44a9ae9e
|
4
|
+
data.tar.gz: 98d4f9798338d20bf7885a9e11b40d492eeeca4d30e2a7f84c4734e4f83e30d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8482b8a8e5ce147f2bd81d1eea004ba9507c95ecf5d83d0ebd50b7c74cc467a561f035d0049f0d1b44c3d63a29c35f3e55792705d37dfc0b4431e02d2370732a
|
7
|
+
data.tar.gz: c317248a7e396b2d556f397ba1fd91d162b5d38b75a593a7f8a28f555ef77ca15ec097f548162edd6c7a7198bfa7e84c6a902bbde6d7515c17b585f7632e9add
|
@@ -12,7 +12,7 @@ jobs:
|
|
12
12
|
- name: Setup Ruby
|
13
13
|
uses: ruby/setup-ruby@v1
|
14
14
|
with:
|
15
|
-
ruby-version: 2.
|
15
|
+
ruby-version: 2.7
|
16
16
|
- name: Setup
|
17
17
|
run: |
|
18
18
|
gem install bundler
|
@@ -31,7 +31,7 @@ jobs:
|
|
31
31
|
fail-fast: false
|
32
32
|
matrix:
|
33
33
|
foreman-core-branch: [develop]
|
34
|
-
ruby-version: [2.5, 2.6]
|
34
|
+
ruby-version: [2.5, 2.6, 2.7]
|
35
35
|
node-version: [12]
|
36
36
|
steps:
|
37
37
|
- run: sudo apt-get update
|
@@ -60,6 +60,7 @@ jobs:
|
|
60
60
|
- name: Setup Bundler
|
61
61
|
run: |
|
62
62
|
echo "gem 'foreman-tasks', path: './foreman-tasks'" > bundler.d/foreman-tasks.local.rb
|
63
|
+
echo "gem 'sqlite3'" >> bundler.d/foreman-tasks.local.rb
|
63
64
|
gem install bundler
|
64
65
|
bundle config set without journald development console libvirt
|
65
66
|
bundle config set path vendor/bundle
|
@@ -70,5 +71,6 @@ jobs:
|
|
70
71
|
bundle exec rake db:migrate
|
71
72
|
- name: Run plugin tests
|
72
73
|
run: |
|
73
|
-
bundle exec rake test:
|
74
|
+
bundle exec rake test:foreman_tasks
|
75
|
+
bundle exec rake test:foreman_tasks_core
|
74
76
|
bundle exec rake test TEST="test/unit/foreman/access_permissions_test.rb"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ForemanTasks
|
2
|
+
module FindTasksCommon
|
3
|
+
def search_query
|
4
|
+
[current_taxonomy_search, params[:search]].select(&:present?).join(' AND ')
|
5
|
+
end
|
6
|
+
|
7
|
+
def current_taxonomy_search
|
8
|
+
conditions = []
|
9
|
+
conditions << "organization_id = #{Organization.current.id}" if Organization.current
|
10
|
+
conditions << "location_id = #{Location.current.id}" if Location.current
|
11
|
+
conditions.empty? ? '' : "(#{conditions.join(' AND ')})"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module ForemanTasks
|
2
2
|
module Api
|
3
3
|
class TasksController < ::Api::V2::BaseController
|
4
|
+
include ForemanTasks::FindTasksCommon
|
4
5
|
include ::Foreman::Controller::SmartProxyAuth
|
5
6
|
add_smart_proxy_filters :callback, :features => 'Dynflow'
|
6
7
|
|
@@ -51,7 +52,7 @@ module ForemanTasks
|
|
51
52
|
In case :type = 'resource', what resource id we're searching the tasks for
|
52
53
|
DESC
|
53
54
|
param :action_types, [String], :desc => <<-DESC
|
54
|
-
Return just tasks of given action type, e.g. ["Actions::Katello::Repository::Synchronize"]
|
55
|
+
Return just tasks of given action type, e.g. `["Actions::Katello::Repository::Synchronize"]`
|
55
56
|
DESC
|
56
57
|
param :active_only, :bool
|
57
58
|
param :page, String
|
@@ -195,7 +196,7 @@ module ForemanTasks
|
|
195
196
|
end
|
196
197
|
|
197
198
|
def search_options
|
198
|
-
[
|
199
|
+
[search_query, {}]
|
199
200
|
end
|
200
201
|
|
201
202
|
def_param_group :callback_target do
|
@@ -253,10 +254,7 @@ module ForemanTasks
|
|
253
254
|
if search_params[:user_id].blank?
|
254
255
|
raise BadRequest, _('User search_params requires user_id to be specified')
|
255
256
|
end
|
256
|
-
scope.
|
257
|
-
{ name: ::ForemanTasks::Lock::OWNER_LOCK_NAME,
|
258
|
-
resource_type: 'User',
|
259
|
-
resource_id: search_params[:user_id] })
|
257
|
+
scope.search_for("user_id = #{search_params[:user_id]}")
|
260
258
|
when 'resource'
|
261
259
|
if search_params[:resource_type].blank? || search_params[:resource_id].blank?
|
262
260
|
raise BadRequest,
|
@@ -2,6 +2,7 @@ module ForemanTasks
|
|
2
2
|
class TasksController < ::ApplicationController
|
3
3
|
include Foreman::Controller::AutoCompleteSearch
|
4
4
|
include Foreman::Controller::CsvResponder
|
5
|
+
include ForemanTasks::FindTasksCommon
|
5
6
|
|
6
7
|
def show
|
7
8
|
@task = resource_base.find(params[:id])
|
@@ -126,19 +127,10 @@ module ForemanTasks
|
|
126
127
|
end
|
127
128
|
|
128
129
|
def filter(scope, paginate: true)
|
129
|
-
search = current_taxonomy_search
|
130
|
-
search = [search, params[:search]].select(&:present?).join(' AND ')
|
131
130
|
scope = DashboardTableFilter.new(scope, params).scope
|
132
|
-
scope = scope.search_for(
|
133
|
-
scope = scope.paginate(:
|
131
|
+
scope = scope.search_for(search_query, order: params[:order])
|
132
|
+
scope = scope.paginate(page: params[:page], per_page: params[:per_page]) if paginate
|
134
133
|
scope.distinct
|
135
134
|
end
|
136
|
-
|
137
|
-
def current_taxonomy_search
|
138
|
-
conditions = []
|
139
|
-
conditions << "organization_id = #{Organization.current.id}" if Organization.current
|
140
|
-
conditions << "location_id = #{Location.current.id}" if Location.current
|
141
|
-
conditions.empty? ? '' : "(#{conditions.join(' AND ')})"
|
142
|
-
end
|
143
135
|
end
|
144
136
|
end
|
@@ -3,6 +3,8 @@ module Actions
|
|
3
3
|
include Helpers::ArgsSerialization
|
4
4
|
include Helpers::Lock
|
5
5
|
|
6
|
+
execution_plan_hooks.use :drop_all_locks!, :on => :stopped
|
7
|
+
|
6
8
|
# what locks to use on the resource? All by default, can be overriden.
|
7
9
|
# It might one or more locks available for the resource. This following
|
8
10
|
# special values are supported as well:
|
@@ -34,12 +36,10 @@ module Actions
|
|
34
36
|
input.update serialize_args(resource, *resource.all_related_resources, *additional_args)
|
35
37
|
|
36
38
|
if resource.is_a? ActiveRecord::Base
|
37
|
-
if resource_locks == :
|
38
|
-
exclusive_lock!(resource)
|
39
|
-
elsif resource_locks == :link
|
39
|
+
if resource_locks == :link
|
40
40
|
link!(resource)
|
41
41
|
else
|
42
|
-
|
42
|
+
exclusive_lock!(resource)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -63,5 +63,9 @@ module Actions
|
|
63
63
|
def self.serializer_class
|
64
64
|
Serializers::ActiveRecordSerializer
|
65
65
|
end
|
66
|
+
|
67
|
+
def drop_all_locks!(_execution_plan)
|
68
|
+
ForemanTasks::Lock.where(:task_id => task.id).destroy_all
|
69
|
+
end
|
66
70
|
end
|
67
71
|
end
|
@@ -4,19 +4,25 @@ module Actions
|
|
4
4
|
# @see Lock.exclusive!
|
5
5
|
def exclusive_lock!(resource)
|
6
6
|
phase! Dynflow::Action::Plan
|
7
|
-
::ForemanTasks::Lock.
|
7
|
+
parent_lock = ::ForemanTasks::Lock.for_resource(resource).where(:task_id => task.self_and_parents.map(&:id)).first
|
8
|
+
if parent_lock
|
9
|
+
ForemanTasks::Link.link_resource_and_related!(resource, task)
|
10
|
+
parent_lock
|
11
|
+
else
|
12
|
+
::ForemanTasks::Lock.exclusive!(resource, task)
|
13
|
+
end
|
8
14
|
end
|
9
15
|
|
10
16
|
# @see Lock.lock!
|
11
|
-
def lock!(resource, *
|
12
|
-
|
13
|
-
|
17
|
+
def lock!(resource, *_lock_names)
|
18
|
+
Foreman::Deprecation.deprecation_warning('2.4', 'locking in foreman-tasks was reworked, please use a combination of exclusive_lock! and link! instead.')
|
19
|
+
exclusive_lock!(resource)
|
14
20
|
end
|
15
21
|
|
16
22
|
# @see Lock.link!
|
17
23
|
def link!(resource)
|
18
24
|
phase! Dynflow::Action::Plan
|
19
|
-
::ForemanTasks::
|
25
|
+
::ForemanTasks::Link.link!(resource, task)
|
20
26
|
end
|
21
27
|
end
|
22
28
|
end
|
@@ -21,7 +21,10 @@ module Actions
|
|
21
21
|
|
22
22
|
# Run all execution plan lifecycle hooks as the original request_id
|
23
23
|
def hook(*args)
|
24
|
-
|
24
|
+
store_current_request_id if !action.input.key?(:current_request_id) && ::Logging.mdc['request']
|
25
|
+
restore_current_request_id do
|
26
|
+
pass(*args)
|
27
|
+
end
|
25
28
|
end
|
26
29
|
|
27
30
|
private
|
@@ -16,9 +16,19 @@ module Actions
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def finalize
|
19
|
+
current_id = User.current.try(:id)
|
20
|
+
saved_id = action.input[:current_user_id]
|
21
|
+
if User.current && saved_id && current_id != saved_id
|
22
|
+
Foreman::Deprecation.deprecation_warning('2.5', 'relying on per-step setting of current user in finalize phase')
|
23
|
+
end
|
24
|
+
|
19
25
|
restore_curent_user { pass }
|
20
26
|
end
|
21
27
|
|
28
|
+
def finalize_phase(execution_plan, *args)
|
29
|
+
restore_curent_user(execution_plan.entry_action) { pass(execution_plan, *args) }
|
30
|
+
end
|
31
|
+
|
22
32
|
# Run all execution plan lifecycle hooks as the original user
|
23
33
|
def hook(*args)
|
24
34
|
restore_curent_user { pass(*args) }
|
@@ -38,7 +48,7 @@ module Actions
|
|
38
48
|
action.input[:current_user_id] = User.current.try(:id)
|
39
49
|
end
|
40
50
|
|
41
|
-
def restore_curent_user
|
51
|
+
def restore_curent_user(action = self.action)
|
42
52
|
old_user = User.current
|
43
53
|
User.current = User.unscoped.find(action.input[:current_user_id]) if action.input[:current_user_id].present?
|
44
54
|
yield
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Actions
|
2
|
+
module Middleware
|
3
|
+
class LoadSettingValues < ::Dynflow::Middleware
|
4
|
+
# ::Actions::Middleware::LoadSettingValues
|
5
|
+
#
|
6
|
+
# A middleware to ensure we load current setting values
|
7
|
+
|
8
|
+
def delay(*args)
|
9
|
+
reload_setting_values
|
10
|
+
pass(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def plan(*args)
|
14
|
+
reload_setting_values
|
15
|
+
pass(*args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def run(*args)
|
19
|
+
reload_setting_values
|
20
|
+
pass(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def finalize(*args)
|
24
|
+
reload_setting_values
|
25
|
+
pass(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def reload_setting_values
|
31
|
+
::Foreman.settings.load_values
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Actions
|
2
|
+
# Examples:
|
3
|
+
|
4
|
+
# # Action A which emits an event when it successfully finishes.
|
5
|
+
# class A
|
6
|
+
# include ::Actions::ObservableAction
|
7
|
+
# # ... rest ...
|
8
|
+
# end
|
9
|
+
|
10
|
+
# # Action B which emits an event when it successfully finishes or fails.
|
11
|
+
# class B
|
12
|
+
# include ::Actions::ObservableAction
|
13
|
+
#
|
14
|
+
# execution_plan_hooks.use :emit_event_failure, :on => [:failure]
|
15
|
+
#
|
16
|
+
# def self.event_names
|
17
|
+
# super + [event_name_base + '_' + event_name_suffix(:failure)]
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def emit_event_failure(plan)
|
21
|
+
# emit_event(plan, :failure)
|
22
|
+
# end
|
23
|
+
# # ... rest ...
|
24
|
+
# end
|
25
|
+
module ObservableAction
|
26
|
+
module ClassMethods
|
27
|
+
def event_name_suffix(hook)
|
28
|
+
case hook
|
29
|
+
when :success
|
30
|
+
'succeeded'
|
31
|
+
when :failure
|
32
|
+
'failed'
|
33
|
+
else
|
34
|
+
hook
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def event_names
|
39
|
+
[event_name_base + '_' + event_name_suffix(:success)]
|
40
|
+
end
|
41
|
+
|
42
|
+
def namespaced_event_names
|
43
|
+
event_names.map { |e| ::Foreman::Observable.event_name_for(e) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def event_name_base
|
47
|
+
to_s.underscore.tr('/', '.')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.included(base)
|
52
|
+
base.extend ClassMethods
|
53
|
+
base.include ::Foreman::Observable
|
54
|
+
base.execution_plan_hooks.use :emit_event, :on => :success
|
55
|
+
end
|
56
|
+
|
57
|
+
def emit_event(execution_plan, hook = :success)
|
58
|
+
return unless root_action?
|
59
|
+
|
60
|
+
trigger_hook "#{self.class.event_name_base}_#{self.class.event_name_suffix(hook)}",
|
61
|
+
payload: event_payload(execution_plan)
|
62
|
+
end
|
63
|
+
|
64
|
+
def event_payload(_execution_plan)
|
65
|
+
{ object: self }
|
66
|
+
end
|
67
|
+
|
68
|
+
extend ApipieDSL::Module
|
69
|
+
|
70
|
+
apipie :class, "An common ancestor action for observable actions" do
|
71
|
+
name 'Actions::ObservableAction'
|
72
|
+
refs 'Actions::ObservableAction'
|
73
|
+
sections only: %w[all webhooks]
|
74
|
+
property :task, object_of: 'Task', desc: 'Returns the task to which this action belongs'
|
75
|
+
end
|
76
|
+
class Jail < Safemode::Jail
|
77
|
+
allow :task
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -218,10 +218,8 @@ module Actions
|
|
218
218
|
end
|
219
219
|
|
220
220
|
def get_proxy_data(response)
|
221
|
-
|
222
|
-
|
223
|
-
end
|
224
|
-
proxy_data.fetch('output', {})
|
221
|
+
response['actions'].detect { |action| action.fetch('input', {})['task_id'] == task.id }
|
222
|
+
.try(:fetch, 'output', {}) || {}
|
225
223
|
end
|
226
224
|
|
227
225
|
def proxy_version(proxy)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ForemanTasks
|
2
|
+
class Link < ApplicationRecord
|
3
|
+
belongs_to :task
|
4
|
+
|
5
|
+
belongs_to :resource, polymorphic: true
|
6
|
+
|
7
|
+
scope :for_resource, ->(resource) { where(:resource => resource) }
|
8
|
+
|
9
|
+
validates :task_id, :resource_id, :resource_type, presence: true
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# Assigns the resource to the task to easily track the task in context of
|
13
|
+
# the resource. This doesn't prevent other actions to lock the resource
|
14
|
+
# and should be used only for actions that tolerate other actions to be
|
15
|
+
# performed on the resource. Usually, this shouldn't needed to be done
|
16
|
+
# through the action directly, because the lock should assign it's parent
|
17
|
+
# objects to the action recursively (using +related_resources+ method in model
|
18
|
+
# objects)
|
19
|
+
def link!(resource, task)
|
20
|
+
link = build(resource, task)
|
21
|
+
link.save!
|
22
|
+
link
|
23
|
+
end
|
24
|
+
|
25
|
+
# Records the information about the user that triggered the task
|
26
|
+
def owner!(user, task)
|
27
|
+
link!(user, task)
|
28
|
+
end
|
29
|
+
|
30
|
+
def link_resource_and_related!(resource, task)
|
31
|
+
link!(resource, task)
|
32
|
+
build_related_links(resource, task).each(&:save!)
|
33
|
+
end
|
34
|
+
|
35
|
+
def build_related_links(resource, task)
|
36
|
+
related_resources(resource).map do |related_resource|
|
37
|
+
build(related_resource, task)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def build(resource, task)
|
44
|
+
find_or_initialize_by(task_id: task.id,
|
45
|
+
resource_type: resource.class.name,
|
46
|
+
resource_id: resource.id)
|
47
|
+
end
|
48
|
+
|
49
|
+
# recursively search for related resources of the resource (using
|
50
|
+
# the +related_resources+ method, avoiding the cycles
|
51
|
+
def related_resources(resource)
|
52
|
+
if resource.respond_to?(:all_related_resources)
|
53
|
+
resource.all_related_resources
|
54
|
+
else
|
55
|
+
[]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|