dynflow 0.7.6 → 0.7.7

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 (151) hide show
  1. data/README.md +11 -3
  2. data/doc/pages/.gitignore +7 -0
  3. data/doc/pages/Gemfile +8 -0
  4. data/doc/pages/Rakefile +26 -0
  5. data/doc/pages/_config.yml +38 -0
  6. data/doc/pages/plugins/alert_block.rb +27 -0
  7. data/doc/pages/plugins/div_tag.rb +24 -0
  8. data/doc/pages/plugins/graphviz.rb +121 -0
  9. data/doc/pages/plugins/plantuml.rb +85 -0
  10. data/doc/pages/plugins/play.rb +13 -0
  11. data/doc/pages/plugins/tags.rb +138 -0
  12. data/doc/pages/plugins/toc.rb +20 -0
  13. data/doc/pages/source/.nojekyll +0 -0
  14. data/doc/pages/source/404.md +6 -0
  15. data/doc/pages/source/_includes/disqus.html +25 -0
  16. data/doc/pages/source/_includes/google_analytics.html +12 -0
  17. data/doc/pages/source/_includes/google_plus_one.html +2 -0
  18. data/doc/pages/source/_includes/menu.html +19 -0
  19. data/doc/pages/source/_includes/menu_brand.html +2 -0
  20. data/doc/pages/source/_includes/menu_right.html +1 -0
  21. data/doc/pages/source/_includes/post_item.html +10 -0
  22. data/doc/pages/source/_includes/scroll_to.html +24 -0
  23. data/doc/pages/source/_includes/twitter_sharing.html +9 -0
  24. data/doc/pages/source/_layouts/default.html +70 -0
  25. data/doc/pages/source/_layouts/page.html +47 -0
  26. data/doc/pages/source/_layouts/post.html +19 -0
  27. data/doc/pages/source/_layouts/presentation.html +39 -0
  28. data/doc/pages/source/_layouts/tag_page.html +12 -0
  29. data/doc/pages/source/_sass/_bootstrap-compass.scss +9 -0
  30. data/doc/pages/source/_sass/_bootstrap-mincer.scss +19 -0
  31. data/doc/pages/source/_sass/_bootstrap-sprockets.scss +9 -0
  32. data/doc/pages/source/_sass/_bootstrap-variables.sass +865 -0
  33. data/doc/pages/source/_sass/_bootstrap.scss +50 -0
  34. data/doc/pages/source/_sass/_specific.scss +16 -0
  35. data/doc/pages/source/_sass/_style.scss +172 -0
  36. data/doc/pages/source/_sass/bootstrap/_alerts.scss +73 -0
  37. data/doc/pages/source/_sass/bootstrap/_badges.scss +67 -0
  38. data/doc/pages/source/_sass/bootstrap/_breadcrumbs.scss +26 -0
  39. data/doc/pages/source/_sass/bootstrap/_button-groups.scss +243 -0
  40. data/doc/pages/source/_sass/bootstrap/_buttons.scss +160 -0
  41. data/doc/pages/source/_sass/bootstrap/_carousel.scss +269 -0
  42. data/doc/pages/source/_sass/bootstrap/_close.scss +36 -0
  43. data/doc/pages/source/_sass/bootstrap/_code.scss +69 -0
  44. data/doc/pages/source/_sass/bootstrap/_component-animations.scss +38 -0
  45. data/doc/pages/source/_sass/bootstrap/_dropdowns.scss +214 -0
  46. data/doc/pages/source/_sass/bootstrap/_forms.scss +570 -0
  47. data/doc/pages/source/_sass/bootstrap/_glyphicons.scss +301 -0
  48. data/doc/pages/source/_sass/bootstrap/_grid.scss +84 -0
  49. data/doc/pages/source/_sass/bootstrap/_input-groups.scss +166 -0
  50. data/doc/pages/source/_sass/bootstrap/_jumbotron.scss +50 -0
  51. data/doc/pages/source/_sass/bootstrap/_labels.scss +66 -0
  52. data/doc/pages/source/_sass/bootstrap/_list-group.scss +124 -0
  53. data/doc/pages/source/_sass/bootstrap/_media.scss +61 -0
  54. data/doc/pages/source/_sass/bootstrap/_mixins.scss +39 -0
  55. data/doc/pages/source/_sass/bootstrap/_modals.scss +148 -0
  56. data/doc/pages/source/_sass/bootstrap/_navbar.scss +663 -0
  57. data/doc/pages/source/_sass/bootstrap/_navs.scss +244 -0
  58. data/doc/pages/source/_sass/bootstrap/_normalize.scss +427 -0
  59. data/doc/pages/source/_sass/bootstrap/_pager.scss +54 -0
  60. data/doc/pages/source/_sass/bootstrap/_pagination.scss +88 -0
  61. data/doc/pages/source/_sass/bootstrap/_panels.scss +265 -0
  62. data/doc/pages/source/_sass/bootstrap/_popovers.scss +135 -0
  63. data/doc/pages/source/_sass/bootstrap/_print.scss +107 -0
  64. data/doc/pages/source/_sass/bootstrap/_progress-bars.scss +87 -0
  65. data/doc/pages/source/_sass/bootstrap/_responsive-embed.scss +35 -0
  66. data/doc/pages/source/_sass/bootstrap/_responsive-utilities.scss +177 -0
  67. data/doc/pages/source/_sass/bootstrap/_scaffolding.scss +150 -0
  68. data/doc/pages/source/_sass/bootstrap/_tables.scss +234 -0
  69. data/doc/pages/source/_sass/bootstrap/_theme.scss +273 -0
  70. data/doc/pages/source/_sass/bootstrap/_thumbnails.scss +38 -0
  71. data/doc/pages/source/_sass/bootstrap/_tooltip.scss +103 -0
  72. data/doc/pages/source/_sass/bootstrap/_type.scss +298 -0
  73. data/doc/pages/source/_sass/bootstrap/_utilities.scss +56 -0
  74. data/doc/pages/source/_sass/bootstrap/_variables.scss +862 -0
  75. data/doc/pages/source/_sass/bootstrap/_wells.scss +29 -0
  76. data/doc/pages/source/_sass/bootstrap/mixins/_alerts.scss +14 -0
  77. data/doc/pages/source/_sass/bootstrap/mixins/_background-variant.scss +11 -0
  78. data/doc/pages/source/_sass/bootstrap/mixins/_border-radius.scss +18 -0
  79. data/doc/pages/source/_sass/bootstrap/mixins/_buttons.scss +52 -0
  80. data/doc/pages/source/_sass/bootstrap/mixins/_center-block.scss +7 -0
  81. data/doc/pages/source/_sass/bootstrap/mixins/_clearfix.scss +22 -0
  82. data/doc/pages/source/_sass/bootstrap/mixins/_forms.scss +88 -0
  83. data/doc/pages/source/_sass/bootstrap/mixins/_gradients.scss +58 -0
  84. data/doc/pages/source/_sass/bootstrap/mixins/_grid-framework.scss +81 -0
  85. data/doc/pages/source/_sass/bootstrap/mixins/_grid.scss +122 -0
  86. data/doc/pages/source/_sass/bootstrap/mixins/_hide-text.scss +21 -0
  87. data/doc/pages/source/_sass/bootstrap/mixins/_image.scss +33 -0
  88. data/doc/pages/source/_sass/bootstrap/mixins/_labels.scss +12 -0
  89. data/doc/pages/source/_sass/bootstrap/mixins/_list-group.scss +31 -0
  90. data/doc/pages/source/_sass/bootstrap/mixins/_nav-divider.scss +10 -0
  91. data/doc/pages/source/_sass/bootstrap/mixins/_nav-vertical-align.scss +9 -0
  92. data/doc/pages/source/_sass/bootstrap/mixins/_opacity.scss +8 -0
  93. data/doc/pages/source/_sass/bootstrap/mixins/_pagination.scss +23 -0
  94. data/doc/pages/source/_sass/bootstrap/mixins/_panels.scss +24 -0
  95. data/doc/pages/source/_sass/bootstrap/mixins/_progress-bar.scss +10 -0
  96. data/doc/pages/source/_sass/bootstrap/mixins/_reset-filter.scss +8 -0
  97. data/doc/pages/source/_sass/bootstrap/mixins/_resize.scss +6 -0
  98. data/doc/pages/source/_sass/bootstrap/mixins/_responsive-visibility.scss +21 -0
  99. data/doc/pages/source/_sass/bootstrap/mixins/_size.scss +10 -0
  100. data/doc/pages/source/_sass/bootstrap/mixins/_tab-focus.scss +9 -0
  101. data/doc/pages/source/_sass/bootstrap/mixins/_table-row.scss +28 -0
  102. data/doc/pages/source/_sass/bootstrap/mixins/_text-emphasis.scss +11 -0
  103. data/doc/pages/source/_sass/bootstrap/mixins/_text-overflow.scss +8 -0
  104. data/doc/pages/source/_sass/bootstrap/mixins/_vendor-prefixes.scss +222 -0
  105. data/doc/pages/source/atom.xml +32 -0
  106. data/doc/pages/source/bootstrap/config.json +429 -0
  107. data/doc/pages/source/bootstrap/css/bootstrap-theme.css +479 -0
  108. data/doc/pages/source/bootstrap/css/bootstrap-theme.min.css +10 -0
  109. data/doc/pages/source/bootstrap/css/bootstrap.css +6564 -0
  110. data/doc/pages/source/bootstrap/css/bootstrap.min.css +10 -0
  111. data/doc/pages/source/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  112. data/doc/pages/source/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
  113. data/doc/pages/source/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  114. data/doc/pages/source/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  115. data/doc/pages/source/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
  116. data/doc/pages/source/bootstrap/js/bootstrap.js +2309 -0
  117. data/doc/pages/source/bootstrap/js/bootstrap.min.js +12 -0
  118. data/doc/pages/source/css/app.scss +10 -0
  119. data/doc/pages/source/css/syntax.css +60 -0
  120. data/doc/pages/source/documentation/index.md +977 -0
  121. data/doc/pages/source/faq/index.md +16 -0
  122. data/doc/pages/source/images/dynflow-logos.svg +423 -0
  123. data/doc/pages/source/images/logo-long.png +0 -0
  124. data/doc/pages/source/images/logo-long.svg +116 -0
  125. data/doc/pages/source/images/logo-square.png +0 -0
  126. data/doc/pages/source/images/logo-square.svg +75 -0
  127. data/doc/pages/source/images/noise.png +0 -0
  128. data/doc/pages/source/images/screenshot.png +0 -0
  129. data/doc/pages/source/index.md +64 -0
  130. data/doc/pages/source/media/index.md +20 -0
  131. data/doc/pages/source/projects/index.md +32 -0
  132. data/dynflow.gemspec +2 -3
  133. data/examples/sub_plans.rb +37 -0
  134. data/lib/dynflow/action.rb +28 -8
  135. data/lib/dynflow/action/polling.rb +6 -5
  136. data/lib/dynflow/action/with_sub_plans.rb +162 -0
  137. data/lib/dynflow/execution_plan.rb +25 -7
  138. data/lib/dynflow/execution_plan/steps/abstract.rb +5 -2
  139. data/lib/dynflow/execution_plan/steps/plan_step.rb +6 -2
  140. data/lib/dynflow/execution_plan/steps/run_step.rb +4 -0
  141. data/lib/dynflow/persistence.rb +5 -0
  142. data/lib/dynflow/persistence_adapters/sequel.rb +12 -2
  143. data/lib/dynflow/persistence_adapters/sequel_migrations/003_parent_action.rb +9 -0
  144. data/lib/dynflow/version.rb +1 -1
  145. data/lib/dynflow/web_console.rb +21 -7
  146. data/lib/dynflow/world.rb +26 -2
  147. data/test/action_test.rb +107 -0
  148. data/test/persistance_adapters_test.rb +2 -2
  149. data/test/test_helper.rb +1 -1
  150. data/web/views/flow_step.erb +3 -0
  151. metadata +137 -4
