dynflow 0.7.6 → 0.7.7

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