aws-cfn-compiler 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MWFiNWI1NTIzY2VhNjQ4NjM3ZDgzOWIwNTg4MzczZDI3OWFlNzFlMw==
4
+ NTljZDM3YzBkMWZhYzVhM2RhMDBkMjA4MjQ3MTljZjdlNjczZDUyYg==
5
5
  data.tar.gz: !binary |-
6
- YjIwMjMwNzlkY2Q2YzliM2U3ZWM0YjE2ODgyZDcyMjUzMDM2MzJmMw==
6
+ YWIzN2E1YTE3YTk3OWVlNDVlYTYyZGQzNWZiYjc5N2QzNDY1NjU5YQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NDhjYjgyZjZlNzM4ZWExMTA0Y2QxNGIzMWM3ZTNmZWQ2ZWUxNDdlMDlhNzA3
10
- ZjI4Y2QxNDgwNDYwZjNiNTRjODY5ZmE3ZjBlYmJjYmYxMzhkYmJjN2YzMjNh
11
- YTMwYjQ2Y2U4NDE2N2Q0NTJlZWZiNDVlZjg5MjFhMDRhMWRjNzk=
9
+ Y2RhZTI5ODI1Y2MzODU2OTQzYjI3ODFlOGEzMDU4MjM2ZTJiMDU5MWIyODg3
10
+ YWJmOGQwYTFlNDNiMWQ5Y2FjMmJhN2JiMzBlMTg1YTE3ZGQ5NDk3N2FiZTU5
11
+ YTYyMjU0ZjU2YWY5MWUxYTMxOThmYzc5NjU3MDlhNjI0ZTQ2MjQ=
12
12
  data.tar.gz: !binary |-
13
- NGYyNzU2MWE2NTZhOGFkZTRjYTgxZTMzNzVjOGVmZDJjZjQzOTQxNzg3M2Vh
14
- ODY1YzQ1YWI5OWMxOGU5OGY5MzEyMTg0YjY2NzdiODcyYzUzNTk0ODVjNjQw
15
- MjBhMmQzMmZmYzM3MTIxNTc1MzFlZTkyZWUxNGUzYWYxNTFkZDg=
13
+ NDdkYjUyOTA4Y2U1MDM0YjZlZmM5YmZhN2E2NzIyMWZiZjQ1ODQwNWRmOGM2
14
+ MTZjMzExOWVhNWE0MjM5YjQyZTc3ZGFmZDk1OTY5MTFmN2RlYmVmZjljMThj
15
+ M2Q4YzIyNzJkNGFlZDFiMDFmYWRkOTdlZjYwNzcxNDhiN2ZhOWI=
data/Gemfile CHANGED
@@ -9,9 +9,9 @@ gem 'dldinternet-mixlib-logging', :path => '../dldinternet-mixlib-logging', :gro
9
9
  gem 'dldinternet-mixlib-cli', :path => '../dldinternet-mixlib-cli', :group => :development
10
10
  gem 'aws-cfn-dsl', :path => '../aws-cfn-dsl', :group => :development
11
11
  gem 'aws-cfn-decompiler', :path => '../aws-cfn-decompiler', :group => :development
12
- gem 'aws-cfn-compiler', :path => '../aws-cfn-compiler', :group => :development
13
12
  gem 'aws-cfn-yats', :path => '../aws-cfn-yats', :group => :development
14
13
  gem 'aws-cfn-stacker', :path => '../aws-cfn-stacker', :group => :development
15
14
 
15
+ gem 'json_pure', :group => :development
16
16
  gem 'bundler', "~> 1.6", :group => :development
17
17
  gem 'rake', :group => :development
@@ -18,13 +18,11 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "awesome_print", '~> 1.2', '>= 1.2.0'
22
- spec.add_dependency "psych"
23
- spec.add_dependency "json"
24
- spec.add_dependency "slop"
25
- spec.add_dependency 'colorize'
26
- spec.add_dependency 'inifile'
27
- spec.add_dependency 'aws-cfn-dsl', '>= 0.2.0', '~> 0.2'
21
+ spec.add_dependency 'awesome_print', '~> 1.2', '>= 1.2.0'
22
+ spec.add_dependency 'psych'
23
+ spec.add_dependency 'json'
24
+ spec.add_dependency 'slop'
25
+ spec.add_dependency 'aws-cfn-dsl', '>= 0.7.0', '~> 0.7'
28
26
  spec.add_dependency 'dldinternet-mixlib-cli', ">= 0.1.0", '~> 0.1'
29
27
  spec.add_dependency 'dldinternet-mixlib-logging', '>= 0.1.6', '~> 0.1'
30
28
 
data/aws-cfn-compiler.iml CHANGED
@@ -52,6 +52,7 @@
52
52
  <orderEntry type="library" scope="PROVIDED" name="detabulator (v0.1.0, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
53
53
  <orderEntry type="library" scope="PROVIDED" name="inifile (v2.0.2, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
54
54
  <orderEntry type="library" scope="PROVIDED" name="json (v1.8.1, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
55
+ <orderEntry type="library" scope="PROVIDED" name="json_pure (v1.8.1, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
55
56
  <orderEntry type="library" scope="PROVIDED" name="little-plugger (v1.1.3, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
