foreman-tasks 4.1.3 → 5.1.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/.github/workflows/ruby_tests.yml +0 -1
- data/app/controllers/foreman_tasks/api/tasks_controller.rb +3 -3
- data/app/controllers/foreman_tasks/tasks_controller.rb +2 -2
- data/app/graphql/types/recurring_logic.rb +19 -0
- data/app/graphql/types/task.rb +25 -0
- data/app/graphql/types/triggering.rb +16 -0
- data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +4 -1
- data/app/lib/actions/helpers/with_continuous_output.rb +1 -1
- data/app/lib/actions/trigger_proxy_batch.rb +79 -0
- data/app/models/foreman_tasks/recurring_logic.rb +10 -0
- data/app/models/foreman_tasks/task.rb +29 -0
- data/app/models/foreman_tasks/triggering.rb +14 -4
- data/app/views/foreman_tasks/api/tasks/show.json.rabl +1 -1
- data/app/views/foreman_tasks/layouts/react.html.erb +0 -1
- data/app/views/foreman_tasks/recurring_logics/index.html.erb +4 -2
- data/app/views/foreman_tasks/task_groups/recurring_logic_task_groups/_recurring_logic_task_group.html.erb +4 -0
- data/db/migrate/20210720115251_add_purpose_to_recurring_logic.rb +6 -0
- data/extra/foreman-tasks-cleanup.sh +127 -0
- data/extra/foreman-tasks-export.sh +117 -0
- data/foreman-tasks.gemspec +0 -1
- data/lib/foreman_tasks/continuous_output.rb +50 -0
- data/lib/foreman_tasks/engine.rb +6 -15
- data/lib/foreman_tasks/tasks/export_tasks.rake +6 -5
- data/lib/foreman_tasks/version.rb +1 -1
- data/lib/foreman_tasks.rb +2 -5
- data/test/controllers/api/tasks_controller_test.rb +29 -0
- data/test/controllers/tasks_controller_test.rb +19 -0
- data/test/graphql/queries/recurring_logic_test.rb +28 -0
- data/test/graphql/queries/recurring_logics_query_test.rb +30 -0
- data/test/graphql/queries/task_query_test.rb +33 -0
- data/test/graphql/queries/tasks_query_test.rb +31 -0
- data/test/unit/actions/trigger_proxy_batch_test.rb +59 -0
- data/test/unit/task_test.rb +39 -8
- data/test/unit/triggering_test.rb +22 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTable.js +8 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +21 -10
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableReducer.js +17 -16
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +3 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableReducer.test.js +3 -1
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +10 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableReducer.test.js.snap +5 -0
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/selectionHeaderCellFormatter.test.js.snap +1 -0
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/selectionHeaderCellFormatter.test.js +1 -1
- data/webpack/ForemanTasks/Components/TasksTable/formatters/selectionHeaderCellFormatter.js +1 -0
- data/webpack/ForemanTasks/Components/TasksTable/index.js +2 -0
- metadata +20 -26
- data/test/core/unit/dispatcher_test.rb +0 -43
- data/test/core/unit/runner_test.rb +0 -116
- data/test/core/unit/task_launcher_test.rb +0 -56
- data/test/foreman_tasks_core_test_helper.rb +0 -4
- data/test/unit/otp_manager_test.rb +0 -77
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f89f36fdd06c5a49c3c11fa09f203cb238402478c3aaa19f9e82c1227965186
|
|
4
|
+
data.tar.gz: dbc346e70a1a549eb9ba5b9a7096cdba03fb1f8687aebab4a5f5f575cd33015c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 17dcdf97e44f9040693011a96060bcabca50b8e3e2af025354d925a63f8e0477ee3547e0af4c38ce052581f1066880337e7dbc020440bec4942aff997e56b32d
|
|
7
|
+
data.tar.gz: e84cc64acfcd8f54c7b029f1af3b0126bdb4ac90539c46dc072c33520d9e5aa8d0e020ea282a1d2bafc0766ac5f4ce4ccf1ac9cd962b52db13ced8c655475ec3
|
|
@@ -260,7 +260,7 @@ module ForemanTasks
|
|
|
260
260
|
raise BadRequest,
|
|
261
261
|
_('Resource search_params requires resource_type and resource_id to be specified')
|
|
262
262
|
end
|
|
263
|
-
scope.joins(:
|
|
263
|
+
scope.joins(:links).where(foreman_tasks_links:
|
|
264
264
|
{ resource_type: search_params[:resource_type],
|
|
265
265
|
resource_id: search_params[:resource_id] })
|
|
266
266
|
when 'task'
|
|
@@ -320,7 +320,7 @@ module ForemanTasks
|
|
|
320
320
|
end
|
|
321
321
|
|
|
322
322
|
def find_task
|
|
323
|
-
@task = resource_scope.find(params[:id])
|
|
323
|
+
@task = resource_scope.with_duration.find(params[:id])
|
|
324
324
|
end
|
|
325
325
|
|
|
326
326
|
def resource_scope(_options = {})
|
|
@@ -330,7 +330,7 @@ module ForemanTasks
|
|
|
330
330
|
end
|
|
331
331
|
|
|
332
332
|
def resource_scope_for_index(*args)
|
|
333
|
-
super.
|
|
333
|
+
super.with_duration.distinct
|
|
334
334
|
end
|
|
335
335
|
|
|
336
336
|
def controller_permission
|
|
@@ -99,8 +99,8 @@ module ForemanTasks
|
|
|
99
99
|
private
|
|
100
100
|
|
|
101
101
|
def respond_with_tasks(scope)
|
|
102
|
-
@tasks = filter(scope, paginate: false)
|
|
103
|
-
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'])
|
|
102
|
+
@tasks = filter(scope, paginate: false).with_duration
|
|
103
|
+
csv_response(@tasks, [:id, :action, :state, :result, 'started_at.in_time_zone', 'ended_at.in_time_zone', :duration, :username], ['Id', 'Action', 'State', 'Result', 'Started At', 'Ended At', 'Duration', 'User'])
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
def controller_permission
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Types
|
|
2
|
+
class RecurringLogic < Types::BaseObject
|
|
3
|
+
description 'A Recurring Logic'
|
|
4
|
+
model_class ::ForemanTasks::RecurringLogic
|
|
5
|
+
|
|
6
|
+
global_id_field :id
|
|
7
|
+
field :cron_line, String
|
|
8
|
+
field :end_time, GraphQL::Types::ISO8601DateTime
|
|
9
|
+
field :max_iteration, Integer
|
|
10
|
+
field :iteration, Integer
|
|
11
|
+
field :state, String
|
|
12
|
+
field :purpose, String
|
|
13
|
+
belongs_to :triggering, Types::Triggering
|
|
14
|
+
|
|
15
|
+
def self.graphql_definition
|
|
16
|
+
super.tap { |type| type.instance_variable_set(:@name, 'ForemanTasks::RecurringLogic') }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Types
|
|
2
|
+
class Task < Types::BaseObject
|
|
3
|
+
description 'A Task'
|
|
4
|
+
model_class ::ForemanTasks::Task
|
|
5
|
+
|
|
6
|
+
global_id_field :id
|
|
7
|
+
field :type, String
|
|
8
|
+
field :label, String
|
|
9
|
+
field :started_at, GraphQL::Types::ISO8601DateTime
|
|
10
|
+
field :ended_at, GraphQL::Types::ISO8601DateTime
|
|
11
|
+
field :state, String
|
|
12
|
+
field :result, String
|
|
13
|
+
field :external_id, String
|
|
14
|
+
field :parent_task_id, String
|
|
15
|
+
field :start_at, GraphQL::Types::ISO8601DateTime
|
|
16
|
+
field :start_before, GraphQL::Types::ISO8601DateTime
|
|
17
|
+
field :action, String
|
|
18
|
+
field :user_id, Integer
|
|
19
|
+
field :state_updated_at, GraphQL::Types::ISO8601DateTime
|
|
20
|
+
|
|
21
|
+
def self.graphql_definition
|
|
22
|
+
super.tap { |type| type.instance_variable_set(:@name, 'ForemanTasks::Task') }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Types
|
|
2
|
+
class Triggering < Types::BaseObject
|
|
3
|
+
description 'A Task Triggering'
|
|
4
|
+
model_class ::ForemanTasks::Triggering
|
|
5
|
+
|
|
6
|
+
global_id_field :id
|
|
7
|
+
field :mode, String
|
|
8
|
+
field :start_at, GraphQL::Types::ISO8601DateTime
|
|
9
|
+
field :start_before, GraphQL::Types::ISO8601DateTime
|
|
10
|
+
field :recurring_logic, Types::RecurringLogic
|
|
11
|
+
|
|
12
|
+
def self.graphql_definition
|
|
13
|
+
super.tap { |type| type.instance_variable_set(:@name, 'ForemanTasks::Triggering') }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -141,7 +141,10 @@ module ForemanTasks
|
|
|
141
141
|
weekly_fieldset(f, triggering),
|
|
142
142
|
time_picker_fieldset(f, triggering),
|
|
143
143
|
]
|
|
144
|
-
|
|
144
|
+
tags << text_f(f, :start_at_raw, :label => _('Start at'), :placeholder => 'YYYY-mm-dd HH:MM')
|
|
145
|
+
tags << text_f(f, :purpose,
|
|
146
|
+
:label => _('Purpose'),
|
|
147
|
+
:label_help => N_('A special label for tracking a recurring job. There can be only one active job with a given purpose at a time.'))
|
|
145
148
|
content_tag(:fieldset, nil, :id => 'trigger_mode_recurring', :class => "trigger_mode_form #{'hidden' unless triggering.recurring?}") do
|
|
146
149
|
tags.join.html_safe
|
|
147
150
|
end
|
|
@@ -8,7 +8,7 @@ module Actions
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def continuous_output
|
|
11
|
-
continuous_output = ::
|
|
11
|
+
continuous_output = ::ForemanTasks::ContinuousOutput.new
|
|
12
12
|
continuous_output_providers.each do |continous_output_provider|
|
|
13
13
|
continous_output_provider.fill_continuous_output(continuous_output)
|
|
14
14
|
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Actions
|
|
2
|
+
# This action plans proxy tasks in batches.
|
|
3
|
+
# It needs to be manually notified about the next batch being available by sending a TriggerNextBatch event.
|
|
4
|
+
#
|
|
5
|
+
# The ProxyAction needs to be planned with `:use_batch_triggering => true` to activate the feature
|
|
6
|
+
class TriggerProxyBatch < Base
|
|
7
|
+
TriggerNextBatch = Algebrick.type do
|
|
8
|
+
fields! batches: Integer
|
|
9
|
+
end
|
|
10
|
+
TriggerLastBatch = Algebrick.atom
|
|
11
|
+
|
|
12
|
+
def run(event = nil)
|
|
13
|
+
case event
|
|
14
|
+
when nil
|
|
15
|
+
if output[:planned_count]
|
|
16
|
+
check_finish
|
|
17
|
+
else
|
|
18
|
+
init_counts and suspend
|
|
19
|
+
end
|
|
20
|
+
when TriggerNextBatch
|
|
21
|
+
trigger_remote_tasks_batches(event.batches) and suspend
|
|
22
|
+
when TriggerLastBatch
|
|
23
|
+
trigger_remote_tasks_batch and on_finish
|
|
24
|
+
when ::Dynflow::Action::Skip
|
|
25
|
+
# do nothing
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def trigger_remote_tasks_batches(amount = 1)
|
|
30
|
+
amount.times { trigger_remote_tasks_batch }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def trigger_remote_tasks_batch
|
|
34
|
+
# Find the tasks in batches, order them by proxy_url so we get all tasks
|
|
35
|
+
# to a certain proxy "close to each other"
|
|
36
|
+
batch = remote_tasks.pending.order(:proxy_url, :id).first(batch_size)
|
|
37
|
+
# Group the tasks by operation, in theory there should be only one operation
|
|
38
|
+
batch.group_by(&:operation).each do |operation, group|
|
|
39
|
+
ForemanTasks::RemoteTask.batch_trigger(operation, group)
|
|
40
|
+
end
|
|
41
|
+
output[:planned_count] += batch.size
|
|
42
|
+
rescue => e
|
|
43
|
+
action_logger.warn "Could not trigger task on the smart proxy: #{e.message}"
|
|
44
|
+
batch.each { |remote_task| remote_task.update_from_batch_trigger({}) }
|
|
45
|
+
output[:failed_count] += batch.size
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def init_counts
|
|
49
|
+
output[:planned_count] = 0
|
|
50
|
+
output[:failed_count] = 0
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def check_finish
|
|
54
|
+
if output[:planned_count] + output[:failed_count] + batch_size >= input[:total_count]
|
|
55
|
+
trigger_remote_tasks_batch and on_finish
|
|
56
|
+
else
|
|
57
|
+
suspend
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def done?
|
|
62
|
+
output[:planned_count] + output[:failed_count] >= input[:total_count]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def remote_tasks
|
|
66
|
+
task.remote_sub_tasks
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def on_finish
|
|
70
|
+
# nothing for now
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def batch_size
|
|
76
|
+
input[:batch_size] || Setting['foreman_tasks_proxy_batch_size']
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -4,6 +4,8 @@ module ForemanTasks
|
|
|
4
4
|
class RecurringLogic < ApplicationRecord
|
|
5
5
|
include Authorizable
|
|
6
6
|
|
|
7
|
+
graphql_type '::Types::RecurringLogic'
|
|
8
|
+
|
|
7
9
|
belongs_to :task_group
|
|
8
10
|
belongs_to :triggering
|
|
9
11
|
|
|
@@ -15,6 +17,9 @@ module ForemanTasks
|
|
|
15
17
|
scoped_search :on => :iteration, :complete_value => false
|
|
16
18
|
scoped_search :on => :cron_line, :complete_value => true
|
|
17
19
|
scoped_search :on => :state, :complete_value => true
|
|
20
|
+
scoped_search :on => :purpose, :complete_value => true
|
|
21
|
+
|
|
22
|
+
validate :valid_purpose
|
|
18
23
|
|
|
19
24
|
before_create do
|
|
20
25
|
task_group.save
|
|
@@ -167,6 +172,7 @@ module ForemanTasks
|
|
|
167
172
|
::ForemanTasks::RecurringLogic.new_from_cronline(cronline).tap do |manager|
|
|
168
173
|
manager.end_time = triggering.end_time if triggering.end_time_limited.present?
|
|
169
174
|
manager.max_iteration = triggering.max_iteration if triggering.max_iteration.present?
|
|
175
|
+
manager.purpose = triggering.purpose if triggering.purpose.present?
|
|
170
176
|
manager.triggering = triggering
|
|
171
177
|
end
|
|
172
178
|
end
|
|
@@ -187,5 +193,9 @@ module ForemanTasks
|
|
|
187
193
|
end
|
|
188
194
|
hash.select { |key, _| allowed_keys.include? key }
|
|
189
195
|
end
|
|
196
|
+
|
|
197
|
+
def valid_purpose?
|
|
198
|
+
!(purpose.present? && self.class.where(:purpose => purpose, :state => %w[active disabled]).any?)
|
|
199
|
+
end
|
|
190
200
|
end
|
|
191
201
|
end
|
|
@@ -5,6 +5,8 @@ module ForemanTasks
|
|
|
5
5
|
include Authorizable
|
|
6
6
|
extend Search
|
|
7
7
|
|
|
8
|
+
graphql_type '::Types::Task'
|
|
9
|
+
|
|
8
10
|
def check_permissions_after_save
|
|
9
11
|
# there's no create_tasks permission, tasks are created as a result of internal actions, in such case we
|
|
10
12
|
# don't do authorization, that should have been performed on wrapping action level
|
|
@@ -76,6 +78,7 @@ module ForemanTasks
|
|
|
76
78
|
:"foreman_tasks_links.resource_type" => resource.class.name)
|
|
77
79
|
end)
|
|
78
80
|
scope :for_action_types, (->(action_types) { where('foreman_tasks_tasks.label IN (?)', Array(action_types)) })
|
|
81
|
+
scope :with_duration, -> { select("foreman_tasks_tasks.*, coalesce(ended_at, current_timestamp) - coalesce(coalesce(started_at, ended_at), current_timestamp) as duration") }
|
|
79
82
|
|
|
80
83
|
apipie :class, "A class representing #{model_name.human} object" do
|
|
81
84
|
name 'Task'
|
|
@@ -255,6 +258,32 @@ module ForemanTasks
|
|
|
255
258
|
main_action.continuous_output.raw_outputs
|
|
256
259
|
end
|
|
257
260
|
|
|
261
|
+
def self.latest_tasks_by_resource_ids(label, resource_type, resource_ids)
|
|
262
|
+
tasks = arel_table
|
|
263
|
+
links = ForemanTasks::Link.arel_table
|
|
264
|
+
started_at = tasks[:started_at]
|
|
265
|
+
resource_id = links[:resource_id]
|
|
266
|
+
|
|
267
|
+
base_combined_table = tasks
|
|
268
|
+
.join(links).on(tasks[:id].eq(links[:task_id]))
|
|
269
|
+
.where(tasks[:label].eq(label)
|
|
270
|
+
.and(links[:resource_type].eq(resource_type))
|
|
271
|
+
.and(links[:resource_id].in(resource_ids)))
|
|
272
|
+
|
|
273
|
+
grouped = base_combined_table.project(
|
|
274
|
+
started_at.maximum.as('started_at_max'),
|
|
275
|
+
resource_id
|
|
276
|
+
).group(resource_id).order(resource_id).as('grouped')
|
|
277
|
+
|
|
278
|
+
max_per_resource_id = tasks
|
|
279
|
+
.join(links).on(tasks[:id].eq(links[:task_id]))
|
|
280
|
+
.join(grouped).on(grouped[:started_at_max].eq(started_at).and(grouped[:resource_id].eq(resource_id)))
|
|
281
|
+
.distinct
|
|
282
|
+
.project(tasks[Arel.star], grouped[:resource_id])
|
|
283
|
+
|
|
284
|
+
find_by_sql(max_per_resource_id.to_sql).index_by(&:resource_id)
|
|
285
|
+
end
|
|
286
|
+
|
|
258
287
|
protected
|
|
259
288
|
|
|
260
289
|
def generate_id
|
|
@@ -2,9 +2,11 @@ module ForemanTasks
|
|
|
2
2
|
class Triggering < ApplicationRecord
|
|
3
3
|
PARAMS = [:start_at_raw, :start_before_raw, :max_iteration, :input_type,
|
|
4
4
|
:cronline, :days, :days_of_week, :time, :end_time_limited,
|
|
5
|
-
:end_time].freeze
|
|
5
|
+
:end_time, :purpose].freeze
|
|
6
6
|
attr_accessor(*PARAMS)
|
|
7
7
|
|
|
8
|
+
graphql_type '::Types::Triggering'
|
|
9
|
+
|
|
8
10
|
before_save do
|
|
9
11
|
if future?
|
|
10
12
|
parse_start_at!
|
|
@@ -28,7 +30,7 @@ module ForemanTasks
|
|
|
28
30
|
validates :input_type, :if => :recurring?,
|
|
29
31
|
:inclusion => { :in => ALLOWED_INPUT_TYPES,
|
|
30
32
|
:message => _('%{value} is not allowed input type') }
|
|
31
|
-
validates :start_at_raw, format: { :with => TIME_REGEXP, :if =>
|
|
33
|
+
validates :start_at_raw, format: { :with => TIME_REGEXP, :if => ->(triggering) { triggering.future? || (triggering.recurring? && triggering.start_at_raw) },
|
|
32
34
|
:message => _('%{value} is wrong format') }
|
|
33
35
|
validates :start_before_raw, format: { :with => TIME_REGEXP, :if => :future?,
|
|
34
36
|
:message => _('%{value} is wrong format'), :allow_blank => true }
|
|
@@ -68,7 +70,7 @@ module ForemanTasks
|
|
|
68
70
|
delay_options,
|
|
69
71
|
*args
|
|
70
72
|
when :recurring
|
|
71
|
-
recurring_logic.
|
|
73
|
+
recurring_logic.start_after(action, delay_options[:start_at] || Time.zone.now, *args)
|
|
72
74
|
end
|
|
73
75
|
end
|
|
74
76
|
|
|
@@ -92,7 +94,13 @@ module ForemanTasks
|
|
|
92
94
|
end
|
|
93
95
|
|
|
94
96
|
def parse_start_at!
|
|
95
|
-
self.start_at ||= Time.zone.parse(start_at_raw)
|
|
97
|
+
self.start_at ||= Time.zone.parse(start_at_raw) if start_at_raw.present?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def parse_start_at
|
|
101
|
+
self.start_at ||= Time.zone.parse(start_at_raw) if start_at_raw.present?
|
|
102
|
+
rescue ArgumentError
|
|
103
|
+
errors.add(:start_at, _('is not a valid format'))
|
|
96
104
|
end
|
|
97
105
|
|
|
98
106
|
def parse_start_before!
|
|
@@ -102,7 +110,9 @@ module ForemanTasks
|
|
|
102
110
|
private
|
|
103
111
|
|
|
104
112
|
def can_start_recurring
|
|
113
|
+
parse_start_at
|
|
105
114
|
errors.add(:input_type, _('No task could be started')) unless recurring_logic.valid?
|
|
115
|
+
errors.add(:purpose, _('Active or disabled recurring logic with purpose %s already exists') % recurring_logic.purpose) unless recurring_logic.valid_purpose?
|
|
106
116
|
errors.add(:cronline, _('%s is not valid format of cron line') % cronline) unless recurring_logic.valid_cronline?
|
|
107
117
|
end
|
|
108
118
|
|
|
@@ -3,6 +3,6 @@ object @task if @task
|
|
|
3
3
|
extends 'api/v2/layouts/permissions'
|
|
4
4
|
|
|
5
5
|
attributes :id, :label, :pending, :action
|
|
6
|
-
attributes :username, :started_at, :ended_at, :state, :result, :progress
|
|
6
|
+
attributes :username, :started_at, :ended_at, :duration, :state, :result, :progress
|
|
7
7
|
attributes :input, :output, :humanized, :cli_example, :start_at
|
|
8
8
|
node(:available_actions) { |t| { cancellable: t.execution_plan&.cancellable?, resumable: t.resumable? } }
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
<% end %>
|
|
7
7
|
|
|
8
8
|
<% content_for(:content) do %>
|
|
9
|
-
<%= notifications %>
|
|
10
9
|
<div id="organization-id" data-id="<%= Organization.current.id if Organization.current %>" ></div>
|
|
11
10
|
<div id="user-id" data-id="<%= User.current.id if User.current %>" ></div>
|
|
12
11
|
<%= react_component('ForemanTasks') %>
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
<% if authorized_for(:permission => :edit_recurring_logics, :auth_object => @recurring_logics) %>
|
|
9
9
|
<% title_actions link_to(_('Clear Cancelled'),
|
|
10
10
|
clear_cancelled_foreman_tasks_recurring_logics_path,
|
|
11
|
-
class: ['btn', 'btn-sm', 'btn-danger'],
|
|
11
|
+
class: ['btn', 'btn-sm', 'btn-danger'],
|
|
12
12
|
:'data-toggle' => "modal",
|
|
13
|
-
:'data-target' => "#clear_modal")
|
|
13
|
+
:'data-target' => "#clear_modal")
|
|
14
14
|
%>
|
|
15
15
|
|
|
16
16
|
<div class="modal fade" id="clear_modal" tabindex="-1" role="dialog" aria-labelledby="Deploy" aria-hidden="true">
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
<th><%= N_("Iteration limit") %></th>
|
|
48
48
|
<th><%= N_("Repeat until") %></th>
|
|
49
49
|
<th><%= N_("State") %></th>
|
|
50
|
+
<th><%= N_("Purpose") %></th>
|
|
50
51
|
<th/>
|
|
51
52
|
</thead>
|
|
52
53
|
<% @recurring_logics.each do |recurring_logic| %>
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
<td><%= format_recurring_logic_limit recurring_logic.max_iteration %></td>
|
|
62
63
|
<td><%= format_recurring_logic_limit recurring_logic.end_time.try(:in_time_zone) %></td>
|
|
63
64
|
<td><%= recurring_logic_state(recurring_logic) %></td>
|
|
65
|
+
<td><%= recurring_logic.purpose %></td>
|
|
64
66
|
<td><%= recurring_logic_action_buttons(recurring_logic) %></td>
|
|
65
67
|
</tr>
|
|
66
68
|
<% end %>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
PROGNAME="$0"
|
|
4
|
+
EXECUTE=0
|
|
5
|
+
RAKE_COMMAND="${RAKE_COMMAND:-"foreman-rake"}"
|
|
6
|
+
|
|
7
|
+
function die() {
|
|
8
|
+
local code="$1"
|
|
9
|
+
local message="$2"
|
|
10
|
+
echo "$message" >&2
|
|
11
|
+
exit $code
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function build_rake() {
|
|
15
|
+
echo -n "$RAKE_COMMAND "
|
|
16
|
+
echo -n 'foreman_tasks:cleanup '
|
|
17
|
+
for env in AFTER TASK_BACKUP BATCH_SIZE NOOP TASK_SEARCH STATES VERBOSE; do
|
|
18
|
+
local value="${!env}"
|
|
19
|
+
[ -n "${value}" ] && echo -n "${env}=$(printf '%q' "$value") "
|
|
20
|
+
done
|
|
21
|
+
echo
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function usage() {
|
|
25
|
+
cat << EOF
|
|
26
|
+
Usage: $PROGNAME [script_options...] [options...]
|
|
27
|
+
|
|
28
|
+
An interface script for setting environment variables properly
|
|
29
|
+
for foreman-tasks:cleanup rake task. By default only prints
|
|
30
|
+
the command to run, with -E|--execute flag performs the cleanup.
|
|
31
|
+
|
|
32
|
+
Environment variables:
|
|
33
|
+
RAKE_COMMAND: can be used to redefine path to rake, by default foreman-rake
|
|
34
|
+
|
|
35
|
+
Script options:
|
|
36
|
+
EOF
|
|
37
|
+
cat <<EOF | column -s\& -t
|
|
38
|
+
-E|--execute & execute the created rake command
|
|
39
|
+
-h|--help & show this output
|
|
40
|
+
EOF
|
|
41
|
+
|
|
42
|
+
echo
|
|
43
|
+
echo Cleanup options:
|
|
44
|
+
cat <<EOF | column -s\& -t
|
|
45
|
+
-B|--batch-size BATCH_SIZE & process tasks in batches of BATCH_SIZE, 1000 by default
|
|
46
|
+
-S|--states STATES & operate on tasks in STATES, comma separated list of states, set to all to operate on tasks in any state
|
|
47
|
+
-a|--after AGE & operate on tasks older than AGE. Expected format is a number followed by the time unit (s,h,m,y), such as '10d' for 10 days
|
|
48
|
+
-b|--backup & backup deleted tasks
|
|
49
|
+
-n|--noop & do a dry run, print what would be done
|
|
50
|
+
-s|--search QUERY & use QUERY in scoped search format to match tasks to delete
|
|
51
|
+
-v|--verbose & be verbose
|
|
52
|
+
EOF
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
SHORTOPTS="a:bB:Ehns:S:v"
|
|
56
|
+
LONGOPTS="after:,backup,batch-size:,execute,help,noop,search:,states:,verbose"
|
|
57
|
+
|
|
58
|
+
ARGS=$(getopt -s bash \
|
|
59
|
+
--options $SHORTOPTS \
|
|
60
|
+
--longoptions $LONGOPTS \
|
|
61
|
+
--name $PROGNAME \
|
|
62
|
+
-- "$@" )
|
|
63
|
+
|
|
64
|
+
if [ $? -gt 0 ]; then
|
|
65
|
+
die 1 "getopt failed"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
eval set -- "$ARGS"
|
|
69
|
+
|
|
70
|
+
while true; do
|
|
71
|
+
case $1 in
|
|
72
|
+
-a|--after)
|
|
73
|
+
shift
|
|
74
|
+
AFTER="$1"
|
|
75
|
+
;;
|
|
76
|
+
-b|--backup)
|
|
77
|
+
TASK_BACKUP=true
|
|
78
|
+
;;
|
|
79
|
+
-B|--batch-size)
|
|
80
|
+
shift
|
|
81
|
+
BATCH_SIZE="$1"
|
|
82
|
+
;;
|
|
83
|
+
-n|--noop)
|
|
84
|
+
NOOP=1
|
|
85
|
+
;;
|
|
86
|
+
-s|--search)
|
|
87
|
+
shift
|
|
88
|
+
TASK_SEARCH="$1"
|
|
89
|
+
;;
|
|
90
|
+
-S|--states)
|
|
91
|
+
shift
|
|
92
|
+
if [ "$1" == "all" ]; then
|
|
93
|
+
STATES=","
|
|
94
|
+
else
|
|
95
|
+
STATES="$1"
|
|
96
|
+
fi
|
|
97
|
+
;;
|
|
98
|
+
-v|--verbose)
|
|
99
|
+
VERBOSE=1
|
|
100
|
+
;;
|
|
101
|
+
-h|--help)
|
|
102
|
+
usage
|
|
103
|
+
exit 0
|
|
104
|
+
;;
|
|
105
|
+
-E|--execute)
|
|
106
|
+
EXECUTE=1
|
|
107
|
+
;;
|
|
108
|
+
\?)
|
|
109
|
+
die 1 "Invalid option: -$OPTARG"
|
|
110
|
+
;;
|
|
111
|
+
--)
|
|
112
|
+
;;
|
|
113
|
+
*)
|
|
114
|
+
[ -n "$1" ] || break
|
|
115
|
+
die 1 "Unaccepted parameter: $1"
|
|
116
|
+
shift
|
|
117
|
+
;;
|
|
118
|
+
esac
|
|
119
|
+
shift
|
|
120
|
+
done
|
|
121
|
+
|
|
122
|
+
if [ "$EXECUTE" -eq 1 ]; then
|
|
123
|
+
build_rake | sh
|
|
124
|
+
else
|
|
125
|
+
build_rake
|
|
126
|
+
fi
|
|
127
|
+
|