Last active
February 26, 2020 01:22
-
-
Save mgreenly/42475f8d3f4e5ce354e188629ff09d3a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'pp' | |
Rules = [ | |
{ | |
is: { state: 'created', status: 'pending', provider: 'aws' }, | |
in: 'taggable', | |
to: { state: 'tagged' }, | |
as: { state: 'tagging' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'tagged', status: 'pending', provider: 'aws' }, | |
in: 'executable', | |
to: { state: 'executed' }, | |
as: { state: 'executing' }, | |
on: 'agent' | |
}, | |
{ | |
is: { state: 'created', status: 'pending', provider: 'dc' }, | |
in: 'excutable', | |
to: { state: 'executed' }, | |
as: { state: 'executing' }, | |
on: 'agent' | |
}, | |
{ | |
is: { state: 'executed', status: 'pending' }, | |
in: 'excutable', | |
to: { state: 'notified' }, | |
as: { state: 'notifying' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'notified', status: 'pending' }, | |
in: 'completable', | |
to: { state: 'completed' }, | |
as: { state: 'completing' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'created', status: 'canceling' }, | |
in: 'excutable', | |
to: { state: 'completed' }, | |
as: { state: 'completing' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'created', status: 'aborting' }, | |
in: 'notifiable', | |
to: { state: 'notified' }, | |
as: { state: 'notifying' }, | |
on: 'housekeeper' | |
}, | |
] | |
puts 'digraph G {' | |
clusters = [] | |
Rules.each do |rule| | |
is_state = rule[:is][:state] | |
as_state = rule[:as][:state] | |
to_state = rule[:to][:state] | |
if as_state | |
conditions = rule[:is].values.map{|c| c.to_s + '?' }.join(' & ') | |
print " #{is_state} -> #{as_state} " | |
print ' [label="' | |
print conditions | |
puts '"]' | |
clusters << { from: as_state, to: to_state, on: rule[:on] } | |
else | |
puts " #{is_state} -> #{to_state} " | |
end | |
end | |
clusters.uniq.each_with_index do |cluster, index| | |
puts " subgraph cluster#{index} {" | |
print ' label="' | |
print cluster[:on] | |
puts '"' | |
puts " #{cluster[:from]} -> #{cluster[:to]} " | |
puts " }" | |
end | |
puts '}' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// dot -Tpng graph1.gv -o ~/Downloads/graph1.png | |
digraph G { | |
created [shape=box]; | |
completed [shape=box]; | |
created -> tagged [label="pending?"]; | |
tagged -> executed [label="pending?"] | |
executed -> untagged [label="pending?"]; | |
untagged -> notified [label="pending?"]; | |
notified -> completed [label="pending?"]; | |
created -> completed [label="canceling?"]; | |
tagged -> untagged [label="canceling?"]; | |
executed -> untagged [label="canceling?"]; | |
untagged -> completed [label="canceling?"]; | |
notified -> completed [label="canceling?"]; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// dot -Tpng graph1.gv -o ~/Downloads/graph1.png | |
// dot -v -Tpdf graph1.gv -o ~/Downloads/graph1.pdf | |
// created pending aws = | |
// created pending dc = | |
// created cancelling _ = | |
// created aborting _ = | |
digraph G { | |
label = "workflow: 'Thread Dump'" | |
labelloc = top | |
labeljust = left | |
created [shape=box]; | |
succeeded [shape=box]; | |
aborted [shape=box]; | |
canceled [shape=box]; | |
edge [color="green"] | |
created -> tagging [label="'taggable'\ncreated? & pending? & aws?"]; | |
created -> executing_dc [label="'executable'\ncreated? & pending? & dc?"]; | |
subgraph cluster0 { | |
label = "housekeeper" | |
tagging -> tagged | |
} | |
tagged -> executing_aws [label="'executable'\ntagged? & pending? & aws?"] | |
subgraph cluster1 { | |
label = "agent dc" | |
executing_dc -> executed_dc | |
} | |
executed_dc -> notifying [label="'notifiable'\nexecuted? & pending? & dc?"]; | |
subgraph cluster1a { | |
label = "agent aws" | |
executing_aws -> executed_aws | |
} | |
executed_aws -> untaging [label="'untaggable'\nexecuted? & pending? & aws?"]; | |
subgraph cluster2 { | |
label = "housekeeper" | |
untaging -> untagged | |
} | |
untagged -> notifying [label="'notifiable'\nuntagged? & pending?"]; | |
subgraph cluster3 { | |
label = "housekeeper" | |
notifying -> notified | |
} | |
notified -> completing [label="'completable'\nnotified? & pending?"]; | |
subgraph cluster4 { | |
label = "housekeeper" | |
completing -> succeeded [label="pending?"]; | |
edge [color="red"] | |
completing -> canceled [label="canceling?"]; | |
completing -> aborted [label="aborting?"]; | |
} | |
edge [color="red"] | |
created -> completing [label="'completable'\ncreated? & canceling?"]; | |
tagged -> untaging [label="'untaggable'\ntagged? & canceling?"]; | |
executed_aws -> untaging [label="'untaggable'\nexecuted? & canceling? & aws?"]; | |
executed_dc -> completing [label="'completable'\nexecuted? & canceling? & dc?"]; | |
untagged -> completing [label="'completable'\nuntagged? & canceling?"]; | |
notified -> completing [label="'completable'\nnotified? & canceling?"]; | |
created -> notifying [label="'notifiable'\ncreated? & aborting?"]; | |
tagged -> untaging [label="'untaggable'\ntagged? & aborting?"]; | |
executed_aws -> untaging [label="'untaggable'\nexecuted? & aborting? & aws?"]; | |
executed_dc -> notifying [label="'notifiable'\nexecuted? & aborting? & dc?"]; | |
untagged -> notifying [label="'notifiable'\nuntagged? & aborting?"]; | |
notified -> completing [label="'completable'\nnotified? & aborting?"]; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# A workflow is a directed acyclic graph (DAG). | |
# | |
# It must always move forward toward completed, it can't loop back on it's self. | |
# | |
Transitions = [ | |
{ status: 'pending', state: 'created', action: 'tag' }, | |
{ status: 'pending', state: 'tagged', action: 'execute' }, | |
{ status: 'pending', state: 'executed', action: 'untag' }, | |
{ status: 'pending', state: 'untagged', action: 'notify' }, | |
{ status: 'pending', state: 'notified', action: 'complete' }, | |
{ status: 'cancelling', state: 'created', action: 'complete' }, | |
{ status: 'cancelling', state: 'tagged', action: 'untag' }, | |
{ status: 'cancelling', state: 'executed', action: 'untag' }, | |
{ status: 'cancelling', state: 'untagged', action: 'complete' }, | |
{ status: 'cancelling', state: 'notified', action: 'complete' }, | |
{ status: 'aborting', state: 'created', action: 'complete' }, | |
{ status: 'aborting', state: 'tagged', action: 'untag' }, | |
{ status: 'aborting', state: 'executed', action: 'untag' }, | |
{ status: 'aborting', state: 'untagged', action: 'complete' }, | |
{ status: 'aborting', state: 'notified', action: 'complete' }, | |
] | |
# make sure every possible transiton is handled exactly once | |
errors = [] | |
Transitions.map{|t| t[:status] }.uniq.each do |status| | |
Transitions.map{|t| t[:state] }.uniq.each do |state| | |
result = Transitions.select { |t| t[:status] == status && t[:state] == state } | |
errors << "Missing handler for transition from: '#{status}-#{state}'" if result.empty? | |
errors << "Duplicate handler for transition from: '#{status}-#{state}'" if result.count > 1 | |
end | |
end | |
errors.each do |error| | |
puts error | |
end | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'pp' | |
class Array | |
def intersperse(separator) | |
(inject([]) { |a,v| a+[v,separator] })[0...-1] | |
end | |
end | |
RULES = [ | |
{ | |
is: { state: 'created', status: 'pending', provider: 'aws' }, | |
in: 'taggable', | |
to: { state: 'tagged' }, | |
as: { state: 'tagging' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'tagged', status: 'pending', provider: 'aws' }, | |
in: 'executable_aws', | |
to: { state: 'executed_aws' }, | |
as: { state: 'executing_aws' }, | |
on: 'agent-aws' | |
}, | |
{ | |
is: { state: 'created', status: 'pending', provider: 'dc' }, | |
in: 'excutable_dc', | |
to: { state: 'executed_dc' }, | |
as: { state: 'executing_dc' }, | |
on: 'agent-dc' | |
}, | |
{ | |
is: { state: 'executed_dc', status: 'pending' }, | |
in: 'notifiable', | |
to: { state: 'notified' }, | |
as: { state: 'notifying' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'executed_aws', status: 'pending' }, | |
in: 'untaggable', | |
to: { state: 'untagged' }, | |
as: { state: 'untagging' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'untagged', status: 'pending' }, | |
in: 'untaggable', | |
to: { state: 'notified' }, | |
as: { state: 'notifying' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'notified', status: 'pending' }, | |
in: 'completable', | |
to: { state: 'completed' }, | |
as: { state: 'completing' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'created', status: 'canceling' }, | |
in: 'completable', | |
to: { state: 'completed' }, | |
as: { state: 'completing' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'tagged', status: 'canceling' }, | |
in: 'untaggable', | |
to: { state: 'untagged' }, | |
as: { state: 'untagging' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'executed_aws', status: 'canceling' }, | |
in: 'untaggable', | |
to: { state: 'untagged' }, | |
as: { state: 'untagging' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'executed_dc', status: 'canceling' }, | |
in: 'completable', | |
to: { state: 'completed' }, | |
as: { state: 'completing' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'notified', status: 'canceling' }, | |
in: 'completable', | |
to: { state: 'completed' }, | |
as: { state: 'completing' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'untagged', status: 'canceling' }, | |
in: 'completable', | |
to: { state: 'completed' }, | |
as: { state: 'completing' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'created', status: 'aborting' }, | |
in: 'completable', | |
to: { state: 'completed' }, | |
as: { state: 'completing' }, | |
on: 'housekeeper' | |
}, | |
{ | |
is: { state: 'tagged', status: 'aborting' }, | |
in: 'untagging', | |
to: { state: 'untagged' }, | |
as: { state: 'untagging' }, | |
on: 'housekeeper' | |
}, | |
] | |
def clusters | |
RULES.map do |rule| | |
{ from: rule[:as][:state], to: rule[:to][:state], on: rule[:on] } | |
end.uniq.each_with_index.map do |cluster, index| | |
[ | |
" subgraph cluster#{index} {", | |
" label=\"#{cluster[:on]}\"", | |
" #{cluster[:from]} -> #{cluster[:to]}", | |
" }" | |
] | |
end.intersperse('') | |
end | |
def conditions(rule) | |
["workflow = ThreadDump"].concat(rule[:is].map{ |k, v| "#{k} = #{v}" }).join('\\n & ') | |
end | |
def group(rule) | |
"'#{rule[:in]}'\\n" | |
end | |
def label(rule) | |
"[label=\"#{conditions(rule)}\"]" | |
end | |
def transition(rule) | |
a = rule[:is][:state] | |
b = rule[:as][:state] | |
"#{a} -> #{b}" | |
end | |
def rules(val) | |
RULES.select do |rule| | |
rule[:is][:status] == val | |
end.map do |rule| | |
" #{transition(rule)} #{label(rule)}" | |
end | |
end | |
def terminals | |
[ | |
'created', | |
'completed' | |
].map{|name| "#{name} [shape=box]" } | |
end | |
def dot | |
[ | |
'digraph G {', | |
terminals, | |
'edge [color="green"]', | |
clusters, | |
rules('pending'), | |
'edge [color="purple"]', | |
rules('canceling'), | |
'edge [color="red"]', | |
rules('aborting'), | |
'}' | |
].intersperse('').flatten.join("\n") | |
end | |
puts dot |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment