lib/sfn/command/graph.rb in sfn-3.0.26 vs lib/sfn/command/graph.rb in sfn-3.0.28

- old
+ new

@@ -4,10 +4,12 @@ module Sfn class Command # Graph command class Graph < Command + autoload :Provider, 'sfn/command/graph/provider' + include Sfn::CommandModule::Base include Sfn::CommandModule::Template include Sfn::CommandModule::Stack # Valid graph styles @@ -19,17 +21,21 @@ # Generate graph def execute! config[:print_only] = true validate_graph_style! file = load_template_file - if(file.provider == :aws) + provider = Bogo::Utility.camel(file.provider).to_sym + if(Provider.constants.include?(provider)) + graph_const = Provider.const_get(provider) + ui.debug "Loading provider graph implementation - #{graph_const}" + extend graph_const @outputs = Smash.new ui.info "Template resource graph generation - Style: #{ui.color(config[:graph_style], :bold)}" if(config[:file]) ui.puts " -> path: #{config[:file]}" end - template_dump = file.compile.dump!.to_smash + template_dump = file.compile.sparkle_dump!.to_smash run_action 'Pre-processing template for graphing' do output_discovery(template_dump, @outputs, nil, nil) ui.debug 'Output remapping results from pre-processing:' @outputs.each_pair do |o_key, o_value| ui.debug "#{o_key} -> #{o_value}" @@ -51,12 +57,15 @@ graph.save config[:output_file], config[:output_type] end nil end else + valid_providers = Provider.constants.sort.map{ |provider| + Bogo::Utility.snake(provider) + }.join('`, `') ui.error "Graphing for provider `#{file.provider}` not currently supported." - ui.error "Currently supported providers: `aws`." + ui.error "Currently supported providers: `#{valid_providers}`." end end def generate_graph(template, args={}) graph = ::Graph.new @@ -75,141 +84,10 @@ end edge_detection(template, graph, args[:name].to_s.sub('cluster_', ''), args.fetch(:resource_names, [])) graph end - def output_discovery(template, outputs, resource_name, parent_template, name='') - if(template['Resources']) - template['Resources'].keys.each do |r_name| - r_info = template['Resources'][r_name] - if(r_info['Type'] == 'AWS::CloudFormation::Stack') - output_discovery(r_info['Properties']['Stack'], outputs, r_name, template, r_name) - end - end - end - if(parent_template) - ui.debug "Pre-processing stack resource `#{resource_name}`" - substack_parameters = Smash[ - parent_template.fetch('Resources', resource_name, 'Properties', 'Parameters', {}).map do |key, value| - result = [key, value] - if(value.is_a?(Hash)) - v_key = value.keys.first - v_value = value.values.first - if(v_key == 'Fn::GetAtt' && parent_template.fetch('Resources', {}).keys.include?(v_value.first) && v_value.last.start_with?('Outputs.')) - output_key = v_value.first + '__' + v_value.last.split('.', 2).last - ui.debug "Output key for check: #{output_key}" - if(outputs.key?(output_key)) - new_value = outputs[output_key] - result = [key, new_value] - ui.debug "Parameter for output swap `#{key}`: #{value} -> #{new_value}" - end - end - end - result - end - ] - - ui.debug "Generated internal parameters for `#{resource_name}`: #{substack_parameters}" - - processor = GraphProcessor.new({}, - :parameters => substack_parameters - ) - template['Resources'] = processor.dereference_processor( - template['Resources'], ['Ref'] - ) - template['Outputs'] = processor.dereference_processor( - template['Outputs'], ['Ref'] - ) - rename_processor = GraphProcessor.new({}, - :parameters => Smash[ - template.fetch('Resources', {}).keys.map do |r_key| - [r_key, {'Ref' => [name, r_key].join}] - end - ] - ) - derefed_outs = rename_processor.dereference_processor( - template.fetch('Outputs', {}) - ) || {} - - derefed_outs.each do |o_name, o_data| - o_key = [name, o_name].join('__') - outputs[o_key] = o_data['Value'] - end - end - outputs.dup.each do |key, value| - if(value.is_a?(Hash)) - v_key = value.keys.first - v_value = value.values.first - if(v_key == 'Fn::GetAtt' && v_value.last.start_with?('Outputs.')) - output_key = v_value.first << '__' << v_value.last.split('.', 2).last - if(outputs.key?(output_key)) - outputs[key] = outputs[output_key] - end - end - end - end - end - - def edge_detection(template, graph, name = '', resource_names = []) - resources = template.fetch('Resources', {}) - node_prefix = name - resources.each do |resource_name, resource_data| - node_name = [node_prefix, resource_name].join - if(resource_data['Type'] == 'AWS::CloudFormation::Stack') - graph.subgraph << generate_graph( - resource_data['Properties'].delete('Stack'), - :name => resource_name, - :type => resource_data['Type'], - :resource_names => resource_names - ) - next - else - graph.node(node_name).attributes << graph.fillcolor(colorize(node_prefix.empty? ? config[:file] : node_prefix).inspect) - graph.box3d << graph.node(node_name) - end - graph.filled << graph.node(node_name) - graph.node(node_name).label "#{resource_name}\n<#{resource_data['Type']}>\n#{name}" - resource_dependencies(resource_data, resource_names + resources.keys).each do |dep_name| - if(resources.keys.include?(dep_name)) - dep_name = [node_prefix, dep_name].join - end - if(config[:graph_style] == 'creation') - @root_graph.edge(dep_name, node_name) - else - @root_graph.edge(node_name, dep_name) - end - end - end - resource_names.concat resources.keys.map{|r_name| [node_prefix, r_name].join} - end - - def resource_dependencies(data, names) - case data - when Hash - data.map do |key, value| - if(key == 'Ref' && names.include?(value)) - value - elsif(key == 'DependsOn') - [value].flatten.compact.find_all do |dependson_name| - names.include?(dependson_name) - end - elsif(key == 'Fn::GetAtt' && names.include?(res = [value].flatten.compact.first)) - res - else - resource_dependencies(key, names) + - resource_dependencies(value, names) - end - end.flatten.compact.uniq - when Array - data.map do |item| - resource_dependencies(item, names) - end.flatten.compact.uniq - else - [] - end - end - def colorize(string) hash = string.chars.inject(0) do |memo, chr| if(memo + chr.ord > 127) (memo - chr.ord).abs else @@ -236,41 +114,9 @@ end config[:graph_style] = config[:graph_style].to_s unless(GRAPH_STYLES.include?(config[:graph_style])) raise ArgumentError.new "Invalid graph style provided `#{config[:graph_style]}`. Valid: `#{GRAPH_STYLES.join('`, `')}`" end - end - - class GraphProcessor < SparkleFormation::Translation - MAP = {} - REF_MAPPING = {} - FN_MAPPING = {} - - attr_accessor :name - - def initialize(template, args={}) - super - @name = args[:name] - end - - def apply_function(hash, funcs=[]) - k, v = hash.first - if(hash.size == 1) - case k - when 'Ref' - parameters.key?(v) ? parameters[v] : hash - when 'Fn::Join' - v.last - when 'Fn::Select' - v.last[v.first.to_i] - else - hash - end - else - hash - end - end - end end end end