foreman-tasks 0.14.6 → 0.15.0

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,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 675f98353547dd5b378bfe5055c60e976618b39754e4d46e0d361641ee8640e0
4
- data.tar.gz: 2de7b22aaaacdb644d953e0aeb4c0e7ba6083e2f9650248baf021b78d0ff7474
3
+ metadata.gz: e29d69e4d1a7ad804db75f4ce19d20004bdebced6e85e04d2648bcb5c571c2b1
4
+ data.tar.gz: 50e6b2708a35731579806eaef2a13c83dee4c92244db7a1a8f73c712e565891a
5
5
  SHA512:
6
- metadata.gz: bab26f5c355c09ea6d292e2b10fc011d41c769c91fa863985a89a43075d831b2115f5222c3f5c276054f09272fb39e4d03b9957dbb0d75a50b6a7025b4031e57
7
- data.tar.gz: 2651f86adccfd7469c01268c7408115547e94e90723a752a9d047c59c4431204cbb606815b40245893ff8d142657cf65e7a493d063a20d89416fd7f0a917879d
6
+ metadata.gz: 311b48cc5a9cd2d8a502635f70a544b8e73fae9f7495148d67976f5e2ae0615c96ef7ca184b6992923e885abe942f0354e3f6ce0c76d0449f65099c1aede8077
7
+ data.tar.gz: f92ebcdad66e0f02ea0b3f27a954520792fe2e31a0057639fcec59e68658be686db75086c936aae6811b5ded4307cb9effccdec95e0554d7d7b047f6fb7d5762
@@ -21,7 +21,7 @@ module ForemanTasks
21
21
 
22
22
  api :GET, '/tasks/summary', 'Show task summary'
23
23
  def summary
24
- render :json => ForemanTasks::Task::Summarizer.new(resource_scope).summarize_by_status
24
+ render :json => ForemanTasks::Task::Summarizer.new.summarize_by_status
25
25
  end
26
26
 
27
27
  api :GET, '/tasks/:id', 'Show task details'
@@ -160,7 +160,7 @@ module ForemanTasks
160
160
  ForemanTasks.dynflow.world.event(task.external_id,
161
161
  params[:callback][:step_id].to_i,
162
162
  # We need to call .to_unsafe_h to unwrap the hash from ActionController::Parameters
163
- ::Actions::ProxyAction::CallbackData.new(params[:data].to_unsafe_h))
163
+ ::Actions::ProxyAction::CallbackData.new(params[:data].to_unsafe_h, :request_id => ::Logging.mdc['request']))
164
164
  render :json => { :message => 'processing' }.to_json
165
165
  end
166
166
 
@@ -248,7 +248,7 @@ module ForemanTasks
248
248
  end
249
249
 
250
250
  def find_task
251
- @task = resource_scope.find(params[:id])
251
+ @task = Task.find(params[:id])
252
252
  end
253
253
 
254
254
  def resource_scope(_options = {})
@@ -257,7 +257,7 @@ module ForemanTasks
257
257
 
258
258
  def action_permission
259
259
  case params[:action]
260
- when 'bulk_search', 'summary'
260
+ when 'bulk_search'
261
261
  :view
262
262
  when 'bulk_resume'
263
263
  :edit
@@ -6,7 +6,8 @@ module ForemanTasks
6
6
  before_action :restrict_dangerous_actions, :only => [:unlock, :force_unlock]
7
7
 
8
8
  def show
9
- @task = resource_base.find(params[:id])
9
+ @task = Task.find(params[:id])
10
+ render :layout => !request.xhr?
10
11
  end
11
12
 
12
13
  def index
@@ -24,7 +25,7 @@ module ForemanTasks
24
25
  end
25
26
 
26
27
  def sub_tasks
27
- task = resource_base.find(params[:id])
28
+ task = Task.find(params[:id])
28
29
  @tasks = filter(task.sub_tasks)
29
30
  render :index
30
31
  end
@@ -96,10 +97,6 @@ module ForemanTasks
96
97
  ForemanTasks::Task
97
98
  end
98
99
 
99
- def auto_complete_controller_name
100
- '/foreman_tasks/tasks'
101
- end
102
-
103
100
  private
104
101
 
105
102
  def restrict_dangerous_actions
