Class: Lono::Inspector::Graph

Inherits:
Base
  • Object
show all
Defined in:
lib/lono/inspector/graph.rb

Defined Under Namespace

Classes: Node

Instance Method Summary collapse

Methods inherited from Base

#check_template_exists, #data, #generate_templates, #run

Constructor Details

#initialize(stack_name, options) ⇒ Graph

Returns a new instance of Graph.



5
6
7
8
# File 'lib/lono/inspector/graph.rb', line 5

def initialize(stack_name, options)
  super
  @nodes = [] # lookup map
end

Instance Method Details

#check_graphviz_installedObject

Check if Graphiz is installed and prints a user friendly message if it is not installed. Provide instructions if on macosx.



95
96
97
98
99
100
101
102
103
104
105
# File 'lib/lono/inspector/graph.rb', line 95

def check_graphviz_installed
  installed = system("type dot > /dev/null") # dot is a command that is part of the graphviz package
  unless installed
    puts "It appears that the Graphviz is not installed.  Please install it to generate the graph."
    if RUBY_PLATFORM =~ /darwin/
      puts "You can install Graphviz with homebrew:"
      puts "  brew install brew install graphviz"
    end
    exit 1
  end
end

#normalize_children(node_list, node) ⇒ Object

It is possible with bad CloudFormation templates that the dependency is not resolved, but we wont deal with that. Users can validate their CloudFormation template before using this tool.



55
56
57
58
59
60
61
62
# File 'lib/lono/inspector/graph.rb', line 55

def normalize_children(node_list, node)
  kids = []
  node.depends_on.each do |dependent_logical_id|
    node = node_list.find { |n| n.name == dependent_logical_id }
    kids << node
  end
  kids
end

#normalize_depends_on(resource) ⇒ Object

normalized DependOn attribute to an Array of Strings



42
43
44
45
# File 'lib/lono/inspector/graph.rb', line 42

def normalize_depends_on(resource)
  dependencies = resource["DependOn"] || []
  [dependencies].flatten
end

#normalize_resource_type(resource) ⇒ Object



47
48
49
50
# File 'lib/lono/inspector/graph.rb', line 47

def normalize_resource_type(resource)
  type = resource["Type"]
  type.sub("AWS::", "") # strip out AWS to make less verbose
end

#performObject



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/lono/inspector/graph.rb', line 10

def perform
  puts "Generating dependencies tree..."
  return if @options[:noop]

  # First loop through top level nodes and build set depends_on property
  node_list = [] # top level node list
  resources = data["Resources"]
  resources.each do |logical_id, resource|
    node = Node.new(logical_id)
    node.depends_on = normalize_depends_on(resource)
    node.resource_type = normalize_resource_type(resource)
    node_list << node
  end

  # Now that we have loop through all the top level resources once
  # we can use the depends_on attribute on each node and set the
  # children property since the identity nodes are in memory.
  node_list.each do |node|
    node.children = normalize_children(node_list, node)
  end

  # At this point we have a tree of nodes.
  if @options[:display] == "text"
    puts "CloudFormation Dependencies:"
    node_list.each { |node| print_tree(node) }
  else
    print_graph(node_list)
    puts "CloudFormation Dependencies graph generated."
  end
end


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/lono/inspector/graph.rb', line 72

def print_graph(node_list)
  check_graphviz_installed
  digraph do
    # graph_attribs << 'size="6,6"'
    node_attribs << lightblue << filled

    node_list.each do |n|
      node(n.graph_name)
      n.children.each do |child|
        edge n.graph_name, child.graph_name
      end
    end

    random = (0...8).map { (65 + rand(26)).chr }.join
    path = "/tmp/cloudformation-depends-on-#{random}"
    save path, "png"
    # Check if open command exists and use it to open the image.
    system "open #{path}.png" if system("type open > /dev/null")
  end
end


64
65
66
67
68
69
70
# File 'lib/lono/inspector/graph.rb', line 64

def print_tree(node, depth=0)
  spacing = "  " * depth
  puts "#{spacing}#{node.name}"
  node.children.each do |node|
    print_tree(node, depth+1)
  end
end