dynflow 0.8.23 → 0.8.24

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: 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