foreman-tasks 6.0.3 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5c1faf969670adf42567f2dd46b7b78dc2199561bb96b483516fe4169b7558d
4
- data.tar.gz: 9aad850fd8decb284e1ccfc1869e5220da554e53286d31ccb3abb6da492a3ca3
3
+ metadata.gz: 7b653c4a46abfb110f3a5c1ff0f130814110475989e364d39aa2756bc76c4ae2
4
+ data.tar.gz: b7ad044c96a9cd2f7f7dbf10416ce42b88e5f2ddaf4fe5cbed696cfde39dee32
5
5
  SHA512:
6
- metadata.gz: 9f76c37ad5faccedfee63f80b35b9bb1a3b65974fbef664c49baac53f6991846ed215c76cef6452bd966ea9da219d1adbae82edba3b3e783c7619ce832d51e96
7
- data.tar.gz: d32b7d573c6809bba7bc28a4568513544752657461a9877c4f3824e8f5b0eca0b674fa9161f97cd8c72204b6edcc52f3e86480e19636538c53c8a4f473304e4f
6
+ metadata.gz: b0525a6e659bf05476e86b69c6147600463f16274e71fc15b1ccee628011a785dc53fc6b8cad0ef5f3439cac8c6581a73cea7a1175c4489778f5204fe3914c76
7
+ data.tar.gz: feab08efc57055c27fab6010dd1f5dac438f74091dee9c239da5722a7236eacf467433f17838066e3ef707f2268e39e2f3130615a25a9094286a9c65d66b2485
@@ -29,7 +29,7 @@ jobs:
29
29
  fail-fast: false
30
30
  matrix:
31
31
  foreman-core-branch: [develop]
32
- ruby-version: [2.5, 2.6, 2.7]
32
+ ruby-version: [2.7]
33
33
  node-version: [12]
34
34
  steps:
35
35
  - run: sudo apt-get update
@@ -3,7 +3,7 @@ module Actions
3
3
  module WithDelegatedAction
4
4
  include ::Actions::Helpers::WithContinuousOutput
5
5
 
6
- def plan_delegated_action(proxy, klass, options)
6
+ def plan_delegated_action(proxy, klass, options, proxy_action_class: ::Actions::ProxyAction)
7
7
  case proxy
8
8
  when :not_defined
9
9
  if klass.is_a?(String)
@@ -14,7 +14,7 @@ module Actions
14
14
  when :not_available
15
15
  raise Foreman::Exception, _('All proxies with the required feature are unavailable at the moment')
16
16
  when ::SmartProxy
17
- delegated_action = plan_action(::Actions::ProxyAction, proxy, klass, options)
17
+ delegated_action = plan_action(proxy_action_class, proxy, klass, options)
18
18
  end
19
19
 
20
20
  input[:delegated_action_id] = delegated_action.id
@@ -7,7 +7,7 @@ module ForemanTasks
7
7
  :foreign_key => :execution_plan_id,
8
8
  :inverse_of => :remote_tasks
9
9
 
10
- scope :triggered, -> { where(:state => 'triggered') }
10
+ scope :triggered, -> { where(:state => ['triggered', 'parent-triggered']) }
11
11
  scope :pending, -> { where(:state => 'new') }
12
12
  scope :external, -> { where(:state => 'external') }
13
13
 
@@ -10,4 +10,4 @@
10
10
  <div id="user-id" data-id="<%= User.current.id if User.current %>" ></div>
11
11
  <%= react_component('ForemanTasks') %>
12
12
  <% end %>
13
- <%= render file: "layouts/base" %>
13
+ <%= render template: "layouts/base" %>
@@ -38,16 +38,16 @@
38
38
  <table class="<%= table_css_classes('table-condensed table-fixed') %>">
39
39
  <thead>
40
40
  <th class="col-md-1"><%= N_("ID") %></th>
