foreman-tasks 0.12.2 → 0.13.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
  SHA1:
3
- metadata.gz: 7c9314affd9bff690bd88cbd4aa26c65033b10eb
4
- data.tar.gz: 24fc605d6fd7038ed08a4dfbd55ef1c88a81421c
3
+ metadata.gz: 0111beaab4e9971f1039c81e0ccf31ad5cbb79e4
4
+ data.tar.gz: 0077b9efb0e93957f99a37fe4d08469b6f6b62d2
5
5
  SHA512:
6
- metadata.gz: 60bb278cd278df8fef607c162335e70419da9fc52ebca9a6e4de7b5520976638e2c04e17108ad594e1788933ffca2a16fc754044011ecf289ab1ead78642ae17
7
- data.tar.gz: f20a4318e569a2d11ab00c60bf23b21dfb6b579ac0373eb6a0e444325904b56d5ae8fa11a3f782858efdc35b3143f956ef7028feec32fb1691a1862fcf7e3678
6
+ metadata.gz: 0aff56c13e49c3cca9802a8ae63f0f4c024ba69a4b6bb07d9453e236de2571d284895906192c8b52cc2e8ed8300b083e3643d47bf8894fab8fbde55da5bc3364
7
+ data.tar.gz: 55255ff6e02f84e834b2ea8927364985830f13a0b456c64af503af56b0a76f286e1a94e5ec2fef04b3ee4da48fe905836a242b9baae4efd90f54afe04ee03944
@@ -58,11 +58,11 @@ module ForemanTasks
58
58
  end
59
59
 
60
60
  def recurring_logic_next_occurrence(recurring_logic)
61
- if %w[finished cancelled].include? recurring_logic.state
62
- '-'
63
- else
64
- recurring_logic.next_occurrence_time
65
- end
61
+ default = '-'
62
+ return default if %w[finished cancelled].include? recurring_logic.state
63
+
64
+ last_task = recurring_logic.tasks.order(:start_at).last
65
+ last_task ? last_task.start_at : default
66
66
  end
67
67
 
68
68
  def time_f(f, attr, field_options = {}, time_options = {}, html_options = {})
@@ -126,7 +126,7 @@ module Actions
126
126
  product_id = fetch_data(data, :product, :id)
127
127
  repo_id = fetch_data(data, :repo, :id)
128
128
  if product_id && repo_id
129
- "#/products/#{product_id}/repositories/#{repo_id}"
129
+ "/products/#{product_id}/repositories/#{repo_id}"
130
130
  end
131
131
  end
132
132
  end
@@ -152,7 +152,7 @@ module Actions
152
152
 
153
153
  def link(data)
154
154
  if (content_view_id = fetch_data(data, :content_view, :id))
155
- "#/content_views/#{content_view_id}/versions"
155
+ "/content_views/#{content_view_id}/versions"
156
156
  end
157
157
  end
158
158
  end
@@ -168,7 +168,7 @@ module Actions
168
168
 
169
169
  def link(data)
170
170
  if (product_id = fetch_data(data, :product, :id))
171
- "#/products/#{product_id}/info"
171
+ "/products/#{product_id}/"
172
172
  end
173
173
  end
174
174
  end
@@ -22,7 +22,7 @@ module Actions
22
22
  private
23
23
 
24
24
  def with_current_taxonomies
25
- if has_current_taxonomies?
25
+ if current_taxonomies?
26
26
  yield
27
27
  else
28
28
  restore_current_taxonomies { yield }
@@ -43,9 +43,9 @@ module Actions
43
43
  Location.current = nil
44
44
  end
45
45
 
46
- def has_current_taxonomies?
46
+ def current_taxonomies?
47
47
  (Organization.current || action.input[:current_organization_id].nil?) &&
48
- (Location.current || action.input[:current_location_id].nil?)
48
+ (Location.current || action.input[:current_location_id].nil?)
49
49
  end
50
50
  end
51
51
  end
@@ -7,11 +7,6 @@ module Actions
7
7
  # After an action is delayed, it checks whether the delay_options
8
8
  # hash contains an id of a recurring logic. If so, it adds the task
9
9
  # to the recurring logic's task group, otherwise does nothing.
10
- #
11
- # After the action's plan phase the middleware checks if the task
12
- # is associated with a task group of any recurring logic, in which case
13
- # it triggers another repeat using the task group's recurring logic,
14
- # otherwise does nothing.
15
10
  def delay(delay_options, *args)
16
11
  pass(delay_options, *args).tap do