56
57
  <orderEntry type="library" scope="PROVIDED" name="logging (v1.8.2, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
57
58
  <orderEntry type="library" scope="PROVIDED" name="mixlib-cli (v1.5.0, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
@@ -21,7 +21,7 @@ module Aws
21
21
 
22
22
  def validate(compiled)
23
23
  abort! 'No Resources!?' unless compiled['Resources']
24
- logStep 'Validating compiled file...'
24
+ logStep 'Validating template'
25
25
 
26
26
  # Mappings => Resources
27
27
  maps = find_maps(compiled) #.select { |a| !(a =~ /^AWS::/) }
@@ -55,8 +55,12 @@ module Aws
55
55
  end
56
56
 
57
57
  def save_template(output_file,compiled)
58
- output_file = File.realpath(File.expand_path(output_file)) if @config[:expandedpaths]
59
- logStep "Writing compiled file to #{output_file}..."
58
+ file = output_file
59
+ file = File.expand_path(output_file) if @config[:expandedpaths]
60
+ if File.exists?(file)
61
+ file = File.realpath(file)
62
+ end
63
+ logStep "Writing compiled file to #{file}..."
60
64
  begin
61
65
  hash = {}
62
66
  compiled.each do |item,value|
@@ -67,13 +71,15 @@ module Aws
67
71
  end
68
72
  end
69
73
 
70
- File.open output_file, 'w' do |f|
71
- f.write JSON.pretty_generate(hash, { indent: "\t", space: ' '})
72
- end
74
+ dir = File.dirname(output_file)
75
+ file = File.basename(output_file)
76
+ sect = dir == '.' ? Dir.pwd : dir
77
+ sect = File.basename(sect) unless @config[:expandedpaths]
78
+ save_section(dir, file, @config[:format], sect, hash, '', 'template')
79
+
73
80
  @logger.info ' Compiled file written.'
74
81
  rescue
75
- @logger.error "!!! Could not write compiled file: #{$!}"
76
- abort!
82
+ abort! "!!! Could not write compiled file: #{$!}"
77
83
  end
78
84
  end
79
85
 
@@ -107,295 +113,29 @@ module Aws
107
113
  abort! "Unsupported file type for specification: #{spec}"
108
114
  end
109
115
  else
110
- abort! "Unable to open specification"+ (abs.nil? ? " or {,#{@config[:directory]}/}#{spec} not found" : ": #{abs}")
111
- end
112
- @dsl ||= Aws::Cfn::Dsl::Template.new(@config[:directory])
113
- %w( Mappings Parameters Resources Outputs ).each do |dir|
114
- load_dir(dir,@spec)
116
+ abort! 'Unable to open specification'+ (abs.nil? ? " or {,#{@config[:directory]}/}#{spec} not found" : ": #{abs}")
115
117
  end
116
118
  else
117
- raise "No specification provided"
119
+ abort! 'No specification provided'
118
120
  end
119
121
  end
120
122
 
121
123
  protected
122
124
 
