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 +4 -4
- data/lib/dynflow/action.rb +6 -6
- data/lib/dynflow/action/missing.rb +6 -3
- data/lib/dynflow/errors.rb +3 -0
- data/lib/dynflow/execution_plan.rb +60 -1
- data/lib/dynflow/testing/dummy_world.rb +1 -1
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/world.rb +12 -2
- data/test/execution_plan_test.rb +22 -0
- data/test/support/code_workflow_example.rb +1 -1
- data/web/views/show.erb +24 -14
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b848a13e89e2a73d08a697b3ac772e5eb0d9fc0
|
4
|
+
data.tar.gz: 9d2693f7fcbf3670fe8b82b6d3aaea7de6a43bf7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce3762a492003730c50d5dd3ec537ac0bbd18f3dc4d37227f20e6d85fcde1b0ef18fb39fba4da12058398480ba4cc85d872e386fa677c0bf8cc89e73d5769396
|
7
|
+
data.tar.gz: 0ed13a183242fcd1110b9f02805872918e9d89132891c127622eb3aa0343bcb4a63e6f2a14fe3271ac0f23ba7cda1a0c5eab59a5dc448c41431c223e0c0ed228
|
data/lib/dynflow/action.rb
CHANGED
@@ -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,
|
165
|
+
def set_plan_context(execution_plan, triggering_action, from_subscription)
|
166
166
|
phase! Plan
|
167
167
|
@execution_plan = Type! execution_plan, ExecutionPlan
|
168
|
-
@
|
168
|
+
@triggering_action = Type! triggering_action, Action, NilClass
|
169
169
|
@from_subscription = Type! from_subscription, TrueClass, FalseClass
|
170
170
|
end
|
171
171
|
|
172
|
-
def
|
172
|
+
def triggering_action
|
173
173
|
phase! Plan
|
174
|
-
@
|
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(
|
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
|
-
@
|
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,
|
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,
|
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,
|
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
|
data/lib/dynflow/errors.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/dynflow/version.rb
CHANGED
data/lib/dynflow/world.rb
CHANGED
@@ -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
|
289
|
-
|
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
|
data/test/execution_plan_test.rb
CHANGED
@@ -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
|
data/web/views/show.erb
CHANGED
@@ -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
|
-
|
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
|
-
|
60
|
-
<
|
61
|
-
<
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
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.
|
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
|
12
|
+
date: 2017-05-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|