dynflow 1.9.3 → 2.0.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/bats.yml +50 -0
  3. data/.github/workflows/release.yml +1 -1
  4. data/.github/workflows/ruby.yml +27 -46
  5. data/.gitignore +2 -0
  6. data/.rubocop.yml +3 -0
  7. data/Dockerfile +1 -1
  8. data/Gemfile +7 -8
  9. data/README.md +4 -4
  10. data/doc/pages/source/documentation/index.md +4 -4
  11. data/dynflow.gemspec +2 -3
  12. data/examples/example_helper.rb +1 -1
  13. data/examples/execution_plan_chaining.rb +56 -0
  14. data/examples/remote_executor.rb +5 -8
  15. data/lib/dynflow/action/format.rb +4 -33
  16. data/lib/dynflow/debug/telemetry/persistence.rb +1 -1
  17. data/lib/dynflow/delayed_executors/abstract_core.rb +1 -1
  18. data/lib/dynflow/delayed_plan.rb +6 -0
  19. data/lib/dynflow/director.rb +9 -1
  20. data/lib/dynflow/executors/sidekiq/core.rb +1 -1
  21. data/lib/dynflow/executors/sidekiq/redis_locking.rb +10 -3
  22. data/lib/dynflow/extensions/msgpack.rb +4 -0
  23. data/lib/dynflow/persistence.rb +14 -2
  24. data/lib/dynflow/persistence_adapters/abstract.rb +9 -1
  25. data/lib/dynflow/persistence_adapters/sequel.rb +91 -48
  26. data/lib/dynflow/persistence_adapters/sequel_migrations/025_create_execution_plan_dependencies.rb +22 -0
  27. data/lib/dynflow/rails/daemon.rb +16 -7
  28. data/lib/dynflow/testing.rb +1 -1
  29. data/lib/dynflow/version.rb +1 -1
  30. data/lib/dynflow/world.rb +34 -13
  31. data/lib/dynflow.rb +0 -1
  32. data/test/action_test.rb +3 -3
  33. data/test/bats/helpers/common.bash +67 -0
  34. data/test/bats/helpers/containers.bash +146 -0
  35. data/test/bats/setup_suite.bash +46 -0
  36. data/test/bats/sidekiq-orchestrator.bats +178 -0
  37. data/test/bats/teardown_suite.bash +16 -0
  38. data/test/concurrency_control_test.rb +0 -1
  39. data/test/daemon_test.rb +21 -2
  40. data/test/extensions_test.rb +3 -3
  41. data/test/future_execution_test.rb +150 -3
  42. data/test/persistence_test.rb +70 -3
  43. data/test/support/dummy_example.rb +4 -0
  44. data/test/test_helper.rb +19 -4
  45. data/web/views/show.erb +24 -0
  46. metadata +15 -17
  47. data/.github/install_dependencies.sh +0 -35
@@ -342,7 +342,7 @@ module Dynflow
342
342
  end
343
343
  end
344
344
 
345
- describe '#find_past_delayed_plans' do
345
+ describe '#find_ready_delayed_plans' do
346
346
  it 'finds plans with start_before in past' do
347
347
  start_time = Time.now.utc
348
348
  prepare_and_save_plans
@@ -352,7 +352,7 @@ module Dynflow
352
352
  adapter.save_delayed_plan('plan3', :execution_plan_uuid => 'plan3', :frozen => false, :start_at => format_time(start_time + 60))
353
353
  adapter.save_delayed_plan('plan4', :execution_plan_uuid => 'plan4', :frozen => false, :start_at => format_time(start_time - 60),
354
354
  :start_before => format_time(start_time - 60))
355
- plans = adapter.find_past_delayed_plans(start_time)
355
+ plans = adapter.find_ready_delayed_plans(start_time)
356
356
  _(plans.length).must_equal 3
357
357
  _(plans.map { |plan| plan[:execution_plan_uuid] }).must_equal %w(plan2 plan4 plan1)
358
358
  end
@@ -366,10 +366,77 @@ module Dynflow
366
366
  adapter.save_delayed_plan('plan2', :execution_plan_uuid => 'plan2', :frozen => true, :start_at => format_time(start_time + 60),
367
367
  :start_before => format_time(start_time - 60))