123
- # noinspection RubyGlobalVariableNamingConvention
124
- def load_dir(dir,spec=nil)
125
- logStep "Loading #{dir}..."
126
-
127
- if spec and spec[dir]
128
- raise "No such directory: #{@config[:directory]}" unless File.directory?(@config[:directory])
129
- path = vet_path(dir)
130
- @items ||= {}
131
- @items[dir] ||= {}
132
- set = {}
133
- get = {}
134
- get[path] = get_file_set([".*"], path, @config[:precedence])
135
-
136
- item = {}
137
- spec[dir].each do |rsrc|
138
- @logger.info "\tUsing #{dir}::#{rsrc}"
139
- set = get[path]
140
- refp,sub,base,rel = map_resource_reference(rsrc)
141
- unless refp.nil?
142
- path = vet_path(sub ? sub : dir,refp, rel)
143
- unless get[path]
144
- get[path] = get_file_set([".*"], path, @config[:precedence])
145
- set = get[path]
146
- end
147
- end
148
- if set[base]
149
- if item.has_key?(base)
150
- @logger.error " !! error: Duplicate item: #{dir}/#{base}"
151
- abort!
152
- end
153
-
154
- filename = set[base]
155
- unless filename =~ /\.(ru?by?|ya?ml|js(|on))\z/i
156
- @logger.info "Brick not supported/ relevant: #{filename}"
157
- next
158
- end
159
-
160
- begin
161
- @logger.debug " reading #{filename}"
162
- content = File.read(filename)
163
- next if content.size==0
164
-
165
- if filename =~ /\.(rb|ruby)\z/i
166
- compile_rb_file(base, dir, filename)
167
- item.merge! @dsl.dict[dir.to_sym]
168
- elsif filename =~ /\.js(|on)\z/i
169
- item.merge! JSON.parse(content)
170
- elsif filename =~ /\.ya?ml\z/i
171
- item.merge! YAML.load(content)
172
- else
173
- next
174
- end
175
-
176
- rescue
177
- @logger.error " !! error: #{$!}"
178
- abort!
179
- end
180
- else
181
- @logger.error " !! error: #{dir}/#{base} not found!"
182
- abort!
183
- end
184
- end
185
- item.keys.each { |key|
186
- if @items[dir].has_key?(key)
187
- @logger.error " !! error: Duplicate item: #{dir}/#{key}"
188
- abort!
189
- end
190
- }
191
- @items[dir].merge! item
192
-
193
- unless @items[dir].keys.count == spec[dir].count
194
- @logger.error " !! error: Suspect that a #{dir} item was missed! \nRequested: #{spec[dir]}\n Found: #{@items[dir].keys}"
195
- abort!
196
- end
197
- end
198
-
199
- end
200
-
201
- def compile_rb_file(base, dir, filename)
202
- Aws::Cfn::Compiler.binding ||= {}
203
- Aws::Cfn::Compiler.binding[dir] ||= {}
204
- Aws::Cfn::Compiler.binding[dir][base] ||= {
205
- brick_path: @config[:directory],
206
- template: @dsl,
207
- logger: @logger
208
- }
209
- source_file = File.expand_path(filename)
210
- # source = IO.read(source_file)
211
- eval "require source_file", binding
212
- unless @dsl.dict[dir.to_sym]
213
- abort! "Unable to compile/expand #{filename} for #{dir}/#{base}"
214
- end
215
- end
216
-
217
- def find_refs(hash, type='Reference', parent='')
218
- h = {}
219
- newparent = parent
220
- if hash.is_a? Hash
221
- hash.keys.collect do |key|
222
- if %w{Mappings Parameters Resources Outputs}.include? key
223
- type = key#.gsub(/s$/, '')
224
- newparent = key
225
- elsif %w{Mappings Parameters Resources Outputs}.include? parent
226
- newparent = key
227
- end
228
- if %w{Ref}.include? key
229
- h = { hash[key] => [type,newparent] }
230
- elsif 'Fn::GetAtt' == key
231
- h = { hash[key].first => [type,newparent] }
232
- # elsif %w{SourceSecurityGroupName CacheSecurityGroupNames SecurityGroupNames}.include? key
233
- # a = find_refs(hash[key],type,newparent)
234
- # h = merge(h, a, *[type,newparent])
235
- else
236
- a = find_refs(hash[key],type,newparent)
237
- h = merge(h, a, *[type,newparent])
238
- end
239
- end.flatten.compact.uniq
240
- elsif hash.is_a? Array
241
- a = hash.map{|i| find_refs(i,type,newparent) }
242
- h = merge(h, a, type, *[type,newparent])
243
- end
244
- h
245
- end
246
-
247
- def merge(h, a, *type)
248
- if a.is_a? Hash
249
- if a.size > 0
250
- h.merge! a
251
- end
252
- else
253
- a.flatten.compact.uniq.map { |i|
254
- if i.is_a? Hash
255
- if i.size > 0
256
- h.merge! i
257
- h
258
- end
259
- else
260
- h[i] = type
261
- end
262
- }
263
- end
264
- h
265
- end
125
+ require 'aws/cfn/compiler/mixins/options'
126
+ include Aws::Cfn::Compiler::Options
266
127
 
267
- def find_maps(hash)
268
- if hash.is_a? Hash
269
- tr = []
270
- hash.keys.collect do |key|
271
- if 'Fn::FindInMap' == key
272
- hash[key].first
273
- else
274
- find_maps(hash[key])
275
- end
276
- end.flatten.compact.uniq
277
- elsif hash.is_a? Array
278
- hash.collect{|a| find_maps(a)}.flatten.compact.uniq
279
- end
280
- end
128
+ require 'aws/cfn/compiler/mixins/load'
129
+ include Aws::Cfn::Compiler::Load
281
130
 