41
- <th><%= N_("Cron line") %></th>
42
- <th><%= N_("Task count") %></th>
43
- <th><%= N_("Action") %></th>
44
- <th><%= N_("Last occurrence") %></th>
45
- <th><%= N_("Next occurrence") %></th>
46
- <th><%= N_("Current iteration") %></th>
47
- <th><%= N_("Iteration limit") %></th>
48
- <th><%= N_("Repeat until") %></th>
49
- <th><%= N_("State") %></th>
50
- <th><%= N_("Purpose") %></th>
41
+ <th><%= _("Cron line") %></th>
42
+ <th><%= _("Task count") %></th>
43
+ <th><%= _("Action") %></th>
44
+ <th><%= _("Last occurrence") %></th>
45
+ <th><%= _("Next occurrence") %></th>
46
+ <th><%= _("Current iteration") %></th>
47
+ <th><%= _("Iteration limit") %></th>
48
+ <th><%= _("Repeat until") %></th>
49
+ <th><%= _("State") %></th>
50
+ <th><%= _("Purpose") %></th>
51
51
  <th/>
52
52
  </thead>
53
53
  <% @recurring_logics.each do |recurring_logic| %>
@@ -1,11 +1,11 @@
1
1
  <div>
2
2
  <table class='<%= table_css_classes('table-condensed') %>'>
3
3
  <tr>
4
- <th><%= N_('ID') %></th>
4
+ <th><%= _('ID') %></th>
5
5
  <td><%= link_to(task_group.id, foreman_tasks_task_group_url(task_group)) %></td>
6
6
  </tr>
7
7
  <tr>
8
- <th><%= N_('Task count') %></th>
8
+ <th><%= _('Task count') %></th>
9
9
  <td><%= link_to(task_group.tasks.count, foreman_tasks_tasks_url(:search => "task_group.id = #{task_group.id}")) %></td>
10
10
  </tr>
11
11
  </table>
@@ -1,47 +1,47 @@
1
1
  <% recurring_logic = task_group.recurring_logic -%>
2
2
  <table class='<%= table_css_classes('table-condensed') %>'>
3
3
  <tr>
4
- <th>ID</th>
4
+ <th><%= _('ID') %></th>
5
5
  <td><%= link_to(recurring_logic.id, recurring_logic) %></td>
6
6
  </tr>
7
7
  <tr>
8
- <th><%= N_("Cron line") %></th>
8
+ <th><%= _("Cron line") %></th>
9
9
  <td><%= recurring_logic.cron_line %></td>
10
10
  </tr>
11
11
  <tr>
12
- <th><%= N_("Action") %></th>
12
+ <th><%= _("Action") %></th>
13
13
  <td><%= format_task_input(recurring_logic.tasks.last) %></td>
14
14
  </tr>
15
15
  <tr>
16
- <th><%= N_("Last occurrence") %></th>
16
+ <th><%= _("Last occurrence") %></th>
17
17
  <td><%= recurring_logic.tasks.order(:started_at).where('started_at IS NOT NULL').last.try(:started_at) || '-' %></td>
18
18
  </tr>
19
19
  <tr>
20
- <th><%= N_("Next occurrence") %></th>
20
+ <th><%= _("Next occurrence") %></th>
21
21
  <td><%= recurring_logic_next_occurrence recurring_logic %></td>
22
22
  </tr>
23
23
  <tr>
24
- <th><%= N_("Current iteration") %></th>
24
+ <th><%= _("Current iteration") %></th>
25
25
  <td><%= recurring_logic.iteration %></td>
26
26
  </tr>
27
27
  <tr>
28
- <th><%= N_("Iteration limit") %></th>
28
+ <th><%= _("Iteration limit") %></th>
29
29
  <td><%= format_recurring_logic_limit recurring_logic.max_iteration %></td>
30
30
  </tr>
31
31
  <tr>