17
12
  if delay_options[:recurring_logic_id]
@@ -20,13 +15,6 @@ module Actions
20
15
  end
21
16
  end
22
17
 
23
- def plan(*args)
24
- pass(*args).tap do
25
- task_group = task.task_groups.find { |tg| tg.is_a? ::ForemanTasks::TaskGroups::RecurringLogicTaskGroup }
26
- task_group.recurring_logic.trigger_repeat(action.class, *args) if task_group
27
- end
28
- end
29
-
30
18
  private
31
19
 
32
20
  def recurring_logic(delay_options)
@@ -0,0 +1,27 @@
1
+ module Actions
2
+ module RecurringAction
3
+ # When included sets the base action to use the RecurringLogic middleware and configures
4
+ # #trigger_repeat to be called when appropriate to trigger the next repeat.
5
+ def self.included(base)
6
+ base.middleware.use Actions::Middleware::RecurringLogic
7
+ base.execution_plan_hooks.use :trigger_repeat, :on => [:planned, :failure]
8
+ end
9
+
10
+ # Hook to be called when a repetition needs to be triggered. This either happens when the plan goes into planned state
11
+ # or when it fails.
12
+ def trigger_repeat(execution_plan)
13
+ if execution_plan.delay_record && recurring_logic_task_group
14
+ args = execution_plan.delay_record.args
15
+ logic = recurring_logic_task_group.recurring_logic
16
+ logic.trigger_repeat_after(task.start_at, self.class, *args)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def recurring_logic_task_group
23
+ @task_group ||= task.task_groups
24
+ .find { |tg| tg.is_a? ::ForemanTasks::TaskGroups::RecurringLogicTaskGroup }
25
+ end
26
+ end
27
+ end
@@ -33,12 +33,13 @@ module ForemanTasks
33
33
  trigger_repeat(action_class, *args)
34
34
  end
35
35
 
36
- def trigger_repeat(action_class, *args)
37
- if can_continue?
36
+ def trigger_repeat_after(time, action_class, *args)
37
+ return if cancelled?
38
+ if can_continue?(time)
38
39
  self.iteration += 1
39
40
  save!
40
41
  ::ForemanTasks.delay action_class,
41
- generate_delay_options,
42
+ generate_delay_options(time),
42
43
  *args
43
44
  else
44
45
  self.state = 'finished'
@@ -47,6 +48,10 @@ module ForemanTasks
47
48
  end
48
49
  end
49
50
 
51
+ def trigger_repeat(action_class, *args)
52
+ trigger_repeat_after(Time.zone.now, action_class, *args)
53
+ end
54
+
50
55
  def cancel
51
56
  self.state = 'cancelled'
52
57
  save!
@@ -89,6 +94,10 @@ module ForemanTasks
89
94
  state == 'finished'
90
95
  end
91
96
 
97
+ def cancelled?
98
+ state == 'cancelled'
99
+ end
100
+
92
101
  def humanized_state
93
102
  case state
94
103
  when 'active'
@@ -109,10 +109,16 @@ module ForemanTasks
109
109
  state == 'paused'
110
110
  end
111
111
 
112
+ # returns true if the task is *CURRENTLY* waiting to be executed in the future
113
+ def scheduled?
114
+ state == 'scheduled'
115
+ end
116
+
112
117
  def recurring?
113
118
  !recurring_logic_task_group_ids.empty?
114
119
  end
115
120
 
121
+ # returns true if the task was planned to execute in the future
116
122
  def delayed?
117
123
  start_at.to_i != started_at.to_i
118
124
  end
@@ -8,12 +8,12 @@ module ForemanTasks
8
8
  def update_from_dynflow(data)
9
9
  utc_zone = ActiveSupport::TimeZone.new('UTC')
10
10
  self.external_id = data[:id]
11
- self.started_at = utc_zone.parse(data[:started_at]) unless data[:started_at].nil?
12
- self.ended_at = utc_zone.parse(data[:ended_at]) unless data[:ended_at].nil?
13
11
  self.result = map_result(data).to_s
14
12
  self.state = data[:state].to_s
15
- self.start_at = utc_zone.parse(data[:start_at]) if data[:start_at]
16
- self.start_before = utc_zone.parse(data[:start_before]) if data[:start_before]
13
+ self.started_at = string_to_time(utc_zone, data[:started_at]) unless data[:started_at].nil?
14
+ self.ended_at = string_to_time(utc_zone, data[:ended_at]) unless data[:ended_at].nil?
15
+ self.start_at = string_to_time(utc_zone, data[:start_at]) if data[:start_at]
16
+ self.start_before = string_to_time(utc_zone, data[:start_before]) if data[:start_before]
17
17
  self.parent_task_id ||= begin
