foreman-tasks 0.7.1 → 0.7.2
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 +8 -8
- data/README.md +62 -1
- data/app/controllers/foreman_tasks/tasks_controller.rb +1 -1
- data/app/lib/actions/foreman/host/import_facts.rb +5 -0
- data/app/models/foreman_tasks/task.rb +1 -0
- data/app/models/foreman_tasks/task/dynflow_task.rb +3 -2
- data/app/views/foreman_tasks/tasks/index.html.erb +6 -3
- data/app/views/foreman_tasks/tasks/show.html.erb +36 -35
- data/config/foreman-tasks.yaml.example +26 -0
- data/lib/foreman_tasks.rb +1 -0
- data/lib/foreman_tasks/cleaner.rb +152 -0
- data/lib/foreman_tasks/dynflow/configuration.rb +11 -9
- data/lib/foreman_tasks/dynflow/daemon.rb +4 -1
- data/lib/foreman_tasks/dynflow/persistence.rb +1 -3
- data/lib/foreman_tasks/engine.rb +5 -2
- data/lib/foreman_tasks/tasks/cleanup.rake +67 -0
- data/lib/foreman_tasks/version.rb +1 -1
- data/test/factories/task_factory.rb +9 -2
- data/test/unit/cleaner_test.rb +74 -0
- data/test/unit/task_test.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MDE1OGU2NTc0M2VkNTdhYjUwODE4YTM1YTNiMTVmYWQwNTUyNjIxZQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YmU0NzM5ZjNhZTMxODdjZjVkYTFkYTkwYWY5YjhiZDljMjUxNGZkZA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
Yzc2NTgwODNhODY2MTM5MWFjNmU3NzBkODZlZmNhYjZjZTJlZTIxMzVlZjll
|
10
|
+
MDQ1OTk0ZmE4MTk5ZDRiMmUyMzViNzQxYTdjZDViNmMyNTcyZjQwNmIyODI0
|
11
|
+
MzRiZjM4ZjA0YWJlNTRkZGY1ZjVjODNiNWJjMmZkYjAxYWU5OWM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
Y2VjMzU0Y2UxNjg0YjJlNGY1NDMwMWVjMzQ3NGMwNGVmMzI1ZjhkMmM2YWRl
|
14
|
+
NTU1NGJmZDVhMWFiODkyYjQzMzJlMmExYTI3YmJmNjY4MGU1Yzg2ZGIwNGY1
|
15
|
+
Njc1NzE3YTRmZmYxOTVjN2IyMGVhYmFiMDkyNmY1ZjM1ZTcyNmM=
|
data/README.md
CHANGED
@@ -93,6 +93,7 @@ it would be:
|
|
93
93
|
```ruby
|
94
94
|
initializer "your_engine.require_dynflow", :before => "foreman_tasks.initialize_dynflow" do |app|
|
95
95
|
ForemanTasks.dynflow.require!
|
96
|
+
ForemanTasks.dynflow.config.eager_load_paths << File.join(YourEngine::Engine.root, 'app/lib/actions')
|
96
97
|
end
|
97
98
|
```
|
98
99
|
|
@@ -128,13 +129,73 @@ The executor process needs to be executed before the web server. You
|
|
128
129
|
can run it by:
|
129
130
|
|
130
131
|
```
|
131
|
-
|
132
|
+
foreman-rake foreman_tasks:dynflow:executor
|
132
133
|
```
|
133
134
|
|
134
135
|
Also, there is a possibility to run the executor in daemonized mode
|
135
136
|
using the `dynflow-executor`. It expects to be executed from Foreman
|
136
137
|
rails root directory. See `-h` for more details and options
|
137
138
|
|
139
|
+
Tasks cleanup
|
140
|
+
-------------
|
141
|
+
|
142
|
+
Although, the history of tasks has an auditing value, some kinds of
|
143
|
+
tasks can grow up in number quite soon. Therefore there is a mechanism
|
144
|
+
how to clean the tasks, using a rake command. When running without
|
145
|
+
any arguments, the tasks are deleted based on the default parameters
|
146
|
+
defined in the code.
|
147
|
+
|
148
|
+
```
|
149
|
+
foreman-rake foreman_tasks:cleanup
|
150
|
+
```
|
151
|
+
|
152
|
+
To see what tasks would be deleted, without actually deleting the records, you can run
|
153
|
+
|
154
|
+
```
|
155
|
+
foreman-rake foreman_tasks:cleanup NOOP=true
|
156
|
+
```
|
157
|
+
|
158
|
+
By default, only the actions explicitly defined with expiration time
|
159
|
+
in the code, will get cleaned. One can configure new actions, or
|
160
|
+
override the default configuration inside the configuration
|
161
|
+
`config/settings.plugins.d/foreman_tasks.yaml`, such as:
|
162
|
+
|
163
|
+
|
164
|
+
```
|
165
|
+
:foreman-tasks:
|
166
|
+
:cleanup:
|
167
|
+
# the period after witch to delete all the tasks (by default all tasks are not being deleted after some period)
|
168
|
+
:after: 365d
|
169
|
+
# per action settings to override the default defined in the actions (cleanup_after method)
|
170
|
+
:actions:
|
171
|
+
- :name: Actions::Foreman::Host::ImportFacts
|
172
|
+
:after: 10d
|
173
|
+
|
174
|
+
```
|
175
|
+
|
176
|
+
The `foreman_tasks:cleanup` script also accepts additional parameters
|
177
|
+
to specify the search criteria for the cleanup manually:
|
178
|
+
|
179
|
+
* `FILTER`: scoped search filter (example: 'label = "Actions::Foreman::Host::ImportFacts"')
|
180
|
+
* `AFTER`: delete tasks created after `AFTER` period. Expected format
|
181
|
+
is a number followed by the time unit (`s`, `h`, `m`, `y`), such as
|
182
|
+
`10d` for 10 days (applicable only when the `FILTER` option is specified)
|
183
|
+
* `STATES`: comma separated list of task states to touch with the
|
184
|
+
cleanup, by default only stopped tasks are affected
|
185
|
+
(applicable only when the `FILTER` option is specified)
|
186
|
+
* `NOOP`: set to "true" if the task should not actuall perform the
|
187
|
+
deletion, only report the actions the script would perform
|
188
|
+
* `VERBOSE`: set to "true" for more verbose output
|
189
|
+
* `BATCH_SIZE`: the size of batches the tasks get processed in (1000 by default)
|
190
|
+
|
191
|
+
To see the current configuration (what actions get cleaned
|
192
|
+
automatically and what is their `after` period), this script can be
|
193
|
+
used:
|
194
|
+
|
195
|
+
```
|
196
|
+
foreman-rake foreman_tasks:cleanup:config
|
197
|
+
```
|
198
|
+
|
138
199
|
Issues
|
139
200
|
------
|
140
201
|
|
@@ -23,6 +23,7 @@ module ForemanTasks
|
|
23
23
|
scoped_search :on => :state, :complete_value => true
|
24
24
|
scoped_search :on => :result, :complete_value => true
|
25
25
|
scoped_search :on => :started_at, :complete_value => false
|
26
|
+
scoped_search :on => :parent_task_id, :complete_value => true
|
26
27
|
scoped_search :in => :locks, :on => :resource_type, :complete_value => true, :rename => "resource_type", :ext_method => :search_by_generic_resource
|
27
28
|
scoped_search :in => :locks, :on => :resource_id, :complete_value => false, :rename => "resource_id", :ext_method => :search_by_generic_resource
|
28
29
|
scoped_search :in => :owners, :on => :id, :complete_value => true, :rename => "owner.id", :ext_method => :search_by_owner
|
@@ -85,15 +85,16 @@ module ForemanTasks
|
|
85
85
|
|
86
86
|
def self.consistency_check
|
87
87
|
fixed_count = 0
|
88
|
+
logger = Foreman::Logging.logger('foreman-tasks')
|
88
89
|
self.running.each do |task|
|
89
90
|
begin
|
90
91
|
changes = task.update_from_dynflow(task.execution_plan.to_hash)
|
91
92
|
unless changes.empty?
|
92
93
|
fixed_count += 1
|
93
|
-
|
94
|
+
logger.warn("Task %s updated at consistency check: %s" % [task.id, changes.inspect])
|
94
95
|
end
|
95
96
|
rescue => e
|
96
|
-
|
97
|
+
Foreman::Logging.exception("Failed at consistency check for task #{task.id}", e, :logger => logger)
|
97
98
|
end
|
98
99
|
end
|
99
100
|
return fixed_count
|
@@ -14,11 +14,14 @@
|
|
14
14
|
</style>
|
15
15
|
|
16
16
|
<script>
|
17
|
+
|
18
|
+
var currentTwoPaneTask;
|
19
|
+
|
17
20
|
$(document).on('click', ".table-two-pane td.two-pane-link", function(e) {
|
18
|
-
|
19
|
-
if(
|
21
|
+
currentTwoPaneTask = $(this).find("a");
|
22
|
+
if(currentTwoPaneTask.length){
|
20
23
|
e.preventDefault();
|
21
|
-
two_pane_open(
|
24
|
+
two_pane_open(currentTwoPaneTask);
|
22
25
|
}
|
23
26
|
});
|
24
27
|
|
@@ -1,44 +1,45 @@
|
|
1
1
|
<script>
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
2
|
+
if (typeof taskProgressReloader === 'undefined') {
|
3
|
+
var taskProgressReloader = {
|
4
|
+
timeoutId: null,
|
5
|
+
reload: function () {
|
6
|
+
// we need different reload mechanism for two-pane and non-two-pane
|
7
|
+
if (typeof currentTwoPaneTask !== 'undefined') {
|
8
|
+
taskProgressReloader.timeoutId = null;
|
9
|
+
two_pane_open(currentTwoPaneTask);
|
10
|
+
} else {
|
11
|
+
document.location.reload();
|
11
12
|
}
|
12
|
-
}
|
13
|
-
},
|
13
|
+
},
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
15
|
+
start: function () {
|
16
|
+
if (!taskProgressReloader.timeoutId) {
|
17
|
+
taskProgressReloader.timeoutId = setTimeout(this.reload, 5000);
|
18
|
+
}
|
19
|
+
var button = $('.reload-button');
|
20
|
+
button.html('<span class="glyphicon glyphicon-refresh spin"></span> <%= _('Stop auto-reloading') %>');
|
21
|
+
button.show();
|
22
|
+
},
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
stop: function () {
|
25
|
+
if (taskProgressReloader.timeoutId) {
|
26
|
+
clearTimeout(taskProgressReloader.timeoutId);
|
27
|
+
}
|
28
|
+
taskProgressReloader.timeoutId = null;
|
29
|
+
var button = $('.reload-button');
|
30
|
+
button.html('<span class="glyphicon glyphicon-refresh"></span> <%= _('Start auto-reloading') %>');
|
31
|
+
button.show();
|
32
|
+
},
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
toggle: function () {
|
35
|
+
if (taskProgressReloader.timeoutId) {
|
36
|
+
this.stop();
|
37
|
+
} else {
|
38
|
+
this.start();
|
39
|
+
}
|
39
40
|
}
|
40
|
-
}
|
41
|
-
}
|
41
|
+
};
|
42
|
+
}
|
42
43
|
|
43
44
|
$(document).ready(function () {
|
44
45
|
$('.modal-submit').click(function(e){
|
@@ -0,0 +1,26 @@
|
|
1
|
+
:foreman-tasks:
|
2
|
+
#
|
3
|
+
# Logging configuration can be changed by uncommenting the loggers
|
4
|
+
# section and the logger configuration desired.
|
5
|
+
#
|
6
|
+
# :loggers:
|
7
|
+
# :dynflow:
|
8
|
+
# :enabled: true
|
9
|
+
# :action:
|
10
|
+
# :enabled: true
|
11
|
+
|
12
|
+
|
13
|
+
# Cleaning configuration: how long should the actions be kept before deleted
|
14
|
+
# by `rake foreman_tasks:clean` task
|
15
|
+
#
|
16
|
+
# :cleanup:
|
17
|
+
#
|
18
|
+
# the period after which to delete all the tasks (by default all tasks are not being deleted after some period)
|
19
|
+
#
|
20
|
+
# :after: 365d
|
21
|
+
#
|
22
|
+
# per action settings to override the default defined in the actions (self.cleanup_after method)
|
23
|
+
#
|
24
|
+
# :actions:
|
25
|
+
# - :name: Actions::Foreman::Host::ImportFacts
|
26
|
+
# :after: 10d
|
data/lib/foreman_tasks.rb
CHANGED
@@ -0,0 +1,152 @@
|
|
1
|
+
module ForemanTasks
|
2
|
+
# Represents the cleanup mechanism for tasks
|
3
|
+
class Cleaner
|
4
|
+
|
5
|
+
def self.run(options)
|
6
|
+
if options.key?(:filter)
|
7
|
+
self.new(options).delete
|
8
|
+
else
|
9
|
+
[:after, :states].each do |invalid_option|
|
10
|
+
if options.key?(invalid_option)
|
11
|
+
raise "The option #{invalid_option} is not valid unless the filter specified"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
if cleanup_settings[:after]
|
15
|
+
self.new(options.merge(:filter => "", :after => cleanup_settings[:after])).delete
|
16
|
+
end
|
17
|
+
actions_with_default_cleanup.each do |action_class, period|
|
18
|
+
self.new(options.merge(:filter => "label = #{action_class.name}", :after => period)).delete
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.actions_with_default_cleanup
|
24
|
+
actions_with_periods = {}
|
25
|
+
if cleanup_settings[:actions]
|
26
|
+
cleanup_settings[:actions].each do |action|
|
27
|
+
begin
|
28
|
+
action_class = action[:name].constantize
|
29
|
+
actions_with_periods[action_class] = action[:after]
|
30
|
+
rescue => e
|
31
|
+
Foreman::Logging.exception("Error handling #{action} cleanup settings", e)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
(ForemanTasks.dynflow.world.action_classes - actions_with_periods.keys).each do |action_class|
|
36
|
+
if action_class.respond_to?(:cleanup_after)
|
37
|
+
actions_with_periods[action_class] = action_class.cleanup_after
|
38
|
+
end
|
39
|
+
end
|
40
|
+
return actions_with_periods
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.cleanup_settings
|
44
|
+
return @cleanup_settings if @cleanup_settings
|
45
|
+
@cleanup_settings = SETTINGS[:'foreman-tasks'] && SETTINGS[:'foreman-tasks'][:cleanup] || {}
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :filter, :after , :states, :verbose, :batch_size, :noop, :full_filter
|
49
|
+
|
50
|
+
# @param filter [String] scoped search matching the tasks to be deleted
|
51
|
+
# @param after [String|nil] delete the tasks after they are older
|
52
|
+
# than the value: the number in string is expected
|
53
|
+
# to be followed by time unit specification one of s,h,d,y for
|
54
|
+
# seconds ago. If not specified, no implicit filtering on the date.
|
55
|
+
def initialize(options = {})
|
56
|
+
default_options = { :after => '0s',
|
57
|
+
:verbose => false,
|
58
|
+
:batch_size => 1000,
|
59
|
+
:noop => false,
|
60
|
+
:states => ["stopped"] }
|
61
|
+
options = default_options.merge(options)
|
62
|
+
|
63
|
+
@filter = options[:filter]
|
64
|
+
@after = parse_time_interval(options[:after])
|
65
|
+
@states = options[:states]
|
66
|
+
@verbose = options[:verbose]
|
67
|
+
@batch_size = options[:batch_size]
|
68
|
+
@noop = options[:noop]
|
69
|
+
|
70
|
+
raise ArgumentError, 'filter not speficied' if @filter.nil?
|
71
|
+
|
72
|
+
@full_filter = prepare_filter
|
73
|
+
end
|
74
|
+
|
75
|
+
# Delete the filtered tasks, including the dynflow execution plans
|
76
|
+
def delete
|
77
|
+
if noop
|
78
|
+
say "[noop] deleting all tasks matching filter #{full_filter}"
|
79
|
+
say "[noop] #{ForemanTasks::Task.search_for(full_filter).size} tasks would be deleted"
|
80
|
+
else
|
81
|
+
start_tracking_progress
|
82
|
+
while (chunk = ForemanTasks::Task.search_for(full_filter).limit(batch_size)).any?
|
83
|
+
delete_tasks(chunk)
|
84
|
+
delete_dynflow_plans(chunk)
|
85
|
+
report_progress(chunk)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def tasks
|
91
|
+
ForemanTasks::Task.search_for(full_filter).select('DISTINCT foreman_tasks_tasks.id, foreman_tasks_tasks.type, foreman_tasks_tasks.external_id')
|
92
|
+
end
|
93
|
+
|
94
|
+
def delete_tasks(chunk)
|
95
|
+
ForemanTasks::Task.where(:id => chunk.map(&:id)).delete_all
|
96
|
+
end
|
97
|
+
|
98
|
+
def delete_dynflow_plans(chunk)
|
99
|
+
dynflow_ids = chunk.find_all { |task| task.is_a? Task::DynflowTask }.map(&:external_id)
|
100
|
+
ForemanTasks.dynflow.world.persistence.delete_execution_plans({ 'uuid' => dynflow_ids }, batch_size)
|
101
|
+
end
|
102
|
+
|
103
|
+
def prepare_filter
|
104
|
+
filter_parts = [filter]
|
105
|
+
filter_parts << %{started_at < "#{after.ago.to_s(:db)}"} if after > 0
|
106
|
+
filter_parts << states.map { |s| "state = #{s}" }.join(" OR ") if states.any?
|
107
|
+
filter_parts.select(&:present?).join(' AND ')
|
108
|
+
end
|
109
|
+
|
110
|
+
def start_tracking_progress
|
111
|
+
if verbose
|
112
|
+
@current, @total = 0, tasks.size
|
113
|
+
say "#{@current}/#{@total}", false
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def report_progress(chunk)
|
118
|
+
if verbose
|
119
|
+
@current += chunk.size
|
120
|
+
say "#{@current}/#{@total}", false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def say(message, log = true)
|
125
|
+
puts message
|
126
|
+
if log
|
127
|
+
Foreman::Logging.logger('foreman-tasks').info(message)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def parse_time_interval(string)
|
132
|
+
matched_string = string.gsub(' ','').match(/\A(\d+)(\w)\Z/)
|
133
|
+
unless matched_string
|
134
|
+
raise ArgumentError, "String #{string} isn't an expected specification of time in format of \"{number}{time_unit}\""
|
135
|
+
end
|
136
|
+
number = matched_string[1].to_i
|
137
|
+
value = case matched_string[2]
|
138
|
+
when 's'
|
139
|
+
number.seconds
|
140
|
+
when 'h'
|
141
|
+
number.hours
|
142
|
+
when 'd'
|
143
|
+
number.days
|
144
|
+
when 'y'
|
145
|
+
number.years
|
146
|
+
else
|
147
|
+
raise ArgumentError, "Unexpected time unit in #{string}, expected one of [s,h,d,y]"
|
148
|
+
end
|
149
|
+
return value
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -1,13 +1,6 @@
|
|
1
1
|
module ForemanTasks
|
2
2
|
class Dynflow::Configuration
|
3
3
|
|
4
|
-
# for logging action related info (such as exceptions raised in side
|
5
|
-
# the actions' methods
|
6
|
-
attr_accessor :action_logger
|
7
|
-
|
8
|
-
# for logging dynflow related info about the progress of the execution etc.
|
9
|
-
attr_accessor :dynflow_logger
|
10
|
-
|
11
4
|
# the number of threads in the pool handling the execution
|
12
5
|
attr_accessor :pool_size
|
13
6
|
|
@@ -35,8 +28,6 @@ module ForemanTasks
|
|
35
28
|
attr_accessor :disable_active_record_actions
|
36
29
|
|
37
30
|
def initialize
|
38
|
-
self.action_logger = Rails.logger
|
39
|
-
self.dynflow_logger = Rails.logger
|
40
31
|
self.pool_size = 5
|
41
32
|
self.db_pool_size = pool_size + 5
|
42
33
|
self.remote = Rails.env.production?
|
@@ -48,6 +39,17 @@ module ForemanTasks
|
|
48
39
|
@on_init = []
|
49
40
|
end
|
50
41
|
|
42
|
+
# for logging action related info (such as exceptions raised in side
|
43
|
+
# the actions' methods
|
44
|
+
def action_logger
|
45
|
+
Foreman::Logging.logger('foreman-tasks/action')
|
46
|
+
end
|
47
|
+
|
48
|
+
# for logging dynflow related info about the progress of the execution etc.
|
49
|
+
def dynflow_logger
|
50
|
+
Foreman::Logging.logger('foreman-tasks/dynflow')
|
51
|
+
end
|
52
|
+
|
51
53
|
def on_init(&block)
|
52
54
|
@on_init << block
|
53
55
|
end
|
@@ -24,6 +24,7 @@ module ForemanTasks
|
|
24
24
|
default_options = { foreman_root: Dir.pwd,
|
25
25
|
process_name: 'dynflow_executor',
|
26
26
|
pid_dir: "#{Rails.root}/tmp/pids",
|
27
|
+
log_dir: File.join(Rails.root, 'log'),
|
27
28
|
wait_attempts: 300,
|
28
29
|
wait_sleep: 1 }
|
29
30
|
options = default_options.merge(options)
|
@@ -42,15 +43,17 @@ module ForemanTasks
|
|
42
43
|
|
43
44
|
Daemons.run_proc(options[:process_name],
|
44
45
|
:dir => options[:pid_dir],
|
46
|
+
:log_dir => options[:log_dir],
|
45
47
|
:dir_mode => :normal,
|
46
48
|
:monitor => true,
|
47
49
|
:log_output => true,
|
48
50
|
:ARGV => [command]) do |*args|
|
49
51
|
begin
|
52
|
+
::Logging.reopen
|
50
53
|
run(options[:foreman_root])
|
51
54
|
rescue => e
|
52
55
|
STDERR.puts e.message
|
53
|
-
|
56
|
+
Foreman::Logging.exception("Failed running foreman-tasks daemon", e)
|
54
57
|
exit 1
|
55
58
|
end
|
56
59
|
end
|
@@ -13,9 +13,7 @@ module ForemanTasks
|
|
13
13
|
begin
|
14
14
|
on_execution_plan_save(execution_plan_id, value)
|
15
15
|
rescue => e
|
16
|
-
|
17
|
-
ForemanTasks.dynflow.world.logger.error(e.message)
|
18
|
-
ForemanTasks.dynflow.world.logger.error(e.backtrace.join("\n"))
|
16
|
+
Foreman::Logging.exception("Error on on_execution_plan_save event", e, :logger => Foreman::Logging.logger('foreman-tasks/dynflow'))
|
19
17
|
end
|
20
18
|
end
|
21
19
|
ensure
|
data/lib/foreman_tasks/engine.rb
CHANGED
@@ -8,7 +8,7 @@ module ForemanTasks
|
|
8
8
|
|
9
9
|
initializer 'foreman_tasks.register_plugin', :after => :finisher_hook do |app|
|
10
10
|
Foreman::Plugin.register :"foreman-tasks" do
|
11
|
-
requires_foreman '>= 1.
|
11
|
+
requires_foreman '>= 1.9.0'
|
12
12
|
divider :top_menu, :parent => :monitor_menu, :after => :audits
|
13
13
|
menu :top_menu, :tasks,
|
14
14
|
:url_hash => { :controller => 'foreman_tasks/tasks', :action => :index },
|
@@ -22,6 +22,9 @@ module ForemanTasks
|
|
22
22
|
:'foreman_tasks/api/tasks' => [:bulk_resume]}, :resource_type => ForemanTasks::Task.name
|
23
23
|
end
|
24
24
|
|
25
|
+
logger :dynflow, :enabled => true
|
26
|
+
logger :action, :enabled => true
|
27
|
+
|
25
28
|
role "Tasks Manager", [:view_foreman_tasks, :edit_foreman_tasks]
|
26
29
|
role "Tasks Reader", [:view_foreman_tasks]
|
27
30
|
|
@@ -91,7 +94,7 @@ module ForemanTasks
|
|
91
94
|
|
92
95
|
|
93
96
|
rake_tasks do
|
94
|
-
%w[dynflow.rake test.rake export_tasks.rake].each do |rake_file|
|
97
|
+
%w[dynflow.rake test.rake export_tasks.rake cleanup.rake].each do |rake_file|
|
95
98
|
full_path = File.expand_path("../tasks/#{rake_file}", __FILE__)
|
96
99
|
load full_path if File.exists?(full_path)
|
97
100
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
namespace :foreman_tasks do
|
2
|
+
namespace :cleanup do
|
3
|
+
desc <<DESC
|
4
|
+
Clean tasks based on filter and age. ENV variables:
|
5
|
+
|
6
|
+
* FILTER : scoped search filter (example: 'label = "Actions::Foreman::Host::ImportFacts"')
|
7
|
+
* AFTER : delete tasks created after *AFTER* period. Expected format is a number followed by the time unit (s,h,m,y), such as '10d' for 10 days
|
8
|
+
* STATES : comma separated list of task states to touch with the cleanup, by default only stopped tasks are covered
|
9
|
+
* NOOP : set to "true" if the task should not actuall perform the deletion
|
10
|
+
* VERBOSE : set to "true" for more verbose output
|
11
|
+
* BATCH_SIZE : the size of batches the tasks get processed in (1000 by default)
|
12
|
+
|
13
|
+
If none of FILTER, BEFORE, STATES is specified, the tasks will be cleaned based
|
14
|
+
configuration in settings
|
15
|
+
DESC
|
16
|
+
task :run => 'environment' do
|
17
|
+
options = {}
|
18
|
+
|
19
|
+
if ENV['FILTER']
|
20
|
+
options[:filter] = ENV['FILTER']
|
21
|
+
end
|
22
|
+
|
23
|
+
if ENV['AFTER']
|
24
|
+
options[:after] = ENV['AFTER']
|
25
|
+
end
|
26
|
+
|
27
|
+
if ENV['STATES']
|
28
|
+
options[:states] = ENV['STATES'].to_s.split(',')
|
29
|
+
end
|
30
|
+
|
31
|
+
if ENV['NOOP']
|
32
|
+
options[:noop] = true
|
33
|
+
end
|
34
|
+
|
35
|
+
if ENV['VERBOSE']
|
36
|
+
options[:verbose] = true
|
37
|
+
end
|
38
|
+
|
39
|
+
if ENV['BATCH_SIZE']
|
40
|
+
options[:batch_size] = ENV['BATCH_SIZE'].to_i
|
41
|
+
end
|
42
|
+
|
43
|
+
ForemanTasks::Cleaner.run(options)
|
44
|
+
end
|
45
|
+
|
46
|
+
desc 'Show the current configuration for auto-cleanup'
|
47
|
+
task :config => 'environment' do
|
48
|
+
if ForemanTasks::Cleaner.cleanup_settings[:after]
|
49
|
+
puts _('The tasks will be deleted after %{after}') % ForemanTasks::Cleaner.cleanup_settings[:after]
|
50
|
+
else
|
51
|
+
puts _('Global period for cleaning up tasks is not set')
|
52
|
+
end
|
53
|
+
|
54
|
+
if ForemanTasks::Cleaner.actions_with_default_cleanup.empty?
|
55
|
+
puts _('No actions are configured to be cleaned automatically')
|
56
|
+
else
|
57
|
+
puts _('The following actions are configured to be deleted automatically after some time:')
|
58
|
+
printf("%-50s %s\n", _('name'), _('delete after'))
|
59
|
+
ForemanTasks::Cleaner.actions_with_default_cleanup.each do |action, after|
|
60
|
+
printf("%-50s %s\n", action.name, after)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
task :cleanup => 'cleanup:run'
|
67
|
+
end
|
@@ -22,11 +22,18 @@ FactoryGirl.define do
|
|
22
22
|
result "success"
|
23
23
|
parent_task_id nil
|
24
24
|
|
25
|
-
|
25
|
+
transient do
|
26
|
+
sync_with_dynflow false
|
27
|
+
end
|
28
|
+
|
29
|
+
after(:build) do |task, evaluator|
|
26
30
|
execution_plan = ForemanTasks.dynflow.world.plan(Support::DummyDynflowAction)
|
27
31
|
# remove the task created automatically by the persistence
|
28
32
|
ForemanTasks::Task.where(:external_id => execution_plan.id).delete_all
|
29
|
-
task.
|
33
|
+
task.update_attributes!(:external_id => execution_plan.id)
|
34
|
+
if evaluator.sync_with_dynflow
|
35
|
+
task.update_from_dynflow(execution_plan.to_hash)
|
36
|
+
end
|
30
37
|
end
|
31
38
|
|
32
39
|
trait :user_create_task do
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'foreman_tasks_test_helper'
|
2
|
+
|
3
|
+
class TasksTest < ActiveSupport::TestCase
|
4
|
+
describe ForemanTasks::Cleaner do
|
5
|
+
it 'is able to delete tasks (including the dynflow plans) based on filter' do
|
6
|
+
cleaner = ForemanTasks::Cleaner.new(:filter => 'label = "Actions::User::Create"', :after => '10d')
|
7
|
+
|
8
|
+
tasks_to_delete = [FactoryGirl.create(:dynflow_task, :user_create_task),
|
9
|
+
FactoryGirl.create(:dynflow_task, :user_create_task)]
|
10
|
+
tasks_to_keep = [FactoryGirl.create(:dynflow_task, :user_create_task) do |task|
|
11
|
+
task.started_at = task.ended_at = Time.now
|
12
|
+
task.save
|
13
|
+
end,
|
14
|
+
FactoryGirl.create(:dynflow_task, :product_create_task)]
|
15
|
+
cleaner.delete
|
16
|
+
ForemanTasks::Task.where(id: tasks_to_delete).must_be_empty
|
17
|
+
ForemanTasks::Task.where(id: tasks_to_keep).must_equal tasks_to_keep
|
18
|
+
|
19
|
+
ForemanTasks.dynflow.world.persistence.
|
20
|
+
find_execution_plans(filters: {'uuid' => tasks_to_delete.map(&:external_id)}).size.must_equal 0
|
21
|
+
|
22
|
+
ForemanTasks.dynflow.world.persistence.
|
23
|
+
find_execution_plans(filters: {'uuid' => tasks_to_keep.map(&:external_id)}).size.must_equal tasks_to_keep.size
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'deletes all tasks matching the filter when the time limit is not specified' do
|
27
|
+
cleaner = ForemanTasks::Cleaner.new(:filter => 'label = "Actions::User::Create"')
|
28
|
+
tasks_to_delete = [FactoryGirl.create(:dynflow_task, :user_create_task),
|
29
|
+
FactoryGirl.create(:dynflow_task, :user_create_task) do |task|
|
30
|
+
task.started_at = task.ended_at = Time.now
|
31
|
+
task.save
|
32
|
+
end]
|
33
|
+
|
34
|
+
tasks_to_keep = [FactoryGirl.create(:dynflow_task, :product_create_task)]
|
35
|
+
cleaner.delete
|
36
|
+
ForemanTasks::Task.where(id: tasks_to_delete).must_be_empty
|
37
|
+
ForemanTasks::Task.where(id: tasks_to_keep).must_equal tasks_to_keep
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'supports passing empty filter (just delete all)' do
|
41
|
+
cleaner = ForemanTasks::Cleaner.new(:filter => '', :after => '10d')
|
42
|
+
tasks_to_delete = [FactoryGirl.create(:dynflow_task, :user_create_task),
|
43
|
+
FactoryGirl.create(:dynflow_task, :product_create_task)]
|
44
|
+
|
45
|
+
tasks_to_keep = [FactoryGirl.create(:dynflow_task, :user_create_task) do |task|
|
46
|
+
task.started_at = task.ended_at = Time.now
|
47
|
+
task.save
|
48
|
+
end]
|
49
|
+
cleaner.delete
|
50
|
+
ForemanTasks::Task.where(id: tasks_to_delete).must_be_empty
|
51
|
+
ForemanTasks::Task.where(id: tasks_to_keep).must_equal tasks_to_keep
|
52
|
+
end
|
53
|
+
|
54
|
+
class ActionWithCleanup < Actions::Base
|
55
|
+
def self.cleanup_after
|
56
|
+
'15d'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "default behaviour" do
|
61
|
+
it "searches for the actions that have the cleanup_after defined" do
|
62
|
+
ForemanTasks::Cleaner.stubs(:cleanup_settings => {})
|
63
|
+
ForemanTasks::Cleaner.actions_with_default_cleanup[ActionWithCleanup].must_equal '15d'
|
64
|
+
end
|
65
|
+
|
66
|
+
it "searches for the actions that have the cleanup_after defined" do
|
67
|
+
ForemanTasks::Cleaner.stubs(:cleanup_settings =>
|
68
|
+
{ :actions => [{:name => ActionWithCleanup.name, :after => '5d'}]})
|
69
|
+
ForemanTasks::Cleaner.actions_with_default_cleanup[ActionWithCleanup].must_equal '5d'
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/test/unit/task_test.rb
CHANGED
@@ -37,7 +37,7 @@ class TasksTest < ActiveSupport::TestCase
|
|
37
37
|
|
38
38
|
describe 'consistency check' do
|
39
39
|
|
40
|
-
let(:consistent_task) { FactoryGirl.create(:dynflow_task) }
|
40
|
+
let(:consistent_task) { FactoryGirl.create(:dynflow_task, :sync_with_dynflow => true) }
|
41
41
|
let(:inconsistent_task) { FactoryGirl.create(:dynflow_task, :inconsistent_dynflow_task) }
|
42
42
|
|
43
43
|
it 'ensures the tasks marked as running are really running in Dynflow' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman-tasks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Nečas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
11
|
+
date: 2015-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dynflow
|
@@ -126,6 +126,7 @@ files:
|
|
126
126
|
- app/views/foreman_tasks/tasks/show.html.erb
|
127
127
|
- bin/dynflow-executor
|
128
128
|
- bin/foreman-tasks
|
129
|
+
- config/foreman-tasks.yaml.example
|
129
130
|
- config/routes.rb
|
130
131
|
- db/migrate/20131205204140_create_foreman_tasks.rb
|
131
132
|
- db/migrate/20131209122644_create_foreman_tasks_locks.rb
|
@@ -138,6 +139,7 @@ files:
|
|
138
139
|
- lib/foreman-tasks.rb
|
139
140
|
- lib/foreman_tasks.rb
|
140
141
|
- lib/foreman_tasks/authorizer_ext.rb
|
142
|
+
- lib/foreman_tasks/cleaner.rb
|
141
143
|
- lib/foreman_tasks/dynflow.rb
|
142
144
|
- lib/foreman_tasks/dynflow/configuration.rb
|
143
145
|
- lib/foreman_tasks/dynflow/console_authorizer.rb
|
@@ -145,6 +147,7 @@ files:
|
|
145
147
|
- lib/foreman_tasks/dynflow/persistence.rb
|
146
148
|
- lib/foreman_tasks/engine.rb
|
147
149
|
- lib/foreman_tasks/task_error.rb
|
150
|
+
- lib/foreman_tasks/tasks/cleanup.rake
|
148
151
|
- lib/foreman_tasks/tasks/dynflow.rake
|
149
152
|
- lib/foreman_tasks/tasks/export_tasks.rake
|
150
153
|
- lib/foreman_tasks/triggers.rb
|
@@ -156,6 +159,7 @@ files:
|
|
156
159
|
- test/helpers/foreman_tasks/tasks_helper_test.rb
|
157
160
|
- test/support/dummy_dynflow_action.rb
|
158
161
|
- test/unit/actions/action_with_sub_plans_test.rb
|
162
|
+
- test/unit/cleaner_test.rb
|
159
163
|
- test/unit/dynflow_console_authorizer_test.rb
|
160
164
|
- test/unit/task_test.rb
|
161
165
|
homepage: https://github.com/theforeman/foreman-tasks
|
@@ -187,6 +191,7 @@ test_files:
|
|
187
191
|
- test/foreman_tasks_test_helper.rb
|
188
192
|
- test/controllers/api/tasks_controller_test.rb
|
189
193
|
- test/factories/task_factory.rb
|
194
|
+
- test/unit/cleaner_test.rb
|
190
195
|
- test/unit/dynflow_console_authorizer_test.rb
|
191
196
|
- test/unit/actions/action_with_sub_plans_test.rb
|
192
197
|
- test/unit/task_test.rb
|