Class: Lono::Inspector::Graph

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

Defined Under Namespace

Classes: Node

Instance Method Summary collapse

Methods inherited from Base

#all_templates, #generate, #generate_templates, #run

Methods inherited from AbstractBase

#reinitialize, #template_path

Methods included from Blueprint::Root

#find_blueprint_root, #set_blueprint_root

Constructor Details

#initialize(options = {}) ⇒ Graph

Returns a new instance of Graph.



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

def initialize(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.



99
100
101
102
103
104
105
106
107
108
109
# File 'lib/lono/inspector/graph.rb', line 99

def check_graphviz_installed
  installed = system("type dot > /dev/null 2>&1") # 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 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.



59
60
61
62
63
64
65
66
# File 'lib/lono/inspector/graph.rb', line 59

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



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

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

#normalize_resource_type(resource) ⇒ Object



51
52
53
54
# File 'lib/lono/inspector/graph.rb', line 51

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

#perform(template) ⇒ Object



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
40
41
42
43
# File 'lib/lono/inspector/graph.rb', line 11

def perform(template)
  # little dirty but @template is used in data method so we dont have to pass it to the data method
  @template = template

  puts "Generating dependencies tree for template #{@template}..."
  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


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/lono/inspector/graph.rb', line 76

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 2>&1")
  end
end


68
69
70
71
72
73
74
# File 'lib/lono/inspector/graph.rb', line 68

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