foreman-tasks 0.9.5 → 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/foreman-tasks.yaml.example +27 -2
- data/foreman-tasks.gemspec +1 -1
- data/lib/foreman_tasks/cleaner.rb +47 -4
- data/lib/foreman_tasks/dynflow/configuration.rb +26 -0
- data/lib/foreman_tasks/tasks/cleanup.rake +22 -2
- data/lib/foreman_tasks/version.rb +1 -1
- data/test/unit/cleaner_test.rb +51 -0
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1e9246d6fdeb8fc99d6e42f7ed41e87e398341a
|
4
|
+
data.tar.gz: a67dd1ec1cbad7e37a168b8495ed48242a862bf2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3f758c1445380c7b28ee3e132fdfc2f467c1c7b907286e18a56b6fdfeb62abcb0f81753e9c3809f7d8c8342ff7e35860dbdb34d491acc71b7107aac10fbde79
|
7
|
+
data.tar.gz: c814a2d7dd11fadd76cce7a11a7f38e469529301ab6debd6e3fdc7763a5fc8793acc20291bf11c69ba65ad837670f8a3cc027d320641f83e7b5633e9384df03b
|
@@ -9,18 +9,43 @@
|
|
9
9
|
# :action:
|
10
10
|
# :enabled: true
|
11
11
|
|
12
|
+
# Task backup configuration can be changed by altering the values in
|
13
|
+
# the backup section
|
14
|
+
#
|
15
|
+
:backup:
|
16
|
+
#
|
17
|
+
# Whether to back up tasks when they are removed
|
18
|
+
#
|
19
|
+
:backup_deleted_tasks: true
|
20
|
+
#
|
21
|
+
# Where to put the tasks which were backed up
|
22
|
+
#
|
23
|
+
:backup_dir: /var/lib/foreman/tasks-backup
|
12
24
|
|
13
25
|
# Cleaning configuration: how long should the actions be kept before deleted
|
14
26
|
# by `rake foreman_tasks:clean` task
|
15
27
|
#
|
16
|
-
|
28
|
+
:cleanup:
|
17
29
|
#
|
18
30
|
# the period after which to delete all the tasks (by default all tasks are not being deleted after some period)
|
31
|
+
# will be deprecated in Foreman 1.18 and the use of rules is recommended.
|
19
32
|
#
|
20
|
-
#
|
33
|
+
# :after: 30d
|
21
34
|
#
|
22
35
|
# per action settings to override the default defined in the actions (self.cleanup_after method)
|
23
36
|
#
|
24
37
|
# :actions:
|
25
38
|
# - :name: Actions::Foreman::Host::ImportFacts
|
26
39
|
# :after: 10d
|
40
|
+
#
|
41
|
+
# Rules defined in this section by default don't operate
|
42
|
+
# on tasks specified in the actions section. This behavior
|
43
|
+
# can be overriden by setting the override_actions to true
|
44
|
+
:rules:
|
45
|
+
# Delete successful tasks after a month
|
46
|
+
- :filter: result = success
|
47
|
+
:after: 30d
|
48
|
+
# Delete everything (any action, any state) after one year
|
49
|
+
- :states: all # Either list of state names or all
|
50
|
+
:after: 1y
|
51
|
+
:override_actions: true
|
data/foreman-tasks.gemspec
CHANGED
@@ -29,7 +29,7 @@ same resource. It also optionally provides Dynflow infrastructure for using it f
|
|
29
29
|
s.extra_rdoc_files = Dir['README*', 'LICENSE']
|
30
30
|
|
31
31
|
s.add_dependency "foreman-tasks-core"
|
32
|
-
s.add_dependency "dynflow", '~> 0.8.
|
32
|
+
s.add_dependency "dynflow", '~> 0.8.29'
|
33
33
|
s.add_dependency "sequel" # for Dynflow process persistence
|
34
34
|
s.add_dependency "sinatra" # for Dynflow web console
|
35
35
|
s.add_dependency "daemons" # for running remote executor
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
1
3
|
module ForemanTasks
|
2
4
|
# Represents the cleanup mechanism for tasks
|
3
5
|
class Cleaner
|
@@ -11,11 +13,16 @@ module ForemanTasks
|
|
11
13
|
end
|
12
14
|
end
|
13
15
|
if cleanup_settings[:after]
|
16
|
+
Foreman::Deprecation.deprecation_warning('1.18', _(':after setting in tasks cleanup section is deprecated, use :after in :rules section to set the value. to cleanup rules'))
|
14
17
|
new(options.merge(:filter => '', :after => cleanup_settings[:after])).delete
|
15
18
|
end
|
16
|
-
|
19
|
+
with_periods = actions_with_default_cleanup
|
20
|
+
with_periods.each do |action_class, period|
|
17
21
|
new(options.merge(:filter => "label = #{action_class.name}", :after => period)).delete
|
18
22
|
end
|
23
|
+
actions_by_rules(with_periods).each do |hash|
|
24
|
+
new(options.merge(hash)).delete
|
25
|
+
end
|
19
26
|
end
|
20
27
|
end
|
21
28
|
|
@@ -44,6 +51,19 @@ module ForemanTasks
|
|
44
51
|
@cleanup_settings = SETTINGS[:'foreman-tasks'] && SETTINGS[:'foreman-tasks'][:cleanup] || {}
|
45
52
|
end
|
46
53
|
|
54
|
+
def self.actions_by_rules(actions_with_periods)
|
55
|
+
disable_actions_with_periods = "label !^ (#{actions_with_periods.keys.join(', ')})"
|
56
|
+
cleanup_settings.fetch(:rules, []).map do |hash|
|
57
|
+
next if hash[:after].nil?
|
58
|
+
conditions = []
|
59
|
+
conditions << disable_actions_with_periods unless hash[:override_actions]
|
60
|
+
conditions << hash[:filter] if hash[:filter]
|
61
|
+
hash[:states] = [] if hash[:states] == 'all'
|
62
|
+
hash[:filter] = conditions.map { |condition| "(#{condition})" }.join(' AND ')
|
63
|
+
hash
|
64
|
+
end.compact
|
65
|
+
end
|
66
|
+
|
47
67
|
attr_reader :filter, :after, :states, :verbose, :batch_size, :noop, :full_filter
|
48
68
|
|
49
69
|
# @param filter [String] scoped search matching the tasks to be deleted
|
@@ -56,7 +76,8 @@ module ForemanTasks
|
|
56
76
|
:verbose => false,
|
57
77
|
:batch_size => 1000,
|
58
78
|
:noop => false,
|
59
|
-
:states => ['stopped']
|
79
|
+
:states => ['stopped'],
|
80
|
+
:backup_dir => ForemanTasks.dynflow.world.persistence.current_backup_dir }
|
60
81
|
options = default_options.merge(options)
|
61
82
|
|
62
83
|
@filter = options[:filter]
|
@@ -65,6 +86,7 @@ module ForemanTasks
|
|
65
86
|
@verbose = options[:verbose]
|
66
87
|
@batch_size = options[:batch_size]
|
67
88
|
@noop = options[:noop]
|
89
|
+
@backup_dir = options[:backup_dir]
|
68
90
|
|
69
91
|
raise ArgumentError, 'filter not speficied' if @filter.nil?
|
70
92
|
|
@@ -91,12 +113,33 @@ module ForemanTasks
|
|
91
113
|
end
|
92
114
|
|
93
115
|
def delete_tasks(chunk)
|
94
|
-
ForemanTasks::Task.where(:id => chunk.map(&:id))
|
116
|
+
tasks = ForemanTasks::Task.where(:id => chunk.map(&:id))
|
117
|
+
tasks_to_csv(tasks, @backup_dir, 'foreman_tasks.csv') if @backup_dir
|
118
|
+
tasks.delete_all
|
119
|
+
end
|
120
|
+
|
121
|
+
def tasks_to_csv(dataset, backup_dir, file_name)
|
122
|
+
with_backup_file(backup_dir, file_name) do |csv, appending|
|
123
|
+
csv << ForemanTasks::Task.attribute_names.to_csv unless appending
|
124
|
+
dataset.each do |row|
|
125
|
+
csv << row.attributes.values.to_csv
|
126
|
+
end
|
127
|
+
end
|
128
|
+
dataset
|
129
|
+
end
|
130
|
+
|
131
|
+
def with_backup_file(backup_dir, file_name)
|
132
|
+
FileUtils.mkdir_p(backup_dir) unless File.directory?(backup_dir)
|
133
|
+
csv_file = File.join(backup_dir, file_name)
|
134
|
+
appending = File.exist?(csv_file)
|
135
|
+
File.open(csv_file, 'a') do |f|
|
136
|
+
yield f, appending
|
137
|
+
end
|
95
138
|
end
|
96
139
|
|
97
140
|
def delete_dynflow_plans(chunk)
|
98
141
|
dynflow_ids = chunk.find_all { |task| task.is_a? Task::DynflowTask }.map(&:external_id)
|
99
|
-
ForemanTasks.dynflow.world.persistence.delete_execution_plans({ 'uuid' => dynflow_ids }, batch_size)
|
142
|
+
ForemanTasks.dynflow.world.persistence.delete_execution_plans({ 'uuid' => dynflow_ids }, batch_size, @backup_dir)
|
100
143
|
end
|
101
144
|
|
102
145
|
def prepare_filter
|
@@ -103,6 +103,8 @@ module ForemanTasks
|
|
103
103
|
config.transaction_adapter = transaction_adapter
|
104
104
|
config.executor = ->(world, _) { initialize_executor(world) }
|
105
105
|
config.connector = ->(world, _) { initialize_connector(world) }
|
106
|
+
config.backup_deleted_plans = backup_settings[:backup_deleted_plans]
|
107
|
+
config.backup_dir = backup_settings[:backup_dir]
|
106
108
|
|
107
109
|
# we can't do any operation until the ForemanTasks.dynflow.world is set
|
108
110
|
config.auto_execute = false
|
@@ -144,5 +146,29 @@ module ForemanTasks
|
|
144
146
|
def initialize_persistence
|
145
147
|
ForemanTasks::Dynflow::Persistence.new(default_sequel_adapter_options)
|
146
148
|
end
|
149
|
+
|
150
|
+
def backup_settings
|
151
|
+
return @backup_settings if @backup_settings
|
152
|
+
backup_options = {
|
153
|
+
:backup_deleted_plans => true,
|
154
|
+
:backup_dir => default_backup_dir
|
155
|
+
}
|
156
|
+
settings = SETTINGS[:'foreman-tasks'] && SETTINGS[:'foreman-tasks'][:backup]
|
157
|
+
backup_options.merge!(settings) if settings
|
158
|
+
@backup_settings = with_environment_override backup_options
|
159
|
+
end
|
160
|
+
|
161
|
+
def default_backup_dir
|
162
|
+
File.join(Rails.root, 'tmp', 'task-backup')
|
163
|
+
end
|
164
|
+
|
165
|
+
def with_environment_override(options)
|
166
|
+
env_var = ENV['TASK_BACKUP']
|
167
|
+
unless env_var.nil?
|
168
|
+
# Everything except 0, n, no, false is considered to be a truthy value
|
169
|
+
options[:backup_deleted_plans] = !%w[0 n no false].include?(env_var.downcase)
|
170
|
+
end
|
171
|
+
options
|
172
|
+
end
|
147
173
|
end
|
148
174
|
end
|
@@ -5,10 +5,11 @@ namespace :foreman_tasks do
|
|
5
5
|
|
6
6
|
* TASK_SEARCH : scoped search filter (example: 'label = "Actions::Foreman::Host::ImportFacts"')
|
7
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
|
8
|
+
* STATES : comma separated list of task states to touch with the cleanup, by default only stopped tasks are covered, special value all can be used to clean the tasks, disregarding their states
|
9
|
+
* NOOP : set to "true" if the task should not actually perform the deletion
|
10
10
|
* VERBOSE : set to "true" for more verbose output
|
11
11
|
* BATCH_SIZE : the size of batches the tasks get processed in (1000 by default)
|
12
|
+
* TASK_BACKUP : set to "true" or "false" to enable/disable task backup
|
12
13
|
|
13
14
|
If none of TASK_SEARCH, BEFORE, STATES is specified, the tasks will be cleaned based
|
14
15
|
configuration in settings
|
@@ -21,6 +22,7 @@ namespace :foreman_tasks do
|
|
21
22
|
options[:after] = ENV['AFTER'] if ENV['AFTER']
|
22
23
|
|
23
24
|
options[:states] = ENV['STATES'].to_s.split(',') if ENV['STATES']
|
25
|
+
options[:states] = [] if options[:states] == ['all']
|
24
26
|
|
25
27
|
options[:noop] = true if ENV['NOOP']
|
26
28
|
|
@@ -52,6 +54,24 @@ namespace :foreman_tasks do
|
|
52
54
|
printf("%-50s %s\n", action.name, after)
|
53
55
|
end
|
54
56
|
end
|
57
|
+
puts
|
58
|
+
by_rules = ForemanTasks::Cleaner.actions_by_rules(ForemanTasks::Cleaner.actions_with_default_cleanup)
|
59
|
+
if by_rules.empty?
|
60
|
+
puts _('No cleanup rules are configured')
|
61
|
+
else
|
62
|
+
printf("%-50s %-15s %s\n", _('states'), _('delete after'), _('filter'))
|
63
|
+
by_rules.each do |hash|
|
64
|
+
state = case hash[:states]
|
65
|
+
when []
|
66
|
+
_('ANY')
|
67
|
+
when nil
|
68
|
+
'stopped'
|
69
|
+
else
|
70
|
+
hash[:states]
|
71
|
+
end
|
72
|
+
printf("%-50s %-15s %s\n", state, hash[:after], hash[:filter])
|
73
|
+
end
|
74
|
+
end
|
55
75
|
end
|
56
76
|
end
|
57
77
|
|
data/test/unit/cleaner_test.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
require 'foreman_tasks_test_helper'
|
2
2
|
|
3
3
|
class TasksTest < ActiveSupport::TestCase
|
4
|
+
before do
|
5
|
+
# To stop dynflow from backing up actions, execution_plans and steps
|
6
|
+
ForemanTasks.dynflow.world.persistence.adapter.stubs(:backup_to_csv)
|
7
|
+
end
|
8
|
+
|
4
9
|
describe ForemanTasks::Cleaner do
|
5
10
|
it 'is able to delete tasks (including the dynflow plans) based on filter' do
|
6
11
|
cleaner = ForemanTasks::Cleaner.new(:filter => 'label = "Actions::User::Create"', :after => '10d')
|
@@ -12,6 +17,7 @@ class TasksTest < ActiveSupport::TestCase
|
|
12
17
|
task.save
|
13
18
|
end,
|
14
19
|
FactoryGirl.create(:dynflow_task, :product_create_task)]
|
20
|
+
cleaner.expects(:tasks_to_csv)
|
15
21
|
cleaner.delete
|
16
22
|
ForemanTasks::Task.where(id: tasks_to_delete).must_be_empty
|
17
23
|
ForemanTasks::Task.where(id: tasks_to_keep).order(:id).map(&:id).must_equal tasks_to_keep.map(&:id).sort
|
@@ -32,6 +38,7 @@ class TasksTest < ActiveSupport::TestCase
|
|
32
38
|
end]
|
33
39
|
|
34
40
|
tasks_to_keep = [FactoryGirl.create(:dynflow_task, :product_create_task)]
|
41
|
+
cleaner.expects(:tasks_to_csv)
|
35
42
|
cleaner.delete
|
36
43
|
ForemanTasks::Task.where(id: tasks_to_delete).must_be_empty
|
37
44
|
ForemanTasks::Task.where(id: tasks_to_keep).must_equal tasks_to_keep
|
@@ -46,11 +53,32 @@ class TasksTest < ActiveSupport::TestCase
|
|
46
53
|
task.started_at = task.ended_at = Time.zone.now
|
47
54
|
task.save
|
48
55
|
end]
|
56
|
+
cleaner.expects(:tasks_to_csv)
|
49
57
|
cleaner.delete
|
50
58
|
ForemanTasks::Task.where(id: tasks_to_delete).must_be_empty
|
51
59
|
ForemanTasks::Task.where(id: tasks_to_keep).must_equal tasks_to_keep
|
52
60
|
end
|
53
61
|
|
62
|
+
it 'backs tasks up before deleting' do
|
63
|
+
dir = '/tmp'
|
64
|
+
cleaner = ForemanTasks::Cleaner.new(:filter => '', :after => '10d', :backup_dir => dir)
|
65
|
+
tasks_to_delete = [FactoryGirl.create(:dynflow_task, :user_create_task),
|
66
|
+
FactoryGirl.create(:dynflow_task, :product_create_task)]
|
67
|
+
|
68
|
+
r, w = IO.pipe
|
69
|
+
cleaner.expects(:with_backup_file)
|
70
|
+
.with(dir, 'foreman_tasks.csv')
|
71
|
+
.yields(w, false)
|
72
|
+
cleaner.delete
|
73
|
+
w.close
|
74
|
+
header, *data = r.readlines.map(&:chomp)
|
75
|
+
header.must_equal ForemanTasks::Task.attribute_names.join(',')
|
76
|
+
expected_lines = tasks_to_delete.map { |task| task.attributes.values.join(',') }
|
77
|
+
data.count.must_equal expected_lines.count
|
78
|
+
expected_lines.each { |line| data.must_include line }
|
79
|
+
ForemanTasks::Task.where(id: tasks_to_delete).must_be_empty
|
80
|
+
end
|
81
|
+
|
54
82
|
class ActionWithCleanup < Actions::Base
|
55
83
|
def self.cleanup_after
|
56
84
|
'15d'
|
@@ -68,6 +96,29 @@ class TasksTest < ActiveSupport::TestCase
|
|
68
96
|
{ :actions => [{ :name => ActionWithCleanup.name, :after => '5d' }] })
|
69
97
|
ForemanTasks::Cleaner.actions_with_default_cleanup[ActionWithCleanup].must_equal '5d'
|
70
98
|
end
|
99
|
+
|
100
|
+
it 'deprecates the usage of :after' do
|
101
|
+
Foreman::Deprecation.expects(:deprecation_warning)
|
102
|
+
ForemanTasks::Cleaner.any_instance.expects(:delete)
|
103
|
+
ForemanTasks::Cleaner.stubs(:cleanup_settings =>
|
104
|
+
{ :after => '1d' })
|
105
|
+
ForemanTasks::Cleaner.stubs(:actions_with_default_cleanup).returns({})
|
106
|
+
ForemanTasks::Cleaner.run({})
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'generates filters from rules properly' do
|
110
|
+
actions_with_default = { 'action1' => nil, 'action2' => nil }
|
111
|
+
rules = [{ :after => nil },
|
112
|
+
{ :after => '10d', :filter => 'label = something', :states => %w[stopped paused] },
|
113
|
+
{ :after => '15d', :filter => 'label = something_else',
|
114
|
+
:override_actions => true, :states => 'all' }]
|
115
|
+
ForemanTasks::Cleaner.stubs(:cleanup_settings).returns(:rules => rules)
|
116
|
+
r1, r2 = ForemanTasks::Cleaner.actions_by_rules actions_with_default
|
117
|
+
r1[:filter].must_equal '(label !^ (action1, action2)) AND (label = something)'
|
118
|
+
r1[:states].must_equal %w[stopped paused]
|
119
|
+
r2[:filter].must_equal '(label = something_else)'
|
120
|
+
r2[:states].must_equal []
|
121
|
+
end
|
71
122
|
end
|
72
123
|
end
|
73
124
|
end
|
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.9.
|
4
|
+
version: 0.9.6
|
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: 2017-
|
11
|
+
date: 2017-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: foreman-tasks-core
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.8.
|
33
|
+
version: 0.8.29
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.8.
|
40
|
+
version: 0.8.29
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: sequel
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -286,7 +286,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
286
286
|
version: '0'
|
287
287
|
requirements: []
|
288
288
|
rubyforge_project:
|
289
|
-
rubygems_version: 2.
|
289
|
+
rubygems_version: 2.6.12
|
290
290
|
signing_key:
|
291
291
|
specification_version: 4
|
292
292
|
summary: Foreman plugin for showing tasks information for resoruces and users
|