368
368
 
369
- plans = adapter.find_past_delayed_plans(start_time)
369
+ plans = adapter.find_ready_delayed_plans(start_time)
370
370
  _(plans.length).must_equal 1
371
371
  _(plans.first[:execution_plan_uuid]).must_equal 'plan1'
372
372
  end
373
+
374
+ it 'finds plans with null start_at' do
375
+ start_time = Time.now.utc
376
+ prepare_and_save_plans
377
+
378
+ adapter.save_delayed_plan('plan1', :execution_plan_uuid => 'plan1', :frozen => false)
379
+
380
+ plans = adapter.find_ready_delayed_plans(start_time)
381
+ _(plans.length).must_equal 1
382
+ _(plans.first[:execution_plan_uuid]).must_equal 'plan1'
383
+ end
384
+
385
+ it 'properly stored execution plan dependencies' do
386
+ prepare_and_save_plans
387
+
388
+ adapter.save_delayed_plan('plan1', :execution_plan_uuid => 'plan1', :frozen => false)
389
+ adapter.chain_execution_plan('plan2', 'plan1')
390
+ adapter.chain_execution_plan('plan3', 'plan1')
391
+ dependencies = adapter.find_execution_plan_dependencies('plan1')
392
+ _(dependencies.to_set).must_equal ['plan2', 'plan3'].to_set
393
+ end
394
+
395
+ it 'does not find blocked plans' do
396
+ start_time = Time.now.utc
397
+ prepare_and_save_plans
398
+
399
+ adapter.save_delayed_plan('plan1', :execution_plan_uuid => 'plan1', :frozen => false)
400
+ adapter.chain_execution_plan('plan2', 'plan1')
401
+ adapter.chain_execution_plan('plan3', 'plan1')
402
+
403
+ plans = adapter.find_ready_delayed_plans(start_time)
404
+ _(plans.length).must_equal 0
405
+ end
406
+
407
+ it 'finds plans which are no longer blocked' do
408
+ start_time = Time.now.utc
409
+ prepare_and_save_plans
410
+
411
+ adapter.save_delayed_plan('plan1', :execution_plan_uuid => 'plan1', :frozen => false)
412
+ adapter.chain_execution_plan('plan2', 'plan1')
413
+
414
+ plans = adapter.find_ready_delayed_plans(start_time)
415
+ _(plans.length).must_equal 1
416
+ _(plans.first[:execution_plan_uuid]).must_equal 'plan1'
417
+ end
418
+
419
+ it 'does not find plans which are no longer blocked but are frozen' do
420
+ start_time = Time.now.utc
421
+ prepare_and_save_plans
422
+
423
+ adapter.save_delayed_plan('plan1', :execution_plan_uuid => 'plan1', :frozen => true)
424
+ adapter.chain_execution_plan('plan2', 'plan1')
425
+
426
+ plans = adapter.find_ready_delayed_plans(start_time)
427
+ _(plans.length).must_equal 0
428
+ end
429
+
430
+ it 'does not find plans which are no longer blocked but their start_at is in the future' do
431
+ start_time = Time.now.utc
432
+ prepare_and_save_plans
433
+
434
+ adapter.save_delayed_plan('plan1', :execution_plan_uuid => 'plan1', :frozen => false, :start_at => start_time + 60)
435
+ adapter.chain_execution_plan('plan2', 'plan1') # plan2 is already stopped
436
+
437
+ plans = adapter.find_ready_delayed_plans(start_time)
438
+ _(plans.length).must_equal 0
439
+ end
373
440
  end
374
441
 
375
442
  describe '#delete_output_chunks' do
@@ -31,6 +31,10 @@ module Support
31
31
 
32
32
  class FailingDummy < Dynflow::Action
33
33
  def run; raise 'error'; end
34
+
35
+ def rescue_strategy
36
+ Dynflow::Action::Rescue::Fail
37
+ end
34
38
  end
35
39
 
36
40
  class Slow < Dynflow::Action
data/test/test_helper.rb CHANGED
@@ -20,7 +20,9 @@ require 'support/rescue_example'
20
20
  require 'support/dummy_example'
21
21
  require 'support/test_execution_log'
22
22
 