@@ -0,0 +1,59 @@
1
+ module Actions
2
+ module Middleware
3
+ class KeepCurrentRequestID < Dynflow::Middleware
4
+ def delay(*args)
5
+ pass(*args).tap { store_current_request_id }
6
+ end
7
+
8
+ def plan(*args)
9
+ with_current_request_id do
10
+ pass(*args).tap { store_current_request_id }
11
+ end
12
+ end
13
+
14
+ def run(*args)
15
+ restore_current_request_id { pass(*args) }
16
+ end
17
+
18
+ def finalize
19
+ restore_current_request_id { pass }
20
+ end
21
+
22
+ # Run all execution plan lifecycle hooks as the original request_id
23
+ def hook(*args)
24
+ restore_current_request_id { pass(*args) }
25
+ end
26
+
27
+ private
28
+
29
+ def with_current_request_id
30
+ if action.input[:current_request_id].nil?
31
+ yield
32
+ else
33
+ restore_current_request_id { yield }
34
+ end
35
+ end
36
+
37
+ def store_current_request_id
38
+ action.input[:current_request_id] = ::Logging.mdc['request']
39
+ end
40
+
41
+ def restore_current_request_id
42
+ if (restored_id = action.input[:current_request_id]).present?
43
+ old_id = ::Logging.mdc['request']
44
+ if old_id.present? && old_id != restored_id
45
+ action.action_logger.warn(_('Changing request id %{request_id} to saved id %{saved_id}') % { :saved_id => restored_id, :request_id => old_id })
46
+ end
47
+ ::Logging.mdc['request'] = restored_id
48
+ end
49
+ yield
50
+ ensure
51
+ # Reset to original request id only when not nil
52
+ # Otherwise, keep the id until it's cleaned in Dynflow's run_user_code block
53
+ # so that it will stay valid for the rest of the processing of the current step
54
+ # (even outside of the middleware lifecycle)
55
+ ::Logging.mdc['request'] = old_id if old_id.present?
56
+ end
57
+ end
58
+ end
59
+ end
@@ -8,10 +8,11 @@ module Actions
8
8
  execution_plan_hooks.use :wipe_secrets!, :on => :stopped
9
9
 
10
10
  class CallbackData
11
- attr_reader :data
11
+ attr_reader :data, :meta
12
12
 
13
- def initialize(data)
13
+ def initialize(data, meta = {})
14
14
  @data = data
15
+ @meta = meta
15
16
  end
16
17
  end
17
18
 
@@ -46,7 +47,7 @@ module Actions
46
47
  when ::Dynflow::Action::Cancellable::Abort
47
48
  abort_proxy_task
48
49
  when CallbackData
49
- on_data(event.data)
50
+ on_data(event.data, event.meta)
50
51
  when ProxyActionMissing
51
52
  on_proxy_action_missing
52
53
  when ProxyActionStopped
@@ -106,7 +107,8 @@ module Actions
106
107
  end
107
108
 
108
109
  # @override to put custom logic on event handling
109
- def on_data(data)
110
+ def on_data(data, meta = {})
111
+ action_logger.info(_('Event delivered by request %{request_id}') % { :request_id => meta[:request_id] }) if meta[:request_id].present?
110
112
  output[:proxy_output] = data
111
113
  end
112
114
 
@@ -1,3 +1,5 @@
1
+ require 'securerandom'
2
+
1
3
  module Actions
2
4
  module RecurringAction
3
5
  # When included sets the base action to use the RecurringLogic middleware and configures
@@ -10,11 +12,15 @@ module Actions
10
12
  # Hook to be called when a repetition needs to be triggered. This either happens when the plan goes into planned state
11
13
  # or when it fails.
12
14
  def trigger_repeat(execution_plan)
15
+ request_id = ::Logging.mdc['request']
16
+ ::Logging.mdc['request'] = SecureRandom.uuid
13
17
  if execution_plan.delay_record && recurring_logic_task_group
14
18
  args = execution_plan.delay_record.args
15
19
  logic = recurring_logic_task_group.recurring_logic
16
20
  logic.trigger_repeat_after(task.start_at, self.class, *args)
17
21
  end
22
+ ensure
23
+ ::Logging.mdc['request'] = request_id
18
24
  end
19
25
 
20
26
  private
@@ -1,19 +1,15 @@
1
1
  module ForemanTasks
2
2
  class Task::Summarizer
3
- def initialize(scope = Task.authorized)
4
- @scope = scope
5
- end
6
-
7
3
  def summarize_by_status(since = nil)
