foreman-tasks 0.17.0 → 0.17.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/foreman_tasks/tasks.css.scss +3 -3
- data/app/controllers/foreman_tasks/api/tasks_controller.rb +50 -30
- data/app/controllers/foreman_tasks/tasks_controller.rb +8 -17
- data/app/models/foreman_tasks/task.rb +6 -4
- data/app/models/foreman_tasks/task/search.rb +0 -25
- data/app/models/setting/foreman_tasks.rb +19 -23
- data/app/views/foreman_tasks/api/tasks/show.json.rabl +1 -0
- data/app/views/foreman_tasks/layouts/react.html.erb +4 -1
- data/config/routes.rb +17 -3
- data/db/migrate/20180927120509_add_user_id.foreman_tasks.rb +4 -2
- data/lib/foreman_tasks/dynflow.rb +1 -1
- data/lib/foreman_tasks/dynflow/console_authorizer.rb +13 -2
- data/lib/foreman_tasks/engine.rb +1 -1
- data/lib/foreman_tasks/version.rb +1 -1
- data/package.json +1 -0
- data/test/controllers/tasks_controller_test.rb +35 -4
- data/test/unit/dynflow_console_authorizer_test.rb +1 -1
- data/test/unit/otp_manager_test.rb +24 -17
- data/test/unit/task_test.rb +48 -2
- data/webpack/ForemanTasks/Components/TaskDetails/Components/Task.js +29 -16
- data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskHelper.js +3 -17
- data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskInfo.js +18 -5
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskHelper.test.js +1 -56
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Task.test.js.snap +24 -44
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskInfo.test.js.snap +22 -10
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsActions.js +6 -0
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +1 -1
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetails.test.js.snap +2 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.js +6 -3
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardActions.js +2 -3
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardHelper.js +10 -41
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardReducer.js +0 -1
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboard.test.js +1 -1
- data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/__snapshots__/TasksDashboardReducer.test.js.snap +1 -6
- data/webpack/ForemanTasks/Components/TasksTable/SubTasksPage.js +30 -0
- data/webpack/ForemanTasks/Components/TasksTable/TaskTableFormmatters.js +53 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksIndexPage.js +10 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTable.js +119 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableActions.js +67 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableConstants.js +5 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableHelpers.js +64 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +63 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.scss +29 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableReducer.js +35 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableSchema.js +67 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +39 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/SubTasksPage.test.js +20 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksIndexPage.test.js +12 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTable.fixtures.js +42 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTable.test.js +9 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableActions.test.js +48 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableHelpers.test.js +28 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTablePage.test.js +26 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableReducer.test.js +37 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/SubTasksPage.test.js.snap +49 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksIndexPage.test.js.snap +42 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTable.test.js.snap +72 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableActions.test.js.snap +115 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +194 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableReducer.test.js.snap +32 -0
- data/webpack/ForemanTasks/Components/TasksTable/index.js +32 -0
- data/webpack/ForemanTasks/Components/common/ActionButtons/ActionButton.js +39 -0
- data/webpack/ForemanTasks/Components/common/ActionButtons/ActionButton.test.js +45 -0
- data/webpack/ForemanTasks/Components/common/ActionButtons/CancelButton.js +23 -0
- data/webpack/ForemanTasks/Components/common/ActionButtons/CancelButton.test.js +27 -0
- data/webpack/ForemanTasks/Components/common/ActionButtons/ResumeButton.js +23 -0
- data/webpack/ForemanTasks/Components/common/ActionButtons/ResumeButton.test.js +27 -0
- data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/ActionButton.test.js.snap +28 -0
- data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/CancelButton.test.js.snap +15 -0
- data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/ResumeButton.test.js.snap +15 -0
- data/webpack/ForemanTasks/ForemanTasks.js +1 -17
- data/webpack/ForemanTasks/ForemanTasksReducers.js +2 -0
- data/webpack/ForemanTasks/Routes/ForemanTasksRouter.test.js +5 -1
- data/webpack/ForemanTasks/Routes/ForemanTasksRoutes.js +9 -3
- data/webpack/ForemanTasks/Routes/ForemanTasksRoutes.test.js +1 -1
- data/webpack/ForemanTasks/Routes/ShowTask/__tests__/ShowTask.test.js +5 -1
- data/webpack/ForemanTasks/Routes/__snapshots__/ForemanTasksRoutes.test.js.snap +18 -2
- data/webpack/ForemanTasks/__snapshots__/ForemanTasks.test.js.snap +1 -54
- data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
- data/webpack/__mocks__/foremanReact/common/urlHelpers.js +1 -0
- data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +2 -0
- data/webpack/__mocks__/foremanReact/components/common/MessageBox.js +4 -0
- data/webpack/__mocks__/foremanReact/components/common/dates/LongDateTime.js +5 -0
- data/webpack/__mocks__/foremanReact/components/common/dates/RelativeDateTime.js +3 -0
- data/webpack/__mocks__/foremanReact/components/common/table.js +4 -0
- data/webpack/__mocks__/foremanReact/components/common/table/actionsHelpers/actionTypeCreator.js +7 -0
- data/webpack/__mocks__/foremanReact/constants.js +24 -0
- data/webpack/__mocks__/foremanReact/redux/actions/toasts.js +8 -0
- data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +10 -0
- data/webpack/__mocks__/foremanReact/routes/common/PageLayout/components/ExportButton/ExportButton.js +5 -0
- data/webpack/index.js +5 -0
- metadata +49 -9
- data/app/views/foreman_tasks/tasks/index.html.erb +0 -46
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskHelper.test.js.snap +0 -37
- data/webpack/ForemanTasks/Routes/IndexTasks/IndexTasks.js +0 -10
- data/webpack/ForemanTasks/Routes/IndexTasks/__tests__/IndexTasks.test.js +0 -10
- data/webpack/ForemanTasks/Routes/IndexTasks/__tests__/__snapshots__/IndexTasks.test.js.snap +0 -12
- data/webpack/ForemanTasks/Routes/IndexTasks/index.js +0 -1
- data/webpack/ForemanTasks/Routes/IndexTasks/indexTasks.scss +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bfc833d42aeafa9f146457f33ffe29db5f4ddc374a372511a384f05d6932754e
|
4
|
+
data.tar.gz: eda50a6c4a0e2c1257fd72c5764c5eed7df097beb230579304ccf1943a472477
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cbbb4c98796edf5e91c2f7b3fd731a40162c6865b94e5c262f61865a5f4b18f939e38a76920756b9f6919501a72ba47b02653ba27fec54ad035e461d33df60bd
|
7
|
+
data.tar.gz: 868cbea935b39e9dd74ff7a616983e1e68d89355716134f493fd29485280c0cc2e4f01ea015358c1285ac82f3ece89c9234c5cac5a73add22edb6213cfbacd43
|
@@ -31,6 +31,15 @@ module ForemanTasks
|
|
31
31
|
param :id, :identifier, desc: 'UUID of the task'
|
32
32
|
def details; end
|
33
33
|
|
34
|
+
api :GET, '/tasks/:id/sub_tasks', 'Show sub_tasks details'
|
35
|
+
param :id, :identifier, desc: 'UUID of the task'
|
36
|
+
def sub_tasks
|
37
|
+
parent_task = resource_scope.find(params[:id])
|
38
|
+
filtered_scope = parent_task.sub_tasks
|
39
|
+
action_name = { "action_name" => parent_task.action }
|
40
|
+
render :json => action_name.merge(tasks_list(filtered_scope))
|
41
|
+
end
|
42
|
+
|
34
43
|
api :POST, '/tasks/bulk_search', 'List dynflow tasks for uuids'
|
35
44
|
param :searches, Array, :desc => 'List of uuids to fetch info about' do
|
36
45
|
param :search_id, String, :desc => <<-DESC
|
@@ -121,35 +130,8 @@ module ForemanTasks
|
|
121
130
|
param :order, String, :desc => N_('How to order the sorted results (e.g. ASC for ascending)')
|
122
131
|
end
|
123
132
|
def index
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
scope = resource_scope.search_for(params[:search]).select('DISTINCT foreman_tasks_tasks.*')
|
128
|
-
|
129
|
-
ordering_params = {
|
130
|
-
sort_by: params[:sort_by] || 'started_at',
|
131
|
-
sort_order: params[:sort_order] || 'DESC'
|
132
|
-
}
|
133
|
-
scope = ordering_scope(scope, ordering_params)
|
134
|
-
|
135
|
-
pagination_params = {
|
136
|
-
page: params[:page] || 1,
|
137
|
-
per_page: params[:per_page] || Setting[:entries_per_page] || 20
|
138
|
-
}
|
139
|
-
scope = pagination_scope(scope, pagination_params)
|
140
|
-
results = scope.map { |task| task_hash(task) }
|
141
|
-
|
142
|
-
render :json => {
|
143
|
-
total: total,
|
144
|
-
subtotal: subtotal,
|
145
|
-
page: pagination_params[:page],
|
146
|
-
per_page: pagination_params[:per_page],
|
147
|
-
sort: {
|
148
|
-
by: ordering_params[:sort_by],
|
149
|
-
order: ordering_params[:sort_order]
|
150
|
-
},
|
151
|
-
results: results
|
152
|
-
}
|
133
|
+
filtered_scope = DashboardTableFilter.new(resource_scope, params).scope
|
134
|
+
render :json => tasks_list(filtered_scope)
|
153
135
|
end
|
154
136
|
|
155
137
|
def_param_group :callback_target do
|
@@ -255,6 +237,7 @@ module ForemanTasks
|
|
255
237
|
def ordering_scope(scope, ordering_params)
|
256
238
|
sort_by = ordering_params[:sort_by] || 'started_at'
|
257
239
|
sort_order = ordering_params[:sort_order] || 'DESC'
|
240
|
+
scope = scope.select("*, coalesce(ended_at, current_timestamp) - coalesce(coalesce(started_at, ended_at), current_timestamp) as duration")
|
258
241
|
scope.order("#{sort_by} #{sort_order}")
|
259
242
|
end
|
260
243
|
|
@@ -278,9 +261,13 @@ module ForemanTasks
|
|
278
261
|
@resource_scope ||= ForemanTasks::Task.authorized("#{action_permission}_foreman_tasks")
|
279
262
|
end
|
280
263
|
|
264
|
+
def resource_class
|
265
|
+
@resource_class ||= ForemanTasks::Task
|
266
|
+
end
|
267
|
+
|
281
268
|
def action_permission
|
282
269
|
case params[:action]
|
283
|
-
when 'bulk_search', 'summary', 'details'
|
270
|
+
when 'bulk_search', 'summary', 'details', 'sub_tasks'
|
284
271
|
:view
|
285
272
|
when 'bulk_resume'
|
286
273
|
:edit
|
@@ -288,6 +275,39 @@ module ForemanTasks
|
|
288
275
|
super
|
289
276
|
end
|
290
277
|
end
|
278
|
+
|
279
|
+
def tasks_list(filtered_scope)
|
280
|
+
total = resource_scope.count
|
281
|
+
|
282
|
+
search_scope = filtered_scope.search_for(params[:search])
|
283
|
+
subtotal = search_scope.select('DISTINCT foreman_tasks_tasks.id').count
|
284
|
+
filtered_scope = search_scope.select('DISTINCT foreman_tasks_tasks.*')
|
285
|
+
|
286
|
+
ordering_params = {
|
287
|
+
sort_by: params[:sort_by] || 'started_at',
|
288
|
+
sort_order: params[:sort_order] || 'DESC'
|
289
|
+
}
|
290
|
+
filtered_scope = ordering_scope(filtered_scope, ordering_params)
|
291
|
+
|
292
|
+
pagination_params = {
|
293
|
+
page: params[:page] || 1,
|
294
|
+
per_page: params[:per_page] || Setting[:entries_per_page] || 20
|
295
|
+
}
|
296
|
+
filtered_scope = pagination_scope(filtered_scope, pagination_params)
|
297
|
+
results = filtered_scope.map { |task| task_hash(task) }
|
298
|
+
|
299
|
+
{
|
300
|
+
total: total,
|
301
|
+
subtotal: subtotal,
|
302
|
+
page: pagination_params[:page],
|
303
|
+
per_page: pagination_params[:per_page],
|
304
|
+
sort: {
|
305
|
+
by: ordering_params[:sort_by],
|
306
|
+
order: ordering_params[:sort_order]
|
307
|
+
},
|
308
|
+
results: results
|
309
|
+
}
|
310
|
+
end
|
291
311
|
end
|
292
312
|
end
|
293
313
|
end
|
@@ -16,7 +16,8 @@ module ForemanTasks
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def summary
|
19
|
-
|
19
|
+
scope = resource_base.search_for(current_taxonomy_search).select(:id)
|
20
|
+
render json: Task::Summarizer.new(Task.where(:id => scope), params[:recent_timeframe].to_i).summary
|
20
21
|
end
|
21
22
|
|
22
23
|
def sub_tasks
|
@@ -35,11 +36,10 @@ module ForemanTasks
|
|
35
36
|
def cancel
|
36
37
|
task = find_dynflow_task
|
37
38
|
if task.cancel
|
38
|
-
|
39
|
+
render json: { statusText: 'OK' }
|
39
40
|
else
|
40
|
-
|
41
|
+
render json: {}, status: :bad_request
|
41
42
|
end
|
42
|
-
redirect_back(:fallback_location => foreman_tasks_task_path(task))
|
43
43
|
end
|
44
44
|
|
45
45
|
def abort
|
@@ -56,11 +56,10 @@ module ForemanTasks
|
|
56
56
|
task = find_dynflow_task
|
57
57
|
if task.resumable?
|
58
58
|
ForemanTasks.dynflow.world.execute(task.execution_plan.id)
|
59
|
-
|
59
|
+
render json: { statusText: 'OK' }
|
60
60
|
else
|
61
|
-
|
61
|
+
render json: {}, status: :bad_request
|
62
62
|
end
|
63
|
-
redirect_back(:fallback_location => foreman_tasks_task_path(task))
|
64
63
|
end
|
65
64
|
|
66
65
|
def unlock
|
@@ -95,16 +94,8 @@ module ForemanTasks
|
|
95
94
|
private
|
96
95
|
|
97
96
|
def respond_with_tasks(scope)
|
98
|
-
|
99
|
-
|
100
|
-
@tasks = filter(scope)
|
101
|
-
render :index, layout: !request.xhr?
|
102
|
-
end
|
103
|
-
format.csv do
|
104
|
-
@tasks = filter(scope, paginate: false)
|
105
|
-
csv_response(@tasks, [:id, :action, :state, :result, 'started_at.in_time_zone', 'ended_at.in_time_zone', :username], ['Id', 'Action', 'State', 'Result', 'Started At', 'Ended At', 'User'])
|
106
|
-
end
|
107
|
-
end
|
97
|
+
@tasks = filter(scope, paginate: false)
|
98
|
+
csv_response(@tasks, [:id, :action, :state, :result, 'started_at.in_time_zone', 'ended_at.in_time_zone', :username], ['Id', 'Action', 'State', 'Result', 'Started At', 'Ended At', 'User'])
|
108
99
|
end
|
109
100
|
|
110
101
|
def restrict_dangerous_actions
|
@@ -49,10 +49,12 @@ module ForemanTasks
|
|
49
49
|
scoped_search :on => :user_id,
|
50
50
|
:complete_value => true,
|
51
51
|
:rename => 'user.id',
|
52
|
-
:validator => ->(value) { ScopedSearch::Validators::INTEGER.call(value)
|
53
|
-
:
|
54
|
-
|
55
|
-
|
52
|
+
:validator => ->(value) { ScopedSearch::Validators::INTEGER.call(value) },
|
53
|
+
:value_translation => ->(value) { value == 'current_user' ? User.current.id : value },
|
54
|
+
:special_values => %w[current_user],
|
55
|
+
:aliases => ['owner.id'], :only_explicit => true
|
56
|
+
scoped_search :relation => :user, :on => :login, :complete_value => true, :aliases => ['owner.login', 'user'], :only_explicit => true
|
57
|
+
scoped_search :relation => :user, :on => :firstname, :rename => 'user.firstname', :complete_value => true, :aliases => ['owner.firstname'], :only_explicit => true
|
56
58
|
scoped_search :relation => :task_groups, :on => :id, :complete_value => true, :rename => 'task_group.id', :validator => ScopedSearch::Validators::INTEGER
|
57
59
|
|
58
60
|
scope :active, -> { where('foreman_tasks_tasks.state != ?', :stopped) }
|
@@ -22,31 +22,6 @@ module ForemanTasks
|
|
22
22
|
sql = "foreman_tasks_locks_taxonomy#{uniq_suffix}.resource_id #{operator} ? OR foreman_tasks_locks_taxonomy#{uniq_suffix}.resource_id IS NULL"
|
23
23
|
{ :conditions => sanitize_sql_for_conditions([sql, value]), :joins => joins }
|
24
24
|
end
|
25
|
-
|
26
|
-
def search_by_owner(key, operator, value)
|
27
|
-
return { :conditions => '0 = 1' } if value == 'current_user' && User.current.nil?
|
28
|
-
|
29
|
-
key = 'owners.login' if key == 'user'
|
30
|
-
# using uniq suffix to avoid colisions when searching by two different owners via ScopedSearch
|
31
|
-
uniq_suffix = SecureRandom.hex(3)
|
32
|
-
key_name = connection.quote_column_name(key.sub(/^.*\./, ''))
|
33
|
-
value.sub!('*', '%%')
|
34
|
-
condition = if key.blank?
|
35
|
-
sanitize_sql_for_conditions(["users_#{uniq_suffix}.login #{operator} ? or users_#{uniq_suffix}.firstname #{operator} ? ", value, value])
|
36
|
-
elsif key =~ /\.id\Z/
|
37
|
-
value = User.current.id if value == 'current_user'
|
38
|
-
sanitize_sql_for_conditions(["foreman_tasks_tasks.user_id #{operator} ?", value])
|
39
|
-
else
|
40
|
-
placeholder, value = operator == 'IN' ? ['(?)', value.split(',').map(&:strip)] : ['?', value]
|
41
|
-
sanitize_sql_for_conditions(["users_#{uniq_suffix}.#{key_name} #{operator} #{placeholder}", value])
|
42
|
-
end
|
43
|
-
{ :conditions => condition, :joins => joins_for_user_search(key, uniq_suffix) }
|
44
|
-
end
|
45
|
-
|
46
|
-
def joins_for_user_search(key, uniq_suffix)
|
47
|
-
return '' if key =~ /\.id\Z/
|
48
|
-
"INNER JOIN users AS users_#{uniq_suffix} ON users_#{uniq_suffix}.id = foreman_tasks_tasks.user_id"
|
49
|
-
end
|
50
25
|
end
|
51
26
|
end
|
52
27
|
end
|
@@ -1,28 +1,24 @@
|
|
1
1
|
class Setting::ForemanTasks < Setting
|
2
|
-
def self.
|
3
|
-
|
4
|
-
|
2
|
+
def self.default_settings
|
3
|
+
[
|
4
|
+
set('foreman_tasks_sync_task_timeout', N_('Number of seconds to wait for synchronous task to finish.'), 120),
|
5
|
+
set('dynflow_allow_dangerous_actions', N_('Allow unlocking actions which can have dangerous consequences.'), false),
|
6
|
+
set('dynflow_enable_console', N_('Enable the dynflow console (/foreman_tasks/dynflow) for debugging'), true),
|
7
|
+
set('dynflow_console_require_auth', N_('Require user to be authenticated as user with admin rights when accessing dynflow console'), true),
|
8
|
+
set('foreman_tasks_proxy_action_retry_count', N_('Number of attempts to start a task on the smart proxy before failing'), 4),
|
9
|
+
set('foreman_tasks_proxy_action_retry_interval', N_('Time in seconds between retries'), 15),
|
10
|
+
set('foreman_tasks_proxy_batch_trigger', N_('Allow triggering tasks on the smart proxy in batches'), true),
|
11
|
+
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),
|
12
|
+
set('foreman_tasks_troubleshooting_url',
|
13
|
+
N_('Url pointing to the task troubleshooting documentation. '\
|
14
|
+
'It should contain %{label} placeholder, that will be replaced with normalized task label '\
|
15
|
+
'(restricted to only alphanumeric characters)). %{version} placeholder is also available.'),
|
16
|
+
nil)
|
17
|
+
]
|
18
|
+
end
|
5
19
|
|
20
|
+
def self.load_defaults
|
6
21
|
Setting::BLANK_ATTRS.push('foreman_tasks_troubleshooting_url')
|
7
|
-
|
8
|
-
transaction do
|
9
|
-
[
|
10
|
-
set('foreman_tasks_sync_task_timeout', N_('Number of seconds to wait for synchronous task to finish.'), 120),
|
11
|
-
set('dynflow_allow_dangerous_actions', N_('Allow unlocking actions which can have dangerous consequences.'), false),
|
12
|
-
set('dynflow_enable_console', N_('Enable the dynflow console (/foreman_tasks/dynflow) for debugging'), true),
|
13
|
-
set('dynflow_console_require_auth', N_('Require user to be authenticated as user with admin rights when accessing dynflow console'), true),
|
14
|
-
set('foreman_tasks_proxy_action_retry_count', N_('Number of attempts to start a task on the smart proxy before failing'), 4),
|
15
|
-
set('foreman_tasks_proxy_action_retry_interval', N_('Time in seconds between retries'), 15),
|
16
|
-
set('foreman_tasks_proxy_batch_trigger', N_('Allow triggering tasks on the smart proxy in batches'), true),
|
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)
|
23
|
-
].each { |s| create! s.update(:category => 'Setting::ForemanTasks') }
|
24
|
-
end
|
25
|
-
|
26
|
-
true
|
22
|
+
super
|
27
23
|
end
|
28
24
|
end
|
@@ -3,3 +3,4 @@ object @task if @task
|
|
3
3
|
attributes :id, :label, :pending, :action
|
4
4
|
attributes :username, :started_at, :ended_at, :state, :result, :progress
|
5
5
|
attributes :input, :output, :humanized, :cli_example
|
6
|
+
node(:available_actions) { |t| {cancellable: t.execution_plan&.cancellable?, resumable: t.resumable? }}
|
@@ -1,6 +1,9 @@
|
|
1
1
|
<% content_for(:javascripts) do %>
|
2
2
|
<%= webpacked_plugins_js_for :'foreman-tasks' %>
|
3
3
|
<% end %>
|
4
|
+
<% content_for(:stylesheets) do %>
|
5
|
+
<%= webpacked_plugins_css_for :'foreman-tasks' %>
|
6
|
+
<% end %>
|
4
7
|
|
5
8
|
<% content_for(:content) do %>
|
6
9
|
<%= notifications %>
|
@@ -9,4 +12,4 @@
|
|
9
12
|
<div id="foremanTasksReactRoot"></div>
|
10
13
|
<% end %>
|
11
14
|
<%= render file: "layouts/base" %>
|
12
|
-
<%= mount_react_component('ForemanTasks', '#foremanTasksReactRoot'
|
15
|
+
<%= mount_react_component('ForemanTasks', '#foremanTasksReactRoot') %>
|
data/config/routes.rb
CHANGED
@@ -8,13 +8,12 @@ Foreman::Application.routes.draw do
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
resources :tasks, :only => [:
|
11
|
+
resources :tasks, :only => [:show] do
|
12
12
|
collection do
|
13
13
|
get 'auto_complete_search'
|
14
14
|
get '/summary/:recent_timeframe', action: 'summary'
|
15
15
|
end
|
16
16
|
member do
|
17
|
-
get :sub_tasks
|
18
17
|
post :abort
|
19
18
|
post :cancel
|
20
19
|
post :resume
|
@@ -23,8 +22,15 @@ Foreman::Application.routes.draw do
|
|
23
22
|
post :cancel_step
|
24
23
|
end
|
25
24
|
end
|
25
|
+
resources :tasks, :only => [:show], constraints: ->(req) { req.format == :csv } do
|
26
|
+
member do
|
27
|
+
get :sub_tasks
|
28
|
+
end
|
29
|
+
end
|
30
|
+
resources :tasks, :only => [:index], constraints: ->(req) { req.format == :csv }
|
26
31
|
|
27
|
-
match '/
|
32
|
+
match '/tasks' => 'react#index', :via => [:get]
|
33
|
+
match '/tasks/:id/sub_tasks' => 'react#index', :via => [:get]
|
28
34
|
match '/ex_tasks/:id' => 'react#index', :via => [:get]
|
29
35
|
|
30
36
|
namespace :api do
|
@@ -37,6 +43,7 @@ Foreman::Application.routes.draw do
|
|
37
43
|
resources :tasks, :only => [:show, :index] do
|
38
44
|
member do
|
39
45
|
get :details
|
46
|
+
get :sub_tasks
|
40
47
|
end
|
41
48
|
collection do
|
42
49
|
post :bulk_search
|
@@ -50,6 +57,13 @@ Foreman::Application.routes.draw do
|
|
50
57
|
if ForemanTasks.dynflow.required?
|
51
58
|
require 'dynflow/web'
|
52
59
|
mount ForemanTasks.dynflow.web_console => '/dynflow'
|
60
|
+
if defined? ::Sidekiq
|
61
|
+
require 'sidekiq/web'
|
62
|
+
redis_url = SETTINGS.dig(:dynflow, :redis_url)
|
63
|
+
Sidekiq.redis = { url: redis_url }
|
64
|
+
Sidekiq::Web.set :sessions, false
|
65
|
+
mount Sidekiq::Web => '/sidekiq', :constraints => ForemanTasks::Dynflow::SidekiqConsoleConstraint.new
|
66
|
+
end
|
53
67
|
end
|
54
68
|
end
|
55
69
|
end
|
@@ -5,8 +5,10 @@ class AddUserId < ActiveRecord::Migration[5.0]
|
|
5
5
|
return if User.unscoped.find_by(:login => User::ANONYMOUS_ADMIN).nil?
|
6
6
|
User.as_anonymous_admin do
|
7
7
|
user_locks.select(:resource_id).distinct.pluck(:resource_id).each do |owner_id|
|
8
|
-
|
9
|
-
|
8
|
+
if User.exists?(:id => owner_id)
|
9
|
+
tasks = ForemanTasks::Task.joins(:locks).where(:locks => user_locks.where(:resource_id => owner_id))
|
10
|
+
tasks.update_all(:user_id => owner_id)
|
11
|
+
end
|
10
12
|
user_locks.where(:resource_id => owner_id).delete_all
|
11
13
|
end
|
12
14
|
end
|
@@ -9,7 +9,7 @@ module ForemanTasks
|
|
9
9
|
::Dynflow::Web.setup do
|
10
10
|
before do
|
11
11
|
if !Setting[:dynflow_enable_console] ||
|
12
|
-
(Setting[:dynflow_console_require_auth] && !ConsoleAuthorizer.
|
12
|
+
(Setting[:dynflow_console_require_auth] && !ConsoleAuthorizer.from_env(env).allow?)
|
13
13
|
halt 403, 'Access forbidden'
|
14
14
|
end
|
15
15
|
end
|
@@ -1,7 +1,18 @@
|
|
1
1
|
module ForemanTasks
|
2
|
+
class Dynflow::SidekiqConsoleConstraint
|
3
|
+
def matches?(request)
|
4
|
+
Setting[:dynflow_enable_console] &&
|
5
|
+
(!Setting[:dynflow_console_require_auth] || Dynflow::ConsoleAuthorizer.new(request).allow?)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
2
9
|
class Dynflow::ConsoleAuthorizer
|
3
|
-
def
|
4
|
-
|
10
|
+
def self.from_env(env)
|
11
|
+
new(Rack::Request.new(env))
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(request)
|
15
|
+
@rack_request = request
|
5
16
|
@user_id = @rack_request.session[:user]
|
6
17
|
@expires_at = @rack_request.session[:expires_at]
|
7
18
|
@user = User.unscoped.where(:id => @user_id).first unless session_expired?
|
data/lib/foreman_tasks/engine.rb
CHANGED
@@ -58,7 +58,7 @@ module ForemanTasks
|
|
58
58
|
security_block :foreman_tasks do |_map|
|
59
59
|
permission :view_foreman_tasks, { :'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :summary, :show],
|
60
60
|
:'foreman_tasks/react' => [:index],
|
61
|
-
:'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary, :details] }, :resource_type => ForemanTasks::Task.name
|
61
|
+
:'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary, :details, :sub_tasks] }, :resource_type => ForemanTasks::Task.name
|
62
62
|
permission :edit_foreman_tasks, { :'foreman_tasks/tasks' => [:resume, :unlock, :force_unlock, :cancel_step, :cancel, :abort],
|
63
63
|
:'foreman_tasks/api/tasks' => [:bulk_resume] }, :resource_type => ForemanTasks::Task.name
|
64
64
|
|
data/package.json
CHANGED
@@ -3,10 +3,6 @@ require 'foreman_tasks_test_helper'
|
|
3
3
|
module ForemanTasks
|
4
4
|
class TasksControllerTest < ActionController::TestCase
|
5
5
|
describe ForemanTasks::TasksController do
|
6
|
-
basic_index_test('tasks')
|
7
|
-
basic_pagination_per_page_test
|
8
|
-
basic_pagination_rendered_test
|
9
|
-
|
10
6
|
# rubocop:disable Naming/AccessorMethodName
|
11
7
|
def get_factory_name
|
12
8
|
:dynflow_task
|
@@ -41,6 +37,41 @@ module ForemanTasks
|
|
41
37
|
response = JSON.parse(@response.body)
|
42
38
|
assert_equal 0, response['stopped']['total']
|
43
39
|
end
|
40
|
+
|
41
|
+
describe 'taxonomy scoping' do
|
42
|
+
before do
|
43
|
+
@organizations = [0, 0].map { FactoryBot.create(:organization) }
|
44
|
+
@locations = [0, 0].map { FactoryBot.create(:location) }
|
45
|
+
@tasks = [0, 0].map { FactoryBot.create(:some_task) }
|
46
|
+
@tasks.zip(@organizations, @locations).each do |task, org, loc|
|
47
|
+
Lock.link!(org, task.id)
|
48
|
+
Lock.link!(loc, task.id)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'does not limit if unset' do
|
53
|
+
get(:summary, params: { recent_timeframe: 24 }, session: set_session_user)
|
54
|
+
assert_response :success
|
55
|
+
response = JSON.parse(@response.body)
|
56
|
+
assert_equal 2, response['stopped']['total']
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'finds only tasks with matching taxonomies' do
|
60
|
+
get(:summary, params: { recent_timeframe: 24 },
|
61
|
+
session: set_session_user.merge(:organization_id => @organizations.first, :location_id => @locations.first))
|
62
|
+
assert_response :success
|
63
|
+
response = JSON.parse(@response.body)
|
64
|
+
assert_equal 1, response['stopped']['total']
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'find no tasks when taxonomy combination contains no tasks' do
|
68
|
+
get(:summary, params: { recent_timeframe: 24 },
|
69
|
+
session: set_session_user.merge(:organization_id => @organizations.first, :location_id => @locations.last))
|
70
|
+
assert_response :success
|
71
|
+
response = JSON.parse(@response.body)
|
72
|
+
assert_equal 0, response['stopped']['total']
|
73
|
+
end
|
74
|
+
end
|
44
75
|
end
|
45
76
|
|
46
77
|
it 'supports csv export' do
|