32
- <th><%= N_("Repeat until") %></th>
32
+ <th><%= _("Repeat until") %></th>
33
33
  <td><%= format_recurring_logic_limit recurring_logic.end_time.try(:in_time_zone) %></td>
34
34
  </tr>
35
35
  <tr>
36
- <th><%= N_("State") %></th>
36
+ <th><%= _("State") %></th>
37
37
  <td><%= recurring_logic_state(recurring_logic) %></td>
38
38
  </tr>
39
39
  <tr>
40
- <th><%= N_("Purpose") %></th>
40
+ <th><%= _("Purpose") %></th>
41
41
  <td><%= recurring_logic.purpose %></td>
42
42
  </tr>
43
43
  <tr>
44
- <th><%= N_("Task count") %></th>
44
+ <th><%= _("Task count") %></th>
45
45
  <td><%= link_to(task_group.tasks.count, foreman_tasks_tasks_url(:search => "task_group.id = #{task_group.id}")) %></td>
46
46
  </tr>
47
47
  </table>
@@ -27,16 +27,23 @@
27
27
  #
28
28
  :cleanup:
29
29
  #
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.
32
- #
33
- # :after: 30d
34
- #
35
30
  # per action settings to override the default defined in the actions (self.cleanup_after method)
36
31
  #
37
32
  # :actions:
38
33
  # - :name: Actions::Foreman::Host::ImportFacts
39
34
  # :after: 10d
35
+ # # Actions name can also be a list of names
36
+ # - :name:
37
+ # - Actions::Katello::Host::Erratum::Install
38
+ # - Actions::Katello::Host::Erratum::ApplicableErrataInstall
39
+ # :after: 90d
40
+ # # Actions can also define a condition
41
+ # - :name:
42
+ # - Actions::RemoteExecution::RunHostJob
43
+ # - Actions::RemoteExecution::RunHostsJob
44
+ # :filter: remote_execution_feature.label = katello_errata_install
45
+ # :after: 90d
46
+
40
47
  #
41
48
  # Rules defined in this section by default don't operate
42
49
  # on tasks specified in the actions section. This behavior
@@ -2,6 +2,50 @@ require 'csv'
2
2
 
3
3
  module ForemanTasks
4
4
  # Represents the cleanup mechanism for tasks
5
+ class ActionRule
6
+ attr_reader :klass, :after, :condition
7
+ def initialize(klass, after, condition = nil)
8
+ @klass = klass
9
+ @after = after
10
+ @condition = condition
11
+ end
12
+
13
+ def exclude_search
14
+ "NOT (#{include_search})"
15
+ end
16
+
17
+ def include_search
18
+ parts = if klass.is_a? Array
19
+ ["label ^ (#{klass.join(', ')})"]
20
+ else
21
+ ["label = \"#{klass}\""]
22
+ end
23
+ parts << "(#{@condition})" if @condition
24
+ '(' + parts.join(' AND ') + ')'
25
+ end
26
+
27
+ def self.compose_include_rules(rules)
28
+ rules.group_by { |rule| [rule.after, rule.condition] }
29
+ .map do |(after, condition), rules|
30
+ ActionRule.new(rules.map(&:klass), after, condition)
31
+ end
32
+ end
33
+ end
34
+
35
+ class CompositeActionRule
36
+ def initialize(*rules)
37
+ @rules = rules
38
+ end
39
+
40
+ def exclude_search
41
+ partial_condition = @rules.group_by(&:condition)
42
+ .map do |condition, rules|
43
+ ActionRule.new(rules.map(&:klass), nil, condition).include_search
44
+ end.join(' OR ')
45
+ "NOT (#{partial_condition})"
46
+ end
47
+ end
48
+
5
49
  class Cleaner
6
50
  def self.run(options)
7
51
  if options.key?(:filter)
@@ -12,36 +56,30 @@ module ForemanTasks
12
56
  raise "The option #{invalid_option} is not valid unless the filter specified"
13
57
  end
