dynflow 0.7.9 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/.gitignore +2 -0
  2. data/.travis.yml +16 -1
  3. data/Gemfile +13 -1
  4. data/doc/pages/source/_drafts/2015-03-01-new-documentation.markdown +10 -0
  5. data/doc/pages/source/_includes/menu.html +1 -0
  6. data/doc/pages/source/_includes/menu_right.html +1 -1
  7. data/doc/pages/source/_sass/_bootstrap-variables.sass +1 -0
  8. data/doc/pages/source/_sass/_style.scss +4 -0
  9. data/doc/pages/source/blog/index.html +12 -0
  10. data/doc/pages/source/documentation/index.md +330 -5
  11. data/dynflow.gemspec +3 -1
  12. data/examples/example_helper.rb +18 -11
  13. data/examples/orchestrate_evented.rb +2 -1
  14. data/examples/remote_executor.rb +53 -20
  15. data/lib/dynflow.rb +16 -6
  16. data/lib/dynflow/action/suspended.rb +1 -1
  17. data/lib/dynflow/action/with_sub_plans.rb +3 -6
  18. data/lib/dynflow/actor.rb +56 -0
  19. data/lib/dynflow/clock.rb +43 -38
  20. data/lib/dynflow/config.rb +107 -0
  21. data/lib/dynflow/connectors.rb +7 -0
  22. data/lib/dynflow/connectors/abstract.rb +41 -0
  23. data/lib/dynflow/connectors/database.rb +175 -0
  24. data/lib/dynflow/connectors/direct.rb +71 -0
  25. data/lib/dynflow/coordinator.rb +280 -0
  26. data/lib/dynflow/coordinator_adapters.rb +8 -0
  27. data/lib/dynflow/coordinator_adapters/abstract.rb +28 -0
  28. data/lib/dynflow/coordinator_adapters/sequel.rb +29 -0
  29. data/lib/dynflow/dispatcher.rb +58 -0
  30. data/lib/dynflow/dispatcher/abstract.rb +14 -0
  31. data/lib/dynflow/dispatcher/client_dispatcher.rb +139 -0
  32. data/lib/dynflow/dispatcher/executor_dispatcher.rb +86 -0
  33. data/lib/dynflow/errors.rb +7 -1
  34. data/lib/dynflow/execution_history.rb +46 -0
  35. data/lib/dynflow/execution_plan.rb +19 -15
  36. data/lib/dynflow/executors.rb +0 -1
  37. data/lib/dynflow/executors/abstract.rb +5 -10
  38. data/lib/dynflow/executors/parallel.rb +16 -13
  39. data/lib/dynflow/executors/parallel/core.rb +76 -78
  40. data/lib/dynflow/executors/parallel/execution_plan_manager.rb +4 -5
  41. data/lib/dynflow/executors/parallel/pool.rb +22 -52
  42. data/lib/dynflow/executors/parallel/running_steps_manager.rb +9 -2
  43. data/lib/dynflow/executors/parallel/worker.rb +5 -10
  44. data/lib/dynflow/persistence.rb +14 -0
  45. data/lib/dynflow/persistence_adapters/abstract.rb +14 -3
  46. data/lib/dynflow/persistence_adapters/sequel.rb +142 -38
  47. data/lib/dynflow/persistence_adapters/sequel_migrations/004_coordinator_records.rb +14 -0
  48. data/lib/dynflow/persistence_adapters/sequel_migrations/005_envelopes.rb +14 -0
  49. data/lib/dynflow/round_robin.rb +37 -0
  50. data/lib/dynflow/serializable.rb +1 -2
  51. data/lib/dynflow/serializer.rb +46 -0
  52. data/lib/dynflow/testing/dummy_executor.rb +2 -2
  53. data/lib/dynflow/testing/dummy_world.rb +1 -1
  54. data/lib/dynflow/transaction_adapters/abstract.rb +0 -5
  55. data/lib/dynflow/transaction_adapters/active_record.rb +0 -10
  56. data/lib/dynflow/version.rb +1 -1
  57. data/lib/dynflow/web.rb +26 -0
  58. data/lib/dynflow/web/console.rb +108 -0
  59. data/lib/dynflow/web/console_helpers.rb +158 -0
  60. data/lib/dynflow/web/filtering_helpers.rb +85 -0
  61. data/lib/dynflow/web/world_helpers.rb +9 -0
  62. data/lib/dynflow/web_console.rb +3 -310
  63. data/lib/dynflow/world.rb +188 -119
  64. data/test/abnormal_states_recovery_test.rb +152 -0
  65. data/test/action_test.rb +2 -3
  66. data/test/clock_test.rb +1 -5
  67. data/test/coordinator_test.rb +152 -0
  68. data/test/dispatcher_test.rb +146 -0
  69. data/test/execution_plan_test.rb +2 -1
  70. data/test/executor_test.rb +534 -612
  71. data/test/middleware_test.rb +4 -4
  72. data/test/persistence_test.rb +17 -0
  73. data/test/prepare_travis_env.sh +35 -0
  74. data/test/rescue_test.rb +5 -3
  75. data/test/round_robin_test.rb +28 -0
  76. data/test/support/code_workflow_example.rb +0 -73
  77. data/test/support/dummy_example.rb +130 -0
  78. data/test/support/test_execution_log.rb +41 -0
  79. data/test/test_helper.rb +222 -116
  80. data/test/testing_test.rb +10 -10
  81. data/test/web_console_test.rb +3 -3
  82. data/test/world_test.rb +23 -0
  83. data/web/assets/images/logo-square.png +0 -0
  84. data/web/assets/stylesheets/application.css +9 -0
  85. data/web/assets/vendor/bootstrap/config.json +429 -0
  86. data/web/assets/vendor/bootstrap/css/bootstrap-theme.css +479 -0
  87. data/web/assets/vendor/bootstrap/css/bootstrap-theme.min.css +10 -0
  88. data/web/assets/vendor/bootstrap/css/bootstrap.css +5377 -4980
  89. data/web/assets/vendor/bootstrap/css/bootstrap.min.css +9 -8
  90. data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  91. data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
  92. data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  93. data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  94. data/web/assets/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
  95. data/web/assets/vendor/bootstrap/js/bootstrap.js +1674 -1645
  96. data/web/assets/vendor/bootstrap/js/bootstrap.min.js +11 -5
  97. data/web/views/execution_history.erb +17 -0
  98. data/web/views/index.erb +4 -6
  99. data/web/views/layout.erb +44 -8
  100. data/web/views/show.erb +4 -5
  101. data/web/views/worlds.erb +26 -0
  102. metadata +116 -23
  103. checksums.yaml +0 -15
  104. data/lib/dynflow/daemon.rb +0 -30
  105. data/lib/dynflow/executors/remote_via_socket.rb +0 -43
  106. data/lib/dynflow/executors/remote_via_socket/core.rb +0 -184
  107. data/lib/dynflow/future.rb +0 -173
  108. data/lib/dynflow/listeners.rb +0 -7
  109. data/lib/dynflow/listeners/abstract.rb +0 -17
  110. data/lib/dynflow/listeners/serialization.rb +0 -77
  111. data/lib/dynflow/listeners/socket.rb +0 -117
  112. data/lib/dynflow/micro_actor.rb +0 -102
  113. data/lib/dynflow/simple_world.rb +0 -19
  114. data/test/remote_via_socket_test.rb +0 -170
  115. data/web/assets/vendor/bootstrap/css/bootstrap-responsive.css +0 -1109
  116. data/web/assets/vendor/bootstrap/css/bootstrap-responsive.min.css +0 -9
  117. data/web/assets/vendor/bootstrap/img/glyphicons-halflings-white.png +0 -0
  118. data/web/assets/vendor/bootstrap/img/glyphicons-halflings.png +0 -0