18
18
  if main_action.caller_execution_plan_id
19
19
  DynflowTask.where(:external_id => main_action.caller_execution_plan_id).first!.id
@@ -153,6 +153,11 @@ module ForemanTasks
153
153
 
154
154
  private
155
155
 
156
+ def string_to_time(zone, time)
157
+ return time if time.is_a?(Time)
158
+ zone.parse(time)
159
+ end
160
+
156
161
  def set_action_field
157
162
  self.action = to_label
158
163
  end
@@ -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.29'
32
+ s.add_dependency "dynflow", '~> 1.0'
33
33
  s.add_dependency "sinatra" # for Dynflow web console
34
34
  s.add_dependency "parse-cron", '~> 0.1.4'
35
35
  s.add_dependency "get_process_mem" # for memory polling
@@ -35,8 +35,8 @@ module ForemanTasks
35
35
  options
36
36
  end
37
37
 
38
- def initialize_persistence
39
- ForemanTasks::Dynflow::Persistence.new(default_sequel_adapter_options)
38
+ def persistence_class
39
+ ForemanTasks::Dynflow::Persistence
40
40
  end
41
41
  end
42
42
  end
@@ -98,7 +98,7 @@ module ForemanTasks
98
98
  end
99
99
 
100
100
  initializer 'foreman_tasks.register_paths' do |_app|