14
58
  end
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'))
17
- new(options.merge(:filter => '', :after => cleanup_settings[:after])).delete
18
- end
19
59
  with_periods = actions_with_default_cleanup
20
- with_periods.each do |action_class, period|
21
- new(options.merge(:filter => "label = #{action_class.name}", :after => period)).delete
60
+ ActionRule.compose_include_rules(with_periods).each do |rule|
61
+ new(options.merge(:filter => rule.include_search, :after => rule.after)).delete
22
62
  end
23
- actions_by_rules(with_periods).each do |hash|
63
+ actions_by_rules(CompositeActionRule.new(*with_periods)).each do |hash|
24
64
  new(options.merge(hash)).delete
25
65
  end
26
66
  end
27
67
  end
28
68
 
29
69
  def self.actions_with_default_cleanup
30
- actions_with_periods = {}
31
- if cleanup_settings[:actions]
32
- cleanup_settings[:actions].each do |action|
33
- action_class = action[:name].constantize
34
- actions_with_periods[action_class] = action[:after]
35
- rescue => e
36
- Foreman::Logging.exception("Error handling #{action} cleanup settings", e)
37
- end
38
- end
39
- (ForemanTasks.dynflow.world.action_classes - actions_with_periods.keys).each do |action_class|
40
- if action_class.respond_to?(:cleanup_after)
41
- actions_with_periods[action_class] = action_class.cleanup_after
70
+ actions = cleanup_settings.fetch(:actions, [])
71
+ .flat_map do |action|
72
+ Array(action[:name]).map do |klass|
73
+ ActionRule.new(klass.safe_constantize || klass, action[:after], action[:filter])
42
74
  end
43
- end
44
- actions_with_periods
75
+ rescue => e
76
+ Foreman::Logging.exception("Error handling #{action} cleanup settings", e)
77
+ nil
78
+ end.compact
79
+ hardcoded = (ForemanTasks.dynflow.world.action_classes - actions.map(&:klass))
80
+ .select { |klass| klass.respond_to?(:cleanup_after) || klass.respond_to?(:cleanup_rules) }
81
+ .flat_map { |klass| klass.respond_to?(:cleanup_rules) ? klass.cleanup_rules : ActionRule.new(klass, klass.cleanup_after) }
82
+ actions + hardcoded
45
83
  end
46
84
 
47
85
  def self.cleanup_settings
@@ -49,8 +87,8 @@ module ForemanTasks
49
87
  @cleanup_settings = SETTINGS[:'foreman-tasks'] && SETTINGS[:'foreman-tasks'][:cleanup] || {}
50
88
  end
51
89
 
52
- def self.actions_by_rules(actions_with_periods)
53
- disable_actions_with_periods = "label !^ (#{actions_with_periods.keys.join(', ')})"
90
+ def self.actions_by_rules(action_rules)
91
+ disable_actions_with_periods = action_rules.exclude_search
54
92
  cleanup_settings.fetch(:rules, []).map do |hash|
55
93
  next if hash[:after].nil?
56
94
  conditions = []
@@ -25,7 +25,7 @@ module ForemanTasks
25
25
 
26
26
  initializer 'foreman_tasks.register_plugin', :before => :finisher_hook do |_app|
27
27
  Foreman::Plugin.register :"foreman-tasks" do
28
- requires_foreman '>= 3.2.0'
28
+ requires_foreman '>= 3.3.0'
29
29
  divider :top_menu, :parent => :monitor_menu, :last => true, :caption => N_('Foreman Tasks')
30
30
  menu :top_menu, :tasks,
31
31
  :url_hash => { :controller => 'foreman_tasks/tasks', :action => :index },
@@ -39,23 +39,18 @@ namespace :foreman_tasks do
39
39
 
40
40
  desc 'Show the current configuration for auto-cleanup'
41
41
  task :config => ['environment', 'dynflow:client'] do
