lib/aws/cfn/compiler.rb in aws-cfn-compiler-0.3.1 vs lib/aws/cfn/compiler.rb in aws-cfn-compiler-0.5.0
- old
+ new
@@ -1,282 +1,4 @@
require "aws/cfn/compiler/version"
+require "aws/cfn/compiler/base"
+require "aws/cfn/compiler/main"
-require 'json'
-require 'ap'
-require 'yaml'
-require 'slop'
-
-module Aws
- module Cfn
- module Compiler
- class Main
- attr_accessor :items
- attr_accessor :opts
- attr_accessor :spec
-
- def initialize
- @items = {}
- end
-
- def run
-
- @opts = Slop.parse(help: true) do
- on :d, :directory=, 'The directory to look in', as: String
- on :o, :output=, 'The JSON file to output', as: String
- on :s, :specification=, 'The specification to use when selecting components. A JSON or YAML file or JSON object', as: String
- on :f, :formatversion=, 'The AWS Template format version. Default 2010-09-09', as: String
- on :t, :description=, "The AWS Template description. Default: output basename or #{File.basename(__FILE__,'.rb')}", as: String
- end
-
- unless @opts[:directory]
- puts @opts
- exit
- end
-
- load @opts[:specification]
-
- desc = @opts[:output] ? File.basename(@opts[:output]).gsub(%r/\.(json|yaml)/, '') : File.basename(__FILE__,'.rb')
- if @spec and @spec['Description']
- desc = @spec['Description']
- end
- vers = '2010-09-09'
- if @spec and @spec['AWSTemplateFormatVersion']
- vers = @spec['AWSTemplateFormatVersion']
- end
- # noinspection RubyStringKeysInHashInspection
- compiled = {
- 'AWSTemplateFormatVersion' => (@opts[:formatversion].nil? ? vers : @opts[:formatversion]),
- 'Description' => (@opts[:description].nil? ? desc : @opts[:description]),
- 'Mappings' => @items['Mappings'],
- 'Parameters' => @items['Parameters'],
- 'Resources' => @items['Resources'],
- 'Outputs' => @items['Outputs'],
- }
-
- puts
- puts 'Validating compiled file...'
-
- validate(compiled)
-
- output_file = @opts[:output] || 'compiled.json'
- puts
- puts "Writing compiled file to #{output_file}..."
- save(compiled, output_file)
-
- puts
- puts '*** Compiled Successfully ***'
- end
-
- def validate(compiled)
- raise 'No Resources!?' unless compiled['Resources']
-
- # Mappings => Resources
- maps = find_maps(compiled) #.select { |a| !(a =~ /^AWS::/) }
- rscs = compiled['Resources'].keys
- mpgs = compiled['Mappings'].nil? ? [] : compiled['Mappings'].keys
- names = rscs+mpgs
-
- unless (maps-names).empty?
- puts '!!! Unknown mappings !!!'
- (maps-names).each do |name|
- puts " #{name}"
- end
- abort!
- end
- puts ' Mappings validated'
-
- # Parameters => Resources => Outputs
- refs = find_refs(compiled).select { |a,_| !(a =~ /^AWS::/) }
- prms = compiled['Parameters'].keys rescue []
- # outs = compiled['Outputs'].keys rescue []
- names = rscs+prms
-
- unless (refs.keys-names).empty?
- puts '!!! Unknown references !!!'
- (refs.keys-names).each do |name|
- puts " #{name} from #{refs[name][0]}:#{refs[name][1]}"
- end
- abort!
- end
- puts ' References validated'
- end
-
- def save(compiled, output_file)
- begin
- hash = {}
- compiled.each do |item,value|
- unless value.nil?
- if (not value.is_a?(Hash)) or (value.count > 0)
- hash[item] = value
- end
- end
- end
-
- File.open output_file, 'w' do |f|
- f.write JSON.pretty_generate(hash, { indent: "\t", space: ' '})
- end
- puts ' Compiled file written.'
- rescue
- puts "!!! Could not write compiled file: #{$!}"
- abort!
- end
- end
-
- def load(spec=nil)
- if spec
- begin
- abs = File.absolute_path(File.expand_path(spec))
- unless File.exists?(abs)
- abs = File.absolute_path(File.expand_path(File.join(@opts[:directory],spec)))
- end
- rescue
- # pass
- end
- if File.exists?(abs)
- raise "Unsupported specification file type: #{spec}=>#{abs}\n\tSupported types are: json,yaml,jts,yts\n" unless abs =~ /\.(json|ya?ml|jts|yts)\z/i
-
- puts "Loading specification #{abs}..."
- spec = File.read(abs)
-
- case File.extname(File.basename(abs)).downcase
- when /json|jts/
- @spec = JSON.parse(spec)
- when /yaml|yts/
- @spec = YAML.load(spec)
- else
- raise "Unsupported file type for specification: #{spec}"
- end
- # @spec = spec
- else
- raise "Unable to open specification: #{abs}"
- end
- end
- %w( Mappings Parameters Resources Outputs ).each do |dir|
- load_dir(dir,@spec)
- end
- end
-
- protected
-
- def abort!
- puts '!!! Aborting !!!'
- exit
- end
-
- def find_refs(hash, type='Reference', parent='')
- h = {}
- newparent = parent
- if hash.is_a? Hash
- hash.keys.collect do |key|
- if %w{Mappings Parameters Resources Outputs}.include? key
- type = key#.gsub(/s$/, '')
- newparent = key
- elsif %w{Mappings Parameters Resources Outputs}.include? parent
- newparent = key
- end
- if %w{Ref}.include? key
- h = { hash[key] => [type,newparent] }
- elsif 'Fn::GetAtt' == key
- h = { hash[key].first => [type,newparent] }
- # elsif %w{SourceSecurityGroupName CacheSecurityGroupNames SecurityGroupNames}.include? key
- # a = find_refs(hash[key],type,newparent)
- # h = merge(h, a, *[type,newparent])
- else
- a = find_refs(hash[key],type,newparent)
- h = merge(h, a, *[type,newparent])
- end
- end.flatten.compact.uniq
- elsif hash.is_a? Array
- a = hash.map{|i| find_refs(i,type,newparent) }
- h = merge(h, a, type, *[type,newparent])
- end
- h
- end
-
- def merge(h, a, *type)
- if a.is_a? Hash
- if a.size > 0
- h.merge! a
- end
- else
- a.flatten.compact.uniq.map { |i|
- if i.is_a? Hash
- if i.size > 0
- h.merge! i
- h
- end
- else
- h[i] = type
- end
- }
- end
- h
- end
-
- def find_maps(hash)
- if hash.is_a? Hash
- tr = []
- hash.keys.collect do |key|
- if 'Fn::FindInMap' == key
- hash[key].first
- else
- find_maps(hash[key])
- end
- end.flatten.compact.uniq
- elsif hash.is_a? Array
- hash.collect{|a| find_maps(a)}.flatten.compact.uniq
- end
- end
-
- def load_dir(dir,spec=nil)
- puts "Loading #{dir}..."
- raise "No such directory: #{@opts[:directory]}" unless File.directory?(@opts[:directory])
- set = []
- if File.directory?(File.join(@opts[:directory], dir))
- @items[dir] = {}
- set = get_file_set(dir)
- else
- if File.directory?(File.join(@opts[:directory], dir.downcase))
- @items[dir] = {}
- set = get_file_set(dir.downcase)
- end
- end
- set.collect do |filename|
- next unless filename =~ /\.(json|ya?ml)\z/i
- if spec and spec.has_key?(dir)
- base = File.basename(filename).gsub(%r/\.(rb|yaml)/, '')
- next if spec[dir].nil? # Edge case ... explicitly want NONE of these!
- next unless spec[dir].include?(base)
- puts "\tUsing #{dir}/#{base}"
- end
- begin
- puts " reading #{filename}"
- content = File.read(filename)
- next if content.size==0
-
- if filename =~ /\.json\z/i
- item = JSON.parse(content)
- elsif filename =~ /\.ya?ml\z/i
- item = YAML.load(content)
- else
- next
- end
- item.keys.each { |key| raise "Duplicate item: #{key}" if @items[dir].has_key?(key) }
- @items[dir].merge! item
- rescue
- puts " !! error: #{$!}"
- abort!
- end
- end
- if spec and spec[dir]
- raise "Suspect that a #{dir} item was missed! \nRequested: #{spec[dir]}\n Found: #{@items[dir].keys}" unless (@items[dir].keys.count == spec[dir].count)
- end
- end
-
- def get_file_set(dir)
- Dir[File.join(@opts[:directory], "#{dir}.*")] | Dir[File.join(@opts[:directory], dir.to_s, "**", "*")]
- end
-
- end
- end
- end
-end