dynflow 1.4.7 → 1.5.0
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/lib/dynflow.rb +1 -1
- data/lib/dynflow/action.rb +4 -3
- data/lib/dynflow/action/suspended.rb +4 -4
- data/lib/dynflow/action/timeouts.rb +2 -2
- data/lib/dynflow/actor.rb +20 -4
- data/lib/dynflow/clock.rb +2 -2
- data/lib/dynflow/director.rb +5 -1
- data/lib/dynflow/director/running_steps_manager.rb +2 -2
- data/lib/dynflow/dispatcher.rb +2 -1
- data/lib/dynflow/dispatcher/client_dispatcher.rb +7 -1
- data/lib/dynflow/dispatcher/executor_dispatcher.rb +4 -2
- data/lib/dynflow/execution_history.rb +1 -1
- data/lib/dynflow/execution_plan.rb +12 -4
- data/lib/dynflow/executors/abstract/core.rb +1 -1
- data/lib/dynflow/executors/parallel.rb +2 -2
- data/lib/dynflow/flows.rb +1 -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/persistence_adapters/sequel.rb +1 -1
- data/lib/dynflow/testing/in_thread_executor.rb +2 -2
- data/lib/dynflow/testing/in_thread_world.rb +5 -5
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/world.rb +4 -4
- data/test/dispatcher_test.rb +6 -0
- data/test/flows_test.rb +44 -0
- data/test/future_execution_test.rb +1 -1
- data/test/persistence_test.rb +2 -2
- metadata +10 -8
- data/.travis.yml +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b33bcad7864102ad9f0f3bb654c7990b3037b7b0620b770d5f34f1495402855
|
4
|
+
data.tar.gz: f4089f0cb793384a764f4aa042268c19d174c989c5d348f63e126eac5fb94716
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c119f0ce0d3605012206b083b829bc85194d676f762d61a394be6257d376c56388efd72f4a77f78445ece78942c5eb561728031e9a703767113eab7780b9d0e3
|
7
|
+
data.tar.gz: 136dafb81d0766e9c1041a9a52896b4cbe0d300482be729900fd9fe8f83e6095bbc084a6d43d0aa9012b436544690d3c2d774d46f0b9c573775b3707daec3428
|
@@ -1,5 +1,7 @@
|
|
1
1
|
#!/usr/bin/env bash
|
2
2
|
|
3
|
+
set -x
|
4
|
+
|
3
5
|
echo "Setting the environment to use ${DB} database"
|
4
6
|
|
5
7
|
BUNDLE_CONFIG=.bundle/config
|
@@ -12,11 +14,9 @@ EOF
|
|
12
14
|
case $DB in
|
13
15
|
mysql)
|
14
16
|
sed -i 's/:mysql//'g $BUNDLE_CONFIG
|
15
|
-
mysql -e 'create database travis_ci_test;'
|
16
17
|
;;
|
17
18
|
postgresql)
|
18
19
|
sed -i 's/:postgresql//'g $BUNDLE_CONFIG
|
19
|
-
psql -c 'create database travis_ci_test;' -U postgres
|
20
20
|
;;
|
21
21
|
sqlite3)
|
22
22
|
# the tests are by default using sqlite3: do nothing
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
2
|
+
# They are provided by a third-party and are governed by
|
3
|
+
# separate terms of service, privacy policy, and support
|
4
|
+
# documentation.
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
7
|
+
|
8
|
+
name: Ruby
|
9
|
+
|
10
|
+
on: [pull_request]
|
11
|
+
|
12
|
+
env:
|
13
|
+
TESTOPTS: --verbose
|
14
|
+
|
15
|
+
jobs:
|
16
|
+
rubocop:
|
17
|
+
runs-on: ubuntu-latest
|
18
|
+
steps:
|
19
|
+
- uses: actions/checkout@v2
|
20
|
+
- name: Setup Ruby
|
21
|
+
uses: ruby/setup-ruby@v1
|
22
|
+
with:
|
23
|
+
ruby-version: 2.7
|
24
|
+
- name: Setup
|
25
|
+
run: |
|
26
|
+
gem install bundler
|
27
|
+
bundle install --jobs=3 --retry=3
|
28
|
+
- name: Run rubocop
|
29
|
+
run: bundle exec rubocop
|
30
|
+
|
31
|
+
test:
|
32
|
+
runs-on: ubuntu-latest
|
33
|
+
needs: rubocop
|
34
|
+
strategy:
|
35
|
+
fail-fast: false
|
36
|
+
matrix:
|
37
|
+
ruby_version:
|
38
|
+
- 2.5.0
|
39
|
+
- 2.6.0
|
40
|
+
- 2.7.0
|
41
|
+
- 3.0.0
|
42
|
+
concurrent_ruby_ext:
|
43
|
+
- 'true'
|
44
|
+
- 'false'
|
45
|
+
db:
|
46
|
+
- postgresql
|
47
|
+
- mysql
|
48
|
+
- sqlite3
|
49
|
+
include:
|
50
|
+
- db: postgresql
|
51
|
+
conn_string: postgres://postgres@localhost/travis_ci_test
|
52
|
+
- db: mysql
|
53
|
+
conn_string: mysql2://root@127.0.0.1/travis_ci_test
|
54
|
+
- db: sqlite3
|
55
|
+
conn_string: sqlite:/
|
56
|
+
exclude:
|
57
|
+
- db: mysql
|
58
|
+
ruby_version: 2.5.0
|
59
|
+
- db: mysql
|
60
|
+
ruby_version: 2.6.0
|
61
|
+
- db: mysql
|
62
|
+
ruby_version: 3.0.0
|
63
|
+
- db: mysql
|
64
|
+
concurrent_ruby_ext: 'true'
|
65
|
+
- db: sqlite3
|
66
|
+
ruby_version: 2.5.0
|
67
|
+
- db: sqlite3
|
68
|
+
ruby_version: 2.6.0
|
69
|
+
- db: sqlite3
|
70
|
+
ruby_version: 3.0.0
|
71
|
+
- db: sqlite3
|
72
|
+
concurrent_ruby_ext: 'true'
|
73
|
+
- db: postgresql
|
74
|
+
ruby_version: 2.5.0
|
75
|
+
concurrent_ruby_ext: 'true'
|
76
|
+
- db: postgresql
|
77
|
+
ruby_version: 2.6.0
|
78
|
+
concurrent_ruby_ext: 'true'
|
79
|
+
- db: postgresql
|
80
|
+
ruby_version: 3.0.0
|
81
|
+
concurrent_ruby_ext: 'true'
|
82
|
+
|
83
|
+
services:
|
84
|
+
postgres:
|
85
|
+
image: postgres:12.1
|
86
|
+
ports: ['5432:5432']
|
87
|
+
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
88
|
+
env:
|
89
|
+
POSTGRES_DB: travis_ci_test
|
90
|
+
mariadb:
|
91
|
+
image: mariadb:10
|
92
|
+
ports: ['3306:3306']
|
93
|
+
env:
|
94
|
+
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
|
95
|
+
MYSQL_DATABASE: travis_ci_test
|
96
|
+
redis:
|
97
|
+
image: redis:latest
|
98
|
+
ports: ['6379:6379']
|
99
|
+
|
100
|
+
env:
|
101
|
+
DB: ${{ matrix.db }}
|
102
|
+
DB_CONN_STRING: ${{ matrix.conn_string }}
|
103
|
+
CONCURRENT_RUBY_EXT: "${{ matrix.concurrent_ruby_ext }}"
|
104
|
+
|
105
|
+
steps:
|
106
|
+
- uses: actions/checkout@v2
|
107
|
+
- name: Set up Ruby
|
108
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
109
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
110
|
+
uses: ruby/setup-ruby@v1
|
111
|
+
with:
|
112
|
+
ruby-version: ${{ matrix.ruby_version }}
|
113
|
+
- name: Install dependencies
|
114
|
+
run: .github/install_dependencies.sh
|
115
|
+
- name: Run tests
|
116
|
+
run: bundle exec rake test
|
data/lib/dynflow.rb
CHANGED
data/lib/dynflow/action.rb
CHANGED
@@ -93,7 +93,8 @@ module Dynflow
|
|
93
93
|
fields! execution_plan_id: String,
|
94
94
|
step_id: Integer,
|
95
95
|
event: Object,
|
96
|
-
time: type { variants Time, NilClass }
|
96
|
+
time: type { variants Time, NilClass },
|
97
|
+
optional: Algebrick::Types::Boolean
|
97
98
|
end
|
98
99
|
|
99
100
|
def self.constantize(action_name)
|
@@ -332,9 +333,9 @@ module Dynflow
|
|
332
333
|
|
333
334
|
# Plan an +event+ to be send to the action defined by +action+, what defaults to be self.
|
334
335
|
# if +time+ is not passed, event is sent as soon as possible.
|
335
|
-
def plan_event(event, time = nil, execution_plan_id: self.execution_plan_id, step_id: self.run_step_id)
|
336
|
+
def plan_event(event, time = nil, execution_plan_id: self.execution_plan_id, step_id: self.run_step_id, optional: false)
|
336
337
|
time = @world.clock.current_time + time if time.is_a?(Numeric)
|
337
|
-
delayed_events << DelayedEvent[execution_plan_id, step_id, event, time]
|
338
|
+
delayed_events << DelayedEvent[execution_plan_id, step_id, event, time, optional]
|
338
339
|
end
|
339
340
|
|
340
341
|
def delayed_events
|
@@ -9,14 +9,14 @@ module Dynflow
|
|
9
9
|
@step_id = action.run_step_id
|
10
10
|
end
|
11
11
|
|
12
|
-
def plan_event(event, time, sent = Concurrent::Promises.resolvable_future)
|
13
|
-
@world.plan_event(execution_plan_id, step_id, event, time, sent)
|
12
|
+
def plan_event(event, time, sent = Concurrent::Promises.resolvable_future, optional: false)
|
13
|
+
@world.plan_event(execution_plan_id, step_id, event, time, sent, optional: optional)
|
14
14
|
end
|
15
15
|
|
16
|
-
def event(event, sent = Concurrent::Promises.resolvable_future)
|
16
|
+
def event(event, sent = Concurrent::Promises.resolvable_future, optional: false)
|
17
17
|
# TODO: deprecate 2 levels backtrace (to know it's called from clock or internaly)
|
18
18
|
# remove lib/dynflow/clock.rb ClockReference#ping branch condition on removal.
|
19
|
-
plan_event(event, nil, sent)
|
19
|
+
plan_event(event, nil, sent, optional: optional)
|
20
20
|
end
|
21
21
|
|
22
22
|
def <<(event = nil)
|
data/lib/dynflow/actor.rb
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Dynflow
|
3
3
|
|
4
|
+
FULL_BACKTRACE = %w[1 y yes].include?((ENV['DYNFLOW_FULL_BACKTRACE'] || '').downcase)
|
5
|
+
BACKTRACE_LIMIT = begin
|
6
|
+
limit = ENV['DYNFLOW_BACKTRACE_LIMIT'].to_i
|
7
|
+
limit.zero? ? nil : limit
|
8
|
+
end
|
9
|
+
|
4
10
|
module MethodicActor
|
5
11
|
def on_message(message)
|
6
12
|
method, *args = message
|
@@ -44,7 +50,11 @@ module Dynflow
|
|
44
50
|
include LogWithFullBacktrace
|
45
51
|
|
46
52
|
def on_envelope(envelope)
|
47
|
-
|
53
|
+
if FULL_BACKTRACE
|
54
|
+
Actor::BacktraceCollector.with_backtrace(envelope.origin_backtrace) { super }
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
48
58
|
end
|
49
59
|
end
|
50
60
|
|
@@ -83,9 +93,15 @@ module Dynflow
|
|
83
93
|
|
84
94
|
# takes an array of backtrace lines and replaces each chunk
|
85
95
|
def filter_backtrace(backtrace)
|
86
|
-
backtrace.map { |line| filter_line(line) }
|
87
|
-
|
88
|
-
|
96
|
+
trace = backtrace.map { |line| filter_line(line) }
|
97
|
+
.chunk_while { |l1, l2| l1 == l2}
|
98
|
+
.map(&:first)
|
99
|
+
if BACKTRACE_LIMIT
|
100
|
+
count = trace.count
|
101
|
+
trace = trace.take(BACKTRACE_LIMIT)
|
102
|
+
trace << "[ backtrace omitted #{count - BACKTRACE_LIMIT} lines ]" if trace.count < count
|
103
|
+
end
|
104
|
+
trace
|
89
105
|
end
|
90
106
|
end
|
91
107
|
end
|
data/lib/dynflow/clock.rb
CHANGED
@@ -114,11 +114,11 @@ module Dynflow
|
|
114
114
|
Time.now
|
115
115
|
end
|
116
116
|
|
117
|
-
def ping(who, time, with_what = nil, where =
|
117
|
+
def ping(who, time, with_what = nil, where = :<<, optional: false)
|
118
118
|
Type! time, Time, Numeric
|
119
119
|
time = current_time + time if time.is_a? Numeric
|
120
120
|
if who.is_a?(Action::Suspended)
|
121
|
-
who.plan_event(with_what, time)
|
121
|
+
who.plan_event(with_what, time, optional: optional)
|
122
122
|
else
|
123
123
|
timer = Clock::Timer[who, time, with_what.nil? ? Algebrick::Types::None : Some[Object][with_what], where]
|
124
124
|
self.tell([:add_timer, timer])
|
data/lib/dynflow/director.rb
CHANGED
@@ -15,7 +15,8 @@ module Dynflow
|
|
15
15
|
execution_plan_id: String,
|
16
16
|
step_id: Integer,
|
17
17
|
event: Object,
|
18
|
-
result: Concurrent::Promises::ResolvableFuture
|
18
|
+
result: Concurrent::Promises::ResolvableFuture,
|
19
|
+
optional: Algebrick::Types::Boolean
|
19
20
|
end
|
20
21
|
|
21
22
|
UnprocessableEvent = Class.new(Dynflow::Error)
|
@@ -163,6 +164,9 @@ module Dynflow
|
|
163
164
|
execution_plan_manager = @execution_plan_managers[event.execution_plan_id]
|
164
165
|
if execution_plan_manager
|
165
166
|
execution_plan_manager.event(event)
|
167
|
+
elsif event.optional
|
168
|
+
event.result.reject "no manager for #{event.inspect}"
|
169
|
+
[]
|
166
170
|
else
|
167
171
|
raise Dynflow::Error, "no manager for #{event.inspect}"
|
168
172
|
end
|
@@ -20,8 +20,8 @@ module Dynflow
|
|
20
20
|
def terminate
|
21
21
|
pending_work = @work_items.clear.values.flatten(1)
|
22
22
|
pending_work.each do |w|
|
23
|
-
|
24
|
-
|
23
|
+
finish_event_result(w) do |result|
|
24
|
+
result.reject UnprocessableEvent.new("dropping due to termination")
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
data/lib/dynflow/dispatcher.rb
CHANGED
@@ -132,11 +132,13 @@ module Dynflow
|
|
132
132
|
end
|
133
133
|
|
134
134
|
def dispatch_request(request, client_world_id, request_id)
|
135
|
+
ignore_unknown = false
|
135
136
|
executor_id = match request,
|
136
137
|
(on ~Execution do |execution|
|
137
138
|
AnyExecutor
|
138
139
|
end),
|
139
140
|
(on ~Event do |event|
|
141
|
+
ignore_unknown = event.optional
|
140
142
|
find_executor(event.execution_plan_id)
|
141
143
|
end),
|
142
144
|
(on Ping.(~any, ~any) | Status.(~any, ~any) do |receiver_id, _|
|
@@ -144,7 +146,11 @@ module Dynflow
|
|
144
146
|
end)
|
145
147
|
envelope = Envelope[request_id, client_world_id, executor_id, request]
|
146
148
|
if Dispatcher::UnknownWorld === envelope.receiver_id
|
147
|
-
raise Dynflow::Error, "Could not find an executor for #{envelope}"
|
149
|
+
raise Dynflow::Error, "Could not find an executor for #{envelope}" unless ignore_unknown
|
150
|
+
|
151
|
+
message = "Could not find an executor for optional #{envelope}, discarding."
|
152
|
+
log(Logger::DEBUG, message)
|
153
|
+
return respond(envelope, Failed[message])
|
148
154
|
end
|
149
155
|
connector.send(envelope).value!
|
150
156
|
rescue => e
|
@@ -52,12 +52,14 @@ module Dynflow
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
if event_request.time.nil? || event_request.time < Time.now
|
55
|
-
@world.executor.event(envelope.request_id, event_request.execution_plan_id, event_request.step_id, event_request.event, future
|
55
|
+
@world.executor.event(envelope.request_id, event_request.execution_plan_id, event_request.step_id, event_request.event, future,
|
56
|
+
optional: event_request.optional)
|
56
57
|
else
|
57
58
|
@world.clock.ping(
|
58
59
|
@world.executor,
|
59
60
|
event_request.time,
|
60
|
-
Director::Event[envelope.request_id, event_request.execution_plan_id, event_request.step_id, event_request.event, Concurrent::Promises.resolvable_future
|
61
|
+
Director::Event[envelope.request_id, event_request.execution_plan_id, event_request.step_id, event_request.event, Concurrent::Promises.resolvable_future,
|
62
|
+
event_request.optional],
|
61
63
|
:delayed_event
|
62
64
|
)
|
63
65
|
# resolves the future right away - currently we do not wait for the clock ping
|
@@ -418,6 +418,14 @@ module Dynflow
|
|
418
418
|
end
|
419
419
|
end
|
420
420
|
|
421
|
+
def self.load_flow(flow_hash)
|
422
|
+
if flow_hash.is_a? Hash
|
423
|
+
Flows::Abstract.from_hash(flow_hash)
|
424
|
+
else
|
425
|
+
Flows::Abstract.decode(flow_hash)
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
421
429
|
def to_hash
|
422
430
|
recursive_to_hash id: id,
|
423
431
|
class: self.class.to_s,
|
@@ -425,8 +433,8 @@ module Dynflow
|
|
425
433
|
state: state,
|
426
434
|
result: result,
|
427
435
|
root_plan_step_id: root_plan_step && root_plan_step.id,
|
428
|
-
run_flow: run_flow,
|
429
|
-
finalize_flow: finalize_flow,
|
436
|
+
run_flow: run_flow.encode,
|
437
|
+
finalize_flow: finalize_flow.encode,
|
430
438
|
step_ids: steps.map { |id, _| id },
|
431
439
|
started_at: time_to_str(started_at),
|
432
440
|
ended_at: time_to_str(ended_at),
|
@@ -448,8 +456,8 @@ module Dynflow
|
|
448
456
|
hash[:label],
|
449
457
|
hash[:state],
|
450
458
|
steps[hash[:root_plan_step_id]],
|
451
|
-
|
452
|
-
|
459
|
+
load_flow(hash[:run_flow]),
|
460
|
+
load_flow(hash[:finalize_flow]),
|
453
461
|
steps,
|
454
462
|
string_to_time(hash[:started_at]),
|
455
463
|
string_to_time(hash[:ended_at]),
|
@@ -37,7 +37,7 @@ module Dynflow
|
|
37
37
|
|
38
38
|
def plan_events(delayed_events)
|
39
39
|
delayed_events.each do |event|
|
40
|
-
@world.plan_event(event.execution_plan_id, event.step_id, event.event, event.time)
|
40
|
+
@world.plan_event(event.execution_plan_id, event.step_id, event.event, event.time, optional: event.optional)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -33,8 +33,8 @@ module Dynflow
|
|
33
33
|
raise e
|
34
34
|
end
|
35
35
|
|
36
|
-
def event(request_id, execution_plan_id, step_id, event, future = nil)
|
37
|
-
@core.ask([:handle_event, Director::Event[request_id, execution_plan_id, step_id, event, future]])
|
36
|
+
def event(request_id, execution_plan_id, step_id, event, future = nil, optional: false)
|
37
|
+
@core.ask([:handle_event, Director::Event[request_id, execution_plan_id, step_id, event, future, optional]])
|
38
38
|
future
|
39
39
|
end
|
40
40
|
|
data/lib/dynflow/flows.rb
CHANGED
@@ -32,6 +32,20 @@ module Dynflow
|
|
32
32
|
def flatten!
|
33
33
|
raise NotImplementedError
|
34
34
|
end
|
35
|
+
|
36
|
+
def self.new_from_hash(hash)
|
37
|
+
check_class_matching hash
|
38
|
+
new(hash[:flows].map { |flow_hash| from_hash(flow_hash) })
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.decode(data)
|
42
|
+
if data.is_a? Integer
|
43
|
+
Flows::Atom.new(data)
|
44
|
+
else
|
45
|
+
kind, *subflows = data
|
46
|
+
Registry.decode(kind).new(subflows.map { |subflow| self.decode(subflow) })
|
47
|
+
end
|
48
|
+
end
|
35
49
|
end
|
36
50
|
end
|
37
51
|
end
|
@@ -11,8 +11,8 @@ module Dynflow
|
|
11
11
|
@flows = flows
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
|
14
|
+
def encode
|
15
|
+
[Registry.encode(self)] + flows.map(&:encode)
|
16
16
|
end
|
17
17
|
|
18
18
|
def <<(v)
|
@@ -61,11 +61,6 @@ module Dynflow
|
|
61
61
|
|
62
62
|
protected
|
63
63
|
|
64
|
-
def self.new_from_hash(hash)
|
65
|
-
check_class_matching hash
|
66
|
-
new(hash[:flows].map { |flow_hash| from_hash(flow_hash) })
|
67
|
-
end
|
68
|
-
|
69
64
|
# adds the +new_flow+ in a way that it's in sequence with
|
70
65
|
# the +satisfying_flows+
|
71
66
|
def add_to_sequence(satisfying_flows, new_flow)
|
data/lib/dynflow/flows/atom.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Dynflow
|
3
|
+
module Flows
|
4
|
+
class Registry
|
5
|
+
class IdentifierTaken < ArgumentError; end
|
6
|
+
class UnknownIdentifier < ArgumentError; end
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def register!(klass, identifier)
|
10
|
+
if (found = serialization_map[identifier])
|
11
|
+
raise IdentifierTaken, "Error setting up mapping #{identifier} to #{klass}, it already maps to #{found}"
|
12
|
+
else
|
13
|
+
serialization_map.update(identifier => klass)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def encode(klass)
|
18
|
+
klass = klass.class unless klass.is_a?(Class)
|
19
|
+
serialization_map.invert[klass] || raise(UnknownIdentifier, "Could not find mapping for #{klass}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def decode(identifier)
|
23
|
+
serialization_map[identifier] || raise(UnknownIdentifier, "Could not find mapping for #{identifier}")
|
24
|
+
end
|
25
|
+
|
26
|
+
def serialization_map
|
27
|
+
@serialization_map ||= {}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -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/version.rb
CHANGED
data/lib/dynflow/world.rb
CHANGED
@@ -219,12 +219,12 @@ module Dynflow
|
|
219
219
|
publish_request(Dispatcher::Execution[execution_plan_id], done, true)
|
220
220
|
end
|
221
221
|
|
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)
|
222
|
+
def event(execution_plan_id, step_id, event, done = Concurrent::Promises.resolvable_future, optional: false)
|
223
|
+
publish_request(Dispatcher::Event[execution_plan_id, step_id, event, nil, optional], done, false)
|
224
224
|
end
|
225
225
|
|
226
|
-
def plan_event(execution_plan_id, step_id, event, time, accepted = Concurrent::Promises.resolvable_future)
|
227
|
-
publish_request(Dispatcher::Event[execution_plan_id, step_id, event, time], accepted, false)
|
226
|
+
def plan_event(execution_plan_id, step_id, event, time, accepted = Concurrent::Promises.resolvable_future, optional: false)
|
227
|
+
publish_request(Dispatcher::Event[execution_plan_id, step_id, event, time, optional], accepted, false)
|
228
228
|
end
|
229
229
|
|
230
230
|
def ping(world_id, timeout, done = Concurrent::Promises.resolvable_future)
|
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
|
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
|
@@ -55,7 +55,7 @@ module Dynflow
|
|
55
55
|
|
56
56
|
it 'delays the action' do
|
57
57
|
_(execution_plan.steps.count).must_equal 1
|
58
|
-
_(delayed_plan.start_at
|
58
|
+
_(delayed_plan.start_at).must_be_within_delta(@start_at, 0.5)
|
59
59
|
_(history_names.call(execution_plan)).must_equal ['delay']
|
60
60
|
end
|
61
61
|
|
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
|
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.5.0
|
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-05-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|
@@ -242,10 +242,11 @@ executables: []
|
|
242
242
|
extensions: []
|
243
243
|
extra_rdoc_files: []
|
244
244
|
files:
|
245
|
+
- ".github/install_dependencies.sh"
|
246
|
+
- ".github/workflows/ruby.yml"
|
245
247
|
- ".gitignore"
|
246
248
|
- ".rubocop.yml"
|
247
249
|
- ".rubocop_todo.yml"
|
248
|
-
- ".travis.yml"
|
249
250
|
- Dockerfile
|
250
251
|
- Gemfile
|
251
252
|
- MIT-LICENSE
|
@@ -474,6 +475,7 @@ files:
|
|
474
475
|
- lib/dynflow/flows/abstract_composed.rb
|
475
476
|
- lib/dynflow/flows/atom.rb
|
476
477
|
- lib/dynflow/flows/concurrence.rb
|
478
|
+
- lib/dynflow/flows/registry.rb
|
477
479
|
- lib/dynflow/flows/sequence.rb
|
478
480
|
- lib/dynflow/logger_adapters.rb
|
479
481
|
- lib/dynflow/logger_adapters/abstract.rb
|
@@ -576,11 +578,11 @@ files:
|
|
576
578
|
- test/execution_plan_hooks_test.rb
|
577
579
|
- test/execution_plan_test.rb
|
578
580
|
- test/executor_test.rb
|
581
|
+
- test/flows_test.rb
|
579
582
|
- test/future_execution_test.rb
|
580
583
|
- test/memory_cosumption_watcher_test.rb
|
581
584
|
- test/middleware_test.rb
|
582
585
|
- test/persistence_test.rb
|
583
|
-
- test/prepare_travis_env.sh
|
584
586
|
- test/redis_locking_test.rb
|
585
587
|
- test/rescue_test.rb
|
586
588
|
- test/round_robin_test.rb
|
@@ -625,7 +627,7 @@ homepage: https://github.com/Dynflow/dynflow
|
|
625
627
|
licenses:
|
626
628
|
- MIT
|
627
629
|
metadata: {}
|
628
|
-
post_install_message:
|
630
|
+
post_install_message:
|
629
631
|
rdoc_options: []
|
630
632
|
require_paths:
|
631
633
|
- lib
|
@@ -641,7 +643,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
641
643
|
version: '0'
|
642
644
|
requirements: []
|
643
645
|
rubygems_version: 3.1.2
|
644
|
-
signing_key:
|
646
|
+
signing_key:
|
645
647
|
specification_version: 4
|
646
648
|
summary: DYNamic workFLOW engine
|
647
649
|
test_files:
|
@@ -659,11 +661,11 @@ test_files:
|
|
659
661
|
- test/execution_plan_hooks_test.rb
|
660
662
|
- test/execution_plan_test.rb
|
661
663
|
- test/executor_test.rb
|
664
|
+
- test/flows_test.rb
|
662
665
|
- test/future_execution_test.rb
|
663
666
|
- test/memory_cosumption_watcher_test.rb
|
664
667
|
- test/middleware_test.rb
|
665
668
|
- test/persistence_test.rb
|
666
|
-
- test/prepare_travis_env.sh
|
667
669
|
- test/redis_locking_test.rb
|
668
670
|
- test/rescue_test.rb
|
669
671
|
- test/round_robin_test.rb
|
data/.travis.yml
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
|
3
|
-
services:
|
4
|
-
- postgresql
|
5
|
-
- redis
|
6
|
-
|
7
|
-
rvm:
|
8
|
-
- "2.3.1"
|
9
|
-
- "2.4.0"
|
10
|
-
- "2.5.0"
|
11
|
-
|
12
|
-
env:
|
13
|
-
global:
|
14
|
-
- "TESTOPTS=--verbose DB=postgresql DB_CONN_STRING=postgres://postgres@localhost/travis_ci_test"
|
15
|
-
|
16
|
-
matrix:
|
17
|
-
include:
|
18
|
-
- rvm: "2.4.0"
|
19
|
-
env: "DB=mysql DB_CONN_STRING=mysql2://root@localhost/travis_ci_test"
|
20
|
-
services:
|
21
|
-
- mysql
|
22
|
-
- redis
|
23
|
-
- rvm: "2.4.0"
|
24
|
-
env: "DB=sqlite3 DB_CONN_STRING=sqlite:/"
|
25
|
-
- rvm: "2.4.0"
|
26
|
-
env: "CONCURRENT_RUBY_EXT=true"
|
27
|
-
|
28
|
-
install:
|
29
|
-
- test/prepare_travis_env.sh
|
30
|
-
|
31
|
-
script:
|
32
|
-
- bundle exec rubocop
|
33
|
-
- bundle exec rake test
|