8
- result = @scope.where("result <> 'success'")
9
- .select('count(state) AS count, state, result, max(started_at) AS started_at')
10
- .group(:state, :result).order(:state)
4
+ result = ::ForemanTasks::Task.where("result <> 'success'")
5
+ .select('count(state) AS count, state, result, max(started_at) AS started_at')
6
+ .group(:state, :result).order(:state)
11
7
  result = result.where('started_at > ?', since) if since
12
8
  result
13
9
  end
14
10
 
15
11
  def latest_tasks_in_errors_warning(limit = 5)
16
- @scope.where('result in (?)', %w[error warning]).order('started_at DESC').limit(limit)
12
+ ::ForemanTasks::Task.where('result in (?)', %w[error warning]).order('started_at DESC').limit(limit)
17
13
  end
18
14
  end
19
15
  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", '~> 1.0', '>= 1.1.5'
32
+ s.add_dependency "dynflow", '>= 1.2.1'
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
@@ -119,6 +119,7 @@ module ForemanTasks
119
119
  world.middleware.use Actions::Middleware::KeepCurrentTaxonomies
120
120
  world.middleware.use Actions::Middleware::KeepCurrentUser
121
121
  world.middleware.use Actions::Middleware::KeepCurrentTimezone
122
+ world.middleware.use Actions::Middleware::KeepCurrentRequestID
122
123
  end
123
124
 
124
125
  ::ForemanTasks.dynflow.config.on_init do |world|
@@ -1,3 +1,3 @@
1
1
  module ForemanTasks
2
- VERSION = '0.14.6'.freeze
2
+ VERSION = '0.15.0'.freeze
3
3
  end
@@ -25,13 +25,6 @@ module ForemanTasks
25
25
  assert_response :missing
26
26
  assert_includes @response.body, 'Resource task not found by id'
27
27
  end
28
-
29
- it 'does not show task the user is not allowed to see' do
30
- setup_user('view', 'foreman_tasks', 'owner.id = current_user')
31
- get :show, params: { id: FactoryBot.create(:some_task).id },
32
- session: set_session_user(User.current)
33
- assert_response :not_found
34
- end
35
28
  end
36
29
 
37
30
  describe 'GET /api/tasks/summary' do
@@ -48,41 +41,24 @@ module ForemanTasks
48
41
  suspend
49
42
  end
50
43
  end
51
-
52
- def self.while_suspended
53
- triggered = ForemanTasks.trigger(DummyTestSummaryAction, true)
54
- wait_for { ForemanTasks::Task.find_by(external_id: triggered.id).state == 'running' }
55
- wait_for do
56
- w = ForemanTasks.dynflow.world
57
- w.persistence.load_step(triggered.id, 2, w).state == :suspended
58
- end
59
- yield
60
- ForemanTasks.dynflow.world.event(triggered.id, 2, nil)
61
- triggered.finished.wait
62
- end
63
44
  end
64
45
 
65
46
  test_attributes :pid => 'bdcab413-a25d-4fe1-9db4-b50b5c31ebce'
66
47
  it 'get tasks summary' do
67
- DummyTestSummaryAction.while_suspended do
68
- get :summary
69
- assert_response :success
70
- response = JSON.parse(@response.body)
71
- assert_kind_of Array, response
72
- assert_not response.empty?
73
- assert_kind_of Hash, response[0]
74
- end
75
- end
76
-
77
- it 'gets tasks summary only for tasks the user is allowed to see' do
78
- DummyTestSummaryAction.while_suspended do
79
- setup_user('view', 'foreman_tasks', 'owner.id = current_user')
80
- get :summary
81
- assert_response :success
82
- response = JSON.parse(@response.body)
83
- assert_kind_of Array, response
84
- assert response.empty?
48
+ triggered = ForemanTasks.trigger(DummyTestSummaryAction, true)
49
+ wait_for { ForemanTasks::Task.find_by(external_id: triggered.id).state == 'running' }
50
+ wait_for do
51
+ w = ForemanTasks.dynflow.world
52
+ w.persistence.load_step(triggered.id, 2, w).state == :suspended
85
53
  end