42
- if ForemanTasks::Cleaner.cleanup_settings[:after]
43
- puts _('The tasks will be deleted after %{after}') % { :after => ForemanTasks::Cleaner.cleanup_settings[:after] }
44
- else
45
- puts _('Global period for cleaning up tasks is not set')
46
- end
47
-
48
42
  if ForemanTasks::Cleaner.actions_with_default_cleanup.empty?
49
43
  puts _('No actions are configured to be cleaned automatically')
50
44
  else
51
45
  puts _('The following actions are configured to be deleted automatically after some time:')
52
- printf("%-50s %s\n", _('name'), _('delete after'))
53
- ForemanTasks::Cleaner.actions_with_default_cleanup.each do |action, after|
54
- printf("%-50s %s\n", action.name, after)
46
+ printf("%-75s %-20s %-50s\n", _('name'), _('delete after'), _('filter'))
47
+ ForemanTasks::Cleaner.actions_with_default_cleanup.each do |action, _after|
48
+ klass = action.klass
49
+ printf("%-75s %-20s %-50s\n", klass.try(:name) || klass, action.after, action.condition)
55
50
  end
56
51
  end
57
52
  puts
58
- by_rules = ForemanTasks::Cleaner.actions_by_rules(ForemanTasks::Cleaner.actions_with_default_cleanup)
53
+ by_rules = ForemanTasks::Cleaner.actions_by_rules(ForemanTasks::CompositeActionRule.new(*ForemanTasks::Cleaner.actions_with_default_cleanup))
59
54
  if by_rules.empty?
60
55
  puts _('No cleanup rules are configured')
61
56
  else
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '6.0.3'.freeze
2
+ VERSION = '7.1.0'.freeze
3
3
  end
data/package.json CHANGED
@@ -23,18 +23,18 @@
23
23
  "url": "http://projects.theforeman.org/projects/foreman-tasks/issues"
24
24
  },
25
25
  "peerDependencies": {
26
- "@theforeman/vendor": "^8.15.0"
26
+ "@theforeman/vendor": "^10.1.0"
27
27
  },
28
28
  "dependencies": {
29
29
  "c3": "^0.4.11"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@babel/core": "^7.7.0",
33
- "@theforeman/builder": "^8.15.0",
34
- "@theforeman/eslint-plugin-foreman": "^8.15.0",
35
- "@theforeman/stories": "^8.15.0",
36
- "@theforeman/test": "^8.15.0",
37
- "@theforeman/vendor-dev": "^8.15.0",
33
+ "@theforeman/builder": "^10.1.0",
34
+ "@theforeman/eslint-plugin-foreman": "^10.1.0",
35
+ "@theforeman/stories": "^10.1.0",
36
+ "@theforeman/test": "^10.1.0",
37
+ "@theforeman/vendor-dev": "^10.1.0",
38
38
  "babel-eslint": "^10.0.3",
39
39
  "eslint": "^6.7.2",
40
40
  "jed": "^1.1.1",
@@ -108,7 +108,7 @@ module ForemanTasks
108
108
  get :show, params: { id: task.id }, session: set_session_user
109
109
  assert_response :success
110
110
  data = JSON.parse(response.body)
111
- _(data['duration']).must_equal task.duration
111
+ _(data['duration']).must_equal task.duration.in_seconds.to_s
112
112
  end
113
113
  end
114
114
 
@@ -118,7 +118,7 @@ module ForemanTasks
118
118
  get :index, session: set_session_user
119
119
  assert_response :success
120
120
  data = JSON.parse(response.body)
121
- _(data['results'][0]['duration']).must_equal task.duration
121
+ _(data['results'][0]['duration']).must_equal task.duration.in_seconds.to_s
122
122
  end
123
123
  end
124
124
 
@@ -96,7 +96,8 @@ module ForemanTasks
96
96
  task = ForemanTasks::Task.with_duration.find(FactoryBot.create(:some_task).id)
97
97
  get(:index, params: {}, session: set_session_user)
98
98
  assert_response :success
99
- assert_include response.body.lines[1], task.duration
99
+ row = CSV.parse(response.body, headers: true).first
100
+ assert_include row['Duration'], task.duration.in_seconds.to_s
100
101
  end
101
102
  end
102
103
 
@@ -126,7 +127,8 @@ module ForemanTasks
126
127
  child.save!
127
128
  get(:sub_tasks, params: { id: parent.id }, session: set_session_user)
128
129
  assert_response :success
129
- assert_include response.body.lines[1], child.duration
130
+ row = CSV.parse(response.body, headers: true).first
131
+ assert_include row['Duration'], child.duration.in_seconds.to_s
130
132
  end
131
133
  end
132
134
 
@@ -133,33 +133,31 @@ class TasksTest < ActiveSupport::TestCase
133
133
  describe 'default behaviour' do
134
134
  it 'searches for the actions that have the cleanup_after defined' do
135
135
  ForemanTasks::Cleaner.stubs(:cleanup_settings => {})
136
- _(ForemanTasks::Cleaner.actions_with_default_cleanup[ActionWithCleanup]).must_equal '15d'
136
+ actions = ForemanTasks::Cleaner.actions_with_default_cleanup
137
+ example = actions.find { |rule| rule.klass == ActionWithCleanup }
138
+ _(example.after).must_equal '15d'
137
139
  end
138
140
 
139
141
  it 'searches for the actions that have the cleanup_after defined' do
140
142
  ForemanTasks::Cleaner.stubs(:cleanup_settings =>
141
143
  { :actions => [{ :name => ActionWithCleanup.name, :after => '5d' }] })
142
- _(ForemanTasks::Cleaner.actions_with_default_cleanup[ActionWithCleanup]).must_equal '5d'
143
- end
144
-
145
- it 'deprecates the usage of :after' do
146
- Foreman::Deprecation.expects(:deprecation_warning)
147
- ForemanTasks::Cleaner.any_instance.expects(:delete)
148
- ForemanTasks::Cleaner.stubs(:cleanup_settings =>
149
- { :after => '1d' })
150
- ForemanTasks::Cleaner.stubs(:actions_with_default_cleanup).returns({})
151
- ForemanTasks::Cleaner.run({})
144
+ actions = ForemanTasks::Cleaner.actions_with_default_cleanup
145
+ example = actions.find { |rule| rule.klass == ActionWithCleanup }
146
+ _(example.after).must_equal '5d'
152
147
  end
153
148
 
154
149
  it 'generates filters from rules properly' do
155
- actions_with_default = { 'action1' => nil, 'action2' => nil }
150
+ actions_with_default = ForemanTasks::CompositeActionRule.new(
151
+ ForemanTasks::ActionRule.new('action1', nil),
152
+ ForemanTasks::ActionRule.new('action2', nil)
153
+ )
156
154
  rules = [{ :after => nil },
157
155
  { :after => '10d', :filter => 'label = something', :states => %w[stopped paused] },
158
156
  { :after => '15d', :filter => 'label = something_else',
159
157
  :override_actions => true, :states => 'all' }]
160
158
  ForemanTasks::Cleaner.stubs(:cleanup_settings).returns(:rules => rules)
161
159
  r1, r2 = ForemanTasks::Cleaner.actions_by_rules actions_with_default
162
- _(r1[:filter]).must_equal '(label !^ (action1, action2)) AND (label = something)'
160
+ _(r1[:filter]).must_equal '(NOT ((label ^ (action1, action2)))) AND (label = something)'
163
161
  _(r1[:states]).must_equal %w[stopped paused]
164
162
  _(r2[:filter]).must_equal '(label = something_else)'
165
163
  _(r2[:states]).must_equal []
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: 6.0.3
4
+ version: 7.1.0
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: 2022-09-06 00:00:00.000000000 Z
11
+ date: 2022-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dynflow