Skip to content

Commit 4f9254b

Browse files
committed
Cancel the plan if some of its prerequisites failed
1 parent 3809c9f commit 4f9254b

File tree

3 files changed

+38
-10
lines changed

3 files changed

+38
-10
lines changed

examples/execution_plan_chaining.rb

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,34 @@
44
require_relative 'example_helper'
55

66
class DelayedAction < Dynflow::Action
7-
def plan
8-
plan_self
7+
def plan(should_fail = false)
8+
plan_self :should_fail => should_fail
99
end
1010

1111
def run
1212
sleep 5
13+
raise "Controlled failure" if input[:should_fail]
14+
end
15+
16+
def rescue_strategy
17+
Dynflow::Action::Rescue::Fail
1318
end
1419
end
1520

1621
if $PROGRAM_NAME == __FILE__
17-
ExampleHelper.world.action_logger.level = 1
18-
ExampleHelper.world.logger.level = 0
22+
world = ExampleHelper.create_world do |config|
23+
config.auto_rescue = true
24+
end
25+
world.action_logger.level = 1
26+
world.logger.level = 0
1927

20-
plan1 = ExampleHelper.world.trigger(DelayedAction)
21-
plan2 = ExampleHelper.world.chain(plan1.execution_plan_id, DelayedAction)
22-
plan3 = ExampleHelper.world.chain(plan2.execution_plan_id, DelayedAction)
23-
plan4 = ExampleHelper.world.chain(plan2.execution_plan_id, DelayedAction)
28+
plan1 = world.trigger(DelayedAction)
29+
plan2 = world.chain(plan1.execution_plan_id, DelayedAction)
30+
plan3 = world.chain(plan2.execution_plan_id, DelayedAction)
31+
plan4 = world.chain(plan2.execution_plan_id, DelayedAction)
32+
33+
plan5 = world.trigger(DelayedAction, true)
34+
plan6 = world.chain(plan5.execution_plan_id, DelayedAction)
2435

2536
puts <<-MSG.gsub(/^.*\|/, '')
2637
|
@@ -34,9 +45,12 @@ def run
3445
| #{plan2.id} is delayed and should run once #{plan1.id} finishes.
3546
| #{plan3.id} and #{plan4.id} are delayed and should run once #{plan2.id} finishes.
3647
|
48+
| #{plan5.id} runs immediately and is expected to fail.
49+
| #{plan6.id} should not run at all as its prerequisite failed.
50+
|
3751
| Visit #{ExampleHelper::DYNFLOW_URL} to see their status.
3852
|
3953
MSG
4054

41-
ExampleHelper.run_web_console
55+
ExampleHelper.run_web_console(world)
4256
end

lib/dynflow/delayed_plan.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ def timeout
3131
error("Execution plan could not be started before set time (#{@start_before})", 'timeout')
3232
end
3333

34+
def failed_dependencies(uuids)
35+
bullets = uuids.map { |u| "- #{u}" }.join("\n")
36+
msg = "Execution plan could not be started because some of its preqrequisite execution plans failed:\n#{bullets}"
37+
error(msg, 'failed-dependency')
38+
end
39+
3440
def error(message, history_entry = nil)
3541
execution_plan.root_plan_step.state = :error
3642
execution_plan.root_plan_step.error = ::Dynflow::ExecutionPlan::Steps::Error.new(message)

lib/dynflow/director.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,15 @@ def execute
114114
plan = world.persistence.load_delayed_plan(execution_plan_id)
115115
return if plan.nil? || plan.execution_plan.state != :scheduled
116116

117-
if !plan.start_before.nil? && plan.start_before < Time.now.utc()
117+
if plan.start_before.nil?
118+
blocker_ids = world.persistence.find_execution_plan_dependencies(execution_plan_id)
119+
statuses = world.persistence.find_execution_plan_statuses({ filters: { uuid: blocker_ids } })
120+
failed = statuses.select { |_uuid, status| status[:state] == 'stopped' && status[:result] == 'error' }
121+
if failed.any?
122+
plan.failed_dependencies(failed.keys)
123+
return
124+
end
125+
elsif plan.start_before < Time.now.utc()
118126
plan.timeout
119127
return
120128
end

0 commit comments

Comments
 (0)