dynflow 1.4.7 → 1.5.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.
- 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
|