@@ -0,0 +1,75 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6
+ xmlns:cc="http://creativecommons.org/ns#"
7
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8
+ xmlns:svg="http://www.w3.org/2000/svg"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
+ width="156.38499"
13
+ height="156.38499"
14
+ viewBox="0 0 156.385 156.38499"
15
+ id="svg3634"
16
+ version="1.1"
17
+ inkscape:version="0.91 r13725"
18
+ sodipodi:docname="logo-square.svg"
19
+ inkscape:export-filename="/Users/pitr/Workspace/redhat/dynflow/pages/source/images/logo-square.png"
20
+ inkscape:export-xdpi="73.660004"
21
+ inkscape:export-ydpi="73.660004">
22
+ <defs
23
+ id="defs3636" />
24
+ <sodipodi:namedview
25
+ id="base"
26
+ pagecolor="#ffffff"
27
+ bordercolor="#666666"
28
+ borderopacity="1.0"
29
+ inkscape:pageopacity="0.0"
30
+ inkscape:pageshadow="2"
31
+ inkscape:zoom="0.98994949"
32
+ inkscape:cx="-118.07845"
33
+ inkscape:cy="174.77927"
34
+ inkscape:document-units="px"
35
+ inkscape:current-layer="layer1"
36
+ showgrid="false"
37
+ units="px"
38
+ inkscape:window-width="1920"
39
+ inkscape:window-height="977"
40
+ inkscape:window-x="0"
41
+ inkscape:window-y="0"
42
+ inkscape:window-maximized="1" />
43
+ <metadata
44
+ id="metadata3639">
45
+ <rdf:RDF>
46
+ <cc:Work
47
+ rdf:about="">
48
+ <dc:format>image/svg+xml</dc:format>
49
+ <dc:type
50
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
51
+ <dc:title />
52
+ </cc:Work>
53
+ </rdf:RDF>
54
+ </metadata>
55
+ <g
56
+ inkscape:label="Vrstva 1"
57
+ inkscape:groupmode="layer"
58
+ id="layer1"
59
+ transform="translate(0,-895.97718)">
60
+ <g
61
+ id="g3650"
62
+ transform="matrix(0.96003386,0,0,0.96003386,-198.84571,240.90468)">
63
+ <path
64
+ inkscape:connector-curvature="0"
65
+ id="path4180"
66
+ d="m 273.38806,813.90518 c 10.67131,0 21.34262,0 32.01393,0 2.80651,-14.44298 5.61302,-28.88596 8.41953,-43.32896 14.0093,0 28.0186,0 42.0279,0 1.32345,-6.63958 2.64689,-13.27917 3.97034,-19.91875 -14.03211,0 -28.06422,0 -42.09632,0 1.2318,-6.34315 2.46359,-12.6863 3.69539,-19.02944 14.89948,0 29.79898,0 44.69847,0 1.30064,-6.63959 2.60127,-13.27917 3.90192,-19.91876 -21.72018,0.008 -43.44037,0.0166 -65.16055,0.025 -13.50291,13.09022 -17.93615,33.58442 -13.92119,51.63274 4.1749,18.53456 -3.50427,38.1567 -17.54942,50.53818 z"
67
+ style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:199.62593079px;line-height:521.00000381%;font-family:Sans;-inkscape-font-specification:'Sans Bold Italic';letter-spacing:-15.00960922;word-spacing:0px;fill:#f78a29;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
68
+ <path
69
+ inkscape:connector-curvature="0"
70
+ id="path4182"
71
+ d="m 302.88929,711.73558 c -8.97951,0.003 -17.95902,0.007 -26.93853,0.0105 -2.53902,13.13095 -5.07803,26.26191 -7.61704,39.39286 -3.94471,-7.6785 -12.21396,-12.70145 -20.86255,-12.52779 -13.12339,-0.65457 -24.86575,7.77854 -31.69186,18.45936 -7.81528,12.04757 -10.96616,27.6448 -6.82946,41.55316 2.90262,9.5693 12.03429,16.63718 22.02555,17.062 9.83511,1.18439 19.6167,-3.36795 25.89418,-10.86051 2.40839,-3.54988 0.50699,1.60166 0.39438,3.15516 -0.40232,1.97496 -0.80464,3.9499 -1.20696,5.92485 4.97717,0 9.95433,0 14.9315,0 6.23473,-4.38616 10.69735,-10.95265 14.26689,-17.58865 5.72598,-10.85952 6.75245,-23.72144 3.82667,-35.55166 -3.03798,-17.2946 1.49379,-36.20142 13.80723,-49.02929 z m -50.96838,43.93282 c 5.88513,-0.46419 11.40435,4.14517 11.87951,10.04989 0.83507,10.60817 -1.53732,22.75724 -10.22572,29.76296 -5.55537,4.22384 -15.26841,4.98105 -19.7752,-1.15819 -3.69118,-6.08712 -2.40972,-13.72962 -1.09341,-20.34581 2.18961,-8.98104 8.97423,-18.5654 19.21482,-18.30885 z"
72
+ style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:199.62593079px;line-height:521.00000381%;font-family:Sans;-inkscape-font-specification:'Sans Bold Italic';letter-spacing:-15.00960922;word-spacing:0px;fill:#289fd5;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
73
+ </g>
74
+ </g>
75
+ </svg>
Binary file
@@ -0,0 +1,64 @@
1
+ ---
2
+ layout: page
3
+ title: About
4
+ skip-title: true
5
+ sharing: true
6
+ ---
7
+
8
+ <div id="big_logo" class="well"><img src="/images/logo-long.png"></div>
9
+
10
+ Dynflow (**DYN**amic work**FLOW**) is a workflow engine
11
+ written in Ruby that allows to:
12
+
13
+ - Keep track of the progress of running processes
14
+ - Run the code asynchronously
15
+ - When something goes wrong, pause the process, optionally let user interact,
16
+ resume the process, skip some steps when needed
17
+ - Detect independent parts and run them concurrently
18
+ - Compose simple actions into more complex scenarios
19
+ - Extend the workflows from third-party libraries
20
+ - Keep consistency between local transactional database and
21
+ external services
22
+ - Suspend the long-running steps, not blocking the thread pool
23
+ - Cancel steps when possible
24
+ - Extend the actions behavior with middlewares
25
+ - Pick different adapters to provide: storage backend, transactions, or executor implementation
26
+
27
+ Dynflow has been developed to be able to support orchestration of services in the
28
+ [Katello](http://katello.org) and [Foreman](http://theforeman.org/) projects.
29
+
30
+ ### Planned features
31
+
32
+ - Define the input/output interface between the building blocks
33
+ - Define rollback for the workflow
34
+ - Have multiple workers for distributing the load (in progress)
35
+ - Migration to [concurrent-ruby](http://concurrent-ruby.com) (in progress)
36
+
37
+ ## Getting started
38
+
39
+ ### Requirements
40
+
41
+ - Ruby MRI 1.9.3, 2.0, or 2.1.
42
+ - JRuby and Rubinius support is on the way.
43
+
44
+ ### Installation
45
+
46
+ `gem install dynflow`
47
+
48
+ *TODO*
49
+
50
+ ## Links
51
+
52
+ - [Github](https://github.com/dynflow/dynflow)
53
+
54
+ ## Current status
55
+
56
+ ![Build](https://img.shields.io/travis/Dynflow/dynflow/master.svg?style=flat)
57
+ ![Issues](https://img.shields.io/github/issues/Dynflow/dynflow.svg?style=flat)
58
+ ![Gem version](https://img.shields.io/gem/v/dynflow.svg?style=flat)
59
+ ![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)
60
+
61
+ ## Authors
62
+
63
+ - [Ivan Nečas](https://github.com/iNecas)
64
+ - [Petr Chalupa](http://blog.pitr.ch)
@@ -0,0 +1,20 @@
1
+ ---
2
+ layout: page
3
+ title: "Media"
4
+ comments: false
5
+ sharing: false
6
+ footer: true
7
+ ---
8
+
9
+ ## Presentations
10
+
11
+ *TODO add*
12
+
13
+ ## Deep-dives
14
+
15
+ - [Foreman deep dive: Rewriting orchestration to Dynflow](https://www.youtube.com/watch?v=fXKgEvoAQpg)
16
+ - [Foreman deep dive: Rewriting orchestration to Dynflow (part 2)](https://www.youtube.com/watch?v=hOouaJRk3U4)
17
+ - [MCOflow - MCollective over Dynflow](https://www.youtube.com/watch?v=UyO4R7VUFZ4)
18
+ - [Katello DynFlow Walkthrough](https://www.youtube.com/watch?v=CEIdXFA5CU0)
19
+ - [Foreman deep dive: Dynflow tools and concepts (part 1 of 2)](https://www.youtube.com/watch?v=5dprXA7gbSI)
20
+ - [Foreman deep dive: Dynflow executors (part 2 of 2)](https://www.youtube.com/watch?v=drBY8Z1CcKs)
@@ -0,0 +1,32 @@
1
+ ---
2
+ layout: page
3
+ title: "Related projects"
4
+ comments: false
5
+ sharing: false
6
+ footer: true
7
+ ---
8
+
9
+ ## Used by
10
+
11
+ - [Foreman](http://theforeman.org) - lifecycle management tool for
12
+ physical and virtual servers.
13
+ - [Katello](http://katello.org) - content management plugin for
14
+ Foreman: integrates couple of REST services for managing the
15
+ software updates in the infrastructure.
16
+ - [Foreman-tasks](https://github.com/iNecas/foreman-tasks) - Foreman
17
+ plugin providing the tasks management with Dynflow on the back-end.
18
+ - [Dyntask](https://github.com/iNecas/dyntask) - generic Rails engine
19
+ providing the tasks management features with Dynflow on the back-end.
20
+ - [Sysflow](https://github.com/iNecas/sysflow) - set of reusable tools
21
+ for running system tasks with Dynflow, comes with simple Web-UI for
22
+ testing it.
23
+
24
+ ## Using
25
+
26
+ - [Algebrick](https://github.com/pitr-ch/algebrick) - Typed structs on steroids based on
27
+ algebraic types and pattern matching.
28
+ - [concurrent-ruby](http://www.concurrent-ruby.com/) - Modern concurrency tools including agents,
29
+ futures, promises, thread pools, supervisors, and more. Inspired by Erlang, Clojure, Scala,
30
+ Go, Java, JavaScript, and classic concurrency patterns.
31
+
32
+
data/dynflow.gemspec CHANGED
@@ -5,12 +5,11 @@ require "dynflow/version"
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "dynflow"
7
7
  s.version = Dynflow::VERSION
8
- s.authors = ["Ivan Necas"]
8
+ s.authors = ["Ivan Necas", "Petr Chalupa"]
9
9
  s.email = ["inecas@redhat.com"]
10
10
  s.homepage = "http://github.com/Dynflow/dynflow"
11
11
  s.summary = "DYNamic workFLOW engine"
12
- s.description = "Generate and executed workflows dynamically based "+
13
- "on input data and leave it open for others to jump into it as well"
12
+ s.description = "Ruby workflow/orchestration engine"
14
13
  s.license = "MIT"
15
14
 
16
15
  s.files = `git ls-files`.split("\n")
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ example_description = <<DESC
4
+ Sub Plans Example
5
+ ===================
6
+
7
+ This example shows, how to trigger the execution plans from within a
8
+ run method of some action and waing for them to finish.
9
+
10
+ This is useful, when doing bulk actions, where having one big
11
+ execution plan would not be effective, or in case all the data are
12
+ not available by the time of original action planning.
13
+
14
+ DESC
15
+
16
+ require_relative 'example_helper'
17
+ require_relative 'orchestrate_evented'
18
+
19
+
20
+ class SubPlansExample < Dynflow::Action
21
+ include Dynflow::Action::WithSubPlans
22
+
23
+ def create_sub_plans
24
+ 10.times.map { |i| trigger(OrchestrateEvented::CreateMachine, "host-#{i}", 'web_server') }
25
+ end
26
+ end
27
+
28
+ if $0 == __FILE__
29
+ triggered = ExampleHelper.world.trigger(SubPlansExample)
30
+ puts example_description
31
+ puts <<-MSG.gsub(/^.*\|/, '')
32
+ | Execution plan #{triggered.id} with sub plans triggered
33
+ | You can see the details at http://localhost:4567/#{triggered.id}
34
+ MSG
35
+
36
+ ExampleHelper.run_web_console
37
+ end
@@ -22,6 +22,7 @@ module Dynflow
22
22
 
23
23
  require 'dynflow/action/polling'
24
24
  require 'dynflow/action/cancellable'
25
+ require 'dynflow/action/with_sub_plans'
25
26
 
26
27
  def self.all_children
27
28
  children.values.inject(children.values) do |children, child|
@@ -83,7 +84,8 @@ module Dynflow
83
84
  end
84
85
 
85
86
  attr_reader :world, :phase, :execution_plan_id, :id, :input,
86
- :plan_step_id, :run_step_id, :finalize_step_id
87
+ :plan_step_id, :run_step_id, :finalize_step_id,
88
+ :caller_execution_plan_id, :caller_action_id
87
89
 
88
90
  middleware.use Action::Progress::Calculate
89
91
 
@@ -102,6 +104,9 @@ module Dynflow
102
104
 
103
105
  @execution_plan = Type!(attributes.fetch(:execution_plan), ExecutionPlan) if phase? Present
104
106
 
107
+ @caller_execution_plan_id = Type!(attributes.fetch(:caller_execution_plan_id, nil), String, NilClass)
108
+ @caller_action_id = Type!(attributes.fetch(:caller_action_id, nil), Integer, NilClass)
109
+
105
110
  getter =-> key, required do
106
111
  required ? attributes.fetch(key) : attributes.fetch(key, {})
107
112
  end
@@ -140,6 +145,19 @@ module Dynflow
140
145
  end
141
146
  end
142
147
 
148
+ def caller_action
149
+ plase! Present
150
+ return nil if @caller_action_id
151
+ return @caller_action if @caller_action
152
+
153
+ caller_execution_plan = if @caller_execution_plan_id == execution_plan.id
154
+ execution_plan
155
+ else
156
+ world.persistence.load_execution_plan(@caller_execution_plan_id)
157
+ end
158
+ @caller_action = world.persistence.load_action_for_presentation(caller_execution_plan, @caller_action_id)
159
+ end
160
+
143
161
  def set_plan_context(execution_plan, trigger, from_subscription)
144
162
  phase! Plan
145
163
  @execution_plan = Type! execution_plan, ExecutionPlan
@@ -209,13 +227,15 @@ module Dynflow
209
227
 
210
228
  def to_hash
211
229
  recursive_to_hash(
212
- { class: self.class.name,
213
- execution_plan_id: execution_plan_id,
214
- id: id,
215
- plan_step_id: plan_step_id,
216
- run_step_id: run_step_id,
217
- finalize_step_id: finalize_step_id,
218
- input: input },
230
+ { class: self.class.name,
231
+ execution_plan_id: execution_plan_id,
232
+ id: id,
233
+ plan_step_id: plan_step_id,
234
+ run_step_id: run_step_id,
235
+ finalize_step_id: finalize_step_id,
236
+ caller_execution_plan_id: caller_execution_plan_id,
237
+ caller_action_id: caller_action_id,
238
+ input: input },
219
239
  if phase? Run, Finalize, Present
220
240
  { output: output }
221
241
  end)
@@ -16,6 +16,7 @@ module Dynflow
16
16
  else
17
17
  raise "unrecognized event #{event}"
18
18
  end
19
+ done? ? on_finish : suspend_and_ping
19
20
  end
20
21
 
21
22
  def done?
@@ -30,6 +31,9 @@ module Dynflow
30
31
  raise NotImplementedError
31
32
  end
32
33
 
34
+ def on_finish
35
+ end
36
+
33
37
  # External task data. It should return nil when the task has not
34
38
  # been triggered yet.
35
39
  def external_task
@@ -68,7 +72,6 @@ module Dynflow
68
72
 
69
73
  def initiate_external_action
70
74
  self.external_task = invoke_external_task
71
- suspend_and_ping unless done?
72
75
  end
73
76
 
74
77
  def resume_external_action
@@ -85,21 +88,19 @@ module Dynflow
85
88
  poll_attempts[:total] += 1
86
89
  self.external_task = poll_external_task
87
90
  poll_attempts[:failed] = 0
88
- suspend_and_ping unless done?
89
91
  rescue => error
90
92
  poll_attempts[:failed] += 1
91
- rescue_poll_external_task(error)
93
+ rescue_external_task(error)
92
94
  end
93
95
 
94
96
  def poll_attempts
95
97
  output[:poll_attempts] ||= { total: 0, failed: 0 }
96
98
  end
97
99
 
98
- def rescue_poll_external_task(error)
100
+ def rescue_external_task(error)
99
101
  if poll_attempts[:failed] < poll_max_retries
100
102
  action_logger.warn("Polling failed, attempt no. #{poll_attempts[:failed]}, retrying in #{poll_interval}")
101
103
  action_logger.warn(error)
102
- suspend_and_ping
103
104
  else
104
105
  raise error
105
106
  end
@@ -0,0 +1,162 @@
1
+ module Dynflow
2
+ module Action::WithSubPlans
3
+ SubPlanFinished = Algebrick.type do
4
+ fields! :execution_plan_id => String,
5
+ :success => type { variants TrueClass, FalseClass }
6
+ end
7
+
8
+ def run(event = nil)
9
+ match event,
10
+ (on nil do
11
+ if output[:total_count]
12
+ resume
13
+ else
14
+ initiate
15
+ end
16
+ end),
17
+ (on SubPlanFinished do
18
+ mark_as_done(event.execution_plan_id, event.success)
19
+ try_to_finish or suspend
20
+ end)
21
+ end
22
+
23
+ def initiate
24
+ sub_plans = create_sub_plans
25
+ sub_plans = Array[sub_plans] unless sub_plans.is_a? Array
26
+ wait_for_sub_plans(sub_plans)
27
+ end
28
+
29
+ # @abstract when the logic for the initiation of the subtasks
30
+ # is different from the default one.
31
+ # @returns a triggered task or array of triggered tasks
32
+ # @example
33
+ #
34
+ # def create_sub_plans
35
+ # trigger(MyAction, "Hello")
36
+ # end
37
+ #
38
+ # @example
39
+ #
40
+ # def create_sub_plans
41
+ # [trigger(MyAction, "Hello 1"), trigger(MyAction, "Hello 2")]
42
+ # end
43
+ #
44
+ def create_sub_plans
45
+ raise NotImplementedError
46
+ end
47
+
48
+ # @api method to be called after all the sub tasks finished
49
+ def on_finish
50
+ end
51
+
52
+ # Helper for creating sub plans
53
+ def trigger(*args)
54
+ world.trigger do
55
+ world.plan_with_caller(self, *args)
56
+ end
57
+ end
58
+
59
+ def wait_for_sub_plans(sub_plans)
60
+ output.update(total_count: 0,
61
+ failed_count: 0,
62
+ success_count: 0)
63
+
64
+ planned, failed = sub_plans.partition(&:planned?)
65
+
66
+ sub_plan_ids = (planned + failed).map(&:execution_plan_id)
67
+
68
+ output[:total_count] = sub_plan_ids.size
69
+ output[:failed_count] = failed.size
70
+
71
+ if planned.any?
72
+ notify_on_finish(planned)
73
+ else
74
+ check_for_errors!
75
+ end
76
+ end
77
+
78
+ def try_to_finish
79
+ if done?
80
+ check_for_errors!
81
+ on_finish
82
+ return true
83
+ else
84
+ return false
85
+ end
86
+ end
87
+
88
+ def resume
89
+ if sub_plans.all? { |sub_plan| sub_plan.error_in_plan? }
90
+ initiate
91
+ else
92
+ recalculate_counts
93
+ try_to_finish or fail "Some sub plans are still not finished"
94
+ end
95
+ end
96
+
97
+ def sub_plans
98
+ @sub_plans ||= world.persistence.find_execution_plans(filters: { 'caller_execution_plan_id' => execution_plan_id,
99
+ 'caller_action_id' => self.id } )
100
+ end
101
+
102
+ def notify_on_finish(plans)
103
+ suspend do |suspended_action|
104
+ plans.each do |plan|
105
+ plan.finished.do_then do |value|
106
+ suspended_action << SubPlanFinished[plan.execution_plan_id,
107
+ value.result == :success]
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ def mark_as_done(plan_id, success)
114
+ if success
115
+ output[:success_count] += 1
116
+ else
117
+ output[:failed_count] += 1
118
+ end
119
+ end
120
+
121
+ def done?
122
+ if counts_set?
123
+ output[:total_count] - output[:success_count] - output[:failed_count] <= 0
124
+ else
125
+ false
126
+ end
127
+ end
128
+
129
+ def run_progress
130
+ if counts_set? && output[:total_count] > 0
131
+ (output[:success_count] + output[:failed_count]).to_f / output[:total_count]
132
+ else
133
+ 0.1
134
+ end
135
+ end
136
+
137
+ def recalculate_counts
138
+ output.update(total_count: 0,
139
+ failed_count: 0,
140
+ success_count: 0)
141
+ sub_plans.each do |sub_plan|
142
+ output[:total_count] += 1
143
+ if sub_plan.state == :stopped
144
+ if sub_plan.error?
145
+ output[:failed_count] += 1
146
+ else
147
+ output[:success_count] += 1
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ def counts_set?
154
+ output[:total_count] && output[:success_count] && output[:failed_count]
155
+ end
156
+
157
+ def check_for_errors!
158
+ fail "A sub task failed" if output[:failed_count] > 0
159
+ end
160
+
161
+ end
162
+ end