dynflow 0.2.0 → 0.3.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.
- data/.travis.yml +1 -0
- data/lib/dynflow.rb +1 -0
- data/lib/dynflow/action.rb +5 -3
- data/lib/dynflow/action/finalize_phase.rb +3 -1
- data/lib/dynflow/action/plan_phase.rb +4 -2
- data/lib/dynflow/action/run_phase.rb +3 -1
- data/lib/dynflow/daemon.rb +1 -0
- data/lib/dynflow/execution_plan.rb +6 -4
- data/lib/dynflow/executors/abstract.rb +12 -0
- data/lib/dynflow/executors/parallel.rb +16 -35
- data/lib/dynflow/executors/parallel/core.rb +13 -8
- data/lib/dynflow/executors/parallel/execution_plan_manager.rb +21 -29
- data/lib/dynflow/executors/parallel/flow_manager.rb +0 -3
- data/lib/dynflow/executors/parallel/running_steps_manager.rb +5 -3
- data/lib/dynflow/executors/parallel/sequential_manager.rb +6 -2
- data/lib/dynflow/executors/parallel/worker.rb +5 -4
- data/lib/dynflow/executors/remote_via_socket.rb +7 -2
- data/lib/dynflow/executors/remote_via_socket/core.rb +66 -32
- data/lib/dynflow/future.rb +1 -1
- data/lib/dynflow/listeners/abstract.rb +4 -0
- data/lib/dynflow/listeners/serialization.rb +42 -8
- data/lib/dynflow/listeners/socket.rb +49 -19
- data/lib/dynflow/middleware.rb +46 -0
- data/lib/dynflow/middleware/action.rb +9 -0
- data/lib/dynflow/middleware/register.rb +32 -0
- data/lib/dynflow/middleware/resolver.rb +63 -0
- data/lib/dynflow/middleware/stack.rb +29 -0
- data/lib/dynflow/middleware/world.rb +58 -0
- data/lib/dynflow/simple_world.rb +1 -0
- data/lib/dynflow/testing/dummy_world.rb +3 -1
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/web_console.rb +7 -2
- data/lib/dynflow/world.rb +29 -9
- data/test/action_test.rb +5 -6
- data/test/execution_plan_test.rb +10 -11
- data/test/executor_test.rb +152 -89
- data/test/middleware_test.rb +109 -0
- data/test/remote_via_socket_test.rb +166 -0
- data/test/{code_workflow_example.rb → support/code_workflow_example.rb} +39 -30
- data/test/support/middleware_example.rb +132 -0
- data/test/test_helper.rb +18 -16
- data/test/testing_test.rb +4 -3
- data/test/web_console_test.rb +1 -2
- metadata +16 -4
data/lib/dynflow/simple_world.rb
CHANGED
@@ -4,13 +4,14 @@ module Dynflow
|
|
4
4
|
extend Mimic
|
5
5
|
mimic! World
|
6
6
|
|
7
|
-
attr_reader :clock, :executor
|
7
|
+
attr_reader :clock, :executor, :middleware
|
8
8
|
attr_accessor :action
|
9
9
|
|
10
10
|
def initialize
|
11
11
|
@logger_adapter = Testing.logger_adapter
|
12
12
|
@clock = ManagedClock.new
|
13
13
|
@executor = DummyExecutor.new(self)
|
14
|
+
@middleware = Middleware::World.new
|
14
15
|
end
|
15
16
|
|
16
17
|
def action_logger
|
@@ -28,6 +29,7 @@ module Dynflow
|
|
28
29
|
def event(execution_plan_id, step_id, event, future = Future.new)
|
29
30
|
executor.event execution_plan_id, step_id, event, future
|
30
31
|
end
|
32
|
+
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
data/lib/dynflow/version.rb
CHANGED
data/lib/dynflow/web_console.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'dynflow'
|
2
2
|
require 'pp'
|
3
3
|
require 'sinatra'
|
4
|
+
require 'yaml'
|
4
5
|
|
5
6
|
module Dynflow
|
6
7
|
class WebConsole < Sinatra::Base
|
@@ -24,12 +25,16 @@ module Dynflow
|
|
24
25
|
settings.world
|
25
26
|
end
|
26
27
|
|
28
|
+
def prettify_value(value)
|
29
|
+
YAML.dump(value)
|
30
|
+
end
|
31
|
+
|
27
32
|
def prettyprint(value)
|
28
33
|
value = prettyprint_references(value)
|
29
34
|
if value
|
30
|
-
pretty_value = value
|
35
|
+
pretty_value = prettify_value(value)
|
31
36
|
<<-HTML
|
32
|
-
<pre class="prettyprint">#{h(pretty_value)}</pre>
|
37
|
+
<pre class="prettyprint lang-yaml">#{h(pretty_value)}</pre>
|
33
38
|
HTML
|
34
39
|
else
|
35
40
|
""
|
data/lib/dynflow/world.rb
CHANGED
@@ -3,7 +3,7 @@ module Dynflow
|
|
3
3
|
include Algebrick::TypeCheck
|
4
4
|
|
5
5
|
attr_reader :executor, :persistence, :transaction_adapter, :action_classes, :subscription_index,
|
6
|
-
:logger_adapter, :options
|
6
|
+
:logger_adapter, :options, :middleware
|
7
7
|
|
8
8
|
def initialize(options_hash = {})
|
9
9
|
@options = default_options.merge options_hash
|
@@ -13,6 +13,7 @@ module Dynflow
|
|
13
13
|
@persistence = Persistence.new(self, persistence_adapter)
|
14
14
|
@executor = Type! option_val(:executor), Executors::Abstract
|
15
15
|
@action_classes = option_val(:action_classes)
|
16
|
+
@middleware = Middleware::World.new
|
16
17
|
calculate_subscription_index
|
17
18
|
|
18
19
|
executor.initialized.wait
|
@@ -47,6 +48,7 @@ module Dynflow
|
|
47
48
|
# reload actions classes, intended only for devel
|
48
49
|
def reload!
|
49
50
|
@action_classes.map! { |klass| klass.to_s.constantize }
|
51
|
+
middleware.clear_cache!
|
50
52
|
calculate_subscription_index
|
51
53
|
end
|
52
54
|
|
@@ -74,7 +76,11 @@ module Dynflow
|
|
74
76
|
execution_plan = plan(action_class, *args)
|
75
77
|
planned = execution_plan.state == :planned
|
76
78
|
finished = if planned
|
77
|
-
|
79
|
+
begin
|
80
|
+
execute(execution_plan.id)
|
81
|
+
rescue => exception
|
82
|
+
Future.new.fail exception
|
83
|
+
end
|
78
84
|
else
|
79
85
|
Future.new.resolve(execution_plan)
|
80
86
|
end
|
@@ -122,15 +128,19 @@ module Dynflow
|
|
122
128
|
# After the executor is unregistered, the consistency check should be performed
|
123
129
|
# to fix the orphaned plans as well.
|
124
130
|
def consistency_check
|
125
|
-
abnormal_execution_plans =
|
131
|
+
abnormal_execution_plans =
|
132
|
+
self.persistence.find_execution_plans filters: { 'state' => %w(running planning) }
|
126
133
|
if abnormal_execution_plans.empty?
|
127
134
|
logger.info 'Clean start.'
|
128
135
|
else
|
129
136
|
format_str = '%36s %10s %10s'
|
130
137
|
message = ['Abnormal execution plans, process was probably killed.',
|
131
|
-
'Following ExecutionPlans will be set to paused,
|
138
|
+
'Following ExecutionPlans will be set to paused, ',
|
139
|
+
'it should be fixed manually by administrator.',
|
132
140
|
(format format_str, 'ExecutionPlan', 'state', 'result'),
|
133
|
-
*(abnormal_execution_plans.map
|
141
|
+
*(abnormal_execution_plans.map do |ep|
|
142
|
+
format format_str, ep.id, ep.state, ep.result
|
143
|
+
end)]
|
134
144
|
|
135
145
|
logger.error message.join("\n")
|
136
146
|
|
@@ -147,13 +157,23 @@ module Dynflow
|
|
147
157
|
end
|
148
158
|
end
|
149
159
|
|
160
|
+
# should be called after World is initialized, SimpleWorld does it automatically
|
161
|
+
def execute_planned_execution_plans
|
162
|
+
planned_execution_plans =
|
163
|
+
self.persistence.find_execution_plans filters: { 'state' => %w(planned) }
|
164
|
+
planned_execution_plans.each { |ep| execute ep.id }
|
165
|
+
end
|
166
|
+
|
150
167
|
private
|
151
168
|
|
152
169
|
def calculate_subscription_index
|
153
|
-
@subscription_index =
|
154
|
-
|
155
|
-
|
156
|
-
|
170
|
+
@subscription_index =
|
171
|
+
action_classes.each_with_object(Hash.new { |h, k| h[k] = [] }) do |klass, index|
|
172
|
+
next unless klass.subscribe
|
173
|
+
Array(klass.subscribe).each do |subscribed_class|
|
174
|
+
index[subscribed_class.to_s.constantize] << klass
|
175
|
+
end
|
176
|
+
end.tap { |o| o.freeze }
|
157
177
|
end
|
158
178
|
|
159
179
|
def option_val(key)
|
data/test/action_test.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
|
-
require_relative 'code_workflow_example'
|
3
2
|
|
4
3
|
module Dynflow
|
5
4
|
|
@@ -65,8 +64,8 @@ module Dynflow
|
|
65
64
|
specify { smart_action_class.all_children.wont_include smarter_action_class.finalize_phase }
|
66
65
|
|
67
66
|
describe 'World#subscribed_actions' do
|
68
|
-
event_action_class = CodeWorkflowExample::Triage
|
69
|
-
subscribed_action_class = CodeWorkflowExample::NotifyAssignee
|
67
|
+
event_action_class = Support::CodeWorkflowExample::Triage
|
68
|
+
subscribed_action_class = Support::CodeWorkflowExample::NotifyAssignee
|
70
69
|
|
71
70
|
specify { subscribed_action_class.subscribe.must_equal event_action_class }
|
72
71
|
specify { world.subscribed_actions(event_action_class).must_include subscribed_action_class }
|
@@ -78,7 +77,7 @@ module Dynflow
|
|
78
77
|
include WorldInstance
|
79
78
|
|
80
79
|
let :execution_plan do
|
81
|
-
id, planned, finished = *world.trigger(CodeWorkflowExample::IncomingIssues, issues_data)
|
80
|
+
id, planned, finished = *world.trigger(Support::CodeWorkflowExample::IncomingIssues, issues_data)
|
82
81
|
raise unless planned
|
83
82
|
finished.value
|
84
83
|
end
|
@@ -90,11 +89,11 @@ module Dynflow
|
|
90
89
|
|
91
90
|
let :presenter do
|
92
91
|
execution_plan.actions.find do |action|
|
93
|
-
action.is_a? CodeWorkflowExample::IncomingIssues
|
92
|
+
action.is_a? Support::CodeWorkflowExample::IncomingIssues
|
94
93
|
end
|
95
94
|
end
|
96
95
|
|
97
|
-
specify { presenter.action_class.must_equal CodeWorkflowExample::IncomingIssues }
|
96
|
+
specify { presenter.action_class.must_equal Support::CodeWorkflowExample::IncomingIssues }
|
98
97
|
|
99
98
|
it 'allows aggregating data from other actions' do
|
100
99
|
presenter.summary.must_equal(assignees: ["John Doe"])
|
data/test/execution_plan_test.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
|
-
require_relative 'code_workflow_example'
|
3
2
|
|
4
3
|
module Dynflow
|
5
4
|
module ExecutionPlanTest
|
@@ -16,7 +15,7 @@ module Dynflow
|
|
16
15
|
describe 'serialization' do
|
17
16
|
|
18
17
|
let :execution_plan do
|
19
|
-
world.plan(CodeWorkflowExample::FastCommit, 'sha' => 'abc123')
|
18
|
+
world.plan(Support::CodeWorkflowExample::FastCommit, 'sha' => 'abc123')
|
20
19
|
end
|
21
20
|
|
22
21
|
let :deserialized_execution_plan do
|
@@ -47,7 +46,7 @@ module Dynflow
|
|
47
46
|
describe '#result' do
|
48
47
|
|
49
48
|
let :execution_plan do
|
50
|
-
world.plan(CodeWorkflowExample::FastCommit, 'sha' => 'abc123')
|
49
|
+
world.plan(Support::CodeWorkflowExample::FastCommit, 'sha' => 'abc123')
|
51
50
|
end
|
52
51
|
|
53
52
|
describe 'for error in planning phase' do
|
@@ -107,7 +106,7 @@ module Dynflow
|
|
107
106
|
|
108
107
|
describe 'plan steps' do
|
109
108
|
let :execution_plan do
|
110
|
-
world.plan(CodeWorkflowExample::IncomingIssues, issues_data)
|
109
|
+
world.plan(Support::CodeWorkflowExample::IncomingIssues, issues_data)
|
111
110
|
end
|
112
111
|
|
113
112
|
it 'stores the information about the sub actions' do
|
@@ -129,7 +128,7 @@ module Dynflow
|
|
129
128
|
describe 'persisted action' do
|
130
129
|
|
131
130
|
let :execution_plan do
|
132
|
-
world.plan(CodeWorkflowExample::IncomingIssues, issues_data)
|
131
|
+
world.plan(Support::CodeWorkflowExample::IncomingIssues, issues_data)
|
133
132
|
end
|
134
133
|
|
135
134
|
let :action do
|
@@ -148,7 +147,7 @@ module Dynflow
|
|
148
147
|
|
149
148
|
describe 'single dependencies' do
|
150
149
|
let :execution_plan do
|
151
|
-
world.plan(CodeWorkflowExample::IncomingIssues, issues_data)
|
150
|
+
world.plan(Support::CodeWorkflowExample::IncomingIssues, issues_data)
|
152
151
|
end
|
153
152
|
|
154
153
|
it 'constructs the plan of actions to be executed in run phase' do
|
@@ -169,7 +168,7 @@ module Dynflow
|
|
169
168
|
|
170
169
|
describe 'multi dependencies' do
|
171
170
|
let :execution_plan do
|
172
|
-
world.plan(CodeWorkflowExample::Commit, 'sha' => 'abc123')
|
171
|
+
world.plan(Support::CodeWorkflowExample::Commit, 'sha' => 'abc123')
|
173
172
|
end
|
174
173
|
|
175
174
|
it 'constructs the plan of actions to be executed in run phase' do
|
@@ -186,7 +185,7 @@ module Dynflow
|
|
186
185
|
|
187
186
|
describe 'sequence and concurrence keyword used' do
|
188
187
|
let :execution_plan do
|
189
|
-
world.plan(CodeWorkflowExample::FastCommit, 'sha' => 'abc123')
|
188
|
+
world.plan(Support::CodeWorkflowExample::FastCommit, 'sha' => 'abc123')
|
190
189
|
end
|
191
190
|
|
192
191
|
it 'constructs the plan of actions to be executed in run phase' do
|
@@ -202,7 +201,7 @@ module Dynflow
|
|
202
201
|
|
203
202
|
describe 'subscribed action' do
|
204
203
|
let :execution_plan do
|
205
|
-
world.plan(CodeWorkflowExample::DummyTrigger, {})
|
204
|
+
world.plan(Support::CodeWorkflowExample::DummyTrigger, {})
|
206
205
|
end
|
207
206
|
|
208
207
|
it 'constructs the plan of actions to be executed in run phase' do
|
@@ -218,7 +217,7 @@ module Dynflow
|
|
218
217
|
describe 'finalize flow' do
|
219
218
|
|
220
219
|
let :execution_plan do
|
221
|
-
world.plan(CodeWorkflowExample::IncomingIssues, issues_data)
|
220
|
+
world.plan(Support::CodeWorkflowExample::IncomingIssues, issues_data)
|
222
221
|
end
|
223
222
|
|
224
223
|
it 'plans the finalize steps in a sequence' do
|
@@ -237,7 +236,7 @@ module Dynflow
|
|
237
236
|
|
238
237
|
describe 'accessing actions results' do
|
239
238
|
let :execution_plan do
|
240
|
-
world.plan(CodeWorkflowExample::IncomingIssues, issues_data)
|
239
|
+
world.plan(Support::CodeWorkflowExample::IncomingIssues, issues_data)
|
241
240
|
end
|
242
241
|
|
243
242
|
it 'provides the access to the actions data via Action::Presenter' do
|
data/test/executor_test.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
|
-
require_relative 'code_workflow_example'
|
3
2
|
|
4
3
|
module Dynflow
|
5
4
|
module ExecutorTest
|
@@ -28,19 +27,15 @@ module Dynflow
|
|
28
27
|
{ 'author' => 'John Doe', 'text' => 'trolling in finalize' }]
|
29
28
|
end
|
30
29
|
|
31
|
-
let :execution_plan do
|
32
|
-
world.plan(CodeWorkflowExample::IncomingIssues, issues_data)
|
33
|
-
end
|
34
|
-
|
35
30
|
let :failed_execution_plan do
|
36
|
-
plan = world.plan(CodeWorkflowExample::IncomingIssues, failing_issues_data)
|
31
|
+
plan = world.plan(Support::CodeWorkflowExample::IncomingIssues, failing_issues_data)
|
37
32
|
plan = world.execute(plan.id).value
|
38
33
|
plan.state.must_equal :paused
|
39
34
|
plan
|
40
35
|
end
|
41
36
|
|
42
37
|
let :finalize_failed_execution_plan do
|
43
|
-
plan = world.plan(CodeWorkflowExample::IncomingIssues, finalize_failing_issues_data)
|
38
|
+
plan = world.plan(Support::CodeWorkflowExample::IncomingIssues, finalize_failing_issues_data)
|
44
39
|
plan = world.execute(plan.id).value
|
45
40
|
plan.state.must_equal :paused
|
46
41
|
plan
|
@@ -58,10 +53,29 @@ module Dynflow
|
|
58
53
|
|
59
54
|
describe "after successful planning" do
|
60
55
|
|
56
|
+
let :execution_plan do
|
57
|
+
world.plan(Support::CodeWorkflowExample::IncomingIssues, issues_data)
|
58
|
+
end
|
59
|
+
|
61
60
|
it "is pending" do
|
62
61
|
execution_plan.state.must_equal :planned
|
63
62
|
end
|
64
63
|
|
64
|
+
describe "when finished successfully" do
|
65
|
+
it "is stopped" do
|
66
|
+
world.execute(execution_plan.id).value.tap do |plan|
|
67
|
+
plan.state.must_equal :stopped
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "when finished with error" do
|
73
|
+
it "is paused" do
|
74
|
+
world.execute(failed_execution_plan.id).value.tap do |plan|
|
75
|
+
plan.state.must_equal :paused
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
65
79
|
end
|
66
80
|
|
67
81
|
describe "after error in planning" do
|
@@ -85,7 +99,7 @@ module Dynflow
|
|
85
99
|
describe "when being executed" do
|
86
100
|
|
87
101
|
let :execution_plan do
|
88
|
-
world.plan(CodeWorkflowExample::IncomingIssue, { 'text' => 'get a break' })
|
102
|
+
world.plan(Support::CodeWorkflowExample::IncomingIssue, { 'text' => 'get a break' })
|
89
103
|
end
|
90
104
|
|
91
105
|
before do
|
@@ -103,7 +117,7 @@ module Dynflow
|
|
103
117
|
plan.state.must_equal :running
|
104
118
|
triage = plan.steps.values.find do |s|
|
105
119
|
s.is_a?(Dynflow::ExecutionPlan::Steps::RunStep) &&
|
106
|
-
s.action_class ==
|
120
|
+
s.action_class == Support::CodeWorkflowExample::Triage
|
107
121
|
end
|
108
122
|
triage.state.must_equal :running
|
109
123
|
world.persistence.
|
@@ -118,23 +132,6 @@ module Dynflow
|
|
118
132
|
end
|
119
133
|
end
|
120
134
|
end
|
121
|
-
|
122
|
-
describe "when finished successfully" do
|
123
|
-
|
124
|
-
it "is stopped" do
|
125
|
-
world.execute(execution_plan.id).value.tap do |plan|
|
126
|
-
plan.state.must_equal :stopped
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
describe "when finished with error" do
|
132
|
-
it "is paused" do
|
133
|
-
world.execute(failed_execution_plan.id).value.tap do |plan|
|
134
|
-
plan.state.must_equal :paused
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
135
|
end
|
139
136
|
|
140
137
|
describe "execution of run flow" do
|
@@ -144,25 +141,51 @@ module Dynflow
|
|
144
141
|
end
|
145
142
|
|
146
143
|
let :result do
|
147
|
-
world.execute(execution_plan.id).value
|
148
|
-
raise result if result.is_a? Exception
|
149
|
-
end
|
144
|
+
world.execute(execution_plan.id).value!
|
150
145
|
end
|
151
146
|
|
152
147
|
after do
|
153
148
|
TestExecutionLog.teardown
|
154
149
|
end
|
155
150
|
|
156
|
-
|
151
|
+
def persisted_plan
|
157
152
|
result
|
158
|
-
|
153
|
+
super
|
159
154
|
end
|
160
155
|
|
161
156
|
describe 'cancellable action' do
|
162
157
|
|
158
|
+
describe event_class = Listeners::Serialization::Protocol::Event do
|
159
|
+
it 'de/serializes' do
|
160
|
+
Klass = Class.new do
|
161
|
+
def initialize(v)
|
162
|
+
@v = v
|
163
|
+
end
|
164
|
+
|
165
|
+
def to_s
|
166
|
+
@v.to_s
|
167
|
+
end
|
168
|
+
|
169
|
+
def ==(other)
|
170
|
+
@v == other.instance_variable_get(:@v)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
object = Klass.new :value
|
175
|
+
event = event_class['uuid', 0, object]
|
176
|
+
hash = event.to_hash
|
177
|
+
json = MultiJson.dump(hash)
|
178
|
+
hash_loaded = MultiJson.load(json)
|
179
|
+
assert_equal event[:event], event_class.from_hash(hash_loaded)[:event]
|
180
|
+
assert_equal event, event_class.from_hash(hash_loaded)
|
181
|
+
|
182
|
+
ExecutorTest.send :remove_const, :Klass
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
163
186
|
describe 'successful' do
|
164
187
|
let :execution_plan do
|
165
|
-
world.plan(CodeWorkflowExample::CancelableSuspended, {})
|
188
|
+
world.plan(Support::CodeWorkflowExample::CancelableSuspended, {})
|
166
189
|
end
|
167
190
|
|
168
191
|
it "doesn't cause problems" do
|
@@ -173,7 +196,7 @@ module Dynflow
|
|
173
196
|
|
174
197
|
describe 'canceled' do
|
175
198
|
let :execution_plan do
|
176
|
-
world.plan(CodeWorkflowExample::CancelableSuspended, { text: 'cancel' })
|
199
|
+
world.plan(Support::CodeWorkflowExample::CancelableSuspended, { text: 'cancel-self' })
|
177
200
|
end
|
178
201
|
|
179
202
|
it 'cancels' do
|
@@ -187,7 +210,7 @@ module Dynflow
|
|
187
210
|
|
188
211
|
describe 'canceled failed' do
|
189
212
|
let :execution_plan do
|
190
|
-
world.plan(CodeWorkflowExample::CancelableSuspended, { text: 'cancel
|
213
|
+
world.plan(Support::CodeWorkflowExample::CancelableSuspended, { text: 'cancel-fail cancel-self' })
|
191
214
|
end
|
192
215
|
|
193
216
|
it 'fails' do
|
@@ -199,40 +222,37 @@ module Dynflow
|
|
199
222
|
action.output[:progress].must_equal 30
|
200
223
|
end
|
201
224
|
end
|
202
|
-
end
|
203
|
-
|
204
225
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
226
|
+
if world_method == :remote_world
|
227
|
+
describe 'canceled externally' do
|
228
|
+
let :execution_plan do
|
229
|
+
world.plan(Support::CodeWorkflowExample::CancelableSuspended, { text: 'cancel-external' })
|
230
|
+
end
|
209
231
|
|
210
|
-
|
211
|
-
|
212
|
-
|
232
|
+
it 'cancels' do
|
233
|
+
finished = world.execute(execution_plan.id)
|
234
|
+
sleep 0.05
|
235
|
+
world.
|
236
|
+
event(execution_plan.id, 2,
|
237
|
+
Support::CodeWorkflowExample::CancelableSuspended::Cancel).
|
238
|
+
value!.must_equal true
|
239
|
+
result = finished.value!
|
240
|
+
|
241
|
+
result.result.must_equal :success
|
242
|
+
result.state.must_equal :stopped
|
243
|
+
action = world.persistence.load_action result.steps[2]
|
244
|
+
action.output[:progress].must_be :<=, 30
|
245
|
+
action.output[:cancelled].must_equal true
|
246
|
+
end
|
247
|
+
end
|
213
248
|
end
|
214
249
|
|
215
|
-
|
216
|
-
result.started_at.wont_be_nil
|
217
|
-
result.ended_at.wont_be_nil
|
218
|
-
result.execution_time.must_be :<, result.real_time
|
219
|
-
result.execution_time.must_equal(
|
220
|
-
result.steps.inject(0) { |sum, (_, step)| sum + step.execution_time })
|
221
|
-
|
222
|
-
plan_step = result.steps[1]
|
223
|
-
plan_step.started_at.wont_be_nil
|
224
|
-
plan_step.ended_at.wont_be_nil
|
225
|
-
plan_step.execution_time.must_equal plan_step.real_time
|
226
|
-
|
227
|
-
run_step = result.steps[2]
|
228
|
-
run_step.started_at.wont_be_nil
|
229
|
-
run_step.ended_at.wont_be_nil
|
230
|
-
run_step.execution_time.must_be :<, run_step.real_time
|
231
|
-
end
|
250
|
+
end
|
232
251
|
|
252
|
+
describe 'suspended action' do
|
233
253
|
describe 'handling errors in setup' do
|
234
254
|
let :execution_plan do
|
235
|
-
world.plan(CodeWorkflowExample::DummySuspended,
|
255
|
+
world.plan(Support::CodeWorkflowExample::DummySuspended,
|
236
256
|
external_task_id: '123',
|
237
257
|
text: 'troll setup')
|
238
258
|
end
|
@@ -247,6 +267,35 @@ module Dynflow
|
|
247
267
|
end
|
248
268
|
end
|
249
269
|
|
270
|
+
describe 'running' do
|
271
|
+
let :execution_plan do
|
272
|
+
world.plan(Support::CodeWorkflowExample::DummySuspended, { :external_task_id => '123' })
|
273
|
+
end
|
274
|
+
|
275
|
+
it "doesn't cause problems" do
|
276
|
+
result.result.must_equal :success
|
277
|
+
result.state.must_equal :stopped
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'does set times' do
|
281
|
+
result.started_at.wont_be_nil
|
282
|
+
result.ended_at.wont_be_nil
|
283
|
+
result.execution_time.must_be :<, result.real_time
|
284
|
+
result.execution_time.must_equal(
|
285
|
+
result.steps.inject(0) { |sum, (_, step)| sum + step.execution_time })
|
286
|
+
|
287
|
+
plan_step = result.steps[1]
|
288
|
+
plan_step.started_at.wont_be_nil
|
289
|
+
plan_step.ended_at.wont_be_nil
|
290
|
+
plan_step.execution_time.must_equal plan_step.real_time
|
291
|
+
|
292
|
+
run_step = result.steps[2]
|
293
|
+
run_step.started_at.wont_be_nil
|
294
|
+
run_step.ended_at.wont_be_nil
|
295
|
+
run_step.execution_time.must_be :<, run_step.real_time
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
250
299
|
describe 'progress' do
|
251
300
|
before do
|
252
301
|
TestPause.setup
|
@@ -260,7 +309,7 @@ module Dynflow
|
|
260
309
|
|
261
310
|
describe 'plan with one action' do
|
262
311
|
let :execution_plan do
|
263
|
-
world.plan(CodeWorkflowExample::DummySuspended,
|
312
|
+
world.plan(Support::CodeWorkflowExample::DummySuspended,
|
264
313
|
{ external_task_id: '123',
|
265
314
|
text: 'pause in progress 20%' })
|
266
315
|
end
|
@@ -275,7 +324,7 @@ module Dynflow
|
|
275
324
|
|
276
325
|
describe 'plan with more action' do
|
277
326
|
let :execution_plan do
|
278
|
-
world.plan(CodeWorkflowExample::DummyHeavyProgress,
|
327
|
+
world.plan(Support::CodeWorkflowExample::DummyHeavyProgress,
|
279
328
|
{ external_task_id: '123',
|
280
329
|
text: 'pause in progress 20%' })
|
281
330
|
end
|
@@ -291,7 +340,7 @@ module Dynflow
|
|
291
340
|
|
292
341
|
describe 'works when resumed after error' do
|
293
342
|
let :execution_plan do
|
294
|
-
world.plan(CodeWorkflowExample::DummySuspended,
|
343
|
+
world.plan(Support::CodeWorkflowExample::DummySuspended,
|
295
344
|
{ external_task_id: '123',
|
296
345
|
text: 'troll progress' })
|
297
346
|
end
|
@@ -315,7 +364,7 @@ module Dynflow
|
|
315
364
|
describe "action with empty flows" do
|
316
365
|
|
317
366
|
let :execution_plan do
|
318
|
-
world.plan(CodeWorkflowExample::Dummy, { :text => "dummy" }).tap do |plan|
|
367
|
+
world.plan(Support::CodeWorkflowExample::Dummy, { :text => "dummy" }).tap do |plan|
|
319
368
|
assert_equal plan.run_flow.size, 0
|
320
369
|
assert_equal plan.finalize_flow.size, 0
|
321
370
|
end.tap do |w|
|
@@ -338,7 +387,7 @@ module Dynflow
|
|
338
387
|
describe 'action with empty run flow but some finalize flow' do
|
339
388
|
|
340
389
|
let :execution_plan do
|
341
|
-
world.plan(CodeWorkflowExample::DummyWithFinalize, { :text => "dummy" }).tap do |plan|
|
390
|
+
world.plan(Support::CodeWorkflowExample::DummyWithFinalize, { :text => "dummy" }).tap do |plan|
|
342
391
|
assert_equal plan.run_flow.size, 0
|
343
392
|
assert_equal plan.finalize_flow.size, 1
|
344
393
|
end
|
@@ -351,24 +400,29 @@ module Dynflow
|
|
351
400
|
|
352
401
|
end
|
353
402
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
403
|
+
describe 'running' do
|
404
|
+
let :execution_plan do
|
405
|
+
world.plan(Support::CodeWorkflowExample::IncomingIssues, issues_data)
|
406
|
+
end
|
407
|
+
|
408
|
+
it "runs all the steps in the run flow" do
|
409
|
+
assert_run_flow <<-EXECUTED_RUN_FLOW, persisted_plan
|
410
|
+
Dynflow::Flows::Concurrence
|
411
|
+
Dynflow::Flows::Sequence
|
412
|
+
4: Triage(success) {"author"=>"Peter Smith", "text"=>"Failing test"} --> {"classification"=>{"assignee"=>"John Doe", "severity"=>"medium"}}
|
413
|
+
7: UpdateIssue(success) {"author"=>"Peter Smith", "text"=>"Failing test", "assignee"=>"John Doe", "severity"=>"medium"} --> {}
|
414
|
+
9: NotifyAssignee(success) {"triage"=>{"classification"=>{"assignee"=>"John Doe", "severity"=>"medium"}}} --> {}
|
415
|
+
Dynflow::Flows::Sequence
|
416
|
+
13: Triage(success) {"author"=>"John Doe", "text"=>"Internal server error"} --> {"classification"=>{"assignee"=>"John Doe", "severity"=>"medium"}}
|
417
|
+
16: UpdateIssue(success) {"author"=>"John Doe", "text"=>"Internal server error", "assignee"=>"John Doe", "severity"=>"medium"} --> {}
|
418
|
+
18: NotifyAssignee(success) {"triage"=>{"classification"=>{"assignee"=>"John Doe", "severity"=>"medium"}}} --> {}
|
419
|
+
EXECUTED_RUN_FLOW
|
420
|
+
end
|
366
421
|
end
|
367
422
|
|
368
423
|
end
|
369
424
|
|
370
425
|
describe "execution of finalize flow" do
|
371
|
-
|
372
426
|
before do
|
373
427
|
TestExecutionLog.setup
|
374
428
|
result = world.execute(execution_plan.id).value
|
@@ -380,17 +434,19 @@ module Dynflow
|
|
380
434
|
end
|
381
435
|
|
382
436
|
describe "when run flow successful" do
|
437
|
+
let :execution_plan do
|
438
|
+
world.plan(Support::CodeWorkflowExample::IncomingIssues, issues_data)
|
439
|
+
end
|
383
440
|
|
384
441
|
it "runs all the steps in the finalize flow" do
|
385
|
-
assert_finalized(
|
442
|
+
assert_finalized(Support::CodeWorkflowExample::IncomingIssues,
|
386
443
|
{ "issues" => [{ "author" => "Peter Smith", "text" => "Failing test" }, { "author" => "John Doe", "text" => "Internal server error" }] })
|
387
|
-
assert_finalized(
|
444
|
+
assert_finalized(Support::CodeWorkflowExample::Triage,
|
388
445
|
{ "author" => "Peter Smith", "text" => "Failing test" })
|
389
446
|
end
|
390
447
|
end
|
391
448
|
|
392
449
|
describe "when run flow failed" do
|
393
|
-
|
394
450
|
let :execution_plan do
|
395
451
|
failed_execution_plan
|
396
452
|
end
|
@@ -425,7 +481,7 @@ module Dynflow
|
|
425
481
|
resumed_execution_plan.result.must_equal :success
|
426
482
|
|
427
483
|
run_triages = TestExecutionLog.run.find_all do |action_class, input|
|
428
|
-
action_class == CodeWorkflowExample::Triage
|
484
|
+
action_class == Support::CodeWorkflowExample::Triage
|
429
485
|
end
|
430
486
|
run_triages.size.must_equal 1
|
431
487
|
|
@@ -443,6 +499,7 @@ module Dynflow
|
|
443
499
|
end
|
444
500
|
|
445
501
|
end
|
502
|
+
|
446
503
|
describe "re-execution of run flow after fix in finalize phase" do
|
447
504
|
|
448
505
|
after do
|
@@ -466,7 +523,7 @@ module Dynflow
|
|
466
523
|
resumed_execution_plan.result.must_equal :success
|
467
524
|
|
468
525
|
run_triages = TestExecutionLog.finalize.find_all do |action_class, input|
|
469
|
-
action_class == CodeWorkflowExample::Triage
|
526
|
+
action_class == Support::CodeWorkflowExample::Triage
|
470
527
|
end
|
471
528
|
run_triages.size.must_equal 2
|
472
529
|
|
@@ -502,7 +559,7 @@ module Dynflow
|
|
502
559
|
resumed_execution_plan.result.must_equal :success
|
503
560
|
|
504
561
|
run_triages = TestExecutionLog.run.find_all do |action_class, input|
|
505
|
-
action_class == CodeWorkflowExample::Triage
|
562
|
+
action_class == Support::CodeWorkflowExample::Triage
|
506
563
|
end
|
507
564
|
run_triages.size.must_equal 0
|
508
565
|
|
@@ -531,6 +588,10 @@ module Dynflow
|
|
531
588
|
end
|
532
589
|
|
533
590
|
describe 'FlowManager' do
|
591
|
+
let :execution_plan do
|
592
|
+
world.plan(Support::CodeWorkflowExample::IncomingIssues, issues_data)
|
593
|
+
end
|
594
|
+
|
534
595
|
let(:manager) { Executors::Parallel::FlowManager.new execution_plan, execution_plan.run_flow }
|
535
596
|
|
536
597
|
def assert_next_steps(expected_next_step_ids, finished_step_id = nil, success = true)
|
@@ -645,13 +706,13 @@ module Dynflow
|
|
645
706
|
if which == :normal_world
|
646
707
|
it 'executes until its done when terminating' do
|
647
708
|
$slow_actions_done = 0
|
648
|
-
world.trigger(CodeWorkflowExample::Slow, 0.02)
|
709
|
+
world.trigger(Support::CodeWorkflowExample::Slow, 0.02)
|
649
710
|
world.terminate.wait
|
650
711
|
$slow_actions_done.must_equal 1
|
651
712
|
end
|
652
713
|
|
653
714
|
it 'executes until its done when terminating even suspended' do
|
654
|
-
result = world.trigger(CodeWorkflowExample::DummySuspended,
|
715
|
+
result = world.trigger(Support::CodeWorkflowExample::DummySuspended,
|
655
716
|
external_task_id: '123',
|
656
717
|
text: 'none')
|
657
718
|
world.terminate.wait
|
@@ -661,12 +722,14 @@ module Dynflow
|
|
661
722
|
|
662
723
|
it 'does not accept new work' do
|
663
724
|
assert world.terminate.wait
|
664
|
-
|
725
|
+
result = world.trigger(Support::CodeWorkflowExample::Slow, 0.02)
|
726
|
+
result.planned.must_equal true
|
727
|
+
-> { result.finished.value! }.must_raise Dynflow::Error
|
665
728
|
end
|
666
729
|
|
667
730
|
it 'it terminates when no work' do
|
668
731
|
skip 'blocks occasionally' if which == :remote_world # FIXME
|
669
|
-
world.trigger(CodeWorkflowExample::Slow, 0.02).finished.wait
|
732
|
+
world.trigger(Support::CodeWorkflowExample::Slow, 0.02).finished.wait
|
670
733
|
assert world.terminate.wait
|
671
734
|
end
|
672
735
|
|