dynflow 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.travis.yml +9 -0
- data/Gemfile +0 -10
- data/MIT-LICENSE +1 -1
- data/README.md +99 -37
- data/Rakefile +2 -6
- data/doc/images/logo.png +0 -0
- data/dynflow.gemspec +10 -1
- data/examples/generate_work_for_daemon.rb +24 -0
- data/examples/orchestrate.rb +121 -0
- data/examples/run_daemon.rb +17 -0
- data/examples/web_console.rb +29 -0
- data/lib/dynflow.rb +27 -6
- data/lib/dynflow/action.rb +185 -77
- data/lib/dynflow/action/cancellable_polling.rb +18 -0
- data/lib/dynflow/action/finalize_phase.rb +18 -0
- data/lib/dynflow/action/flow_phase.rb +44 -0
- data/lib/dynflow/action/format.rb +46 -0
- data/lib/dynflow/action/missing.rb +26 -0
- data/lib/dynflow/action/plan_phase.rb +85 -0
- data/lib/dynflow/action/polling.rb +49 -0
- data/lib/dynflow/action/presenter.rb +51 -0
- data/lib/dynflow/action/progress.rb +62 -0
- data/lib/dynflow/action/run_phase.rb +43 -0
- data/lib/dynflow/action/suspended.rb +21 -0
- data/lib/dynflow/clock.rb +133 -0
- data/lib/dynflow/daemon.rb +29 -0
- data/lib/dynflow/execution_plan.rb +285 -33
- data/lib/dynflow/execution_plan/dependency_graph.rb +29 -0
- data/lib/dynflow/execution_plan/output_reference.rb +52 -0
- data/lib/dynflow/execution_plan/steps.rb +12 -0
- data/lib/dynflow/execution_plan/steps/abstract.rb +121 -0
- data/lib/dynflow/execution_plan/steps/abstract_flow_step.rb +52 -0
- data/lib/dynflow/execution_plan/steps/error.rb +33 -0
- data/lib/dynflow/execution_plan/steps/finalize_step.rb +23 -0
- data/lib/dynflow/execution_plan/steps/plan_step.rb +81 -0
- data/lib/dynflow/execution_plan/steps/run_step.rb +21 -0
- data/lib/dynflow/executors.rb +9 -0
- data/lib/dynflow/executors/abstract.rb +32 -0
- data/lib/dynflow/executors/parallel.rb +88 -0
- data/lib/dynflow/executors/parallel/core.rb +119 -0
- data/lib/dynflow/executors/parallel/execution_plan_manager.rb +120 -0
- data/lib/dynflow/executors/parallel/flow_manager.rb +48 -0
- data/lib/dynflow/executors/parallel/pool.rb +102 -0
- data/lib/dynflow/executors/parallel/running_steps_manager.rb +63 -0
- data/lib/dynflow/executors/parallel/sequence_cursor.rb +97 -0
- data/lib/dynflow/executors/parallel/sequential_manager.rb +81 -0
- data/lib/dynflow/executors/parallel/work_queue.rb +44 -0
- data/lib/dynflow/executors/parallel/worker.rb +30 -0
- data/lib/dynflow/executors/remote_via_socket.rb +38 -0
- data/lib/dynflow/executors/remote_via_socket/core.rb +150 -0
- data/lib/dynflow/flows.rb +13 -0
- data/lib/dynflow/flows/abstract.rb +36 -0
- data/lib/dynflow/flows/abstract_composed.rb +104 -0
- data/lib/dynflow/flows/atom.rb +36 -0
- data/lib/dynflow/flows/concurrence.rb +28 -0
- data/lib/dynflow/flows/sequence.rb +13 -0
- data/lib/dynflow/future.rb +173 -0
- data/lib/dynflow/listeners.rb +7 -0
- data/lib/dynflow/listeners/abstract.rb +13 -0
- data/lib/dynflow/listeners/serialization.rb +41 -0
- data/lib/dynflow/listeners/socket.rb +88 -0
- data/lib/dynflow/logger_adapters.rb +8 -0
- data/lib/dynflow/logger_adapters/abstract.rb +30 -0
- data/lib/dynflow/logger_adapters/delegator.rb +13 -0
- data/lib/dynflow/logger_adapters/formatters.rb +8 -0
- data/lib/dynflow/logger_adapters/formatters/abstract.rb +33 -0
- data/lib/dynflow/logger_adapters/formatters/exception.rb +15 -0
- data/lib/dynflow/logger_adapters/simple.rb +59 -0
- data/lib/dynflow/micro_actor.rb +102 -0
- data/lib/dynflow/persistence.rb +53 -0
- data/lib/dynflow/persistence_adapters.rb +6 -0
- data/lib/dynflow/persistence_adapters/abstract.rb +56 -0
- data/lib/dynflow/persistence_adapters/sequel.rb +160 -0
- data/lib/dynflow/persistence_adapters/sequel_migrations/001_initial.rb +52 -0
- data/lib/dynflow/serializable.rb +66 -0
- data/lib/dynflow/simple_world.rb +18 -0
- data/lib/dynflow/stateful.rb +40 -0
- data/lib/dynflow/testing.rb +32 -0
- data/lib/dynflow/testing/assertions.rb +64 -0
- data/lib/dynflow/testing/dummy_execution_plan.rb +40 -0
- data/lib/dynflow/testing/dummy_executor.rb +29 -0
- data/lib/dynflow/testing/dummy_planned_action.rb +18 -0
- data/lib/dynflow/testing/dummy_step.rb +19 -0
- data/lib/dynflow/testing/dummy_world.rb +33 -0
- data/lib/dynflow/testing/factories.rb +83 -0
- data/lib/dynflow/testing/managed_clock.rb +23 -0
- data/lib/dynflow/testing/mimic.rb +38 -0
- data/lib/dynflow/transaction_adapters.rb +9 -0
- data/lib/dynflow/transaction_adapters/abstract.rb +26 -0
- data/lib/dynflow/transaction_adapters/active_record.rb +27 -0
- data/lib/dynflow/transaction_adapters/none.rb +12 -0
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/web_console.rb +277 -0
- data/lib/dynflow/world.rb +168 -0
- data/test/action_test.rb +89 -11
- data/test/clock_test.rb +59 -0
- data/test/code_workflow_example.rb +382 -0
- data/test/execution_plan_test.rb +195 -64
- data/test/executor_test.rb +692 -0
- data/test/persistance_adapters_test.rb +173 -0
- data/test/test_helper.rb +316 -1
- data/test/testing_test.rb +148 -0
- data/test/web_console_test.rb +38 -0
- data/web/assets/javascripts/application.js +25 -0
- data/web/assets/stylesheets/application.css +101 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.css +1109 -0
- data/web/assets/vendor/bootstrap/css/bootstrap-responsive.min.css +9 -0
- data/web/assets/vendor/bootstrap/css/bootstrap.css +6167 -0
- data/web/assets/vendor/bootstrap/css/bootstrap.min.css +9 -0
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/web/assets/vendor/bootstrap/img/glyphicons-halflings.png +0 -0
- data/web/assets/vendor/bootstrap/js/bootstrap.js +2280 -0
- data/web/assets/vendor/bootstrap/js/bootstrap.min.js +6 -0
- data/web/assets/vendor/google-code-prettify/lang-basic.js +3 -0
- data/web/assets/vendor/google-code-prettify/prettify.css +1 -0
- data/web/assets/vendor/google-code-prettify/prettify.js +30 -0
- data/web/assets/vendor/google-code-prettify/run_prettify.js +34 -0
- data/web/assets/vendor/jquery/jquery.js +9807 -0
- data/web/views/flow.erb +19 -0
- data/web/views/flow_step.erb +31 -0
- data/web/views/index.erb +39 -0
- data/web/views/layout.erb +20 -0
- data/web/views/plan_step.erb +11 -0
- data/web/views/show.erb +54 -0
- metadata +250 -11
- data/examples/events.rb +0 -71
- data/examples/workflow.rb +0 -140
- data/lib/dynflow/bus.rb +0 -168
- data/lib/dynflow/dispatcher.rb +0 -36
- data/lib/dynflow/logger.rb +0 -34
- data/lib/dynflow/step.rb +0 -234
- data/test/bus_test.rb +0 -150
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,25 +1,60 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
![Dynflow](doc/images/logo.png)
|
2
|
+
=======
|
3
|
+
|
4
|
+
Dynflow [DYN(amic work)FLOW] is a workflow engine
|
5
|
+
written in Ruby that allows to:
|
6
|
+
|
7
|
+
* keep track of the progress of running process
|
8
|
+
* run the code asynchronously
|
9
|
+
* resume the process when something goes wrong, skip some steps when needed
|
10
|
+
* detect independent parts and run them concurrently
|
11
|
+
* compose simple actions into more complex scenarios
|
12
|
+
* extend the workflows from third-party libraries
|
13
|
+
* keep consistency between local transactional database and
|
14
|
+
external services
|
15
|
+
* define the input/output interface between the building blocks (planned)
|
16
|
+
* define rollback for the workflow (planned)
|
17
|
+
* have multiple workers for distributing the load (planned)
|
18
|
+
|
19
|
+
Dynflow doesn't try to choose the best tool for the jobs, as the right
|
20
|
+
tool depends on the context. Instead, it provides interfaces for
|
21
|
+
persistence, transaction layer or executor implementation, giving you
|
22
|
+
the last word in choosing the right one (providing default
|
23
|
+
implementations as well).
|
24
|
+
|
25
|
+
* [Current status](#current-status)
|
26
|
+
* [How it works](#how-it-works)
|
27
|
+
* [Glossary](#glossary)
|
28
|
+
* [Related projects](#related-projects)
|
29
|
+
|
30
|
+
Current status
|
31
|
+
-------------
|
32
|
+
|
33
|
+
Dynflow has been under heavy development for several months to be able
|
34
|
+
to support the services orchestration in the
|
35
|
+
[Katello](http://katello.org) and [Foreman](http://theforeman.org/)
|
36
|
+
projects, getting to production-ready state in couple of weeks.
|
37
|
+
|
38
|
+
How it works
|
39
|
+
------------
|
3
40
|
|
4
41
|
In traditional workflow engines, you specify a static workflow and
|
5
42
|
then run it with various inputs. Dynflow takes different approach.
|
6
|
-
|
7
43
|
You specify the inputs and the workflow is generated on the fly. You
|
8
44
|
can either specify the steps explicitly or subscribe one action to
|
9
45
|
another. This is suitable for plugin architecture, where you can't
|
10
46
|
write the whole process on one place.
|
11
47
|
|
12
48
|
Dynflow doesn't differentiate between workflow and action. Instead,
|
13
|
-
every action can populate another actions
|
14
|
-
|
49
|
+
every action can populate another actions. This allows composing
|
50
|
+
more simpler workflows into a big one.
|
15
51
|
|
16
52
|
The whole execution is done in three phases:
|
17
53
|
|
18
|
-
1. *
|
54
|
+
1. *Plan phase*
|
19
55
|
|
20
|
-
Construct the execution plan for the workflow.
|
21
|
-
|
22
|
-
of actions to be executed:
|
56
|
+
Construct the execution plan for the workflow. Two mechanisms are
|
57
|
+
used to get the set of actions to be executed:
|
23
58
|
|
24
59
|
a. explicit calls of `plan_action` methods in the `plan` method
|
25
60
|
|
@@ -29,7 +64,7 @@ The whole execution is done in three phases:
|
|
29
64
|
|
30
65
|
The output of this phase is a set of actions and their inputs.
|
31
66
|
|
32
|
-
2. *
|
67
|
+
2. *Run phase*
|
33
68
|
|
34
69
|
The plan is being executed step by step, calling the run method of
|
35
70
|
an action with corresponding input. The results of every action are
|
@@ -41,7 +76,7 @@ The output of this phase is a set of actions and their inputs.
|
|
41
76
|
serialized therefore the workflow itself can be persisted. This makes
|
42
77
|
it easy to recover from failed actions by rerunning it.
|
43
78
|
|
44
|
-
3. *
|
79
|
+
3. *Finalize phase*
|
45
80
|
|
46
81
|
Take the results from the execution phase and perform some additional
|
47
82
|
tasks. This is suitable for example for recording the results into
|
@@ -91,45 +126,26 @@ class Action < Dynflow::Action
|
|
91
126
|
plan_action SubAction, object_1
|
92
127
|
# we can specify, where in the workflow this action should be
|
93
128
|
# placed, as well as prepare the input.
|
94
|
-
plan_self {
|
129
|
+
plan_self { id: object_2.id, name: object_2.name}
|
95
130
|
end
|
96
131
|
|
97
132
|
# OPTIONAL: run the execution part of this action. Transform the
|
98
133
|
# data from +input+ to +output+. When not specified, the action is
|
99
134
|
# not used in the execution phase.
|
100
135
|
def run
|
101
|
-
output[
|
136
|
+
output[:uuid] = "#{input[:name]}-#{input[:id]}"
|
102
137
|
end
|
103
138
|
|
104
139
|
# OPTIONAL: finalize the action after the execution phase finishes.
|
105
140
|
# in the +input+ and +output+ attributes are available the data from
|
106
141
|
# execution phase. in the +outputs+ argument, all the execution
|
107
142
|
# phase actions are available, each providing its input and output.
|
108
|
-
def finalize
|
109
|
-
puts output[
|
143
|
+
def finalize
|
144
|
+
puts output[:uuid]
|
110
145
|
end
|
111
146
|
end
|
112
147
|
```
|
113
148
|
|
114
|
-
One can generate the execution plan for an action without actually
|
115
|
-
running it:
|
116
|
-
|
117
|
-
```ruby
|
118
|
-
pp Publish.plan(short_article).actions
|
119
|
-
# the expanded workflow is:
|
120
|
-
# [
|
121
|
-
# Publish: {"title"=>"Short", "body"=>"Short"} ~> {},
|
122
|
-
# Review: {"title"=>"Short", "body"=>"Short"} ~> {},
|
123
|
-
# Print: {"title"=>"Short", "body"=>"Short", "color"=>false} ~> {}
|
124
|
-
# ]
|
125
|
-
```
|
126
|
-
|
127
|
-
Therefore it's suitable for the plan methods to not have any side
|
128
|
-
effects (except of database writes that can be roll-backed)
|
129
|
-
|
130
|
-
In the finalization phase, `finalize` method is called on every action
|
131
|
-
if defined. The order is the same as in the execution plan.
|
132
|
-
|
133
149
|
Every action should be as atomic as possible, providing better
|
134
150
|
granularity when manipulating the process. Since every action can be
|
135
151
|
subscribed by another one, adding new behaviour to an existing
|
@@ -140,12 +156,58 @@ that other developers can use when extending the workflows.
|
|
140
156
|
|
141
157
|
See the examples directory for more complete examples.
|
142
158
|
|
159
|
+
Glossary
|
160
|
+
--------
|
161
|
+
|
162
|
+
* **action** - building block for the workflows: a Ruby class
|
163
|
+
inherited from `Dynflow::Action`. Defines code to be run in
|
164
|
+
plan/run/finalize phase. It has defined input and output data.
|
165
|
+
* **execution plan** - definition of the workflow: product of the plan
|
166
|
+
phase
|
167
|
+
* **trigger an action** - entering the plan phase, starting with the
|
168
|
+
`plan` method of the action. The execution follows immediately.
|
169
|
+
* **plan_self** - converts the arguments of the `plan` method into
|
170
|
+
action input, that can be accessed from the `run`/`finalize`
|
171
|
+
phase.
|
172
|
+
* **plan_action** - includes another action into the workflow, passing
|
173
|
+
the arguments into the `plan` method of the action
|
174
|
+
* **step** - execution unit of the action. It represents the action in
|
175
|
+
specific phase (plan step, run step, finalize step).
|
176
|
+
* **flow** - definition of the run/finalize phase, holding the
|
177
|
+
information about steps that can run concurrently/in sequence.
|
178
|
+
Part of execution plan.
|
179
|
+
* **executor** - service that executes the run and finalize flows
|
180
|
+
based on the execution plan. It can run in the same process as the
|
181
|
+
plan phase or in different process (using the remote executor)
|
182
|
+
* **world** - the universe where the Dynflow runs the code: it holds
|
183
|
+
all needed configuration.
|
184
|
+
|
185
|
+
Related projects
|
186
|
+
----------------
|
187
|
+
|
188
|
+
* [Foreman](http://theforeman.org) - lifecycle management tool for
|
189
|
+
physical and virtual servers
|
190
|
+
|
191
|
+
* [Katello](http://katello.org) - content management plugin for
|
192
|
+
Foreman: integrates couple of REST services for managing the
|
193
|
+
software updates in the infrastructure.
|
194
|
+
|
195
|
+
* [Foreman-tasks](https://github.com/iNecas/foreman-tasks) - Foreman
|
196
|
+
plugin providing the tasks management with Dynflow on the back-end
|
197
|
+
|
198
|
+
* [Dyntask](https://github.com/iNecas/dyntask) - generic Rails engine
|
199
|
+
providing the tasks management features with Dynflow on the back-end
|
200
|
+
|
201
|
+
* [Sysflow](https://github.com/iNecas/sysflow) - set of reusable tools
|
202
|
+
for running system tasks with Dynflow, comes with simple Web-UI for
|
203
|
+
testing it
|
204
|
+
|
143
205
|
License
|
144
206
|
-------
|
145
207
|
|
146
208
|
MIT
|
147
209
|
|
148
|
-
|
149
|
-
|
210
|
+
Authors
|
211
|
+
-------
|
150
212
|
|
151
|
-
Ivan Nečas
|
213
|
+
Ivan Nečas, Petr Chalupa
|
data/Rakefile
CHANGED
@@ -1,15 +1,11 @@
|
|
1
1
|
require 'rake/testtask'
|
2
2
|
require 'fileutils'
|
3
3
|
|
4
|
-
desc "Generic tests"
|
5
4
|
Rake::TestTask.new do |t|
|
6
5
|
t.libs << 'lib' << 'test'
|
7
6
|
t.test_files = FileList['test/*_test.rb']
|
8
7
|
t.verbose = true
|
9
8
|
end
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
task :all => [:test] do
|
14
|
-
end
|
15
|
-
end
|
10
|
+
desc Rake::Task['test'].comment
|
11
|
+
task :default => :test
|
data/doc/images/logo.png
ADDED
Binary file
|
data/dynflow.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.homepage = "http://github.com/iNecas/dynflow"
|
11
11
|
s.summary = "DYNamic workFLOW engine"
|
12
12
|
s.description = "Generate and executed workflows dynamically based "+
|
13
|
-
|
13
|
+
"on input data and leave it open for others to jump into it as well"
|
14
14
|
|
15
15
|
s.files = `git ls-files`.split("\n")
|
16
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -19,5 +19,14 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.add_dependency "activesupport"
|
20
20
|
s.add_dependency "multi_json"
|
21
21
|
s.add_dependency "apipie-params"
|
22
|
+
s.add_dependency "algebrick", '~> 0.4.0'
|
23
|
+
s.add_dependency "uuidtools"
|
24
|
+
|
25
|
+
s.add_development_dependency "rack-test"
|
22
26
|
s.add_development_dependency "minitest"
|
27
|
+
s.add_development_dependency "minitest-reporters"
|
28
|
+
s.add_development_dependency "activerecord"
|
29
|
+
s.add_development_dependency "sequel"
|
30
|
+
s.add_development_dependency "sqlite3"
|
31
|
+
s.add_development_dependency "sinatra"
|
23
32
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
root_path = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
4
|
+
dynflow_path = File.join(root_path, 'lib')
|
5
|
+
$LOAD_PATH << dynflow_path unless $LOAD_PATH.include? dynflow_path
|
6
|
+
|
7
|
+
require 'dynflow'
|
8
|
+
require 'tmpdir'
|
9
|
+
|
10
|
+
socket_path = File.join(Dir.tmpdir, 'dynflow_socket')
|
11
|
+
persistence_adapter = Dynflow::PersistenceAdapters::Sequel.new ARGV[0] || 'sqlite://db.sqlite'
|
12
|
+
|
13
|
+
world = Dynflow::SimpleWorld.new do |world|
|
14
|
+
{ persistence_adapter: persistence_adapter,
|
15
|
+
executor: Dynflow::Executors::RemoteViaSocket.new(world, socket_path) }
|
16
|
+
end
|
17
|
+
|
18
|
+
load File.join(root_path, 'test', 'code_workflow_example.rb')
|
19
|
+
|
20
|
+
loop do
|
21
|
+
world.trigger Dynflow::CodeWorkflowExample::Slow, 1
|
22
|
+
sleep 0.5
|
23
|
+
p 'tick'
|
24
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# Simple example on how Dynflow could be used for simple infrastructure orchestration
|
2
|
+
|
3
|
+
module Orchestrate
|
4
|
+
|
5
|
+
class CreateInfrastructure < Dynflow::Action
|
6
|
+
|
7
|
+
def plan
|
8
|
+
sequence do
|
9
|
+
concurrence do
|
10
|
+
plan_action(CreateMachine, 'host1', 'db')
|
11
|
+
plan_action(CreateMachine, 'host2', 'storage')
|
12
|
+
end
|
13
|
+
plan_action(CreateMachine,
|
14
|
+
'host3',
|
15
|
+
'web_server',
|
16
|
+
:db_machine => 'host1',
|
17
|
+
:storage_machine => 'host2')
|
18
|
+
end
|
19
|
+
sleep 2
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class CreateMachine < Dynflow::Action
|
24
|
+
|
25
|
+
def plan(name, profile, config_options = {})
|
26
|
+
prepare_disk = plan_action(PrepareDisk, 'name' => name)
|
27
|
+
create_vm = plan_action(CreateVM,
|
28
|
+
:name => name,
|
29
|
+
:disk => prepare_disk.output['path'])
|
30
|
+
plan_action(AddIPtoHosts, :name => name, :ip => create_vm.output[:ip])
|
31
|
+
plan_action(ConfigureMachine,
|
32
|
+
:ip => create_vm.output[:ip],
|
33
|
+
:profile => profile,
|
34
|
+
:config_options => config_options)
|
35
|
+
plan_self(:name => name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def finalize
|
39
|
+
puts "We've create a machine #{input[:name]}"
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class PrepareDisk < Dynflow::Action
|
45
|
+
|
46
|
+
input_format do
|
47
|
+
param :name
|
48
|
+
end
|
49
|
+
|
50
|
+
output_format do
|
51
|
+
param :path
|
52
|
+
end
|
53
|
+
|
54
|
+
def run
|
55
|
+
sleep(rand(5))
|
56
|
+
output[:path] = "/var/images/#{input[:name]}.img"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
class CreateVM < Dynflow::Action
|
62
|
+
|
63
|
+
input_format do
|
64
|
+
param :name
|
65
|
+
param :disk
|
66
|
+
end
|
67
|
+
|
68
|
+
output_format do
|
69
|
+
param :ip
|
70
|
+
end
|
71
|
+
|
72
|
+
def run
|
73
|
+
sleep(rand(5))
|
74
|
+
output[:ip] = "192.168.100.#{rand(256)}"
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
class AddIPtoHosts < Dynflow::Action
|
80
|
+
|
81
|
+
input_format do
|
82
|
+
param :ip
|
83
|
+
end
|
84
|
+
|
85
|
+
def run
|
86
|
+
sleep(rand(5))
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
class ConfigureMachine < Dynflow::Action
|
92
|
+
|
93
|
+
input_format do
|
94
|
+
param :ip
|
95
|
+
param :profile
|
96
|
+
param :config_options
|
97
|
+
end
|
98
|
+
|
99
|
+
def run
|
100
|
+
# for demonstration of resuming after error
|
101
|
+
if Orchestrate.should_fail?
|
102
|
+
Orchestrate.should_pass!
|
103
|
+
raise "temporary unavailabe"
|
104
|
+
end
|
105
|
+
|
106
|
+
sleep(rand(5))
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
# for simulation of the execution failing for the first time
|
113
|
+
def self.should_fail?
|
114
|
+
! @should_pass
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.should_pass!
|
118
|
+
@should_pass = true
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
root_path = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
4
|
+
dynflow_path = File.join(root_path, 'lib')
|
5
|
+
$LOAD_PATH << dynflow_path unless $LOAD_PATH.include? dynflow_path
|
6
|
+
|
7
|
+
require 'dynflow'
|
8
|
+
require 'tmpdir'
|
9
|
+
|
10
|
+
socket = File.join(Dir.tmpdir, 'dynflow_socket')
|
11
|
+
persistence_adapter = Dynflow::PersistenceAdapters::Sequel.new ARGV[0] || 'sqlite://db.sqlite'
|
12
|
+
world = Dynflow::SimpleWorld.new persistence_adapter: persistence_adapter
|
13
|
+
listener = Dynflow::Listeners::Socket.new world, socket
|
14
|
+
|
15
|
+
load File.join(root_path, 'test', 'code_workflow_example.rb')
|
16
|
+
|
17
|
+
Dynflow::Daemon.new(listener, world).run
|