54
+ get :summary
55
+ assert_response :success
56
+ response = JSON.parse(@response.body)
57
+ assert_kind_of Array, response
58
+ assert_not response.empty?
59
+ assert_kind_of Hash, response[0]
60
+ ForemanTasks.dynflow.world.event(triggered.id, 2, nil)
61
+ triggered.finished.wait
86
62
  end
87
63
  end
88
64
 
@@ -32,24 +32,6 @@ module ForemanTasks
32
32
  assert_include response.body.lines[1], 'Some action'
33
33
  end
34
34
 
35
- describe 'show' do
36
- it 'does not allow user without permissions to see task details' do
37
- setup_user('view', 'foreman_tasks', 'owner.id = current_user')
38
- get :show, params: { id: FactoryBot.create(:some_task).id },
39
- session: set_session_user(User.current)
40
- assert_response :not_found
41
- end
42
- end
43
-
44
- describe 'sub_tasks' do
45
- it 'does not allow user without permissions to see task details' do
46
- setup_user('view', 'foreman_tasks', 'owner.id = current_user')
47
- get :sub_tasks, params: { id: FactoryBot.create(:some_task).id },
48
- session: set_session_user(User.current)
49
- assert_response :not_found
50
- end
51
- end
52
-
53
35
  describe 'taxonomy scoping' do
54
36
  let(:organizations) { (0..1).map { FactoryBot.create(:organization) } }
55
37
  let(:tasks) { organizations.map { |o| linked_task(o) } + [FactoryBot.create(:some_task)] }
@@ -0,0 +1,50 @@
1
+ require 'foreman_tasks_test_helper'
2
+
3
+ module ForemanTasks
4
+ class KeepCurrentRequestIDTest < ActiveSupport::TestCase
5
+ class DummyAction < Actions::EntryAction
6
+ middleware.use ::Actions::Middleware::KeepCurrentRequestID
7
+
8
+ def plan(plan = false)
9
+ plan_self if plan
10
+ end
11
+
12
+ def run
13
+ output[:run_result] = ::Logging.mdc['request']
14
+ end
15
+
16
+ def finalize
17
+ output[:finalize_result] = ::Logging.mdc['request']
18
+ end
19
+ end
20
+
21
+ describe Actions::Middleware::KeepCurrentRequestID do
22
+ include ::Dynflow::Testing
23
+
24
+ before { @old_id = ::Logging.mdc['request'] }
25
+ after { ::Logging.mdc['request'] = @old_id }
26
+
27
+ let(:expected_id) { 'an_id' }
28
+
29
+ it 'stores the id on planning' do
30
+ ::Logging.mdc['request'] = expected_id
31
+ action = create_and_plan_action(DummyAction)
32
+ action.input[:current_request_id].must_equal expected_id
33
+ end
34
+
35
+ it 'restores the id for run' do
36
+ ::Logging.mdc['request'] = expected_id
37
+ action = create_and_plan_action(DummyAction, true)
38
+ action = run_action action
39
+ action.output[:run_result].must_equal expected_id
40
+ end
41
+
42
+ it 'restores the id for finalize' do
43
+ ::Logging.mdc['request'] = expected_id
44
+ action = create_and_plan_action(DummyAction, true)
45
+ action = finalize_action(run_action(action))
46
+ action.output[:finalize_result].must_equal expected_id
47
+ end
48
+ end
49
+ end
50
+ end
@@ -7,13 +7,13 @@ module Support
7
7
 
8
8
  def initialize
9
9
  @log = Hash.new { |h, k| h[k] = [] }
10
- @task_triggered = defined?(Concurrent::Promises) ? Concurrent::Promises.resolvable_future : Concurrent.future
10
+ @task_triggered = Concurrent::Promises.resolvable_future
11
11
  @uuid = SecureRandom.uuid
12
12
  end
13
13
 
14
14
  def trigger_task(*args)
15
15
  @log[:trigger_task] << args
16
- defined?(Concurrent::Promises) ? @task_triggered.fulfill(true) : @task_triggered.success(true)
16
+ @task_triggered.fulfill(true)
17
17
  { 'task_id' => @uuid }
18
18
  end
19
19
 
@@ -82,6 +82,27 @@ module ForemanTasks
82
82
  # Check no new repetitions were planned
83
83
  recurring_logic.tasks.count.must_equal 2
84
84
  end
85
+
86
+ specify 'it resets the request id on repetition' do
87
+ begin
88
+ expected_id = 'an_id'
89
+ new_id = SecureRandom.uuid
90
+ old_id = ::Logging.mdc['request']
91
+ ::Logging.mdc['request'] = expected_id
92
+
93
+ delay_options = recurring_logic.generate_delay_options
94
+ task = ForemanTasks.delay HookedAction, delay_options, true, args.last
95
+ task.input[:current_request_id].must_equal expected_id
96
+
97
+ SecureRandom.stubs(:uuid).returns(new_id)
98
+ # Perform the planning (trigger repeat)
99
+ task.execution_plan.delay_record.plan
100
+ repetition = recurring_logic.tasks.find { |t| t.id != task.id }
101
+ repetition.input[:current_request_id].must_equal new_id
102
+ ensure
103
+ ::Logging.mdc['request'] = old_id
104
+ end
105
+ end
85
106
  end
86
107
  end
87
108
  end
@@ -84,11 +84,9 @@ class TasksTest < ActiveSupport::TestCase
84
84
  end
85
85
 
86
86
  describe 'task without valid execution plan' do
87
- let(:missing_task_uuid) { '11111111-2222-3333-4444-555555555555' }
88
-
89
87
  let(:task) do
90
88
  task = FactoryBot.create(:dynflow_task).tap do |task|
91
- task.external_id = missing_task_uuid
89
+ task.external_id = 'missing-task'
92
90
  task.save
93
91
  end
94
92
  ForemanTasks::Task.find(task.id)
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.14.6
4
+ version: 0.15.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: 2019-07-16 00:00:00.000000000 Z
11
+ date: 2019-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: foreman-tasks-core
@@ -28,22 +28,16 @@ dependencies:
28
28
  name: dynflow
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '1.0'
34
31
  - - ">="
35
32
  - !ruby/object:Gem::Version
36
- version: 1.1.5
33
+ version: 1.2.1
37
34
  type: :runtime
38
35
  prerelease: false
39
36
  version_requirements: !ruby/object:Gem::Requirement
40
37
  requirements:
41
- - - "~>"
42
- - !ruby/object:Gem::Version
43
- version: '1.0'
44
38
  - - ">="
45
39
  - !ruby/object:Gem::Version
46
- version: 1.1.5
40
+ version: 1.2.1
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: sinatra
49
43
  requirement: !ruby/object:Gem::Requirement
@@ -146,6 +140,7 @@ files:
146
140
  - app/lib/actions/helpers/with_delegated_action.rb
147
141
  - app/lib/actions/middleware/hide_secrets.rb
148
142
  - app/lib/actions/middleware/inherit_task_groups.rb
143
+ - app/lib/actions/middleware/keep_current_request_id.rb
149
144
  - app/lib/actions/middleware/keep_current_taxonomies.rb
150
145
  - app/lib/actions/middleware/keep_current_timezone.rb
151
146
  - app/lib/actions/middleware/keep_current_user.rb
@@ -258,6 +253,7 @@ files:
258
253
  - test/factories/triggering_factory.rb
259
254
  - test/foreman_tasks_test_helper.rb
260
255
  - test/helpers/foreman_tasks/tasks_helper_test.rb
256
+ - test/lib/actions/middleware/keep_current_request_id_test.rb
261
257
  - test/lib/actions/middleware/keep_current_taxonomies_test.rb
262
258
  - test/lib/actions/middleware/keep_current_timezone_test.rb
263
259
  - test/lib/actions/middleware/keep_current_user_test.rb
@@ -299,7 +295,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
299
295
  - !ruby/object:Gem::Version
300
296
  version: '0'
301
297
  requirements: []
302
- rubygems_version: 3.0.3
298
+ rubyforge_project:
299
+ rubygems_version: 2.7.6
303
300
  signing_key:
304
301
  specification_version: 4
305
302
  summary: Foreman plugin for showing tasks information for resoruces and users
@@ -313,6 +310,7 @@ test_files:
313
310
  - test/factories/triggering_factory.rb
314
311
  - test/foreman_tasks_test_helper.rb
315
312
  - test/helpers/foreman_tasks/tasks_helper_test.rb
313
+ - test/lib/actions/middleware/keep_current_request_id_test.rb
316
314
  - test/lib/actions/middleware/keep_current_taxonomies_test.rb
317
315
  - test/lib/actions/middleware/keep_current_timezone_test.rb
318
316
  - test/lib/actions/middleware/keep_current_user_test.rb