dynflow 0.7.6 → 0.7.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +11 -3
- data/doc/pages/.gitignore +7 -0
- data/doc/pages/Gemfile +8 -0
- data/doc/pages/Rakefile +26 -0
- data/doc/pages/_config.yml +38 -0
- data/doc/pages/plugins/alert_block.rb +27 -0
- data/doc/pages/plugins/div_tag.rb +24 -0
- data/doc/pages/plugins/graphviz.rb +121 -0
- data/doc/pages/plugins/plantuml.rb +85 -0
- data/doc/pages/plugins/play.rb +13 -0
- data/doc/pages/plugins/tags.rb +138 -0
- data/doc/pages/plugins/toc.rb +20 -0
- data/doc/pages/source/.nojekyll +0 -0
- data/doc/pages/source/404.md +6 -0
- data/doc/pages/source/_includes/disqus.html +25 -0
- data/doc/pages/source/_includes/google_analytics.html +12 -0
- data/doc/pages/source/_includes/google_plus_one.html +2 -0
- data/doc/pages/source/_includes/menu.html +19 -0
- data/doc/pages/source/_includes/menu_brand.html +2 -0
- data/doc/pages/source/_includes/menu_right.html +1 -0
- data/doc/pages/source/_includes/post_item.html +10 -0
- data/doc/pages/source/_includes/scroll_to.html +24 -0
- data/doc/pages/source/_includes/twitter_sharing.html +9 -0
- data/doc/pages/source/_layouts/default.html +70 -0
- data/doc/pages/source/_layouts/page.html +47 -0
- data/doc/pages/source/_layouts/post.html +19 -0
- data/doc/pages/source/_layouts/presentation.html +39 -0
- data/doc/pages/source/_layouts/tag_page.html +12 -0
- data/doc/pages/source/_sass/_bootstrap-compass.scss +9 -0
- data/doc/pages/source/_sass/_bootstrap-mincer.scss +19 -0
- data/doc/pages/source/_sass/_bootstrap-sprockets.scss +9 -0
- data/doc/pages/source/_sass/_bootstrap-variables.sass +865 -0
- data/doc/pages/source/_sass/_bootstrap.scss +50 -0
- data/doc/pages/source/_sass/_specific.scss +16 -0
- data/doc/pages/source/_sass/_style.scss +172 -0
- data/doc/pages/source/_sass/bootstrap/_alerts.scss +73 -0
- data/doc/pages/source/_sass/bootstrap/_badges.scss +67 -0
- data/doc/pages/source/_sass/bootstrap/_breadcrumbs.scss +26 -0
- data/doc/pages/source/_sass/bootstrap/_button-groups.scss +243 -0
- data/doc/pages/source/_sass/bootstrap/_buttons.scss +160 -0
- data/doc/pages/source/_sass/bootstrap/_carousel.scss +269 -0
- data/doc/pages/source/_sass/bootstrap/_close.scss +36 -0
- data/doc/pages/source/_sass/bootstrap/_code.scss +69 -0
- data/doc/pages/source/_sass/bootstrap/_component-animations.scss +38 -0
- data/doc/pages/source/_sass/bootstrap/_dropdowns.scss +214 -0
- data/doc/pages/source/_sass/bootstrap/_forms.scss +570 -0
- data/doc/pages/source/_sass/bootstrap/_glyphicons.scss +301 -0
- data/doc/pages/source/_sass/bootstrap/_grid.scss +84 -0
- data/doc/pages/source/_sass/bootstrap/_input-groups.scss +166 -0
- data/doc/pages/source/_sass/bootstrap/_jumbotron.scss +50 -0
- data/doc/pages/source/_sass/bootstrap/_labels.scss +66 -0
- data/doc/pages/source/_sass/bootstrap/_list-group.scss +124 -0
- data/doc/pages/source/_sass/bootstrap/_media.scss +61 -0
- data/doc/pages/source/_sass/bootstrap/_mixins.scss +39 -0
- data/doc/pages/source/_sass/bootstrap/_modals.scss +148 -0
- data/doc/pages/source/_sass/bootstrap/_navbar.scss +663 -0
- data/doc/pages/source/_sass/bootstrap/_navs.scss +244 -0
- data/doc/pages/source/_sass/bootstrap/_normalize.scss +427 -0
- data/doc/pages/source/_sass/bootstrap/_pager.scss +54 -0
- data/doc/pages/source/_sass/bootstrap/_pagination.scss +88 -0
- data/doc/pages/source/_sass/bootstrap/_panels.scss +265 -0
- data/doc/pages/source/_sass/bootstrap/_popovers.scss +135 -0
- data/doc/pages/source/_sass/bootstrap/_print.scss +107 -0
- data/doc/pages/source/_sass/bootstrap/_progress-bars.scss +87 -0
- data/doc/pages/source/_sass/bootstrap/_responsive-embed.scss +35 -0
- data/doc/pages/source/_sass/bootstrap/_responsive-utilities.scss +177 -0
- data/doc/pages/source/_sass/bootstrap/_scaffolding.scss +150 -0
- data/doc/pages/source/_sass/bootstrap/_tables.scss +234 -0
- data/doc/pages/source/_sass/bootstrap/_theme.scss +273 -0
- data/doc/pages/source/_sass/bootstrap/_thumbnails.scss +38 -0
- data/doc/pages/source/_sass/bootstrap/_tooltip.scss +103 -0
- data/doc/pages/source/_sass/bootstrap/_type.scss +298 -0
- data/doc/pages/source/_sass/bootstrap/_utilities.scss +56 -0
- data/doc/pages/source/_sass/bootstrap/_variables.scss +862 -0
- data/doc/pages/source/_sass/bootstrap/_wells.scss +29 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_alerts.scss +14 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_background-variant.scss +11 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_border-radius.scss +18 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_buttons.scss +52 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_center-block.scss +7 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_clearfix.scss +22 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_forms.scss +88 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_gradients.scss +58 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_grid-framework.scss +81 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_grid.scss +122 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_hide-text.scss +21 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_image.scss +33 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_labels.scss +12 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_list-group.scss +31 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_nav-divider.scss +10 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_nav-vertical-align.scss +9 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_opacity.scss +8 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_pagination.scss +23 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_panels.scss +24 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_progress-bar.scss +10 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_reset-filter.scss +8 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_resize.scss +6 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_responsive-visibility.scss +21 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_size.scss +10 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_tab-focus.scss +9 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_table-row.scss +28 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_text-emphasis.scss +11 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_text-overflow.scss +8 -0
- data/doc/pages/source/_sass/bootstrap/mixins/_vendor-prefixes.scss +222 -0
- data/doc/pages/source/atom.xml +32 -0
- data/doc/pages/source/bootstrap/config.json +429 -0
- data/doc/pages/source/bootstrap/css/bootstrap-theme.css +479 -0
- data/doc/pages/source/bootstrap/css/bootstrap-theme.min.css +10 -0
- data/doc/pages/source/bootstrap/css/bootstrap.css +6564 -0
- data/doc/pages/source/bootstrap/css/bootstrap.min.css +10 -0
- data/doc/pages/source/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
- data/doc/pages/source/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
- data/doc/pages/source/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/doc/pages/source/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
- data/doc/pages/source/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/doc/pages/source/bootstrap/js/bootstrap.js +2309 -0
- data/doc/pages/source/bootstrap/js/bootstrap.min.js +12 -0
- data/doc/pages/source/css/app.scss +10 -0
- data/doc/pages/source/css/syntax.css +60 -0
- data/doc/pages/source/documentation/index.md +977 -0
- data/doc/pages/source/faq/index.md +16 -0
- data/doc/pages/source/images/dynflow-logos.svg +423 -0
- data/doc/pages/source/images/logo-long.png +0 -0
- data/doc/pages/source/images/logo-long.svg +116 -0
- data/doc/pages/source/images/logo-square.png +0 -0
- data/doc/pages/source/images/logo-square.svg +75 -0
- data/doc/pages/source/images/noise.png +0 -0
- data/doc/pages/source/images/screenshot.png +0 -0
- data/doc/pages/source/index.md +64 -0
- data/doc/pages/source/media/index.md +20 -0
- data/doc/pages/source/projects/index.md +32 -0
- data/dynflow.gemspec +2 -3
- data/examples/sub_plans.rb +37 -0
- data/lib/dynflow/action.rb +28 -8
- data/lib/dynflow/action/polling.rb +6 -5
- data/lib/dynflow/action/with_sub_plans.rb +162 -0
- data/lib/dynflow/execution_plan.rb +25 -7
- data/lib/dynflow/execution_plan/steps/abstract.rb +5 -2
- data/lib/dynflow/execution_plan/steps/plan_step.rb +6 -2
- data/lib/dynflow/execution_plan/steps/run_step.rb +4 -0
- data/lib/dynflow/persistence.rb +5 -0
- data/lib/dynflow/persistence_adapters/sequel.rb +12 -2
- data/lib/dynflow/persistence_adapters/sequel_migrations/003_parent_action.rb +9 -0
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/web_console.rb +21 -7
- data/lib/dynflow/world.rb +26 -2
- data/test/action_test.rb +107 -0
- data/test/persistance_adapters_test.rb +2 -2
- data/test/test_helper.rb +1 -1
- data/web/views/flow_step.erb +3 -0
- metadata +137 -4
Binary file
|
@@ -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
|
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 = "
|
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
|
data/lib/dynflow/action.rb
CHANGED
@@ -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:
|
213
|
-
execution_plan_id:
|
214
|
-
id:
|
215
|
-
plan_step_id:
|
216
|
-
run_step_id:
|
217
|
-
finalize_step_id:
|
218
|
-
|
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
|
-
|
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
|
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
|