foreman-tasks 0.14.6 → 0.15.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: 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