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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NDU0Mzk4MWU2NDIzMmY3ZDVhMTEyZDdhN2RiNDcyNGVjZGVjMGQ1Mg==
4
+ MDE1OGU2NTc0M2VkNTdhYjUwODE4YTM1YTNiMTVmYWQwNTUyNjIxZQ==
5
5
  data.tar.gz: !binary |-
6
- MTcyNGQ0ZDdhN2JkZWY0NzNiM2Q1MzBkNWUzZDU5Y2I0MjMxNjliZQ==
6
+ YmU0NzM5ZjNhZTMxODdjZjVkYTFkYTkwYWY5YjhiZDljMjUxNGZkZA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- Yjg3Yjk4OTJlMmY5MDk0OWUyZWI0MDRmOTIyYjFmMDY2NDE2ZjU0YTNmODgz
10
- ODI1YWY1NTBjNDk3NTgwOWFmZjgyZDhiMzcxNzVlNDg1ZTM3ODNjZjNhOWEw
11
- NDcxZWY4MDEyNTJkZDk0M2RkNDQ0YTNlOWMxZDNkOTIzZDdkN2M=
9
+ Yzc2NTgwODNhODY2MTM5MWFjNmU3NzBkODZlZmNhYjZjZTJlZTIxMzVlZjll
10
+ MDQ1OTk0ZmE4MTk5ZDRiMmUyMzViNzQxYTdjZDViNmMyNTcyZjQwNmIyODI0
11
+ MzRiZjM4ZjA0YWJlNTRkZGY1ZjVjODNiNWJjMmZkYjAxYWU5OWM=
12
12
  data.tar.gz: !binary |-
13
- OGQzOWFmNTgyZjJlMTIyNDRiNmY4Nzk4OTdlYTEyZmMzNmFlMmY4MzE3YmM5
14
- M2YxMmVlOWRiMDZiODI1ZjZiMWQ4ZDhkMDMzNDIwZGY1MGY4NzEyODRhNmFj
15
- ZTNhOWZiY2YwYjE2MDFlMTE2YjBjNjhkYTY3NjYxYTE0MmRjZjQ=
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
- RAILS_ENV=production bundle exec rake foreman_tasks:dynflow:executor
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
 
@@ -93,7 +93,7 @@ module ForemanTasks
93
93
 
94
94
  def filter(scope)
95
95
  scope.search_for(params[:search], :order => params[:order]).
96
- paginate(:page => params[:page])
96
+ paginate(:page => params[:page]).select('DISTINCT foreman_tasks_tasks.*')
97
97
  end
98
98
 
99
99
  end
@@ -35,6 +35,11 @@ module Actions
35
35
  input[:host] && input[:host][:name]
36
36
  end
37
37
 
38
+ # default value for cleaning up the tasks, it can be overriden by settings
39
+ def self.cleanup_after
40
+ '30d'
41
+ end
42
+
38
43
  end
39
44
  end
40
45
  end
@@ -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
- Rails.logger.warn("Task %s updated at consistency check: %s" % [task.id, changes.inspect])
94
+ logger.warn("Task %s updated at consistency check: %s" % [task.id, changes.inspect])
94
95
  end
95
96
  rescue => e
96
- Rails.logger.warn("Failed at consistency check for task %s: %s\n %s" % [task.id, e.message, e.backtrace.join("\n")])
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
- var item = $(this).find("a");
19
- if(item.length){
21
+ currentTwoPaneTask = $(this).find("a");
22
+ if(currentTwoPaneTask.length){
20
23
  e.preventDefault();
21
- two_pane_open(item);
24
+ two_pane_open(currentTwoPaneTask);
22
25
  }
23
26
  });
24
27
 
@@ -1,44 +1,45 @@
1
1
  <script>
2
- var taskProgressReloader = {
3
- timeoutId: null,
4
- reload: function () {
5
- $.ajax({
6
- url: "",
7
- context: document.body,
8
- success: function (s, x) {
9
- $(this).html(s);
10
- taskProgressReloader.start();
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
- start: function () {
16
- if (!this.timeoutId) {
17
- this.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
- },
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
- stop: function () {
25
- if (this.timeoutId) {
26
- clearTimeout(this.timeoutId);
27
- }
28
- this.timeoutId = null;
29
- var button = $('.reload-button');
30
- button.html('<span class="glyphicon glyphicon-refresh"></span> <%= _('Start auto-reloading') %>');
31
- button.show();
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
- toggle: function () {
35
- if (this.timeoutId) {
36
- this.stop();
37
- } else {
38
- this.start();
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
@@ -4,6 +4,7 @@ require 'foreman_tasks/engine'
4
4
  require 'foreman_tasks/dynflow'
5
5
  require 'foreman_tasks/triggers'
6
6
  require 'foreman_tasks/authorizer_ext'
7
+ require 'foreman_tasks/cleaner'
7
8
 
8
9
  module ForemanTasks
9
10
  extend Algebrick::TypeCheck
@@ -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
- Rails.logger.fatal e
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
- ForemanTasks.dynflow.world.logger.error('Error on on_execution_plan_save event')
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
@@ -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.6'
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
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = "0.7.1"
2
+ VERSION = "0.7.2"
3
3
  end
@@ -22,11 +22,18 @@ FactoryGirl.define do
22
22
  result "success"
23
23
  parent_task_id nil
24
24
 
25
- after(:build) do |task|
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.update_from_dynflow(execution_plan.to_hash)
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
@@ -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.1
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-03 00:00:00.000000000 Z
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