23
- Concurrent.disable_at_exit_handlers!
23
+ Concurrent.global_logger = lambda do |level, progname, message = nil, &block|
24
+ ::Dynflow::Testing.logger_adapter.logger.add level, message, progname, &block
25
+ end
24
26
 
25
27
  # To be able to stop a process in some step and perform assertions while paused
26
28
  class TestPause
@@ -109,12 +111,19 @@ module WorldFactory
109
111
  # This world survives though the whole run of the test suite: careful with it, it can
110
112
  # introduce unnecessary test dependencies
111
113
  def self.logger_adapter
112
- @adapter ||= Dynflow::LoggerAdapters::Simple.new $stderr, ::Logger::FATAL
114
+ ::Dynflow::Testing.logger_adapter
113
115
  end
114
116
 
115
117
  def self.persistence_adapter
116
118
  @persistence_adapter ||= begin
117
- db_config = ENV['DB_CONN_STRING'] || 'sqlite:/'
119
+ db_config = ENV.fetch('DB_CONN_STRING') do
120
+ case ENV['DB']
121
+ when 'postgresql'
122
+ "postgres://postgres@localhost/#{ENV.fetch('POSTGRES_DB', 'ci_test')}"
123
+ else
124
+ 'sqlite:/'
125
+ end
126
+ end
118
127
  puts "Using database configuration: #{db_config}"
119
128
  Dynflow::PersistenceAdapters::Sequel.new(db_config)
120
129
  end
@@ -127,7 +136,7 @@ module WorldFactory
127
136
  def self.clean_coordinator_records
128
137
  persistence_adapter = WorldFactory.persistence_adapter
129
138
  persistence_adapter.find_coordinator_records({}).each do |w|
130
- warn "Unexpected coordinator record: #{w}"
139
+ ::Dynflow::Testing.logger_adapter.logger.warn "Unexpected coordinator record: #{w}"
131
140
  persistence_adapter.delete_coordinator_record(w[:class], w[:id])
132
141
  end
133
142
  end
@@ -234,12 +243,18 @@ module TestHelpers
234
243
  end
235
244
 
236
245
  class MiniTest::Test
246
+ def logger
247
+ ::Dynflow::Testing.logger_adapter.logger
248
+ end
249
+
237
250
  def setup
251
+ logger.info(">>>>> #{location}")
238
252
  WorldFactory.clean_coordinator_records
239
253
  end
240
254
 
241
255
  def teardown
242
256
  WorldFactory.terminate_worlds
257
+ logger.info("<<<<< #{location}")
243
258
  end
244
259
  end
245
260
 
data/web/views/show.erb CHANGED
@@ -43,6 +43,30 @@
43
43
  <%= h(@plan.ended_at) %>
44
44
  </p>
45
45
 
46
+ <% dependencies = @plan.world.persistence.find_execution_plan_dependencies(@plan.id) %>
47
+ <% if dependencies.any? %>
48
+ <p>
49
+ <b>Depends on execution plans:</b>
50
+ <ul>
51
+ <% dependencies.each do |dep_id| %>
52
+ <li><a href="<%= url("/#{dep_id}") %>"><%= h(dep_id) %></a></li>
53
+ <% end %>
54
+ </ul>
55
+ </p>
56
+ <% end %>
57
+
58
+ <% blocked_plans = @plan.world.persistence.find_blocked_execution_plans(@plan.id) %>
59
+ <% if blocked_plans.any? %>
60
+ <p>
61
+ <b>Blocks execution plans:</b>
62
+ <ul>
63
+ <% blocked_plans.each do |dep_id| %>
64
+ <li><a href="<%= url("/#{dep_id}") %>"><%= h(dep_id) %></a></li>
65
+ <% end %>
66
+ </ul>
67
+ </p>
68
+ <% end %>
69
+
46
70
  <ul class="phases nav nav-tabs" id="myTab">
47
71
  <li><a href="#plan">Plan</a></li>
48
72
  <li class="active"><a href="#run">Run</a></li>
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.9.3
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Necas
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.7.0
27
- - !ruby/object:Gem::Dependency
28
- name: apipie-params
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: concurrent-ruby
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -331,7 +317,7 @@ executables: []
331
317
  extensions: []
