dynflow 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Executors
|
|
3
|
+
class Parallel < Abstract
|
|
4
|
+
class SequenceCursor
|
|
5
|
+
|
|
6
|
+
def initialize(flow_manager, sequence, parent_cursor = nil)
|
|
7
|
+
@flow_manager = flow_manager
|
|
8
|
+
@sequence = sequence
|
|
9
|
+
@parent_cursor = parent_cursor
|
|
10
|
+
@todo = []
|
|
11
|
+
@index = -1 # starts before first element
|
|
12
|
+
@no_error_so_far = true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @param [ExecutionPlan::Steps::Abstract, SequenceCursor] work
|
|
16
|
+
# step or sequence cursor that was done
|
|
17
|
+
# @param [true, false] success was the work finished successfully
|
|
18
|
+
# @return [Array<Integer>] new step_ids that can be done next
|
|
19
|
+
def what_is_next(work = nil, success = true)
|
|
20
|
+
unless work.nil? || @todo.delete(work)
|
|
21
|
+
raise "marking as done work that was not expected: #{work.inspect}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
@no_error_so_far &&= success
|
|
25
|
+
|
|
26
|
+
if done_here?
|
|
27
|
+
return next_steps
|
|
28
|
+
else
|
|
29
|
+
return []
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# return true if we can't move the cursor further, either when
|
|
35
|
+
# everyting is done in the sequence or there was some failure
|
|
36
|
+
# that prevents us from moving
|
|
37
|
+
def done?
|
|
38
|
+
(!@no_error_so_far && done_here?) || @index == @sequence.size
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
protected
|
|
42
|
+
|
|
43
|
+
# steps we can do right now without waiting for anything
|
|
44
|
+
def steps_todo
|
|
45
|
+
@todo.map do |item|
|
|
46
|
+
case item
|
|
47
|
+
when SequenceCursor
|
|
48
|
+
item.steps_todo
|
|
49
|
+
else
|
|
50
|
+
item
|
|
51
|
+
end
|
|
52
|
+
end.flatten
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def move
|
|
56
|
+
@index += 1
|
|
57
|
+
next_flow = @sequence.sub_flows[@index]
|
|
58
|
+
add_todo(next_flow)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def done_here?
|
|
64
|
+
@todo.empty?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def next_steps
|
|
68
|
+
move if @no_error_so_far
|
|
69
|
+
if done?
|
|
70
|
+
if @parent_cursor
|
|
71
|
+
return @parent_cursor.what_is_next(self, @no_error_so_far)
|
|
72
|
+
else
|
|
73
|
+
return []
|
|
74
|
+
end
|
|
75
|
+
else
|
|
76
|
+
return steps_todo
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def add_todo(flow)
|
|
81
|
+
case flow
|
|
82
|
+
when Flows::Sequence
|
|
83
|
+
@todo << SequenceCursor.new(@flow_manager, flow, self).tap do |cursor|
|
|
84
|
+
cursor.move
|
|
85
|
+
end
|
|
86
|
+
when Flows::Concurrence
|
|
87
|
+
flow.sub_flows.each { |sub_flow| add_todo(sub_flow) }
|
|
88
|
+
when Flows::Atom
|
|
89
|
+
@flow_manager.cursor_index[flow.step_id] = self
|
|
90
|
+
@todo << @flow_manager.execution_plan.steps[flow.step_id]
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Executors
|
|
3
|
+
class Parallel::SequentialManager
|
|
4
|
+
attr_reader :execution_plan, :world
|
|
5
|
+
|
|
6
|
+
def initialize(world, execution_plan)
|
|
7
|
+
@world = world
|
|
8
|
+
@execution_plan = execution_plan
|
|
9
|
+
@done = false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def run
|
|
13
|
+
with_state_updates do
|
|
14
|
+
dispatch(execution_plan.run_flow)
|
|
15
|
+
finalize
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
return execution_plan
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def finalize
|
|
22
|
+
reset_finalize_steps
|
|
23
|
+
unless execution_plan.error?
|
|
24
|
+
world.transaction_adapter.transaction do
|
|
25
|
+
unless dispatch(execution_plan.finalize_flow)
|
|
26
|
+
world.transaction_adapter.rollback
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
@done = true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def reset_finalize_steps
|
|
34
|
+
execution_plan.finalize_flow.all_step_ids.each do |step_id|
|
|
35
|
+
step = execution_plan.steps[step_id]
|
|
36
|
+
step.state = :pending if [:success, :error].include? step.state
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def done?
|
|
41
|
+
@done
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def dispatch(flow)
|
|
47
|
+
case flow
|
|
48
|
+
when Flows::Sequence
|
|
49
|
+
run_in_sequence(flow.flows)
|
|
50
|
+
when Flows::Concurrence
|
|
51
|
+
run_in_concurrence(flow.flows)
|
|
52
|
+
when Flows::Atom
|
|
53
|
+
run_step(execution_plan.steps[flow.step_id])
|
|
54
|
+
else
|
|
55
|
+
raise ArgumentError, "Don't know how to run #{flow}"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def run_in_sequence(steps)
|
|
60
|
+
steps.all? { |s| dispatch(s) }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def run_in_concurrence(steps)
|
|
64
|
+
run_in_sequence(steps)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def run_step(step)
|
|
68
|
+
step.execute
|
|
69
|
+
execution_plan.update_execution_time step.execution_time
|
|
70
|
+
execution_plan.save
|
|
71
|
+
return step.state != :error
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def with_state_updates(&block)
|
|
75
|
+
execution_plan.update_state(:running)
|
|
76
|
+
block.call
|
|
77
|
+
execution_plan.update_state(execution_plan.error? ? :paused : :stopped)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Executors
|
|
3
|
+
class Parallel < Abstract
|
|
4
|
+
class WorkQueue
|
|
5
|
+
include Algebrick::TypeCheck
|
|
6
|
+
|
|
7
|
+
def initialize(key_type = Object, work_type = Object)
|
|
8
|
+
@key_type = key_type
|
|
9
|
+
@work_type = work_type
|
|
10
|
+
@stash = Hash.new { |hash, key| hash[key] = [] }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def push(key, work)
|
|
14
|
+
Type! key, @key_type
|
|
15
|
+
Type! work, @work_type
|
|
16
|
+
@stash[key].push work
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def shift(key)
|
|
20
|
+
return nil unless present? key
|
|
21
|
+
@stash[key].shift.tap { |work| @stash.delete(key) if @stash[key].empty? }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def present?(key)
|
|
25
|
+
@stash.key?(key)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def empty?(key)
|
|
29
|
+
!present?(key)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def size(key)
|
|
33
|
+
return 0 if empty?(key)
|
|
34
|
+
@stash[key].size
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def first(key)
|
|
38
|
+
return nil if empty?(key)
|
|
39
|
+
@stash[key].first
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Dynflow
|
|
2
|
+
module Executors
|
|
3
|
+
class Parallel < Abstract
|
|
4
|
+
class Worker < MicroActor
|
|
5
|
+
def initialize(pool, transaction_adapter)
|
|
6
|
+
super(pool.logger, pool, transaction_adapter)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def delayed_initialize(pool, transaction_adapter)
|
|
12
|
+
@pool = pool
|
|
13
|
+
@transaction_adapter = Type! transaction_adapter, TransactionAdapters::Abstract
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def on_message(message)
|
|
17
|
+
match message,
|
|
18
|
+
Work::Step.(step: ~any) | Work::Event.(step: ~any, event: Event.(event: ~any)) >-> step, event do
|
|
19
|
+
step.execute event
|
|
20
|
+
end,
|
|
21
|
+
Work::Finalize.(~any, any) >-> sequential_manager do
|
|
22
|
+
sequential_manager.finalize
|
|
23
|
+
end
|
|
24
|
+
@pool << WorkerDone[work: message, worker: self]
|
|
25
|
+
@transaction_adapter.cleanup
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
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::Execute[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
|
+
raise 'events are handled in a process with real executor'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def terminate(future = Future.new)
|
|
30
|
+
@core.ask(MicroActor::Terminate, future)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def initialized
|
|
34
|
+
@core.initialized
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
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
|
+
variants Closed = atom,
|
|
9
|
+
Received = type { fields message: SocketMessage },
|
|
10
|
+
Execute = type { fields execution_plan_uuid: String, future: Future }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
Execution = Algebrick.type do
|
|
14
|
+
fields! id: Integer, accepted: Future, finished: Future
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module Execution
|
|
18
|
+
def accept!
|
|
19
|
+
accepted.resolve true
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def reject!(error)
|
|
24
|
+
accepted.fail error
|
|
25
|
+
finished.fail error
|
|
26
|
+
self
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def success!(value)
|
|
30
|
+
raise unless accepted.ready?
|
|
31
|
+
finished.resolve value
|
|
32
|
+
self
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def fail!(error)
|
|
36
|
+
if accepted.ready?
|
|
37
|
+
finished.fail error
|
|
38
|
+
else
|
|
39
|
+
reject! error
|
|
40
|
+
end
|
|
41
|
+
self
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def initialize(world, socket_path)
|
|
46
|
+
super(world.logger, world, socket_path)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def delayed_initialize(world, socket_path)
|
|
52
|
+
@socket_path = Type! socket_path, String
|
|
53
|
+
@world = Type! world, World
|
|
54
|
+
@socket = nil
|
|
55
|
+
@last_id = 0
|
|
56
|
+
@executions = {}
|
|
57
|
+
connect
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def termination
|
|
61
|
+
disconnect
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def on_message(message)
|
|
65
|
+
match message,
|
|
66
|
+
|
|
67
|
+
(on Core::Execute.(~any, ~any) do |execution_plan_uuid, future|
|
|
68
|
+
raise 'terminating' if terminating?
|
|
69
|
+
id, accepted = add_execution future
|
|
70
|
+
success = connect && begin
|
|
71
|
+
send_message @socket, RemoteViaSocket::Execute[id, execution_plan_uuid]
|
|
72
|
+
true
|
|
73
|
+
rescue IOError => error
|
|
74
|
+
logger.warn error
|
|
75
|
+
false
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
unless success
|
|
79
|
+
@executions[id].reject! Dynflow::Error.new(
|
|
80
|
+
'No connection to RemoteViaSocket::Listener')
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
return accepted
|
|
84
|
+
end),
|
|
85
|
+
|
|
86
|
+
(on Received.(Accepted.(~any)) do |id|
|
|
87
|
+
@executions[id].accept!
|
|
88
|
+
end),
|
|
89
|
+
|
|
90
|
+
(on Received.(Failed.(~any, ~any)) do |id, error|
|
|
91
|
+
@executions.delete(id).reject! Dynflow::Error.new(error)
|
|
92
|
+
end),
|
|
93
|
+
|
|
94
|
+
(on Received.(Done.(~any, ~any)) do |id, uuid|
|
|
95
|
+
@executions.delete(id).success! @world.persistence.load_execution_plan(uuid)
|
|
96
|
+
end),
|
|
97
|
+
|
|
98
|
+
(on Closed do
|
|
99
|
+
@socket = nil
|
|
100
|
+
logger.info 'Disconnected from server.'
|
|
101
|
+
@executions.each { |_, c| c.fail! 'No connection to RemoteViaSocket::Listener' }
|
|
102
|
+
@executions.clear
|
|
103
|
+
terminate! if terminating?
|
|
104
|
+
end)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def add_execution(finished)
|
|
108
|
+
@executions[id = (@last_id += 1)] = Execution[id, accepted = Future.new, finished]
|
|
109
|
+
return id, accepted
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def connect
|
|
113
|
+
return true if @socket
|
|
114
|
+
@socket = UNIXSocket.new @socket_path
|
|
115
|
+
logger.info 'Connected to server.'
|
|
116
|
+
read_socket_until_closed
|
|
117
|
+
true
|
|
118
|
+
rescue IOError => error
|
|
119
|
+
logger.warn error
|
|
120
|
+
false
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def disconnect
|
|
124
|
+
return true unless @socket
|
|
125
|
+
@socket.shutdown :RDWR
|
|
126
|
+
true
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def read_socket_until_closed
|
|
130
|
+
Thread.new do
|
|
131
|
+
catch(:stop_reading) do
|
|
132
|
+
loop { read_socket }
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def read_socket
|
|
138
|
+
match message = receive_message(@socket),
|
|
139
|
+
SocketMessage >-> { self << Received[message] },
|
|
140
|
+
NilClass.to_m >-> do
|
|
141
|
+
self << Closed
|
|
142
|
+
throw :stop_reading
|
|
143
|
+
end
|
|
144
|
+
rescue => error
|
|
145
|
+
logger.fatal error
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|