dynflow 1.4.9 → 1.6.3

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/{test/prepare_travis_env.sh → .github/install_dependencies.sh} +2 -2
  3. data/.github/workflows/release.yml +48 -0
  4. data/.github/workflows/ruby.yml +116 -0
  5. data/Gemfile +1 -1
  6. data/dynflow.gemspec +1 -0
  7. data/examples/chunked_output_benchmark.rb +77 -0
  8. data/extras/expand/Dockerfile +9 -0
  9. data/extras/expand/README.md +25 -0
  10. data/extras/expand/go.mod +5 -0
  11. data/extras/expand/go.sum +11 -0
  12. data/extras/expand/main.go +66 -0
  13. data/lib/dynflow/action.rb +11 -1
  14. data/lib/dynflow/delayed_executors/abstract_core.rb +11 -9
  15. data/lib/dynflow/director.rb +37 -4
  16. data/lib/dynflow/dispatcher/client_dispatcher.rb +1 -1
  17. data/lib/dynflow/dispatcher/executor_dispatcher.rb +8 -0
  18. data/lib/dynflow/dispatcher.rb +5 -1
  19. data/lib/dynflow/execution_history.rb +1 -1
  20. data/lib/dynflow/execution_plan/hooks.rb +1 -1
  21. data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +1 -0
  22. data/lib/dynflow/execution_plan.rb +16 -5
  23. data/lib/dynflow/executors/abstract/core.rb +9 -0
  24. data/lib/dynflow/executors/parallel.rb +4 -0
  25. data/lib/dynflow/extensions/msgpack.rb +41 -0
  26. data/lib/dynflow/extensions.rb +6 -0
  27. data/lib/dynflow/flows/abstract.rb +14 -0
  28. data/lib/dynflow/flows/abstract_composed.rb +2 -7
  29. data/lib/dynflow/flows/atom.rb +2 -2
  30. data/lib/dynflow/flows/concurrence.rb +2 -0
  31. data/lib/dynflow/flows/registry.rb +32 -0
  32. data/lib/dynflow/flows/sequence.rb +2 -0
  33. data/lib/dynflow/flows.rb +1 -0
  34. data/lib/dynflow/persistence.rb +10 -0
  35. data/lib/dynflow/persistence_adapters/sequel.rb +51 -16
  36. data/lib/dynflow/persistence_adapters/sequel_migrations/021_create_output_chunks.rb +30 -0
  37. data/lib/dynflow/persistence_adapters/sequel_migrations/022_store_flows_as_msgpack.rb +90 -0
  38. data/lib/dynflow/persistence_adapters/sequel_migrations/023_sqlite_workarounds.rb +19 -0
  39. data/lib/dynflow/serializable.rb +2 -2
  40. data/lib/dynflow/testing/dummy_coordinator.rb +10 -0
  41. data/lib/dynflow/testing/dummy_planned_action.rb +4 -0
  42. data/lib/dynflow/testing/dummy_world.rb +2 -1
  43. data/lib/dynflow/testing.rb +1 -0
  44. data/lib/dynflow/version.rb +1 -1
  45. data/lib/dynflow/world.rb +12 -0
  46. data/lib/dynflow.rb +2 -1
  47. data/test/execution_plan_hooks_test.rb +36 -0
  48. data/test/extensions_test.rb +42 -0
  49. data/test/flows_test.rb +44 -0
  50. data/test/future_execution_test.rb +6 -3
  51. data/test/persistence_test.rb +2 -2
  52. data/web/views/flow_step.erb +1 -0
  53. metadata +42 -5
  54. data/.travis.yml +0 -33
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,
@@ -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
@@ -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 wrong plan state' do
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 :planned
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.inspect).must_equal (@start_at).inspect
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
 
@@ -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.inspect).must_equal value.inspect
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).inspect).must_equal value.inspect
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
@@ -31,6 +31,7 @@
31
31
  <% end %>
32
32
  <%= show_action_data("Input:", action.input) %>
33
33
  <%= show_action_data("Output:", action.output) %>
34
+ <%= show_action_data("Chunked output:", action.stored_output_chunks) %>
34
35
  <% if step.error %>
35
36
  <p>
36
37
  <b>Error:</b>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.9
4
+ version: 1.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Necas
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-08-10 00:00:00.000000000 Z
12
+ date: 2022-01-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
@@ -25,6 +25,26 @@ 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'
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: 1.3.3
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - "~>"
43
+ - !ruby/object:Gem::Version
44
+ version: '1.3'
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.3
28
48
  - !ruby/object:Gem::Dependency
29
49
  name: apipie-params
30
50
  requirement: !ruby/object:Gem::Requirement
@@ -242,10 +262,12 @@ executables: []
242
262
  extensions: []
243
263
  extra_rdoc_files: []
244
264
  files:
265
+ - ".github/install_dependencies.sh"
266
+ - ".github/workflows/release.yml"
267
+ - ".github/workflows/ruby.yml"
245
268
  - ".gitignore"
246
269
  - ".rubocop.yml"
247
270
  - ".rubocop_todo.yml"
248
- - ".travis.yml"
249
271
  - Dockerfile
250
272
  - Gemfile
251
273
  - MIT-LICENSE
@@ -386,6 +408,7 @@ files:
386
408
  - doc/pages/source/projects/index.md
387
409
  - docker-compose.yml
388
410
  - dynflow.gemspec
411
+ - examples/chunked_output_benchmark.rb
389
412
  - examples/clock_benchmark.rb
390
413
  - examples/example_helper.rb
391
414
  - examples/future_execution.rb
@@ -397,6 +420,11 @@ files:
397
420
  - examples/sub_plan_concurrency_control.rb
398
421
  - examples/sub_plans.rb
399
422
  - examples/termination.rb
423
+ - extras/expand/Dockerfile
424
+ - extras/expand/README.md
425
+ - extras/expand/go.mod
426
+ - extras/expand/go.sum
427
+ - extras/expand/main.go
400
428
  - extras/statsd_mapping.conf
401
429
  - lib/dynflow.rb
402
430
  - lib/dynflow/action.rb
@@ -469,11 +497,14 @@ files:
469
497
  - lib/dynflow/executors/sidekiq/redis_locking.rb
470
498
  - lib/dynflow/executors/sidekiq/serialization.rb
471
499
  - lib/dynflow/executors/sidekiq/worker_jobs.rb
500
+ - lib/dynflow/extensions.rb
501
+ - lib/dynflow/extensions/msgpack.rb
472
502
  - lib/dynflow/flows.rb
473
503
  - lib/dynflow/flows/abstract.rb
474
504
  - lib/dynflow/flows/abstract_composed.rb
475
505
  - lib/dynflow/flows/atom.rb
476
506
  - lib/dynflow/flows/concurrence.rb
507
+ - lib/dynflow/flows/registry.rb
477
508
  - lib/dynflow/flows/sequence.rb
478
509
  - lib/dynflow/logger_adapters.rb
479
510
  - lib/dynflow/logger_adapters/abstract.rb
@@ -513,6 +544,9 @@ files:
513
544
  - lib/dynflow/persistence_adapters/sequel_migrations/018_add_uuid_column.rb
514
545
  - lib/dynflow/persistence_adapters/sequel_migrations/019_update_mysql_time_precision.rb
515
546
  - lib/dynflow/persistence_adapters/sequel_migrations/020_drop_duplicate_indices.rb
547
+ - lib/dynflow/persistence_adapters/sequel_migrations/021_create_output_chunks.rb
548
+ - lib/dynflow/persistence_adapters/sequel_migrations/022_store_flows_as_msgpack.rb
549
+ - lib/dynflow/persistence_adapters/sequel_migrations/023_sqlite_workarounds.rb
516
550
  - lib/dynflow/rails.rb
517
551
  - lib/dynflow/rails/configuration.rb
518
552
  - lib/dynflow/rails/daemon.rb
@@ -534,6 +568,7 @@ files:
534
568
  - lib/dynflow/telemetry_adapters/statsd.rb
535
569
  - lib/dynflow/testing.rb
536
570
  - lib/dynflow/testing/assertions.rb
571
+ - lib/dynflow/testing/dummy_coordinator.rb
537
572
  - lib/dynflow/testing/dummy_execution_plan.rb
538
573
  - lib/dynflow/testing/dummy_executor.rb
539
574
  - lib/dynflow/testing/dummy_planned_action.rb
@@ -576,11 +611,12 @@ files:
576
611
  - test/execution_plan_hooks_test.rb
577
612
  - test/execution_plan_test.rb
578
613
  - test/executor_test.rb
614
+ - test/extensions_test.rb
615
+ - test/flows_test.rb
579
616
  - test/future_execution_test.rb
580
617
  - test/memory_cosumption_watcher_test.rb
581
618
  - test/middleware_test.rb
582
619
  - test/persistence_test.rb
583
- - test/prepare_travis_env.sh
584
620
  - test/redis_locking_test.rb
585
621
  - test/rescue_test.rb
586
622
  - test/round_robin_test.rb
@@ -659,11 +695,12 @@ test_files:
659
695
  - test/execution_plan_hooks_test.rb
660
696
  - test/execution_plan_test.rb
661
697
  - test/executor_test.rb
698
+ - test/extensions_test.rb
699
+ - test/flows_test.rb
662
700
  - test/future_execution_test.rb
663
701
  - test/memory_cosumption_watcher_test.rb
664
702
  - test/middleware_test.rb
665
703
  - test/persistence_test.rb
666
- - test/prepare_travis_env.sh
667
704
  - test/redis_locking_test.rb
668
705
  - test/rescue_test.rb
669
706
  - 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