282
- # --------------------------------------------------------------------------------
283
- def get_file_set(want, path, exts=[])
284
- raise "Bad call to #{self.class.name}.get_file_set: want == nil" unless want
285
- @logger.debug "Look for #{want.ai} in #{[path]} with #{exts} extensions"
286
- if exts.nil?
287
- exts = @config[:precedence]
288
- end
289
- file_regex=%r/^(\S+)\.(#{exts.join('|')})$/
290
- if exts.empty?
291
- file_regex=%r/^(\S+)()$/
292
- exts=['']
293
- end
294
- regex = "^(#{want.join('|')})$"
295
- set = {}
296
- abs = File.expand_path(path)
297
- abs = path unless @config[:expandedpaths]
298
- raise "Oops! Does '#{path}' directory exist?" unless File.directory?(abs)
299
- #abs = File.realpath(abs)
300
- begin
301
- Dir.glob("#{abs}/*").each{ |f|
302
- match = File.basename(f).match(file_regex)
303
- if match
304
- name = match[1]
305
- ext = match[2]
306
- set[ext] = {} unless set[ext]
307
- @logger.trace "#{name} =~ #{regex}"
308
- set[ext][name] = f if name.match(regex)
309
- end
310
- }
311
- rescue RegexpError => e
312
- raise "The regular expression attempting to match resources in '#{path}' is incorrect! #{e.message}"
313
- end
314
- @logger.debug "getPathSet set=#{set.ai}"
315
- res = {}
316
- # Iterate extension sets in increasing precedence order ...
317
- # Survivor will be the most desireable version of the item
318
- # i.e. the .rb environment, role, data bag, etc. will be preferred over the .json version
319
- exts.each{ |e|
320
- h = set[e]
321
- if h
322
- h.each{ |n,f|
323
- @logger.info "Ignoring #{File.basename(res[n])}" if res[n]
324
- res[n] = f
325
- }
326
- else
327
- @logger.debug "'#{e}' set is empty! (No #{path}/*.#{e} files found using precedence #{exts})"
328
- end
329
- }
330
- res
331
- end
131
+ require 'aws/cfn/compiler/mixins/parse'
132
+ include Aws::Cfn::Compiler::Parse
332
133
 
333
- def map_resource_reference(rsrc)
334
- path = nil
335
- sub = nil
336
- ref = nil
337
- rel = false
338
- # noinspection RubyParenthesesAroundConditionInspection
339
- if rsrc.match %r'^(\.\./.*?)::(.*)$'
340
- # Relative path stack reference
341
- path,sub,ref,rel = map_resource_reference(File.basename(rsrc))
342
- elsif rsrc.match %r'^(~/.*?)$'
343
- # Relative to HOME
344
- path,sub,ref,rel = map_resource_reference(File.expand_path(rsrc))
345
- elsif rsrc.match %r'^(\.\./[^:]*?)$'
346
- # Relative path
347
- path = File.dirname(rsrc)
348
- sub = File.basename(path)
349
- path = File.dirname(path)
350
- ref = File.basename(rsrc)
351
- rel = true
352
- elsif rsrc.match %r'(^/.*?)::(.*)$'
353
- # Absolute path
354
- _,sub,ref,rel = map_resource_reference(File.basename(rsrc))
355
- path = File.realpath(File.join(File.dirname(rsrc),_))
356
- elsif rsrc.match %r'(^/.*?[^:]*?)$'
357
- # Absolute path
358
- path = File.dirname(rsrc)
359
- sub = File.basename(path)
360
- path = File.dirname(path)
361
- ref = File.basename(rsrc)
362
- elsif (match = rsrc.match %r'^(.*?)::(.*)$')
363
- # Inherited stack reference
364
- ref = match[2]
365
- # noinspection RubyParenthesesAroundConditionInspection
366
- if (subm = match[1].match(%r'^(.+?)/(.+)$'))
367
- path = File.join(File.dirname(@config[:directory]),subm[1])
368
- sub = subm[2]
369
- else
370
- # sub = nil
371
- path = File.join(File.dirname(@config[:directory]),match[1])
372
- end
373
- else
374
- # Otherwise it is what it seems ;)
375
- ref = rsrc
376
- end
377
- [path,sub,ref,rel]
378
- end
134
+ require 'aws/cfn/compiler/mixins/compile'
135
+ include Aws::Cfn::Compiler::Compile
379
136
 
380
- def vet_path(dir,base=nil,rel=false)
381
- if rel
382
- base = File.realpath(File.expand_path(File.join(@config[:directory], base)))
383
- else
384
- base = @config[:directory] unless base
385
- end
386
- path = nil
387
- [dir, dir.downcase].each do |d|
388
- path = File.join(base, dir)
389
- if File.directory?(path)
390
- break
391
- end
392
- end
393
- unless File.directory?(path)
394
- @logger.error " !! error: Cannot load bricks from #{path} (started with #{File.join(base, dir)}')"
395
- abort!
396
- end
397
- path
398
- end
137
+ require 'aws/cfn/compiler/mixins/save'
138
+ include Aws::Cfn::Compiler::Save
399
139
 
400
140
  end
401
141
  end
@@ -1,8 +1,4 @@
1
1
 
2
- require 'json'
3
- require 'ap'
4
- require 'yaml'
5
- require 'slop'
6
2
 
7
3
  module Aws
8
4
  module Cfn
@@ -11,60 +7,19 @@ module Aws
11
7
 
12
8
  def run
13
9
 
14
- @opts = Slop.parse(help: true) do
15
- # command File.basename(__FILE__,'.rb')
16
- on :d, :directory=, 'The directory containing template sections', as: String
17
- on :o, :output=, 'The JSON file to output', as: String
18
- on :s, :specification=, 'The specification to use when selecting components. A JSON or YAML file or JSON object', as: String
19
- on :f, :formatversion=, 'The AWS Template format version. ', { as: String,
20
- optional_argument: true,
21
- default: '2010-09-09' }
22
- on :p, :precedence=, 'The precedence of template component types. Default: rb,ruby,yaml,yml,json,js', { as: String,
23
- default: 'rb,ruby,yaml,yml,json,js' }
24
- on :t, :description=, "The AWS Template description. Default: output basename or #{File.basename(__FILE__,'.rb')}", { as: String,
25
- default: File.basename(__FILE__,'.rb') }
26
- on :x, :expandedpaths, 'Show expanded paths in output', { as: String,
27
- optional_argument: true,
28
- default: 'off',
29
- match: %r/0|1|yes|no|on|off|enable|disable|set|unset|true|false|raw/i }
30
- on :O, :overwrite, 'Overwrite existing generated source files. (HINT: Think twice ...)', { as: String,
31
- optional_argument: true,
32
- default: 'off',
33
- match: %r/0|1|yes|no|on|off|enable|disable|set|unset|true|false|raw/i }
34
- end
10
+ parse_options
35
11
 
36
- unless @opts[:directory]
37
- puts @opts
38
- exit
39
- end
12
+ set_config_options
40
13
 
41
- @config[:precedence] = @opts[:precedence].split(%r',+\s*').reverse
42
- @config[:expandedpaths] = @opts[:expandedpaths].downcase.match %r'^(1|true|on|yes|enable|set)$'
43
- @config[:directory] = @opts[:directory]
14
+ load_spec @config[:specification]
44
15
 
45
- load_spec @opts[:specification]
16
+ parse
46
17
 
47
- desc = @opts[:output] ? File.basename(@opts[:output]).gsub(%r/\.(json|yaml)/, '') : File.basename(__FILE__,'.rb')
48
- if @spec and @spec['Description']
49
- desc = @spec['Description']
50
- end
51
- vers = '2010-09-09'
52
- if @spec and @spec['AWSTemplateFormatVersion']
53
- vers = @spec['AWSTemplateFormatVersion']
54
- end
55
- # noinspection RubyStringKeysInHashInspection
56
- compiled = {
57
- 'AWSTemplateFormatVersion' => (@opts[:formatversion].nil? ? vers : @opts[:formatversion]),
58
- 'Description' => (@opts[:description].nil? ? desc : @opts[:description]),
59
- 'Mappings' => @items['Mappings'],
60
- 'Parameters' => @items['Parameters'],
61
- 'Resources' => @items['Resources'],
62
- 'Outputs' => @items['Outputs'],
63
- }
18
+ compiled = compile
64
19
 
65
20
  validate(compiled)
66
21
 
67
- output_file = @opts[:output] || 'compiled.json'
22
+ output_file = @config[:template] || 'compiled.json'
68
23
  save_template(output_file,compiled)
69
24
 
70
25
  @logger.step '*** Compiled Successfully ***'
@@ -0,0 +1,154 @@
1
+ module Aws
2
+ module Cfn
3
+ module Compiler
4
+ module Compile
5
+
6
+ def compile
7
+ desc = if @config[:description]
8
+ @config[:description]
9
+ elsif @spec and @spec['Description']
10
+ @spec['Description']
11
+ elsif @config[:template]
12
+ File.basename(@config[:template]).gsub(%r/\..*$/, '')
13
+ else
14
+ 'compiled template'
15
+ end
16
+ vers = if @config[:formatversion]
17
+ @config[:formatversion]
18
+ else
19
+ if @spec and @spec['AWSTemplateFormatVersion']
20
+ @spec['AWSTemplateFormatVersion']
21
+ else
22
+ '2010-09-09'
23
+ end
24
+ end
25
+ # [2014-06-29 Christo] IIRC it is important that the section names be strings instead of symbols ...
26
+ # noinspection RubyStringKeysInHashInspection
27
+ compiled =
28
+ {
29
+ 'AWSTemplateFormatVersion' => vers,
30
+ 'Description' => desc,
31
+ 'Mappings' => @items['Mappings'],
32
+ 'Parameters' => @items['Parameters'],
33
+ 'Resources' => @items['Resources'],
34
+ 'Outputs' => @items['Outputs'],
35
+ }
36
+ end
37
+
38
+ def find_refs(hash, type='Reference', parent='')
39
+ h = {}
40
+ newparent = parent
41
+ if hash.is_a? Hash
42
+ hash.keys.collect do |key|
43
+ if %w{Mappings Parameters Resources Outputs}.include? key
44
+ type = key#.gsub(/s$/, '')
45
+ newparent = key
46
+ elsif %w{Mappings Parameters Resources Outputs}.include? parent
47
+ newparent = key
48
+ end
49
+ if %w{Ref}.include? key
50
+ h = { hash[key] => [type,newparent] }
51
+ elsif 'Fn::GetAtt' == key
52
+ h = { hash[key].first => [type,newparent] }
53
+ # elsif %w{SourceSecurityGroupName CacheSecurityGroupNames SecurityGroupNames}.include? key
54
+ # a = find_refs(hash[key],type,newparent)
55
+ # h = merge(h, a, *[type,newparent])
56
+ else
57
+ a = find_refs(hash[key],type,newparent)
58
+ h = merge(h, a, *[type,newparent])
59
+ end
60
+ end.flatten.compact.uniq
61
+ elsif hash.is_a? Array
62
+ a = hash.map{|i| find_refs(i,type,newparent) }
63
+ h = merge(h, a, type, *[type,newparent])
64
+ end
65
+ h
66
+ end
67
+
68
+ def merge(h, a, *type)
69
+ if a.is_a? Hash
70
+ if a.size > 0
71
+ h.merge! a
72
+ end
73
+ else
74
+ a.flatten.compact.uniq.map { |i|
75
+ if i.is_a? Hash
76
+ if i.size > 0
77
+ h.merge! i
78
+ h
79
+ end
80
+ else
81
+ h[i] = type
82
+ end
83
+ }
84
+ end
85
+ h
86
+ end
87
+
88
+ def find_maps(hash)
89
+ if hash.is_a? Hash
90
+ tr = []
91
+ hash.keys.collect do |key|
92
+ if 'Fn::FindInMap' == key
93
+ hash[key].first
94
+ else
95
+ find_maps(hash[key])
96
+ end
97
+ end.flatten.compact.uniq
98
+ elsif hash.is_a? Array
99
+ hash.collect{|a| find_maps(a)}.flatten.compact.uniq
100
+ end
101
+ end
102
+
103
+ def map_resource_reference(rsrc)
104
+ path = nil
105
+ sub = nil
106
+ ref = nil
107
+ rel = false
108
+ # noinspection RubyParenthesesAroundConditionInspection
109
+ if rsrc.match %r'^(\.\./.*?)::(.*)$'
110
+ # Relative path stack reference
111
+ path,sub,ref,rel = map_resource_reference(File.basename(rsrc))
112
+ elsif rsrc.match %r'^(~/.*?)$'
113
+ # Relative to HOME
114
+ path,sub,ref,rel = map_resource_reference(File.expand_path(rsrc))
115
+ elsif rsrc.match %r'^(\.\./[^:]*?)$'
116
+ # Relative path
117
+ path = File.dirname(rsrc)
118
+ sub = File.basename(path)
119
+ path = File.dirname(path)
120
+ ref = File.basename(rsrc)
121
+ rel = true
122
+ elsif rsrc.match %r'(^/.*?)::(.*)$'
123
+ # Absolute path
124
+ _,sub,ref,rel = map_resource_reference(File.basename(rsrc))
125
+ path = File.realpath(File.join(File.dirname(rsrc),_))
126
+ elsif rsrc.match %r'(^/.*?[^:]*?)$'
127
+ # Absolute path
128
+ path = File.dirname(rsrc)
129
+ sub = File.basename(path)
130
+ path = File.dirname(path)
131
+ ref = File.basename(rsrc)
132
+ elsif (match = rsrc.match %r'^(.*?)::(.*)$')
133
+ # Inherited stack reference
134
+ ref = match[2]
135
+ # noinspection RubyParenthesesAroundConditionInspection
136
+ if (subm = match[1].match(%r'^(.+?)/(.+)$'))
137
+ path = File.join(File.dirname(@config[:directory]),subm[1])
138
+ sub = subm[2]
139
+ else
140
+ # sub = nil
141
+ path = File.join(File.dirname(@config[:directory]),match[1])
142
+ end
143
+ else
144
+ # Otherwise it is what it seems ;)
145
+ ref = rsrc
146
+ end
147
+ [path,sub,ref,rel]
148
+ end
149
+
150
+
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,81 @@
1
+ module Aws
2
+ module Cfn
3
+ module Compiler
4
+ module Load
5
+
6
+ # --------------------------------------------------------------------------------
7
+ def get_file_set(want, path, exts=[])
8
+ raise "Bad call to #{self.class.name}.get_file_set: want == nil" unless want
9
+ @logger.debug "Look for #{want.ai} in #{[path]} with #{exts} extensions"
10
+ if exts.nil?
11
+ exts = @config[:precedence]
12
+ end
13
+ file_regex=%r/^(\S+)\.(#{exts.join('|')})$/
14
+ if exts.empty?
15
+ file_regex=%r/^(\S+)()$/
16
+ exts=['']
17
+ end
18
+ regex = "^(#{want.join('|')})$"
19
+ set = {}
20
+ abs = File.expand_path(path)
21
+ abs = path unless @config[:expandedpaths]
22
+ raise "Oops! Does '#{path}' directory exist?" unless File.directory?(abs)
23
+ #abs = File.realpath(abs)
24
+ begin
25
+ Dir.glob("#{abs}/*").each{ |f|
26
+ match = File.basename(f).match(file_regex)
27
+ if match
28
+ name = match[1]
29
+ ext = match[2]
30
+ set[ext] = {} unless set[ext]
31
+ @logger.trace "#{name} =~ #{regex}"
32
+ set[ext][name] = f if name.match(regex)
33
+ end
34
+ }
35
+ rescue RegexpError => e
36
+ raise "The regular expression attempting to match resources in '#{path}' is incorrect! #{e.message}"
37
+ end
38
+ @logger.debug "getPathSet set=#{set.ai}"
39
+ res = {}
40
+ # Iterate extension sets in increasing precedence order ...
41
+ # Survivor will be the most desireable version of the item
42
+ # i.e. the .rb environment, role, data bag, etc. will be preferred over the .json version
43
+ exts.each{ |e|
44
+ h = set[e]
45
+ if h
46
+ h.each{ |n,f|
47
+ @logger.info "Ignoring #{File.basename(res[n])}" if res[n]
48
+ res[n] = f
49
+ }
50
+ else
51
+ @logger.debug "'#{e}' set is empty! (No #{path}/*.#{e} files found using precedence #{exts})"
52
+ end
53
+ }
54
+ res
55
+ end
56
+
57
+ def vet_path(dir,base=nil,rel=false)
58
+ if rel
59
+ base = File.realpath(File.expand_path(File.join(@config[:directory], base)))
60
+ else
61
+ base = @config[:directory] unless base
62
+ end
63
+ path = nil
64
+ [dir, dir.downcase].each do |d|
65
+ path = File.join(base, dir)
66
+ if File.directory?(path)
67
+ break
68
+ end
69
+ end
70
+ unless File.directory?(path)
71
+ @logger.error " !! error: Cannot load bricks from #{path} (started with #{File.join(base, dir)}')"
72
+ abort!
73
+ end
74
+ path
75
+ end
76
+
77
+
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,40 @@
1
+ module Aws
2
+ module Cfn
3
+ module Compiler
4
+ module Options
5
+
6
+ # TODO: [2014-06-29 Christo] Hook into super class and add on options instead of starting from scratch every time
7
+ def parse_options
8
+
9
+ setup_options
10
+
11
+ @opts.on :F, :format=, 'The output format of the components. [JSON|YAML|Ruby]', { as: String, match: @format_regex, default: 'yaml' }
12
+ @opts.on :s, :specification=, 'The specification to use when selecting components. A JSON or YAML file', { as: String
13
+ }
14
+ @opts.on :f, :formatversion=, 'The AWS Template format version. ', {as: String,
15
+ default: '2010-09-09'
16
+ }
17
+ @opts.on :D, :description=, "The AWS Template description. Default: template name", { as: String }
18
+ @opts.on :p, :precedence=, 'The precedence of template component types. Default: rb,ruby,yaml,yml,json,js', { as: String,
19
+ default: 'rb,ruby,yaml,yml,json,js', }
20
+
21
+ @opts.parse!
22
+
23
+ if ARGV.size > 0
24
+ puts @opts
25
+ abort! "Extra arguments! #{ARGV}"
26
+ end
27
+ end
28
+
29
+ def set_config_options
30
+
31
+ @config[:precedence] = @opts[:precedence].split(%r',+\s*').reverse
32
+
33
+ setup_config
34
+
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,119 @@
1
+ module Aws
2
+ module Cfn
3
+ module Compiler
4
+ module Parse
5
+
6
+ def parse
7
+ @dsl ||= Aws::Cfn::Dsl::Template.new(@config[:directory])
8
+ %w( Mappings Parameters Resources Outputs ).each do |section|
9
+ parse_section(section,@spec)
10
+ end
11
+ end
12
+
13
+ # noinspection RubyGlobalVariableNamingConvention
14
+ def parse_section(section,spec=nil)
15
+ logStep "Parsing #{section}..."
16
+
17
+ if spec and spec[section]
18
+ abort! "No such directory: #{@config[:directory]} (I am here: #{Dir.pwd})" unless File.directory?(@config[:directory])
19
+ @items ||= {}
20
+ @items[section] ||= {}
21
+ get = {}
22
+ item = {}
23
+ spec[section].each do |rsrc|
24
+ @logger.info "\tUsing #{section}::#{rsrc}"
25
+ refp,sub,base,rel = map_resource_reference(rsrc)
26
+ if refp.nil?
27
+ path = vet_path(section)
28
+ else
29
+ path = vet_path(sub ? sub : section, refp, rel)
30
+ end
31
+ unless get[path]
32
+ get[path] = get_file_set([".*"], path, @config[:precedence])
33
+ end
34
+ set = get[path]
35
+ if set[base]
36
+ if item.has_key?(base)
37
+ @logger.error " !! error: Duplicate item: #{section}/#{base}"
38
+ abort!
39
+ end
40
+
41
+ filename = set[base]
42
+ unless filename =~ /\.(ru?by?|ya?ml|js(|on))\z/i
43
+ @logger.info "Brick not supported/ relevant: #{filename}"
44
+ next
45
+ end
46
+
47
+ begin
48
+ @logger.debug " reading #{filename}"
49
+ content = File.read(filename)
50
+ next if content.size==0
51
+
52
+ if filename =~ /\.(rb|ruby)\z/i
53
+ item.merge! parse_rb_file(base, section, filename)
54
+ elsif filename =~ /\.js(|on)\z/i
55
+ item.merge! JSON.parse(content)
56
+ elsif filename =~ /\.ya?ml\z/i
57
+ item.merge! YAML.load(content)
58
+ else
59
+ next
60
+ end
61
+
62
+ rescue
63
+ abort! " !! error: #{$!}"
64
+ end
65
+ else
66
+ abort! " !! error: #{section}/#{base} not found!"
67
+ end
68
+ end
69
+ item.keys.each { |key|
70
+ if @items[section].has_key?(key)
71
+ abort! " !! error: Duplicate item: #{section}/#{key}"
72
+ end
73
+ }
74
+ @items[section].merge! item
75
+
76
+ unless @items[section].keys.count == spec[section].count
77
+ abort! " !! error: Suspect that a #{section} item was missed! \nRequested: #{spec[section]}\n Found: #{@items[section].keys}"
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ def parse_rb_file(base, section, filename)
84
+ Aws::Cfn::Compiler.binding ||= {}
85
+ Aws::Cfn::Compiler.binding[section] ||= {}
86
+ Aws::Cfn::Compiler.binding[section][base] ||= {
87
+ brick_path: @config[:directory],
88
+ template: @dsl,
89
+ logger: @logger
90
+ }
91
+ source_file = File.expand_path(filename)
92
+ # source = IO.read(source_file)
93
+ eval "require source_file", binding
94
+ unless @dsl.dict[section.to_sym]
95
+ abort! "Unable to compile/expand #{filename} for #{section}/#{base}"
96
+ end
97
+ sym_to_s(@dsl.dict[section.to_sym])
98
+ end
99
+
100
+ def sym_to_s(hash)
101
+ if hash.is_a?(Hash)
102
+ item = {}
103
+ hash.each { |k,v|
104
+ item[k.to_s] = sym_to_s(v)
105
+ }
106
+ item
107
+ elsif hash.is_a?(Array)
108
+ hash.map{|e| sym_to_s(e) }
109
+ elsif hash.is_a?(String)
110
+ hash
111
+ else
112
+ abort! "#{hash} is a #{hash.class.name}"
113
+ end
114
+ end
115
+
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,54 @@
1
+ module Aws
2
+ module Cfn
3
+ module Compiler
4
+ module Save
5
+
6
+ def save_section(dir, file, format, section, hash, join='/', brick=nil)
7
+ brick ||= "brick #{hash.keys[0]}"
8
+ path = File.join(File.expand_path(dir), file)
9
+ filn = if @config[:expandedpaths]
10
+ path
11
+ else
12
+ File.join(dir, file)
13
+ end
14
+ logStep "Saving #{brick} to #{filn} "
15
+ # noinspection RubyAssignmentExpressionInConditionalInspection
16
+ if match = file.match(%r'\.(.*)$')
17
+ if (section != '') and (not formats_compatible?(format, match[1]))
18
+ msg = "The file extension (#{match[1]}) does not match the chosen output format (#{format})!"
19
+ if @config[:force]
20
+ logger.warn msg
21
+ else
22
+ abort! "#{msg}\n\tIf you are SURE this is what you want then use the --force option!"
23
+ end
24
+ end
25
+ end
26
+
27
+ if i_am_maintainer(path) or @config[:force]
28
+ begin
29
+ File.open path, File::CREAT|File::TRUNC|File::RDWR, 0644 do |f|
30
+ case format
31
+ when /ruby|rb|yaml|yml/
32
+ f.write maintainer_comment('')
33
+ f.write hash.to_yaml line_width: 1024, indentation: 4, canonical: false
34
+ when /json|js/
35
+ # You wish ... f.write maintainer_comment('')
36
+ f.write JSON.pretty_generate(hash, { indent: "\t", space: ' '})
37
+ else
38
+ abort! "Internal: Unsupported format #{format}. Should have noticed this earlier!"
39
+ end
40
+ f.close
41
+ end
42
+ @logger.info " decompiled #{filn}."
43
+ rescue
44
+ abort! "!!! Could not write compiled file #{path}: #{$!}"
45
+ end
46
+ else
47
+ @logger.warn " Did not overwrite #{filn}."
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,7 +1,7 @@
1
1
  module Aws
2
2
  module Cfn
3
3
  module Compiler
4
- VERSION = "0.6.0"
4
+ VERSION = '0.7.0'
5
5
  end
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-cfn-compiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - PKinney
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-28 00:00:00.000000000 Z
11
+ date: 2014-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_print
@@ -72,54 +72,26 @@ dependencies:
72
72
  - - ! '>='
73
73
  - !ruby/object:Gem::Version
74
74
  version: '0'
75
- - !ruby/object:Gem::Dependency
76
- name: colorize
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - ! '>='
80
- - !ruby/object:Gem::Version
81
- version: '0'
82
- type: :runtime
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - ! '>='
87
- - !ruby/object:Gem::Version
88
- version: '0'
89
- - !ruby/object:Gem::Dependency
90
- name: inifile
91
- requirement: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - ! '>='
94
- - !ruby/object:Gem::Version
95
- version: '0'
96
- type: :runtime
97
- prerelease: false
98
- version_requirements: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - ! '>='
101
- - !ruby/object:Gem::Version
102
- version: '0'
103
75
  - !ruby/object:Gem::Dependency
104
76
  name: aws-cfn-dsl
105
77
  requirement: !ruby/object:Gem::Requirement
106
78
  requirements:
107
79
  - - ! '>='
108
80
  - !ruby/object:Gem::Version
109
- version: 0.2.0
81
+ version: 0.7.0
110
82
  - - ~>
111
83
  - !ruby/object:Gem::Version
112
- version: '0.2'
84
+ version: '0.7'
113
85
  type: :runtime
114
86
  prerelease: false
115
87
  version_requirements: !ruby/object:Gem::Requirement
116
88
  requirements:
117
89
  - - ! '>='
118
90
  - !ruby/object:Gem::Version
119
- version: 0.2.0
91
+ version: 0.7.0
120
92
  - - ~>
121
93
  - !ruby/object:Gem::Version
122
- version: '0.2'
94
+ version: '0.7'
123
95
  - !ruby/object:Gem::Dependency
124
96
  name: dldinternet-mixlib-cli
125
97
  requirement: !ruby/object:Gem::Requirement
@@ -197,6 +169,11 @@ files:
197
169
  - lib/aws/cfn/compiler.rb
198
170
  - lib/aws/cfn/compiler/base.rb
199
171
  - lib/aws/cfn/compiler/main.rb
172
+ - lib/aws/cfn/compiler/mixins/compile.rb
173
+ - lib/aws/cfn/compiler/mixins/load.rb
174
+ - lib/aws/cfn/compiler/mixins/options.rb
175
+ - lib/aws/cfn/compiler/mixins/parse.rb
176
+ - lib/aws/cfn/compiler/mixins/save.rb
200
177
  - lib/aws/cfn/compiler/version.rb
201
178
  homepage: https://github.com/dldinternet/aws-cfn-compiler
202
179
  licenses: