dynflow 0.8.23 → 0.8.24

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
  SHA1:
3
- metadata.gz: 877042adab09ea7aeb07b0459738b0016e06998c
4
- data.tar.gz: b2ffef502d8ab247e104ccb54b110ba84252e3c3
3
+ metadata.gz: 8b848a13e89e2a73d08a697b3ac772e5eb0d9fc0
4
+ data.tar.gz: 9d2693f7fcbf3670fe8b82b6d3aaea7de6a43bf7
5
5
  SHA512:
6
- metadata.gz: 06dd15b53061f0db20c3c488948d50a6e265fec7fdf99707d934f6a7c0efe856d96c1b6e7829ea0469f9d8cefa6abfc2734545e3b0ab880e0ad493e492518aa2
7
- data.tar.gz: 410f852667748f00077feae8b7f066fca50478c7d481db1d75542bd2c260406b2277c807f18a41c4de7c7228bd111c71d0161bb1f6d857fee29d1506bbefbfc8
6
+ metadata.gz: ce3762a492003730c50d5dd3ec537ac0bbd18f3dc4d37227f20e6d85fcde1b0ef18fb39fba4da12058398480ba4cc85d872e386fa677c0bf8cc89e73d5769396
7
+ data.tar.gz: 0ed13a183242fcd1110b9f02805872918e9d89132891c127622eb3aa0343bcb4a63e6f2a14fe3271ac0f23ba7cda1a0c5eab59a5dc448c41431c223e0c0ed228
@@ -162,16 +162,16 @@ module Dynflow
162
162
  @caller_action = world.persistence.load_action_for_presentation(caller_execution_plan, @caller_action_id)
163
163
  end
164
164
 
165
- def set_plan_context(execution_plan, trigger, from_subscription)
165
+ def set_plan_context(execution_plan, triggering_action, from_subscription)
166
166
  phase! Plan
167
167
  @execution_plan = Type! execution_plan, ExecutionPlan
168
- @trigger = Type! trigger, Action, NilClass
168
+ @triggering_action = Type! triggering_action, Action, NilClass
169
169
  @from_subscription = Type! from_subscription, TrueClass, FalseClass
170
170
  end
171
171
 
172
- def trigger
172
+ def triggering_action
173
173
  phase! Plan
174
- @trigger
174
+ @triggering_action
175
175
  end
176
176
 
177
177
  def from_subscription?
@@ -330,7 +330,7 @@ module Dynflow
330
330
  # if the action is triggered by subscription, by default use the
331
331
  # input of parent action.
332
332
  # should be replaced by referencing the input from input format
333
- plan_self(input.merge(trigger.input))
333
+ plan_self(input.merge(triggering_action.input))
334
334
  else
335
335
  # in this case, the action was triggered by plan_action. Use
336
336
  # the argument specified there.
@@ -546,7 +546,7 @@ module Dynflow
546
546
  end
547
547
 
548
548
  def root_action?
549
- @trigger.nil?
549
+ @triggering_action.nil?
550
550
  end
551
551
  end
552
552
  # rubocop:enable Metrics/ClassLength
@@ -12,15 +12,18 @@ module Dynflow
12
12
  end
13
13
 
14
14
  def plan(*args)
15
- raise StandardError, "This action is not meant to be planned"
15
+ raise StandardError,
16
+ "The action class was not found and therefore plan phase failed, this can happen if the action was added/renamed but the executor was not restarted."
16
17
  end
17
18
 
18
19
  def run
19
- raise StandardError, "This action is not meant to be run"
20
+ raise StandardError,
21
+ "The action class was not found and therefore run phase failed, this can happen if the action was added/renamed but the executor was not restarted."
20
22
  end
21
23
 
22
24
  def finalize
23
- raise StandardError, "This action is not meant to be finalized"
25
+ raise StandardError,
26
+ "The action class was not found and therefore finalize phase failed, this can happen if the action was added/renamed but the executor was not restarted."
24
27
  end
25
28
  end
26
29
  end
@@ -31,6 +31,9 @@ module Dynflow
31
31
  end
32
32
  end
33
33
 
34
+ class DataConsistencyError < Dynflow::Error
35
+ end
36
+
34
37
  class PersistenceError < Dynflow::Error
35
38
  def self.delegate(original_exception)
36
39
  self.new("caused by #{original_exception.class}: #{original_exception.message}").tap do |e|
@@ -5,6 +5,37 @@ module Dynflow
5
5
  # TODO extract planning logic to an extra class ExecutionPlanner
6
6
  class ExecutionPlan < Serializable
7
7
 
8
+ # a fallback object representing a plan with some corrupted data,
9
+ # preventing to load the whole plan properly, this can be used for presenting
10
+ # at least some data and not running into internal server errors
11
+ class InvalidPlan
12
+ attr_reader :exception, :id, :label, :state,
13
+ :started_at, :ended_at,
14
+ :execution_time, :real_time, :execution_history
15
+
16
+ def initialize(exception, id, label, state,
17
+ started_at = nil, ended_at = nil,
18
+ execution_time = nil, real_time = nil, execution_history = nil)
19
+ @exception = exception
20
+ @id = id
21
+ @label = label || 'N/A'
22
+ @state = state
23
+ @started_at = started_at
24
+ @ended_at = ended_at
25
+ @execution_time = execution_time
26
+ @real_time = real_time
27
+ @execution_history = execution_history || []
28
+ end
29
+
30
+ def valid?
31
+ false
32
+ end
33
+
34
+ def result
35
+ 'N/A'
36
+ end
37
+ end
38
+
8
39
  include Algebrick::TypeCheck
9
40
  include Stateful
10
41
 
@@ -69,6 +100,10 @@ module Dynflow
69
100
  @steps = steps
70
101
  end
71
102
 
103
+ def valid?
104
+ true
105
+ end
106
+
72
107
  def logger
73
108
  @world.logger
74
109
  end
@@ -377,6 +412,25 @@ module Dynflow
377
412
  hash[:execution_time].to_f,
378
413
  hash[:real_time].to_f,
379
414
  ExecutionHistory.new_from_hash(hash[:execution_history]))
415
+ rescue => plan_exception
416
+ begin
417
+ world.logger.error("Could not load execution plan #{execution_plan_id}")
418
+ world.logger.error(plan_exception)
419
+ InvalidPlan.new(plan_exception, execution_plan_id,
420
+ hash[:label],
421
+ hash[:state],
422
+ string_to_time(hash[:started_at]),
423
+ string_to_time(hash[:ended_at]),
424
+ hash[:execution_time].to_f,
425
+ hash[:real_time].to_f,
426
+ ExecutionHistory.new_from_hash(hash[:execution_history]))
427
+ rescue => invalid_plan_exception
428
+ world.logger.error("Could not even load a fallback execution plan for #{execution_plan_id}")
429
+ world.logger.error(invalid_plan_exception)
430
+ InvalidPlan.new(invalid_plan_exception, execution_plan_id,
431
+ hash[:label],
432
+ hash[:state])
433
+ end
380
434
  end
381
435
 
382
436
  def compute_execution_time
@@ -439,7 +493,12 @@ module Dynflow
439
493
  end
440
494
  # to make sure to we preserve the order of the steps
441
495
  step_ids.inject({}) do |hash, step_id|
442
- hash[step_id.to_i] = ids_to_steps[step_id.to_i]
496
+ step = ids_to_steps[step_id.to_i]
497
+ if step.nil?
498
+ raise Errors::DataConsistencyError, "Could not load steps for execution plan #{execution_plan_id}"
499
+ else
500
+ hash[step_id.to_i] = step
501
+ end
443
502
  hash
444
503
  end
445
504
  end
@@ -7,7 +7,7 @@ module Dynflow
7
7
  attr_reader :clock, :executor, :middleware
8
8
  attr_accessor :action
9
9
 
10
- def initialize
10
+ def initialize(_config = nil)
11
11
  @logger_adapter = Testing.logger_adapter
12
12
  @clock = ManagedClock.new
13
13
  @executor = DummyExecutor.new(self)
@@ -1,3 +1,3 @@
1
1
  module Dynflow
2
- VERSION = '0.8.23'
2
+ VERSION = '0.8.24'
3
3
  end
@@ -285,8 +285,18 @@ module Dynflow
285
285
  def invalidate_execution_lock(execution_lock)
286
286
  begin
287
287
  plan = persistence.load_execution_plan(execution_lock.execution_plan_id)
288
- rescue KeyError => e
289
- logger.error "invalidated execution plan #{execution_lock.execution_plan_id} missing, skipping"
288
+ rescue => e
289
+ if e.is_a?(KeyError)
290
+ logger.error "invalidated execution plan #{execution_lock.execution_plan_id} missing, skipping"
291
+ else
292
+ logger.error e
293
+ logger.error "unexpected error when invalidating execution plan #{execution_lock.execution_plan_id}, skipping"
294
+ end
295
+ coordinator.release(execution_lock)
296
+ return
297
+ end
298
+ unless plan.valid?
299
+ logger.error "invalid plan #{plan.id}, skipping"
290
300
  coordinator.release(execution_lock)
291
301
  return
292
302
  end
@@ -26,8 +26,10 @@ module Dynflow
26
26
  describe 'serialized execution plan' do
27
27
 
28
28
  before { execution_plan.save }
29
+ after { world.persistence.delete_execution_plans(:uuid => execution_plan.id) }
29
30
 
30
31
  it 'restores the plan properly' do
32
+ assert deserialized_execution_plan.valid?
31
33
  deserialized_execution_plan.id.must_equal execution_plan.id
32
34
  deserialized_execution_plan.label.must_equal execution_plan.label
33
35
 
@@ -41,6 +43,26 @@ module Dynflow
41
43
  assert_run_flow_equal execution_plan, deserialized_execution_plan
42
44
  end
43
45
 
46
+ it 'handles issues with loading the data' do
47
+ world.persistence.adapter.send(:table, :step)
48
+ .where(execution_plan_uuid: execution_plan.id).delete
49
+ refute deserialized_execution_plan.valid?
50
+ assert_equal Dynflow::Errors::DataConsistencyError, deserialized_execution_plan.exception.class
51
+ [:label, :state, :started_at, :ended_at].each do |attr|
52
+ assert_equal execution_plan.send(attr).to_s,
53
+ deserialized_execution_plan.send(attr).to_s,
54
+ "invalid plan is supposed to still store #{attr}"
55
+ end
56
+ [:execution_time, :real_time].each do |attr|
57
+ assert_equal execution_plan.send(attr).to_f,
58
+ deserialized_execution_plan.send(attr).to_f,
59
+ "invalid plan is supposed to still store #{attr}"
60
+ end
61
+ assert_equal execution_plan.execution_history.events,
62
+ deserialized_execution_plan.execution_history.events,
63
+ "invalid plan is supposed to still store execution history"
64
+ end
65
+
44
66
  end
45
67
 
46
68
  end
@@ -110,7 +110,7 @@ module Support
110
110
  end
111
111
 
112
112
  def plan(*args)
113
- plan_self(:triage => trigger.output)
113
+ plan_self(:triage => triggering_action.output)
114
114
  end
115
115
 
116
116
  def run
@@ -12,7 +12,7 @@
12
12
  <% if @plan.state == :paused %>
13
13
  <a href="<%= url("/#{@plan.id}/resume") %>" class="postlink">Resume</a>
14
14
  <% end %>
15
- <% if @plan.cancellable? %>
15
+ <% if @plan.valid? && @plan.cancellable? %>
16
16
  <a href="<%= url("/#{@plan.id}/cancel") %>" class="postlink">Cancel</a>
17
17
  <% end %>
18
18
  </p>
@@ -52,23 +52,33 @@
52
52
 
53
53
  <div class="tab-content">
54
54
  <div class="tab-pane" id="plan">
55
- <%= erb :plan_step, locals: { step: @plan.root_plan_step } %>
55
+ <% if @plan.valid? %>
56
+ <%= erb :plan_step, locals: { step: @plan.root_plan_step } %>
57
+ <% else %>
58
+ N/A
59
+ <% end %>
56
60
  </div>
57
61
  <div class="tab-pane active" id="run">
58
-
59
- <table class="flow-hint">
60
- <tr>
61
- <td class="border sequence"> </td>
62
- <td>sequence</td>
63
- <td class="border concurrence"> </td>
64
- <td>concurrence</td>
65
- </tr>
66
- </table>
67
-
68
- <%= erb :flow, locals: { flow: @plan.run_flow } %>
62
+ <% if @plan.valid? %>
63
+ <%= erb :flow, locals: { flow: @plan.run_flow } %>
64
+ <table class="flow-hint">
65
+ <tr>
66
+ <td class="border sequence"> </td>
67
+ <td>sequence</td>
68
+ <td class="border concurrence"> </td>
69
+ <td>concurrence</td>
70
+ </tr>
71
+ </table>
72
+ <% else %>
73
+ N/A
74
+ <% end %>
69
75
  </div>
70
76
  <div class="tab-pane" id="finalize">
71
- <%= erb :flow, locals: { flow: @plan.finalize_flow } %>
77
+ <% if @plan.valid? %>
78
+ <%= erb :flow, locals: { flow: @plan.finalize_flow } %>
79
+ <% else %>
80
+ N/A
81
+ <% end %>
72
82
  </div>
73
83
  <div class="tab-pane" id="execution-history">
74
84
  <%= erb :execution_history %>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.23
4
+ version: 0.8.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Necas
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-03-27 00:00:00.000000000 Z
12
+ date: 2017-05-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json