aws-cfn-compiler 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: