dynflow 1.4.7 → 1.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{test/prepare_travis_env.sh → .github/install_dependencies.sh} +2 -2
- data/.github/workflows/ruby.yml +116 -0
- data/dynflow.gemspec +1 -0
- data/examples/chunked_output_benchmark.rb +77 -0
- data/extras/expand/main.go +180 -0
- data/lib/dynflow/action/suspended.rb +4 -4
- data/lib/dynflow/action/timeouts.rb +2 -2
- data/lib/dynflow/action.rb +15 -4
- data/lib/dynflow/actor.rb +20 -4
- data/lib/dynflow/clock.rb +2 -2
- data/lib/dynflow/delayed_executors/abstract_core.rb +11 -9
- data/lib/dynflow/director/running_steps_manager.rb +2 -2
- data/lib/dynflow/director.rb +42 -5
- data/lib/dynflow/dispatcher/client_dispatcher.rb +8 -2
- data/lib/dynflow/dispatcher/executor_dispatcher.rb +12 -2
- data/lib/dynflow/dispatcher.rb +7 -2
- data/lib/dynflow/execution_history.rb +1 -1
- data/lib/dynflow/execution_plan/hooks.rb +1 -1
- data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +1 -0
- data/lib/dynflow/execution_plan.rb +16 -5
- data/lib/dynflow/executors/abstract/core.rb +10 -1
- data/lib/dynflow/executors/parallel.rb +6 -2
- data/lib/dynflow/extensions/msgpack.rb +41 -0
- data/lib/dynflow/extensions.rb +6 -0
- data/lib/dynflow/flows/abstract.rb +14 -0
- data/lib/dynflow/flows/abstract_composed.rb +2 -7
- data/lib/dynflow/flows/atom.rb +2 -2
- data/lib/dynflow/flows/concurrence.rb +2 -0
- data/lib/dynflow/flows/registry.rb +32 -0
- data/lib/dynflow/flows/sequence.rb +2 -0
- data/lib/dynflow/flows.rb +1 -0
- data/lib/dynflow/persistence.rb +10 -0
- data/lib/dynflow/persistence_adapters/sequel.rb +51 -16
- data/lib/dynflow/persistence_adapters/sequel_migrations/021_create_output_chunks.rb +30 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/022_store_flows_as_msgpack.rb +90 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/023_sqlite_workarounds.rb +19 -0
- data/lib/dynflow/serializable.rb +2 -2
- data/lib/dynflow/testing/dummy_coordinator.rb +10 -0
- data/lib/dynflow/testing/dummy_planned_action.rb +4 -0
- data/lib/dynflow/testing/dummy_world.rb +2 -1
- data/lib/dynflow/testing/in_thread_executor.rb +2 -2
- data/lib/dynflow/testing/in_thread_world.rb +5 -5
- data/lib/dynflow/testing.rb +1 -0
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/world.rb +16 -4
- data/lib/dynflow.rb +2 -1
- data/test/dispatcher_test.rb +6 -0
- data/test/execution_plan_hooks_test.rb +36 -0
- data/test/extensions_test.rb +42 -0
- data/test/flows_test.rb +44 -0
- data/test/future_execution_test.rb +6 -3
- data/test/persistence_test.rb +2 -2
- data/web/views/flow_step.erb +1 -0
- metadata +34 -8
- data/.travis.yml +0 -33
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
Sequel.migration do
|
3
|
+
up do
|
4
|
+
type = database_type
|
5
|
+
create_table(:dynflow_output_chunks) do
|
6
|
+
primary_key :id
|
7
|
+
|
8
|
+
column_properties = if type.to_s.include?('postgres')
|
9
|
+
{type: :uuid}
|
10
|
+
else
|
11
|
+
{type: String, size: 36, fixed: true, null: false}
|
12
|
+
end
|
13
|
+
foreign_key :execution_plan_uuid, :dynflow_execution_plans, **column_properties
|
14
|
+
index :execution_plan_uuid
|
15
|
+
|
16
|
+
column :action_id, Integer, null: false
|
17
|
+
foreign_key [:execution_plan_uuid, :action_id], :dynflow_actions,
|
18
|
+
name: :dynflow_output_chunks_execution_plan_uuid_fkey1
|
19
|
+
index [:execution_plan_uuid, :action_id]
|
20
|
+
|
21
|
+
column :chunk, String, text: true
|
22
|
+
column :kind, String
|
23
|
+
column :timestamp, Time, null: false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
down do
|
28
|
+
drop_table(:dynflow_output_chunks)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'multi_json'
|
4
|
+
require 'msgpack'
|
5
|
+
|
6
|
+
def table_pkeys(table)
|
7
|
+
case table
|
8
|
+
when :dynflow_execution_plans
|
9
|
+
[:uuid]
|
10
|
+
when :dynflow_actions, :dynflow_steps
|
11
|
+
[:execution_plan_uuid, :id]
|
12
|
+
when :dynflow_coordinator_records
|
13
|
+
[:id, :class]
|
14
|
+
when :dynflow_delayed_plans
|
15
|
+
[:execution_plan_uuid]
|
16
|
+
when :dynflow_envelopes
|
17
|
+
[:id]
|
18
|
+
when :dynflow_output_chunks
|
19
|
+
[:chunk]
|
20
|
+
else
|
21
|
+
raise "Unknown table '#{table}'"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def conditions_for_row(table, row)
|
26
|
+
row.slice(*table_pkeys(table))
|
27
|
+
end
|
28
|
+
|
29
|
+
def migrate_table(table, from_names, to_names, new_type)
|
30
|
+
alter_table(table) do
|
31
|
+
to_names.each do |new|
|
32
|
+
add_column new, new_type
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
relevant_columns = table_pkeys(table) | from_names
|
37
|
+
|
38
|
+
from(table).select(*relevant_columns).each do |row|
|
39
|
+
update = from_names.zip(to_names).reduce({}) do |acc, (from, to)|
|
40
|
+
row[from].nil? ? acc : acc.merge(to => yield(row[from]))
|
41
|
+
end
|
42
|
+
next if update.empty?
|
43
|
+
from(table).where(conditions_for_row(table, row)).update(update)
|
44
|
+
end
|
45
|
+
|
46
|
+
from_names.zip(to_names).each do |old, new|
|
47
|
+
alter_table(table) do
|
48
|
+
drop_column old
|
49
|
+
end
|
50
|
+
|
51
|
+
if database_type == :mysql
|
52
|
+
type = new_type == File ? 'blob' : 'mediumtext'
|
53
|
+
run "ALTER TABLE #{table} CHANGE COLUMN `#{new}` `#{old}` #{type};"
|
54
|
+
else
|
55
|
+
rename_column table, new, old
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
Sequel.migration do
|
61
|
+
|
62
|
+
TABLES = {
|
63
|
+
:dynflow_actions => [:data, :input, :output],
|
64
|
+
:dynflow_coordinator_records => [:data],
|
65
|
+
:dynflow_delayed_plans => [:serialized_args, :data],
|
66
|
+
:dynflow_envelopes => [:data],
|
67
|
+
:dynflow_execution_plans => [:run_flow, :finalize_flow, :execution_history, :step_ids],
|
68
|
+
:dynflow_steps => [:error, :children],
|
69
|
+
:dynflow_output_chunks => [:chunk]
|
70
|
+
}
|
71
|
+
|
72
|
+
up do
|
73
|
+
TABLES.each do |table, columns|
|
74
|
+
new_columns = columns.map { |c| "#{c}_blob" }
|
75
|
+
|
76
|
+
migrate_table table, columns, new_columns, File do |data|
|
77
|
+
::Sequel.blob(MessagePack.pack(MultiJson.load(data)))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
down do
|
83
|
+
TABLES.each do |table, columns|
|
84
|
+
new_columns = columns.map { |c| c + '_text' }
|
85
|
+
migrate_table table, columns, new_columns, String do |data|
|
86
|
+
MultiJson.dump(MessagePack.unpack(data))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
tables = [:dynflow_actions, :dynflow_delayed_plans, :dynflow_steps, :dynflow_output_chunks]
|
3
|
+
Sequel.migration do
|
4
|
+
up do
|
5
|
+
if database_type == :sqlite && Gem::Version.new(SQLite3::SQLITE_VERSION) <= Gem::Version.new('3.7.17')
|
6
|
+
tables.each do |table|
|
7
|
+
alter_table(table) { drop_foreign_key [:execution_plan_uuid] }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
down do
|
13
|
+
if database_type == :sqlite && Gem::Version.new(SQLite3::SQLITE_VERSION) <= Gem::Version.new('3.7.17')
|
14
|
+
tables.each do |table|
|
15
|
+
alter_table(table) { add_foreign_key [:execution_plan_uuid], :dynflow_execution_plans }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/dynflow/serializable.rb
CHANGED
@@ -45,12 +45,12 @@ module Dynflow
|
|
45
45
|
if values.size == 1
|
46
46
|
value = values.first
|
47
47
|
case value
|
48
|
-
when String, Numeric, Symbol, TrueClass, FalseClass, NilClass, Time
|
49
|
-
value
|
50
48
|
when Hash
|
51
49
|
value.inject({}) { |h, (k, v)| h.update k => recursive_to_hash(v) }
|
52
50
|
when Array
|
53
51
|
value.map { |v| recursive_to_hash v }
|
52
|
+
when ->(v) { v.respond_to?(:to_msgpack) }
|
53
|
+
value
|
54
54
|
else
|
55
55
|
value.to_hash
|
56
56
|
end
|
@@ -5,7 +5,7 @@ module Dynflow
|
|
5
5
|
extend Mimic
|
6
6
|
mimic! World
|
7
7
|
|
8
|
-
attr_reader :clock, :executor, :middleware
|
8
|
+
attr_reader :clock, :executor, :middleware, :coordinator
|
9
9
|
attr_accessor :action
|
10
10
|
|
11
11
|
def initialize(_config = nil)
|
@@ -13,6 +13,7 @@ module Dynflow
|
|
13
13
|
@clock = ManagedClock.new
|
14
14
|
@executor = DummyExecutor.new(self)
|
15
15
|
@middleware = Middleware::World.new
|
16
|
+
@coordinator = DummyCoordinator.new
|
16
17
|
end
|
17
18
|
|
18
19
|
def action_logger
|
@@ -34,8 +34,8 @@ module Dynflow
|
|
34
34
|
@director.work_finished(work_item)
|
35
35
|
end
|
36
36
|
|
37
|
-
def event(execution_plan_id, step_id, event, future = Concurrent::Promises.resolvable_future)
|
38
|
-
event = (Director::Event[SecureRandom.uuid, execution_plan_id, step_id, event, future])
|
37
|
+
def event(execution_plan_id, step_id, event, future = Concurrent::Promises.resolvable_future, optional: false)
|
38
|
+
event = (Director::Event[SecureRandom.uuid, execution_plan_id, step_id, event, future, optional])
|
39
39
|
@director.handle_event(event).each do |work_item|
|
40
40
|
@work_items << work_item
|
41
41
|
end
|
@@ -58,15 +58,15 @@ module Dynflow
|
|
58
58
|
future.reject e
|
59
59
|
end
|
60
60
|
|
61
|
-
def event(execution_plan_id, step_id, event, done = Concurrent::Promises.resolvable_future)
|
62
|
-
@executor.event(execution_plan_id, step_id, event, done)
|
61
|
+
def event(execution_plan_id, step_id, event, done = Concurrent::Promises.resolvable_future, optional: false)
|
62
|
+
@executor.event(execution_plan_id, step_id, event, done, optional: optional)
|
63
63
|
end
|
64
64
|
|
65
|
-
def plan_event(execution_plan_id, step_id, event, time, done = Concurrent::Promises.resolvable_future)
|
65
|
+
def plan_event(execution_plan_id, step_id, event, time, done = Concurrent::Promises.resolvable_future, optional: false)
|
66
66
|
if time.nil? || time < Time.now
|
67
|
-
event(execution_plan_id, step_id, event, done)
|
67
|
+
event(execution_plan_id, step_id, event, done, optional: optional)
|
68
68
|
else
|
69
|
-
clock.ping(executor, time, Director::Event[SecureRandom.uuid, execution_plan_id, step_id, event, done], :delayed_event)
|
69
|
+
clock.ping(executor, time, Director::Event[SecureRandom.uuid, execution_plan_id, step_id, event, done, optional], :delayed_event)
|
70
70
|
end
|
71
71
|
end
|
72
72
|
end
|
data/lib/dynflow/testing.rb
CHANGED
@@ -19,6 +19,7 @@ module Dynflow
|
|
19
19
|
|
20
20
|
require 'dynflow/testing/mimic'
|
21
21
|
require 'dynflow/testing/managed_clock'
|
22
|
+
require 'dynflow/testing/dummy_coordinator'
|
22
23
|
require 'dynflow/testing/dummy_world'
|
23
24
|
require 'dynflow/testing/dummy_executor'
|
24
25
|
require 'dynflow/testing/dummy_execution_plan'
|
data/lib/dynflow/version.rb
CHANGED
data/lib/dynflow/world.rb
CHANGED
@@ -200,6 +200,14 @@ module Dynflow
|
|
200
200
|
Scheduled[execution_plan.id]
|
201
201
|
end
|
202
202
|
|
203
|
+
def plan_elsewhere(action_class, *args)
|
204
|
+
execution_plan = ExecutionPlan.new(self, nil)
|
205
|
+
execution_plan.delay(nil, action_class, {}, *args)
|
206
|
+
plan_request(execution_plan.id)
|
207
|
+
|
208
|
+
Scheduled[execution_plan.id]
|
209
|
+
end
|
210
|
+
|
203
211
|
def plan(action_class, *args)
|
204
212
|
plan_with_options(action_class: action_class, args: args)
|
205
213
|
end
|
@@ -219,12 +227,16 @@ module Dynflow
|
|
219
227
|
publish_request(Dispatcher::Execution[execution_plan_id], done, true)
|
220
228
|
end
|
221
229
|
|
222
|
-
def event(execution_plan_id, step_id, event, done = Concurrent::Promises.resolvable_future)
|
223
|
-
publish_request(Dispatcher::Event[execution_plan_id, step_id, event], done, false)
|
230
|
+
def event(execution_plan_id, step_id, event, done = Concurrent::Promises.resolvable_future, optional: false)
|
231
|
+
publish_request(Dispatcher::Event[execution_plan_id, step_id, event, nil, optional], done, false)
|
232
|
+
end
|
233
|
+
|
234
|
+
def plan_event(execution_plan_id, step_id, event, time, accepted = Concurrent::Promises.resolvable_future, optional: false)
|
235
|
+
publish_request(Dispatcher::Event[execution_plan_id, step_id, event, time, optional], accepted, false)
|
224
236
|
end
|
225
237
|
|
226
|
-
def
|
227
|
-
publish_request(Dispatcher::
|
238
|
+
def plan_request(execution_plan_id, done = Concurrent::Promises.resolvable_future)
|
239
|
+
publish_request(Dispatcher::Planning[execution_plan_id], done, false)
|
228
240
|
end
|
229
241
|
|
230
242
|
def ping(world_id, timeout, done = Concurrent::Promises.resolvable_future)
|
data/lib/dynflow.rb
CHANGED
@@ -72,11 +72,12 @@ module Dynflow
|
|
72
72
|
require 'dynflow/throttle_limiter'
|
73
73
|
require 'dynflow/telemetry'
|
74
74
|
require 'dynflow/config'
|
75
|
+
require 'dynflow/extensions'
|
75
76
|
|
76
77
|
if defined? ::ActiveJob
|
77
78
|
require 'dynflow/active_job/queue_adapter'
|
78
79
|
|
79
|
-
class Railtie < Rails::Railtie
|
80
|
+
class Railtie < ::Rails::Railtie
|
80
81
|
config.before_initialize do
|
81
82
|
::ActiveJob::QueueAdapters.send(
|
82
83
|
:include,
|
data/test/dispatcher_test.rb
CHANGED
@@ -49,6 +49,12 @@ module Dynflow
|
|
49
49
|
plan = result.finished.value
|
50
50
|
assert_equal('finish', plan.actions.first.output[:event])
|
51
51
|
end
|
52
|
+
|
53
|
+
it 'does not error on dispatching an optional event' do
|
54
|
+
request = client_world.event('123', 1, nil, optional: true)
|
55
|
+
request.wait(20)
|
56
|
+
assert_match /Could not find an executor for optional .*, discarding/, request.reason.message
|
57
|
+
end
|
52
58
|
end
|
53
59
|
end
|
54
60
|
end
|
@@ -60,6 +60,18 @@ module Dynflow
|
|
60
60
|
execution_plan_hooks.use :raise_flag_root_only, :on => :stopped
|
61
61
|
end
|
62
62
|
|
63
|
+
class PendingAction < ::Dynflow::Action
|
64
|
+
include FlagHook
|
65
|
+
|
66
|
+
execution_plan_hooks.use :raise_flag, :on => :pending
|
67
|
+
end
|
68
|
+
|
69
|
+
class AllTransitionsAction < ::Dynflow::Action
|
70
|
+
include FlagHook
|
71
|
+
|
72
|
+
execution_plan_hooks.use :raise_flag
|
73
|
+
end
|
74
|
+
|
63
75
|
class ComposedAction < RootOnlyAction
|
64
76
|
def plan
|
65
77
|
plan_action(RootOnlyAction)
|
@@ -161,6 +173,30 @@ module Dynflow
|
|
161
173
|
plan.finished.wait!
|
162
174
|
_(Flag.raised_count).must_equal 1
|
163
175
|
end
|
176
|
+
|
177
|
+
it 'runs the pending hooks when execution plan is created' do
|
178
|
+
refute Flag.raised?
|
179
|
+
plan = world.trigger(PendingAction)
|
180
|
+
plan.finished.wait!
|
181
|
+
_(Flag.raised_count).must_equal 1
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'runs the pending hooks when execution plan is created' do
|
185
|
+
refute Flag.raised?
|
186
|
+
delay = world.delay(PendingAction, { :start_at => Time.now.utc + 180 })
|
187
|
+
delayed_plan = world.persistence.load_delayed_plan(delay.execution_plan_id)
|
188
|
+
delayed_plan.execution_plan.cancel.each(&:wait)
|
189
|
+
_(Flag.raised_count).must_equal 1
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'runs the hook on every state transition' do
|
193
|
+
refute Flag.raised?
|
194
|
+
plan = world.trigger(AllTransitionsAction)
|
195
|
+
plan.finished.wait!
|
196
|
+
# There should be 5 transitions
|
197
|
+
# nothing -> pending -> planning -> planned -> running -> stopped
|
198
|
+
_(Flag.raised_count).must_equal 5
|
199
|
+
end
|
164
200
|
end
|
165
201
|
end
|
166
202
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'test_helper'
|
3
|
+
require 'active_support/time'
|
4
|
+
|
5
|
+
module Dynflow
|
6
|
+
module ExtensionsTest
|
7
|
+
describe 'msgpack extensions' do
|
8
|
+
before do
|
9
|
+
Thread.current[:time_zone] = ActiveSupport::TimeZone['Europe/Prague']
|
10
|
+
end
|
11
|
+
after { Thread.current[:time_zone] = nil }
|
12
|
+
|
13
|
+
it 'allows {de,}serializing Time' do
|
14
|
+
time = Time.now
|
15
|
+
transformed = MessagePack.unpack(time.to_msgpack)
|
16
|
+
assert_equal transformed, time
|
17
|
+
assert_equal transformed.class, time.class
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'allows {de,}serializing ActiveSupport::TimeWithZone' do
|
21
|
+
time = Time.zone.now
|
22
|
+
transformed = MessagePack.unpack(time.to_msgpack)
|
23
|
+
assert_equal transformed, time
|
24
|
+
assert_equal transformed.class, time.class
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'allows {de,}serializing DateTime' do
|
28
|
+
time = DateTime.now
|
29
|
+
transformed = MessagePack.unpack(time.to_msgpack)
|
30
|
+
assert_equal transformed, time
|
31
|
+
assert_equal transformed.class, time.class
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'allows {de,}serializing Date' do
|
35
|
+
date = DateTime.current
|
36
|
+
transformed = MessagePack.unpack(date.to_msgpack)
|
37
|
+
assert_equal transformed, date
|
38
|
+
assert_equal transformed.class, date.class
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/test/flows_test.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'test_helper'
|
3
|
+
require 'mocha/minitest'
|
4
|
+
|
5
|
+
module Dynflow
|
6
|
+
describe 'flow' do
|
7
|
+
|
8
|
+
class TestRegistry < Flows::Registry
|
9
|
+
class << self
|
10
|
+
def reset!
|
11
|
+
@serialization_map = {}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
after do
|
17
|
+
TestRegistry.reset!
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "registry" do
|
21
|
+
it "allows registering values" do
|
22
|
+
TestRegistry.register!(TestRegistry, 'TS')
|
23
|
+
TestRegistry.register!(Integer, 'I')
|
24
|
+
map = TestRegistry.instance_variable_get("@serialization_map")
|
25
|
+
_(map).must_equal({'TS' => TestRegistry, 'I' => Integer})
|
26
|
+
end
|
27
|
+
|
28
|
+
it "prevents overwriting values" do
|
29
|
+
TestRegistry.register!(Integer, 'I')
|
30
|
+
_(-> { TestRegistry.register!(Float, 'I') }).must_raise Flows::Registry::IdentifierTaken
|
31
|
+
end
|
32
|
+
|
33
|
+
it "encodes and decodes values" do
|
34
|
+
TestRegistry.register!(Integer, 'I')
|
35
|
+
_(TestRegistry.encode(Integer)).must_equal 'I'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "raises an exception when unknown key is requested" do
|
39
|
+
_(-> { TestRegistry.encode(Float) }).must_raise Flows::Registry::UnknownIdentifier
|
40
|
+
_(-> { TestRegistry.decode('F') }).must_raise Flows::Registry::UnknownIdentifier
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -29,14 +29,17 @@ module Dynflow
|
|
29
29
|
describe 'abstract executor' do
|
30
30
|
let(:abstract_delayed_executor) { DelayedExecutors::AbstractCore.new(world) }
|
31
31
|
|
32
|
-
it 'handles
|
32
|
+
it 'handles plan in planning state' do
|
33
33
|
delayed_plan.execution_plan.state = :planning
|
34
34
|
abstract_delayed_executor.send(:process, [delayed_plan], @start_at)
|
35
|
-
_(delayed_plan.execution_plan.state).must_equal :
|
35
|
+
_(delayed_plan.execution_plan.state).must_equal :scheduled
|
36
|
+
end
|
36
37
|
|
38
|
+
it 'handles plan in running state' do
|
37
39
|
delayed_plan.execution_plan.set_state(:running, true)
|
38
40
|
abstract_delayed_executor.send(:process, [delayed_plan], @start_at)
|
39
41
|
_(delayed_plan.execution_plan.state).must_equal :running
|
42
|
+
_(world.persistence.load_delayed_plan(delayed_plan.execution_plan_uuid)).must_be :nil?
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
@@ -55,7 +58,7 @@ module Dynflow
|
|
55
58
|
|
56
59
|
it 'delays the action' do
|
57
60
|
_(execution_plan.steps.count).must_equal 1
|
58
|
-
_(delayed_plan.start_at.
|
61
|
+
_(delayed_plan.start_at.to_i).must_equal(@start_at.to_i)
|
59
62
|
_(history_names.call(execution_plan)).must_equal ['delay']
|
60
63
|
end
|
61
64
|
|
data/test/persistence_test.rb
CHANGED
@@ -86,7 +86,7 @@ module Dynflow
|
|
86
86
|
original.each do |key, value|
|
87
87
|
loaded_value = loaded[key.to_s]
|
88
88
|
if value.is_a?(Time)
|
89
|
-
_(loaded_value
|
89
|
+
_(loaded_value).must_be_within_delta(value, 0.5)
|
90
90
|
elsif value.is_a?(Hash)
|
91
91
|
assert_equal_attributes!(value, loaded_value)
|
92
92
|
elsif value.nil?
|
@@ -348,7 +348,7 @@ module Dynflow
|
|
348
348
|
if value.nil?
|
349
349
|
assert_nil stored.fetch(name.to_sym)
|
350
350
|
elsif value.is_a?(Time)
|
351
|
-
_(stored.fetch(name.to_sym)
|
351
|
+
_(stored.fetch(name.to_sym)).must_be_within_delta(value, 0.5)
|
352
352
|
else
|
353
353
|
_(stored.fetch(name.to_sym)).must_equal value
|
354
354
|
end
|
data/web/views/flow_step.erb
CHANGED
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynflow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Necas
|
8
8
|
- Petr Chalupa
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-09-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|
@@ -25,6 +25,20 @@ dependencies:
|
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: msgpack
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.3.3
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 1.3.3
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
43
|
name: apipie-params
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -242,10 +256,11 @@ executables: []
|
|
242
256
|
extensions: []
|
243
257
|
extra_rdoc_files: []
|
244
258
|
files:
|
259
|
+
- ".github/install_dependencies.sh"
|
260
|
+
- ".github/workflows/ruby.yml"
|
245
261
|
- ".gitignore"
|
246
262
|
- ".rubocop.yml"
|
247
263
|
- ".rubocop_todo.yml"
|
248
|
-
- ".travis.yml"
|
249
264
|
- Dockerfile
|
250
265
|
- Gemfile
|
251
266
|
- MIT-LICENSE
|
@@ -386,6 +401,7 @@ files:
|
|
386
401
|
- doc/pages/source/projects/index.md
|
387
402
|
- docker-compose.yml
|
388
403
|
- dynflow.gemspec
|
404
|
+
- examples/chunked_output_benchmark.rb
|
389
405
|
- examples/clock_benchmark.rb
|
390
406
|
- examples/example_helper.rb
|
391
407
|
- examples/future_execution.rb
|
@@ -397,6 +413,7 @@ files:
|
|
397
413
|
- examples/sub_plan_concurrency_control.rb
|
398
414
|
- examples/sub_plans.rb
|
399
415
|
- examples/termination.rb
|
416
|
+
- extras/expand/main.go
|
400
417
|
- extras/statsd_mapping.conf
|
401
418
|
- lib/dynflow.rb
|
402
419
|
- lib/dynflow/action.rb
|
@@ -469,11 +486,14 @@ files:
|
|
469
486
|
- lib/dynflow/executors/sidekiq/redis_locking.rb
|
470
487
|
- lib/dynflow/executors/sidekiq/serialization.rb
|
471
488
|
- lib/dynflow/executors/sidekiq/worker_jobs.rb
|
489
|
+
- lib/dynflow/extensions.rb
|
490
|
+
- lib/dynflow/extensions/msgpack.rb
|
472
491
|
- lib/dynflow/flows.rb
|
473
492
|
- lib/dynflow/flows/abstract.rb
|
474
493
|
- lib/dynflow/flows/abstract_composed.rb
|
475
494
|
- lib/dynflow/flows/atom.rb
|
476
495
|
- lib/dynflow/flows/concurrence.rb
|
496
|
+
- lib/dynflow/flows/registry.rb
|
477
497
|
- lib/dynflow/flows/sequence.rb
|
478
498
|
- lib/dynflow/logger_adapters.rb
|
479
499
|
- lib/dynflow/logger_adapters/abstract.rb
|
@@ -513,6 +533,9 @@ files:
|
|
513
533
|
- lib/dynflow/persistence_adapters/sequel_migrations/018_add_uuid_column.rb
|
514
534
|
- lib/dynflow/persistence_adapters/sequel_migrations/019_update_mysql_time_precision.rb
|
515
535
|
- lib/dynflow/persistence_adapters/sequel_migrations/020_drop_duplicate_indices.rb
|
536
|
+
- lib/dynflow/persistence_adapters/sequel_migrations/021_create_output_chunks.rb
|
537
|
+
- lib/dynflow/persistence_adapters/sequel_migrations/022_store_flows_as_msgpack.rb
|
538
|
+
- lib/dynflow/persistence_adapters/sequel_migrations/023_sqlite_workarounds.rb
|
516
539
|
- lib/dynflow/rails.rb
|
517
540
|
- lib/dynflow/rails/configuration.rb
|
518
541
|
- lib/dynflow/rails/daemon.rb
|
@@ -534,6 +557,7 @@ files:
|
|
534
557
|
- lib/dynflow/telemetry_adapters/statsd.rb
|
535
558
|
- lib/dynflow/testing.rb
|
536
559
|
- lib/dynflow/testing/assertions.rb
|
560
|
+
- lib/dynflow/testing/dummy_coordinator.rb
|
537
561
|
- lib/dynflow/testing/dummy_execution_plan.rb
|
538
562
|
- lib/dynflow/testing/dummy_executor.rb
|
539
563
|
- lib/dynflow/testing/dummy_planned_action.rb
|
@@ -576,11 +600,12 @@ files:
|
|
576
600
|
- test/execution_plan_hooks_test.rb
|
577
601
|
- test/execution_plan_test.rb
|
578
602
|
- test/executor_test.rb
|
603
|
+
- test/extensions_test.rb
|
604
|
+
- test/flows_test.rb
|
579
605
|
- test/future_execution_test.rb
|
580
606
|
- test/memory_cosumption_watcher_test.rb
|
581
607
|
- test/middleware_test.rb
|
582
608
|
- test/persistence_test.rb
|
583
|
-
- test/prepare_travis_env.sh
|
584
609
|
- test/redis_locking_test.rb
|
585
610
|
- test/rescue_test.rb
|
586
611
|
- test/round_robin_test.rb
|
@@ -625,7 +650,7 @@ homepage: https://github.com/Dynflow/dynflow
|
|
625
650
|
licenses:
|
626
651
|
- MIT
|
627
652
|
metadata: {}
|
628
|
-
post_install_message:
|
653
|
+
post_install_message:
|
629
654
|
rdoc_options: []
|
630
655
|
require_paths:
|
631
656
|
- lib
|
@@ -641,7 +666,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
641
666
|
version: '0'
|
642
667
|
requirements: []
|
643
668
|
rubygems_version: 3.1.2
|
644
|
-
signing_key:
|
669
|
+
signing_key:
|
645
670
|
specification_version: 4
|
646
671
|
summary: DYNamic workFLOW engine
|
647
672
|
test_files:
|
@@ -659,11 +684,12 @@ test_files:
|
|
659
684
|
- test/execution_plan_hooks_test.rb
|
660
685
|
- test/execution_plan_test.rb
|
661
686
|
- test/executor_test.rb
|
687
|
+
- test/extensions_test.rb
|
688
|
+
- test/flows_test.rb
|
662
689
|
- test/future_execution_test.rb
|
663
690
|
- test/memory_cosumption_watcher_test.rb
|
664
691
|
- test/middleware_test.rb
|
665
692
|
- test/persistence_test.rb
|
666
|
-
- test/prepare_travis_env.sh
|
667
693
|
- test/redis_locking_test.rb
|
668
694
|
- test/rescue_test.rb
|
669
695
|
- test/round_robin_test.rb
|