checksums.yaml DELETED
@@ -1,15 +0,0 @@
1
- ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- OTk4YWEzYzI4ZGUxYmEzYmRiZmYwZGUwN2I3ZjgzY2NlNmNjMDQ2OA==
5
- data.tar.gz: !binary |-
6
- OTFjZmY1OGE4MDI3MmIwY2YyYTIwYjZiOWI1YjljMTNiN2JhN2FhMA==
7
- SHA512:
8
- metadata.gz: !binary |-
9
- ZGRlZGI5Njc4ZDA4ZmFkNzMxMTdkMGM0M2E5Y2ZlNjljYjUxNzc5MDY0ZWY5
10
- MWJmNWQzYjZjOGVhMTM2ODRiZjZlM2JkMTZhY2MwMzUwNDM4M2FlN2VmZjA3
11
- ODIwODIzYjdkZGE2NGI1NGYzNzEzODQ4OTdjOWQyMTc0ZTI5YmQ=
12
- data.tar.gz: !binary |-
13
- OTllZWM0M2U2M2ZiN2I1MjA0MTQzM2ZlYzdiNzUxMzZjMTg2Mzg5ODRiOTcw
14
- MWQwNTdmMjYxN2Q3ZWFlOGRmOTVmZjJhNjIyYTNlMjZiNWI4NTc2ZWNlODg4
15
- MTUyNmMwNGY5MWFiZjc4MTU5YWRjMmEwYTllNzcwM2QzMWMxNDE=
@@ -1,30 +0,0 @@
1
- module Dynflow
2
- class Daemon
3
- include Algebrick::TypeCheck
4
-
5
- def initialize(listener, world, lock_file = nil)
6
- @listener = Type! listener, Listeners::Abstract
7
- @world = Type! world, World
8
- @lock_file = Type! lock_file, String, NilClass
9
- end
10
-
11
- def run
12
- with_lock_file do
13
- terminated = Future.new
14
- trap('SIGINT') { @world.terminate terminated }
15
- terminated.wait
16
- @listener.terminate.wait
17
- end
18
- end
19
-
20
- def with_lock_file(&block)
21
- if @lock_file
22
- raise "Lockfile #{@lock_file} is already present." if File.exist?(@lock_file)
23
- File.write(@lock_file, "Locked at #{Time.now} by process #{$$}\n")
24
- end
25
- block.call
26
- ensure
27
- File.delete(@lock_file) if @lock_file
28
- end
29
- end
30
- end
@@ -1,43 +0,0 @@
1
- require 'multi_json'
2
- require 'socket'
3
-
4
- module Dynflow
5
- module Executors
6
- class RemoteViaSocket < Abstract
7
- require 'dynflow/executors/remote_via_socket/core'
8
-
9
- include Listeners::Serialization
10
- include Algebrick::Matching
11
-
12
- def initialize(world, socket_path)
13
- super world
14
- @core = Core.new world, socket_path
15
- end
16
-
17
- def execute(execution_plan_id, finished = Future.new)
18
- @core.ask(Core::Execution[execution_plan_id, finished]).value!.value!
19
- finished
20
- rescue => e
21
- finished.fail e unless finished.ready?
22
- raise e
23
- end
24
-
25
- def event(execution_plan_id, step_id, event, future = Future)
26
- @core.ask(Core::Event[execution_plan_id, step_id, event, future]).value!
27
- future
28
- end
29
-
30
- def terminate(future = Future.new)
31
- @core.ask(MicroActor::Terminate, future)
32
- end
33
-
34
- def initialized
35
- @core.initialized
36
- end
37
-
38
- def connected?
39
- @core.ask(Core::Connect).value!
40
- end
41
- end
42
- end
43
- end
@@ -1,184 +0,0 @@
1
- module Dynflow
2
- module Executors
3
- class RemoteViaSocket < Abstract
4
- class Core < MicroActor
5
- include Listeners::Serialization
6
-
7
- Message = Algebrick.type do
8
- Job = Algebrick.type do
9
- variants Event = Executors::Abstract::Event,
10
- Execution = Executors::Abstract::Execution
11
- end
12
-
13
- variants Closed = atom,
14
- Received = type { fields message: Protocol::Response },
15
- Connect = atom,
16
- Job
17
- end
18
-
19
- TrackedJob = Algebrick.type do
20
- fields! id: Integer, job: Protocol::Job, accepted: Future, finished: Future
21
- end
22
-
23
- module TrackedJob
24
- def accept!
25
- accepted.resolve true
26
- self
27
- end
28
-
29
- def reject!(error)
30
- accepted.fail error
31
- finished.fail error
32
- self
33
- end
34
-
35
- def success!(world)
36
- raise unless accepted.ready?
37
- finished.resolve(
38
- match job,
39
- (on Core::Protocol::Execution.(execution_plan_id: ~any) do |uuid|
40
- world.persistence.load_execution_plan(uuid)
41
- end),
42
- (on Core::Protocol::Event do
43
- true
44
- end))
45
- self
46
- end
47
-
48
- def fail!(error)
49
- if accepted.ready?
50
- finished.fail error
51
- else
52
- reject! error
53
- end
54
- self
55
- end
56
- end
57
-
58
- def initialize(world, socket_path)
59
- super(world.logger, world, socket_path)
60
- end
61
-
62
- private
63
-
64
- def delayed_initialize(world, socket_path)
65
- @socket_path = Type! socket_path, String
66
- @world = Type! world, World
67
- @socket = nil
68
- @last_id = 0
69
- @tracked_jobs = {}
70
- connect
71
- end
72
-
73
- def termination
74
- terminate! if disconnect
75
- end
76
-
77
- def on_message(message)
78
- Type! message, Message
79
- match message,
80
- (on ~Job do |job|
81
- raise 'terminating' if terminating?
82
- job, future =
83
- match job,
84
- (on ~Execution do |(execution_plan_uuid, future)|
85
- [Protocol::Execution[execution_plan_uuid], future]
86
- end),
87
- (on ~Event do |(execution_plan_id, step_id, event, future)|
88
- [Protocol::Event[execution_plan_id, step_id, event], future]
89
- end)
90
- id, accepted = add_tracked_job future, job
91
- success = connect && begin
92
- send_message @socket, Protocol::Do[id, job]
93
- true
94
- rescue IOError => error
95
- logger.warn error
96
- false
97
- end
98
-
99
- unless success
100
- @tracked_jobs[id].reject!(
101
- Dynflow::Error.new(
102
- "Cannot do #{message}, no connection to a Listener"))
103
- end
104
-
105
- return accepted
106
- end),
107
-
108
- (on Received.(~Protocol::Accepted) do |(id)|
109
- @tracked_jobs[id].accept!
110
- end),
111
-
112
- (on Received.(~Protocol::Failed) do |(id, error)|
113
- @tracked_jobs.delete(id).reject! Dynflow::Error.new(error)
114
- end),
115
-
116
- (on Received.(~Protocol::Done) do |(id)|
117
- @tracked_jobs.delete(id).success! @world
118
- end),
119
-
120
- (on Closed do
121
- @socket = nil
122
- logger.info 'Disconnected from server.'
123
- @tracked_jobs.each do |_, c|
124
- c.fail! 'Connection to a Listener lost.'
125
- end
126
- @tracked_jobs.clear
127
- terminate! if terminating?
128
- end),
129
-
130
- (on Connect do
131
- connect
132
- end)
133
- end
134
-
135
- def add_tracked_job(finished, job)
136
- @tracked_jobs[id = (@last_id += 1)] = TrackedJob[id, job, accepted = Future.new, finished]
137
- return id, accepted
138
- end
139
-
140
- def connect
141
- return true if @socket
142
- @socket = UNIXSocket.new @socket_path
143
- logger.info 'Connected to server.'
144
- read_socket_until_closed
145
- true
146
- rescue SystemCallError, IOError => error
147
- logger.warn error
148
- false
149
- rescue => error
150
- logger.fatal error
151
- raise error
152
- end
153
-
154
- def disconnect
155
- return true unless @socket
156
-
157
- @socket.close
158
- false
159
- rescue Errno::ENOTCONN
160
- true
161
- end
162
-
163
- def read_socket_until_closed
164
- Thread.new do
165
- catch(:stop_reading) do
166
- loop { read_socket }
167
- end
168
- end
169
- end
170
-
171
- def read_socket
172
- match message = receive_message(@socket),
173
- Protocol::Message >-> { self << Received[message] },
174
- NilClass.to_m >-> do
175
- self << Closed
176
- throw :stop_reading
177
- end
178
- rescue => error
179
- logger.fatal error
180
- end
181
- end
182
- end
183
- end
184
- end
@@ -1,173 +0,0 @@
1
- module Dynflow
2
- class Future
3
- Error = Class.new Dynflow::Error
4
- FutureAlreadySet = Class.new Error
5
- FutureFailed = Class.new Error
6
- TimeOut = Class.new Error
7
-
8
- # `#future` will become resolved to `true` when ``#countdown!`` is called `count` times
9
- class CountDownLatch
10
- attr_reader :future
11
-
12
- def initialize(count, future = Future.new)
13
- raise ArgumentError if count < 0
14
- @count = count
15
- @lock = Mutex.new
16
- @future = future
17
- end
18
-
19
- def countdown!
20
- @lock.synchronize do
21
- @count -= 1 if @count > 0
22
- @future.resolve true if @count == 0 && !@future.ready?
23
- end
24
- end
25
-
26
- def count
27
- @lock.synchronize { @count }
28
- end
29
- end
30
-
31
- include Algebrick::TypeCheck
32
- extend Algebrick::TypeCheck
33
-
34
- def self.join(futures, result = Future.new)
35
- countdown = CountDownLatch.new(futures.size, result)
36
- futures.each do |future|
37
- Type! future, Future
38
- future.do_then { |_| countdown.countdown! }
39
- end
40
- result
41
- end
42
-
43
- def initialize(&task)
44
- @lock = Mutex.new
45
- @value = nil
46
- @resolved = false
47
- @failed = false
48
- @waiting = []
49
- @tasks = []
50
- do_then &task if task
51
- end
52
-
53
- def value(timeout = nil)
54
- wait timeout
55
- @lock.synchronize { @value }
56
- end
57
-
58
- def value!
59
- value.tap { raise value if failed? }
60
- end
61
-
62
- def resolve(result)
63
- set result, false
64
- end
65
-
66
- def fail(exception)
67
- Type! exception, Exception, String
68
- if exception.is_a? String
69
- exception = FutureFailed.new(exception).tap { |e| e.set_backtrace caller }
70
- end
71
- set exception, true
72
- end
73
-
74
- def evaluate_to(&block)
75
- resolve block.call
76
- rescue => error
77
- self.fail error
78
- end
79
-
80
- def evaluate_to!(&block)
81
- evaluate_to &block
82
- raise value if self.failed?
83
- end
84
-
85
- def do_then(&task)
86
- call_task = @lock.synchronize do
87
- @tasks << task unless _ready?
88
- @resolved
89
- end
90
- task.call value if call_task
91
- self
92
- end
93
-
94
- def set(value, failed)
95
- @lock.synchronize do
96
- raise FutureAlreadySet, "future already set to #{@value} cannot use #{value}" if _ready?
97
- if failed
98
- @failed = true
99
- else
100
- @resolved = true
101
- end
102
- @value = value
103
- while (thread = @waiting.pop)
104
- begin
105
- thread.wakeup
106
- rescue ThreadError
107
- retry
108
- end
109
- end
110
- !failed
111
- end
112
- @tasks.each { |t| t.call value }
113
- self
114
- end
115
-
116
- def wait(timeout = nil)
117
- @lock.synchronize do
118
- unless _ready?
119
- @waiting << Thread.current
120
- clock.ping self, timeout, Thread.current, :expired if timeout
121
- @lock.sleep
122
- raise TimeOut unless _ready?
123
- end
124
- end
125
- self
126
- end
127
-
128
- def ready?
129
- @lock.synchronize { _ready? }
130
- end
131
-
132
- def resolved?
133
- @lock.synchronize { @resolved }
134
- end
135
-
136
- def failed?
137
- @lock.synchronize { @failed }
138
- end
139
-
140
- def tangle(future)
141
- do_then { |v| future.set v, failed? }
142
- end
143
-
144
- # @api private
145
- def expired(thread)
146
- @lock.synchronize do
147
- thread.wakeup if @waiting.delete(thread)
148
- end
149
- end
150
-
151
- private
152
-
153
- def _ready?
154
- @resolved || @failed
155
- end
156
-
157
- @clock_barrier = Mutex.new
158
-
159
- # @api private
160
- def self.clock
161
- @clock_barrier.synchronize do
162
- # TODO remove global state and use world.clock, needs to be terminated in right order
163
- @clock ||= Clock.new(::Logger.new($stderr)).tap do |clock|
164
- at_exit { clock.ask(Clock::Terminate).wait }
165
- end
166
- end
167
- end
168
-
169
- def clock
170
- self.class.clock
171
- end
172
- end
173
- end