lib/aws/cfn/compiler/base.rb in aws-cfn-compiler-0.5.1 vs lib/aws/cfn/compiler/base.rb in aws-cfn-compiler-0.5.2
- old
+ new
@@ -1,37 +1,29 @@
require 'json'
require 'ap'
require 'yaml'
require 'slop'
+require 'aws/cfn/dsl/base'
require 'aws/cfn/dsl/template'
module Aws
module Cfn
module Compiler
- class Base
+ class Base < Aws::Cfn::Dsl::Base
attr_accessor :items
attr_accessor :opts
attr_accessor :spec
- require 'dldinternet/mixlib/logging'
- include DLDInternet::Mixlib::Logging
-
def initialize
+ super
@items = {}
- @config ||= {}
- @config[:log_opts] = lambda{|mlll| {
- :pattern => "%#{mlll}l: %m %C\n",
- :date_pattern => '%Y-%m-%d %H:%M:%S',
- }
- }
- @config[:log_level] = :step
- @logger = getLogger(@config)
end
def validate(compiled)
- raise 'No Resources!?' unless compiled['Resources']
+ abort! 'No Resources!?' unless compiled['Resources']
+ logStep 'Validating compiled file...'
# Mappings => Resources
maps = find_maps(compiled) #.select { |a| !(a =~ /^AWS::/) }
rscs = compiled['Resources'].keys
mpgs = compiled['Mappings'].nil? ? [] : compiled['Mappings'].keys
@@ -42,13 +34,13 @@
(maps-names).each do |name|
@logger.error " #{name}"
end
abort!
end
- @logger.step ' Mappings validated'
+ @logger.info ' Mappings validated'
- # Parameters => Resources => Out@logger.step
+ # Parameters => Resources => Outputs
refs = find_refs(compiled).select { |a,_| !(a =~ /^AWS::/) }
prms = compiled['Parameters'].keys rescue []
# outs = compiled['Outputs'].keys rescue []
names = rscs+prms
@@ -57,16 +49,16 @@
(refs.keys-names).each do |name|
@logger.error " #{name} from #{refs[name][0]}:#{refs[name][1]}"
end
abort!
end
- @logger.step ' References validated'
+ @logger.info ' References validated'
end
- def save(compiled, output_file)
- output_file = File.expand_path(output_file) if @config[:expandedpaths]
- @logger.step"Writing compiled file to #{output_file}..."
+ def save(output_file,compiled)
+ output_file = File.realpath(File.expand_path(output_file)) if @config[:expandedpaths]
+ logStep "Writing compiled file to #{output_file}..."
begin
hash = {}
compiled.each do |item,value|
unless value.nil?
if (not value.is_a?(Hash)) or (value.count > 0)
@@ -76,44 +68,48 @@
end
File.open output_file, 'w' do |f|
f.write JSON.pretty_generate(hash, { indent: "\t", space: ' '})
end
- @logger.step ' Compiled file written.'
+ @logger.info ' Compiled file written.'
rescue
@logger.error "!!! 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)))
+ abs = nil
+ [spec, File.join(@opts[:directory],spec)].each do |p|
+ begin
+ abs = File.realpath(File.absolute_path(File.expand_path(p)))
+ break if File.exists?(abs)
+ rescue => e
+ @logger.error e
+ # pass
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
+ logStep "Loading specification #{@opts[:expandedpaths] ? abs : spec}..."
+ unless abs =~ /\.(json|ya?ml|jts|yts)\z/i
+ abort! "Unsupported specification file type: #{spec}=>#{abs}\n\tSupported types are: json,yaml,jts,yts\n"
+ end
- @logger.step "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}"
+ abort! "Unsupported file type for specification: #{spec}"
end
- # @spec = spec
else
- raise "Unable to open specification: #{abs}"
+ abort! "Unable to open specification: #{abs}"
end
@dsl ||= Aws::Cfn::Dsl::Template.new(@opts[:directory])
%w( Mappings Parameters Resources Outputs ).each do |dir|
load_dir(dir,@spec)
end
@@ -122,13 +118,97 @@
end
end
protected
- def abort!
- @logger.fatal '!!! Aborting !!!'
- exit
+ # noinspection RubyGlobalVariableNamingConvention
+ def load_dir(dir,spec=nil)
+ logStep "Loading #{dir}..."
+
+ if spec and spec[dir]
+ raise "No such directory: #{@opts[:directory]}" unless File.directory?(@opts[:directory])
+ path = vet_path(dir)
+ @items ||= {}
+ @items[dir] ||= {}
+ set = {}
+ get = {}
+ get[path] = get_file_set([".*"], path, @config[:precedence])
+
+ item = {}
+ spec[dir].each do |rsrc|
+ @logger.info "\tUsing #{dir}/#{rsrc}"
+ set = get[path]
+ refp,sub,base,rel = map_resource_reference(rsrc)
+ unless refp.nil?
+ path = vet_path(sub ? sub : dir,refp, rel)
+ unless get[path]
+ get[path] = get_file_set([".*"], path, @config[:precedence])
+ set = get[path]
+ end
+ end
+ if set[base]
+ if item.has_key?(base)
+ @logger.error " !! error: Duplicate item: #{dir}/#{base}"
+ abort!
+ end
+
+ filename = set[base]
+ unless filename =~ /\.(ru?by?|ya?ml|js(|on))\z/i
+ @logger.info "Brick not supported/ relevant: #{filename}"
+ next
+ end
+
+ begin
+ @logger.debug " reading #{filename}"
+ content = File.read(filename)
+ next if content.size==0
+
+ if filename =~ /\.(rb|ruby)\z/i
+ $Aws_Cfn_Compiler ||= {}
+ $Aws_Cfn_Compiler[dir] ||= {}
+ $Aws_Cfn_Compiler[dir][base] ||= {
+ brick_path: @opts[:directory],
+ template: @dsl,
+ logger: @logger
+ }
+ source_file = File.expand_path(filename)
+ eval "require source_file", binding
+ unless @dsl.dict[dir.to_sym]
+ raise "Unable to expand #{filename} for #{dir}/#{base}"
+ end
+ item.merge! @dsl.dict[dir.to_sym]
+ elsif filename =~ /\.js(|on)\z/i
+ item.merge! JSON.parse(content)
+ elsif filename =~ /\.ya?ml\z/i
+ item.merge! YAML.load(content)
+ else
+ next
+ end
+
+ rescue
+ @logger.error " !! error: #{$!}"
+ abort!
+ end
+ else
+ @logger.error " !! error: #{dir}/#{base} not found!"
+ abort!
+ end
+ end
+ item.keys.each { |key|
+ if @items[dir].has_key?(key)
+ @logger.error " !! error: Duplicate item: #{dir}/#{key}"
+ abort!
+ end
+ }
+ @items[dir].merge! item
+
+ unless @items[dir].keys.count == spec[dir].count
+ @logger.error " !! error: Suspect that a #{dir} item was missed! \nRequested: #{spec[dir]}\n Found: #{@items[dir].keys}"
+ abort!
+ end
+ end
+
end
def find_refs(hash, type='Reference', parent='')
h = {}
newparent = parent
@@ -194,12 +274,11 @@
end
end
# --------------------------------------------------------------------------------
def get_file_set(want, path, exts=[])
- # Dir[File.join(@opts[:directory], "#{dir}.*")] | Dir[File.join(@opts[:directory], dir.to_s, "**", "*")]
- raise "Bad call to #{self.class.name}.getPathSet: want == nil" unless want
+ raise "Bad call to #{self.class.name}.get_file_set: want == nil" unless want
@logger.debug "Look for #{want.ai} in #{[path]} with #{exts} extensions"
if exts.nil?
exts = @config[:precedence]
end
file_regex=%r/^(\S+)\.(#{exts.join('|')})$/
@@ -223,106 +302,92 @@
@logger.trace "#{name} =~ #{regex}"
set[ext][name] = f if name.match(regex)
end
}
rescue RegexpError => e
- raise ChopError.new "The regular expression attempting to match resources in '#{path}' is incorrect! #{e.message}"
+ raise "The regular expression attempting to match resources in '#{path}' is incorrect! #{e.message}"
end
@logger.debug "getPathSet set=#{set.ai}"
res = {}
# Iterate extension sets in increasing precedence order ...
# Survivor will be the most desireable version of the item
# i.e. the .rb environment, role, data bag, etc. will be preferred over the .json version
exts.each{ |e|
h = set[e]
if h
h.each{ |n,f|
- @logger.warn "Ignoring #{File.basename(res[n])}" if res[n]
+ @logger.info "Ignoring #{File.basename(res[n])}" if res[n]
res[n] = f
}
else
- @logger.info "'#{e}' set is empty! (No #{path}/*.#{e} files found using precedence #{exts})"
+ @logger.debug "'#{e}' set is empty! (No #{path}/*.#{e} files found using precedence #{exts})"
end
}
- set = res
+ res
end
- def load_dir(dir,spec=nil)
- logStep "Loading #{dir}..."
-
- if spec and spec[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([".*"], "#{@opts[:directory]}/#{dir}", @config[:precedence])
+ def map_resource_reference(rsrc)
+ path = nil
+ sub = nil
+ ref = nil
+ rel = false
+ # noinspection RubyParenthesesAroundConditionInspection
+ if rsrc.match %r'^(\.\./.*?)::(.*)$'
+ # Relative path stack reference
+ path,sub,ref,rel = map_resource_reference(File.basename(rsrc))
+ elsif rsrc.match %r'^(\.\./[^:]*?)$'
+ # Relative path
+ path = File.dirname(rsrc)
+ sub = File.basename(path)
+ path = File.dirname(path)
+ ref = File.basename(rsrc)
+ rel = true
+ elsif rsrc.match %r'(^/.*?[^:]*?)$'
+ # Absolute path
+ path = File.dirname(rsrc)
+ sub = File.basename(path)
+ path = File.dirname(path)
+ ref = File.basename(rsrc)
+ elsif rsrc.match %r'(^/.*?)::(.*)$'
+ # Absolute path
+ path = File.dirname(rsrc)
+ ref = map_resource_reference(File.basename(rsrc))
+ elsif (match = rsrc.match %r'^(.*?)::(.*)$')
+ # Inherited stack reference
+ ref = match[2]
+ # noinspection RubyParenthesesAroundConditionInspection
+ if (subm = match[1].match(%r'^(.+?)/(.+)$'))
+ path = File.join(File.dirname(@opts[:directory]),subm[1])
+ sub = subm[2]
else
- if File.directory?(File.join(@opts[:directory], dir.downcase))
- @items[dir] = {}
- set = get_file_set(['.*'], dir.downcase, @config[:precedence])
- else
- @logger.error " !! error: Cannot load bricks from #{File.join(@opts[:directory], dir)}"
- abort!
- end
+ # sub = nil
+ path = File.join(File.dirname(@opts[:directory]),match[1])
end
+ else
+ # Otherwise it is what it seems ;)
+ ref = rsrc
+ end
+ [path,sub,ref,rel]
+ end
- item = {}
- spec[dir].each do |base|
- @logger.info "\tUsing #{dir}/#{base}"
- if set[base]
- if item.has_key?(base)
- @logger.error " !! error: Duplicate item: #{dir}/#{base}"
- abort!
- end
-
- filename = set[base]
- unless filename =~ /\.(ru?by?|ya?ml|js(|on))\z/i
- @logger.info "Brick not supported/ relevant: #{filename}"
- next
- end
-
- begin
- @logger.step " reading #{filename}"
- content = File.read(filename)
- next if content.size==0
-
- if filename =~ /\.(rb|ruby)\z/i
- eval "@dsl.#{content.gsub(%r'^\s+','')}"
- unless @dsl.dict[dir.to_sym]
- raise "Unable to expand #{filename} for #{dir}/#{base}"
- end
- item.merge! @dsl.dict[dir.to_sym]
- elsif filename =~ /\.js(|on)\z/i
- item.merge! JSON.parse(content)
- elsif filename =~ /\.ya?ml\z/i
- item.merge! YAML.load(content)
- else
- next
- end
-
- rescue
- @logger.error " !! error: #{$!}"
- abort!
- end
- else
- @logger.error " !! error: #{dir}/#{base} not found!"
- abort!
- end
+ def vet_path(dir,base=nil,rel=false)
+ if rel
+ base = File.realpath(File.expand_path(File.join(@opts[:directory], base)))
+ else
+ base = @opts[:directory] unless base
+ end
+ path = nil
+ [dir, dir.downcase].each do |d|
+ path = File.join(base, dir)
+ if File.directory?(path)
+ break
end
- item.keys.each { |key|
- if @items[dir].has_key?(key)
- @logger.error " !! error: Duplicate item: #{dir}/#{key}"
- abort!
- end
- }
- @items[dir].merge! item
-
- unless @items[dir].keys.count == spec[dir].count
- @logger.error " !! error: Suspect that a #{dir} item was missed! \nRequested: #{spec[dir]}\n Found: #{@items[dir].keys}"
- abort!
- end
end
-
+ unless File.directory?(path)
+ @logger.error " !! error: Cannot load bricks from #{path} (started with #{File.join(base, dir)}')"
+ abort!
+ end
+ path
end
end
end
end