101
- ForemanTasks.dynflow.config.eager_load_paths.concat(%W[#{ForemanTasks::Engine.root}/app/lib/actions])
101
+ ForemanTasks.dynflow.config.eager_load_paths.concat(%W[#{ForemanTasks::Engine.root}/app/lib/actions #{ForemanTasks::Engine.root}/app/lib/hooks])
102
102
  end
103
103
 
104
104
  initializer 'foreman_tasks.test_exceptions' do |_app|
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '0.12.2'.freeze
2
+ VERSION = '0.13.0'.freeze
3
3
  end
@@ -0,0 +1,67 @@
1
+ require 'foreman_tasks_test_helper'
2
+
3
+ module Actions
4
+ module Middleware
5
+ class KeepCurrentTaxonomiesTest < ActiveSupport::TestCase
6
+ include ::Dynflow::Testing
7
+
8
+ class TestAction < Support::DummyDynflowAction
9
+ middleware.use KeepCurrentTaxonomies
10
+
11
+ def run; end
12
+ end
13
+
14
+ before do
15
+ @org = mock('organization')
16
+ @org.stubs(:id).returns(1)
17
+ @loc = mock('location')
18
+ @loc.stubs(:id).returns(2)
19
+ end
20
+
21
+ describe 'plan' do
22
+ test 'with current taxonomies set' do
23
+ Organization.expects(:current).twice.returns(@org)
24
+ Location.expects(:current).twice.returns(@loc)
25
+
26
+ @action = create_and_plan_action(TestAction)
27
+ assert_equal(@org.id, @action.input['current_organization_id'])
28
+ assert_equal(@loc.id, @action.input['current_location_id'])
29
+ end
30
+
31
+ test 'with one current taxonomy set (organization)' do
32
+ Organization.expects(:current).twice.returns(@org)
33
+ Location.expects(:current).twice
34
+
35
+ @action = create_and_plan_action(TestAction)
36
+ assert_equal(@org.id, @action.input['current_organization_id'])
37
+ assert_nil(@action.input['current_location_id'])
38
+ end
39
+ end
40
+
41
+ describe 'run' do
42
+ before do
43
+ Organization.stubs(:current).returns(@org)
44
+ Location.stubs(:current).returns(@loc)
45
+
46
+ @action = create_and_plan_action(TestAction)
47
+
48
+ Organization.stubs(:current)
49
+ Location.stubs(:current)
50
+ end
51
+
52
+ test 'with current taxonomies as input' do
53
+ Organization.unscoped.class.any_instance.expects(:find).with(@org.id).returns(@org)
54
+ Location.unscoped.class.any_instance.expects(:find).with(@loc.id).returns(@loc)
55
+
56
+ Organization.expects(:current=).with(@org)
57
+ Location.expects(:current=).with(@loc)
58
+
59
+ Organization.stubs(:current=).with(nil)
60
+ Location.stubs(:current=).with(nil)
61
+
62
+ @action = run_action(@action)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,49 @@
1
+ require 'foreman_tasks_test_helper'
2
+
3
+ module Actions
4
+ module Middleware
5
+ class KeepCurrentUserTest < ActiveSupport::TestCase
6
+ include ::Dynflow::Testing
7
+
8
+ class TestAction < Support::DummyDynflowAction
9
+ middleware.use KeepCurrentUser
10
+
11
+ def run; end
12
+ end
13
+
14
+ before do
15
+ @user = mock('user')
16
+ @user.stubs(:id).returns(1)
17
+ end
18
+
19
+ describe 'plan' do
20
+ test 'with current user set' do
21
+ User.expects(:current).twice.returns(@user)
22
+
23
+ @action = create_and_plan_action(TestAction)
24
+ assert_equal(@user.id, @action.input['current_user_id'])
25
+ end
26
+ end
27
+
28
+ describe 'run' do
29
+ before do
30
+ User.stubs(:current).returns(@user)
31
+
32
+ @action = create_and_plan_action(TestAction)
33
+
34
+ User.stubs(:current)
35
+ end
36
+
37
+ test 'with current user as input' do
38
+ User.unscoped.class.any_instance.expects(:find).with(@user.id).returns(@user)
39
+
40
+ User.expects(:current=).with(@user)
41
+
42
+ User.stubs(:current=).with(nil)
43
+
44
+ @action = run_action(@action)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,87 @@
1
+ require 'foreman_tasks_test_helper'
2
+
3
+ module ForemanTasks
4
+ class RecurringActionTest < ActiveSupport::TestCase
5
+ class HookedAction < Actions::EntryAction
6
+ include Actions::RecurringAction
7
+
8
+ def plan(should_fail, _numbers)
9
+ plan_self(:should_fail => should_fail)
10
+ end
11
+
12
+ def run
13
+ raise "A controlled failure" if input[:should_fail]
14
+ end
15
+ end
16
+
17
+ describe Actions::RecurringAction do
18
+ include ::Dynflow::Testing
19
+
20
+ let(:preset) do
21
+ {
22
+ :minutes => 0,
23
+ :hours => 12,
24
+ :days => 1,
25
+ :months => (Time.zone.now.month + 1) % 12
26
+ }
27
+ end
28
+
29
+ let(:recurring_logic) do
30
+ cronline = ForemanTasks::RecurringLogic.assemble_cronline(preset)
31
+ logic = ForemanTasks::RecurringLogic.new_from_cronline(cronline)
32
+ logic.state = 'active'
33
+ logic.save!
34
+ logic
35
+ end
36
+
37
+ let(:args) { [false, [1, 2, 3]] }
38
+
39
+ let(:recurring_task) do
40
+ recurring_logic.start(HookedAction, *args)
41
+ recurring_logic.tasks.first
42
+ end
43
+
44
+ specify 'it triggers the repeat when task is cancelled' do
45
+ recurring_task.must_be :delayed?
46
+ recurring_logic.tasks.count.must_equal 1
47
+ cancelled_events = recurring_task.execution_plan.cancel
48
+ cancelled_events.each(&:wait!)
49
+ recurring_logic.reload
50
+ recurring_logic.tasks.count.must_equal 2
51
+ new_task = recurring_logic.tasks.find { |task| task.id != recurring_task.id }
52
+ new_task.execution_plan.delay_record.args.must_equal args
53
+ new_task.start_at.must_equal(recurring_task.start_at + 1.year)
54
+ end
55
+
56
+ specify 'it triggers the repeat when the task goes into planned state' do
57
+ delay_options = recurring_logic.generate_delay_options
58
+ task = ForemanTasks.delay HookedAction, delay_options, args
59
+ recurring_logic.tasks.count.must_equal 1
60
+
61
+ # Perform planning of the delayed plan
62
+ task.execution_plan.delay_record.plan
63
+
64
+ # Check a repetition was planned
65
+ recurring_logic.tasks.count.must_equal 2
66
+ end
67
+
68
+ specify 'it does not trigger repeat when failing in run' do
69
+ delay_options = recurring_logic.generate_delay_options
70
+ task = ForemanTasks.delay HookedAction, delay_options, true, args.last
71
+ recurring_logic.tasks.count.must_equal 1
72
+
73
+ # Perform the planning (trigger repeat)
74
+ task.execution_plan.delay_record.plan
75
+ recurring_logic.tasks.count.must_equal 2
76
+
77
+ # Let it fail
78
+ task.execution_plan.delay_record.execute.finished.wait
79
+ task.reload
80
+ task.result.must_equal 'error'
81
+
82
+ # Check no new repetitions were planned
83
+ recurring_logic.tasks.count.must_equal 2
84
+ end
85
+ end
86
+ end
87
+ end
@@ -106,6 +106,13 @@ class RecurringLogicsTest < ActiveSupport::TestCase
106
106
  logic.end_time.must_be_close_to(triggering.end_time, 1.second)
107
107
  end
108
108
 
109
+ it 'cannot trigger tasks when cancelled' do
110
+ recurring_logic = ForemanTasks::RecurringLogic.new_from_cronline('* * * * *')
111
+ recurring_logic.state = 'cancelled'
112
+ recurring_logic.expects(:can_continue?).never
113
+ recurring_logic.trigger_repeat('this is not important', 'neither is this')
114
+ end
115
+
109
116
  describe 'validation' do
110
117
  let(:logic) { FactoryBot.build(:recurring_logic) }
111
118
 
@@ -122,7 +122,7 @@ class TasksTest < ActiveSupport::TestCase
122
122
 
123
123
  describe 'recurring task' do
124
124
  let(:logic) { FactoryBot.build(:recurring_logic) }
125
- let(:task) { FactoryBot.create(:some_task) }
125
+ let(:task) { FactoryBot.create(:dynflow_task) }
126
126
 
127
127
  it 'can indicate it is recurring' do
128
128
  refute task.recurring?
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.12.2
4
+ version: 0.13.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: 2018-03-23 00:00:00.000000000 Z
11
+ date: 2018-04-02 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.29
33
+ version: '1.0'
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.29
40
+ version: '1.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: sinatra
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -144,6 +144,7 @@ files:
144
144
  - app/lib/actions/middleware/rails_executor_wrap.rb
145
145
  - app/lib/actions/middleware/recurring_logic.rb
146
146
  - app/lib/actions/proxy_action.rb
147
+ - app/lib/actions/recurring_action.rb
147
148
  - app/lib/actions/serializers/active_record_serializer.rb
148
149
  - app/lib/proxy_api/foreman_dynflow/dynflow_proxy.rb
149
150
  - app/models/foreman_tasks/concerns/action_subject.rb
@@ -244,12 +245,15 @@ files:
244
245
  - test/factories/triggering_factory.rb
245
246
  - test/foreman_tasks_test_helper.rb
246
247
  - test/helpers/foreman_tasks/tasks_helper_test.rb
248
+ - test/lib/actions/middleware/keep_current_taxonomies_test.rb
249
+ - test/lib/actions/middleware/keep_current_user_test.rb
247
250
  - test/support/dummy_dynflow_action.rb
248
251
  - test/support/dummy_proxy_action.rb
249
252
  - test/support/dummy_task_group.rb
250
253
  - test/tasks/generate_task_actions_test.rb
251
254
  - test/unit/actions/action_with_sub_plans_test.rb
252
255
  - test/unit/actions/proxy_action_test.rb
256
+ - test/unit/actions/recurring_action_test.rb
253
257
  - test/unit/cleaner_test.rb
254
258
  - test/unit/config/environment.rb
255
259
  - test/unit/dynflow_console_authorizer_test.rb
@@ -279,7 +283,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
279
283
  version: '0'
280
284
  requirements: []
281
285
  rubyforge_project:
282
- rubygems_version: 2.6.12
286
+ rubygems_version: 2.6.8
283
287
  signing_key:
284
288
  specification_version: 4
285
289
  summary: Foreman plugin for showing tasks information for resoruces and users
@@ -293,12 +297,15 @@ test_files:
293
297
  - test/factories/triggering_factory.rb
294
298
  - test/foreman_tasks_test_helper.rb
295
299
  - test/helpers/foreman_tasks/tasks_helper_test.rb
300
+ - test/lib/actions/middleware/keep_current_taxonomies_test.rb
301
+ - test/lib/actions/middleware/keep_current_user_test.rb
296
302
  - test/support/dummy_dynflow_action.rb
297
303
  - test/support/dummy_proxy_action.rb
298
304
  - test/support/dummy_task_group.rb
299
305
  - test/tasks/generate_task_actions_test.rb
300
306
  - test/unit/actions/action_with_sub_plans_test.rb
301
307
  - test/unit/actions/proxy_action_test.rb
308
+ - test/unit/actions/recurring_action_test.rb
302
309
  - test/unit/cleaner_test.rb
303
310
  - test/unit/config/environment.rb
304
311
  - test/unit/dynflow_console_authorizer_test.rb