dynflow 1.4.9 → 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
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