332
318
  extra_rdoc_files: []
333
319
  files:
334
- - ".github/install_dependencies.sh"
320
+ - ".github/workflows/bats.yml"
335
321
  - ".github/workflows/release.yml"
336
322
  - ".github/workflows/ruby.yml"
337
323
  - ".gitignore"
@@ -480,6 +466,7 @@ files:
480
466
  - examples/chunked_output_benchmark.rb
481
467
  - examples/clock_benchmark.rb
482
468
  - examples/example_helper.rb
469
+ - examples/execution_plan_chaining.rb
483
470
  - examples/future_execution.rb
484
471
  - examples/halt.rb
485
472
  - examples/memory_limit_watcher.rb
@@ -621,6 +608,7 @@ files:
621
608
  - lib/dynflow/persistence_adapters/sequel_migrations/022_store_flows_as_msgpack.rb
622
609
  - lib/dynflow/persistence_adapters/sequel_migrations/023_sqlite_workarounds.rb
623
610
  - lib/dynflow/persistence_adapters/sequel_migrations/024_store_execution_plan_data_as_msgpack.rb
611
+ - lib/dynflow/persistence_adapters/sequel_migrations/025_create_execution_plan_dependencies.rb
624
612
  - lib/dynflow/persistence_adapters/sequel_migrations/msgpack_migration_helper.rb
625
613
  - lib/dynflow/rails.rb
626
614
  - lib/dynflow/rails/configuration.rb
@@ -676,6 +664,11 @@ files:
676
664
  - test/action_test.rb
677
665
  - test/activejob_adapter_test.rb
678
666
  - test/batch_sub_tasks_test.rb
667
+ - test/bats/helpers/common.bash
668
+ - test/bats/helpers/containers.bash
669
+ - test/bats/setup_suite.bash
670
+ - test/bats/sidekiq-orchestrator.bats
671
+ - test/bats/teardown_suite.bash
679
672
  - test/clock_test.rb
680
673
  - test/concurrency_control_test.rb
681
674
  - test/coordinator_test.rb
@@ -744,7 +737,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
744
737
  requirements:
745
738
  - - ">="
746
739
  - !ruby/object:Gem::Version
747
- version: 2.7.0
740
+ version: 3.0.0
748
741
  required_rubygems_version: !ruby/object:Gem::Requirement
749
742
  requirements:
750
743
  - - ">="
@@ -759,6 +752,11 @@ test_files:
759
752
  - test/action_test.rb
760
753
  - test/activejob_adapter_test.rb
761
754
  - test/batch_sub_tasks_test.rb
755
+ - test/bats/helpers/common.bash
756
+ - test/bats/helpers/containers.bash
757
+ - test/bats/setup_suite.bash
758
+ - test/bats/sidekiq-orchestrator.bats
759
+ - test/bats/teardown_suite.bash
762
760
  - test/clock_test.rb
763
761
  - test/concurrency_control_test.rb
764
762
  - test/coordinator_test.rb
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- set -x
4
-
5
- echo "Setting the environment to use ${DB} database"
6
-
7
- BUNDLE_CONFIG=.bundle/config
8
- mkdir -p $(dirname $BUNDLE_CONFIG)
9
- cat <<EOF > $BUNDLE_CONFIG
10
- ---
11
- BUNDLE_WITHOUT: pry:mysql:postgresql:concurrent_ruby_ext
12
- EOF
13
-
14
- case $DB in
15
- mysql)
16
- sed -i 's/:mysql//'g $BUNDLE_CONFIG
17
- ;;
18
- postgresql)
19
- sed -i 's/:postgresql//'g $BUNDLE_CONFIG
20
- ;;
21
- sqlite3)
22
- # the tests are by default using sqlite3: do nothing
23
- ;;
24
- *)
25
- echo "Unsupported database ${DB}"
26
- exit 1
27
- ;;
28
- esac
29
-
30
- if [ "$CONCURRENT_RUBY_EXT" = "true" ]; then
31
- echo "Enabling concurrent-ruby-ext"
32
- sed -i 's/:concurrent_ruby_ext//'g $BUNDLE_CONFIG
33
- fi
34
- gem update bundler
35
- bundle install