aws-cfn-compiler 0.3.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 98579909e2c94b50d6d0f5905160c7bb2e033bb9
4
- data.tar.gz: 4c5f6152afa7a9853c0a80ade457367124f65598
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MzY5MDJkOWY3MWRlNGZhOTEzNDMxN2IzMjVlMmZhNmI3ZGE4YWYyMg==
5
+ data.tar.gz: !binary |-
6
+ MmI4Zjc4OGI3NmI0NzQzMTJmZTEyNTUxMjMzMDcxMmZjMDJiOTk5OQ==
5
7
  SHA512:
6
- metadata.gz: 0d37543235669ff4bfbdf7b6f17aaaaba20eb7375734f633902c49bd5df38aa5308f359f2e4ad82fbb9d8d2d8d4f8e56770a8347fc01c27dfdede867969daffc
7
- data.tar.gz: d342f24025ce2b7b2880d1870318b83cae302b9027e8ed6357cb4f35542146db853d195c0687716209b0f55efa958d812db05235db8917f82ab6039e8ca02d2f
8
+ metadata.gz: !binary |-
9
+ NWU0YTAyZTU1MDUxMWYxMGI1NDhhNTBlMzhmMmFiNTViMjhjZjBkYzM5MTMz
10
+ YzQwNzVlMjRmN2JkYmFlOTJhN2M4OWJkYzlmNjkzM2JjMGEzNTEyYzYxMGJk
11
+ ZWU4Y2NhNDQ0NGQ4NzBhMmRiN2Y5ZDc4ZGY2ZTE2MjViNDMxOGY=
12
+ data.tar.gz: !binary |-
13
+ M2QzYTdlMmEyZWI3NDUwODA1YmEzNDc4Nzc2Y2UyODU0MmFjZmQ5MmQzODUx
14
+ N2RlYjFkZDlkYWNjZjM3NGExZThkZmIwNDYyNWY0NjIwNmZhYmM4YWQxMDEx
15
+ NGQwNjkyYzhjMzYzMDZhMzlmZjQzNjM0ZjFlYTBlNTBiMTViNjU=
data/Gemfile CHANGED
@@ -2,3 +2,9 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in aws-cfn-compiler.gemspec
4
4
  gemspec
5
+
6
+ gem 'cloudformation-ruby-dsl', :path => '../cloudformation-ruby-dsl', :group => :development
7
+ gem 'aws-cfn-dsl', :path => '../aws-cfn-dsl', :group => :development
8
+ gem 'aws-cfn-decompiler', :path => '../aws-cfn-decompiler', :group => :development
9
+ gem 'bundler', "~> 1.6", :group => :development
10
+ gem 'rake', :group => :development
@@ -18,12 +18,14 @@ 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"
21
+ spec.add_dependency "awesome_print", '~> 1.2', '>= 1.2.0'
22
22
  spec.add_dependency "psych"
23
23
  spec.add_dependency "json"
24
- spec.add_dependency 'aws-cfn-dsl', '>= 0.0.1', '~> 0.0'
25
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'
28
+ spec.add_dependency 'dldinternet-mixlib-cli', ">= 0.1.0", '~> 0.1'
29
+ spec.add_dependency 'dldinternet-mixlib-logging', '>= 0.1.6', '~> 0.1'
26
30
 
27
- spec.add_development_dependency 'bundler', "~> 1.6"
28
- spec.add_development_dependency 'rake'
29
31
  end
data/aws-cfn-compiler.iml CHANGED
@@ -3,26 +3,25 @@
3
3
  <component name="CompassSettings">
4
4
  <option name="compassSupportEnabled" value="true" />
5
5
  </component>
6
- <component name="FacetManager">
7
- <facet type="gem" name="Ruby Gem">
8
- <configuration>
9
- <option name="GEM_APP_ROOT_PATH" value="$MODULE_DIR$" />
10
- <option name="GEM_APP_TEST_PATH" value="" />
11
- <option name="GEM_APP_LIB_PATH" value="$MODULE_DIR$/lib" />
12
- </configuration>
13
- </facet>
14
- </component>
15
6
  <component name="NewModuleRootManager" inherit-compiler-output="true">
16
7
  <exclude-output />
17
8
  <content url="file://$MODULE_DIR$" />
18
- <orderEntry type="jdk" jdkName="RVM: ruby-1.9.3-p547 [aws-cfn-compile]" jdkType="RUBY_SDK" />
9
+ <orderEntry type="inheritedJdk" />
19
10
  <orderEntry type="sourceFolder" forTests="false" />
11
+ <orderEntry type="module" module-name="aws-cfn-decompiler" />
12
+ <orderEntry type="module" module-name="aws-cfn-dsl" />
13
+ <orderEntry type="module" module-name="cloudformation-ruby-dsl" />
20
14
  <orderEntry type="library" scope="PROVIDED" name="awesome_print (v1.2.0, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
21
- <orderEntry type="library" scope="PROVIDED" name="aws-cfn-dsl (v0.0.4, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
22
- <orderEntry type="library" scope="PROVIDED" name="bundler (v1.6.2, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
23
- <orderEntry type="library" scope="PROVIDED" name="cloudformation-ruby-dsl (v0.4.3, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
15
+ <orderEntry type="library" scope="PROVIDED" name="colorize (v0.7.3, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
24
16
  <orderEntry type="library" scope="PROVIDED" name="detabulator (v0.1.0, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
17
+ <orderEntry type="library" scope="PROVIDED" name="dldinternet-mixlib-cli (v0.1.0, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
18
+ <orderEntry type="library" scope="PROVIDED" name="dldinternet-mixlib-logging (v0.1.6, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
19
+ <orderEntry type="library" scope="PROVIDED" name="inifile (v2.0.2, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
25
20
  <orderEntry type="library" scope="PROVIDED" name="json (v1.8.1, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
21
+ <orderEntry type="library" scope="PROVIDED" name="little-plugger (v1.1.3, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
22
+ <orderEntry type="library" scope="PROVIDED" name="logging (v1.8.2, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
23
+ <orderEntry type="library" scope="PROVIDED" name="mixlib-cli (v1.5.0, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
24
+ <orderEntry type="library" scope="PROVIDED" name="multi_json (v1.10.1, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
26
25
  <orderEntry type="library" scope="PROVIDED" name="psych (v2.0.5, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
27
26
  <orderEntry type="library" scope="PROVIDED" name="rake (v10.3.2, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
28
27
  <orderEntry type="library" scope="PROVIDED" name="slop (v3.5.0, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
data/bin/cfn-compiler CHANGED
@@ -2,27 +2,30 @@
2
2
 
3
3
  require 'rubygems'
4
4
 
5
- # require 'safe_yaml'
6
- # SafeYAML::OPTIONS[:default_mode] = :safe
7
-
5
+ path = File.dirname(__FILE__)
6
+ if File.exists?("#{path}/../.gitignore")
7
+ %w(aws-cfn-decompiler aws-cfn-compiler cloudformation-ruby-dsl).each do |mod|
8
+ add_path = File.expand_path(File.join(path, "../../#{mod}", "lib"))
9
+ $:.unshift(add_path)
10
+ end
11
+ else
8
12
  # Borrowing from "whiches" gem ...
9
- cmd = File.basename(__FILE__, '.rb')
10
- exes = []
11
- exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
12
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
13
- exts.each { |ext|
14
- exe = File.join(path, "#{cmd}#{ext}")
15
- exes << exe if File.executable? exe
16
- }
17
- end
18
- path = if exes.size > 0
19
- File.dirname(exes[0])
20
- else
21
- File.dirname(__FILE__)
22
- end
13
+ cmd = File.basename(__FILE__, '.rb')
14
+ exes = []
15
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
16
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
17
+ exts.each { |ext|
18
+ exe = File.join(path, "#{cmd}#{ext}")
19
+ exes << exe if File.executable? exe
20
+ }
21
+ end
22
+ if exes.size > 0
23
+ path = File.dirname(exes[0])
24
+ end
23
25
 
26
+ end
24
27
  add_path = File.expand_path(File.join(path, "..", "lib"))
25
28
  $:.unshift(add_path)
26
29
 
27
30
  require 'aws/cfn/compiler'
28
- Aws::Cfn::Compiler::Main.new().run
31
+ Aws::Cfn::Compiler::Main.new.run
@@ -0,0 +1,328 @@
1
+
2
+ require 'json'
3
+ require 'ap'
4
+ require 'yaml'
5
+ require 'slop'
6
+ require 'aws/cfn/dsl/template'
7
+
8
+ module Aws
9
+ module Cfn
10
+ module Compiler
11
+ class Base
12
+ attr_accessor :items
13
+ attr_accessor :opts
14
+ attr_accessor :spec
15
+
16
+ require 'dldinternet/mixlib/logging'
17
+ include DLDInternet::Mixlib::Logging
18
+
19
+ def initialize
20
+ @items = {}
21
+ @config ||= {}
22
+ @config[:log_opts] = lambda{|mlll| {
23
+ :pattern => "%#{mlll}l: %m %C\n",
24
+ :date_pattern => '%Y-%m-%d %H:%M:%S',
25
+ }
26
+ }
27
+ @config[:log_level] = :step
28
+ @logger = getLogger(@config)
29
+ end
30
+
31
+ def validate(compiled)
32
+ raise 'No Resources!?' unless compiled['Resources']
33
+
34
+ # Mappings => Resources
35
+ maps = find_maps(compiled) #.select { |a| !(a =~ /^AWS::/) }
36
+ rscs = compiled['Resources'].keys
37
+ mpgs = compiled['Mappings'].nil? ? [] : compiled['Mappings'].keys
38
+ names = rscs+mpgs
39
+
40
+ unless (maps-names).empty?
41
+ @logger.error '!!! Unknown mappings !!!'
42
+ (maps-names).each do |name|
43
+ @logger.error " #{name}"
44
+ end
45
+ abort!
46
+ end
47
+ @logger.step ' Mappings validated'
48
+
49
+ # Parameters => Resources => Out@logger.step
50
+ refs = find_refs(compiled).select { |a,_| !(a =~ /^AWS::/) }
51
+ prms = compiled['Parameters'].keys rescue []
52
+ # outs = compiled['Outputs'].keys rescue []
53
+ names = rscs+prms
54
+
55
+ unless (refs.keys-names).empty?
56
+ @logger.error '!!! Unknown references !!!'
57
+ (refs.keys-names).each do |name|
58
+ @logger.error " #{name} from #{refs[name][0]}:#{refs[name][1]}"
59
+ end
60
+ abort!
61
+ end
62
+ @logger.step ' References validated'
63
+ end
64
+
65
+ def save(compiled, output_file)
66
+ begin
67
+ hash = {}
68
+ compiled.each do |item,value|
69
+ unless value.nil?
70
+ if (not value.is_a?(Hash)) or (value.count > 0)
71
+ hash[item] = value
72
+ end
73
+ end
74
+ end
75
+
76
+ File.open output_file, 'w' do |f|
77
+ f.write JSON.pretty_generate(hash, { indent: "\t", space: ' '})
78
+ end
79
+ @logger.step ' Compiled file written.'
80
+ rescue
81
+ @logger.error "!!! Could not write compiled file: #{$!}"
82
+ abort!
83
+ end
84
+ end
85
+
86
+ def load(spec=nil)
87
+ if spec
88
+ begin
89
+ abs = File.absolute_path(File.expand_path(spec))
90
+ unless File.exists?(abs)
91
+ abs = File.absolute_path(File.expand_path(File.join(@opts[:directory],spec)))
92
+ end
93
+ rescue
94
+ # pass
95
+ end
96
+ if File.exists?(abs)
97
+ 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
98
+
99
+ @logger.step "Loading specification #{abs}..."
100
+ spec = File.read(abs)
101
+
102
+ case File.extname(File.basename(abs)).downcase
103
+ when /json|jts/
104
+ @spec = JSON.parse(spec)
105
+ when /yaml|yts/
106
+ @spec = YAML.load(spec)
107
+ else
108
+ raise "Unsupported file type for specification: #{spec}"
109
+ end
110
+ # @spec = spec
111
+ else
112
+ raise "Unable to open specification: #{abs}"
113
+ end
114
+ @dsl ||= Aws::Cfn::Dsl::Template.new(@opts[:directory])
115
+ %w( Mappings Parameters Resources Outputs ).each do |dir|
116
+ load_dir(dir,@spec)
117
+ end
118
+ else
119
+ raise "No specification provided"
120
+ end
121
+ end
122
+
123
+ protected
124
+
125
+ def abort!
126
+ @logger.fatal '!!! Aborting !!!'
127
+ exit
128
+ end
129
+
130
+ def find_refs(hash, type='Reference', parent='')
131
+ h = {}
132
+ newparent = parent
133
+ if hash.is_a? Hash
134
+ hash.keys.collect do |key|
135
+ if %w{Mappings Parameters Resources Outputs}.include? key
136
+ type = key#.gsub(/s$/, '')
137
+ newparent = key
138
+ elsif %w{Mappings Parameters Resources Outputs}.include? parent
139
+ newparent = key
140
+ end
141
+ if %w{Ref}.include? key
142
+ h = { hash[key] => [type,newparent] }
143
+ elsif 'Fn::GetAtt' == key
144
+ h = { hash[key].first => [type,newparent] }
145
+ # elsif %w{SourceSecurityGroupName CacheSecurityGroupNames SecurityGroupNames}.include? key
146
+ # a = find_refs(hash[key],type,newparent)
147
+ # h = merge(h, a, *[type,newparent])
148
+ else
149
+ a = find_refs(hash[key],type,newparent)
150
+ h = merge(h, a, *[type,newparent])
151
+ end
152
+ end.flatten.compact.uniq
153
+ elsif hash.is_a? Array
154
+ a = hash.map{|i| find_refs(i,type,newparent) }
155
+ h = merge(h, a, type, *[type,newparent])
156
+ end
157
+ h
158
+ end
159
+
160
+ def merge(h, a, *type)
161
+ if a.is_a? Hash
162
+ if a.size > 0
163
+ h.merge! a
164
+ end
165
+ else
166
+ a.flatten.compact.uniq.map { |i|
167
+ if i.is_a? Hash
168
+ if i.size > 0
169
+ h.merge! i
170
+ h
171
+ end
172
+ else
173
+ h[i] = type
174
+ end
175
+ }
176
+ end
177
+ h
178
+ end
179
+
180
+ def find_maps(hash)
181
+ if hash.is_a? Hash
182
+ tr = []
183
+ hash.keys.collect do |key|
184
+ if 'Fn::FindInMap' == key
185
+ hash[key].first
186
+ else
187
+ find_maps(hash[key])
188
+ end
189
+ end.flatten.compact.uniq
190
+ elsif hash.is_a? Array
191
+ hash.collect{|a| find_maps(a)}.flatten.compact.uniq
192
+ end
193
+ end
194
+
195
+ # --------------------------------------------------------------------------------
196
+ def get_file_set(want, path, exts=[])
197
+ # Dir[File.join(@opts[:directory], "#{dir}.*")] | Dir[File.join(@opts[:directory], dir.to_s, "**", "*")]
198
+ raise "Bad call to #{self.class.name}.getPathSet: want == nil" unless want
199
+ @logger.debug "Look for #{want.ai} in #{[path]} with #{exts} extensions"
200
+ if exts.nil?
201
+ exts = @config[:precedence]
202
+ end
203
+ file_regex=%r/^(\S+)\.(#{exts.join('|')})$/
204
+ if exts.empty?
205
+ file_regex=%r/^(\S+)()$/
206
+ exts=['']
207
+ end
208
+ regex = "^(#{want.join('|')})$"
209
+ set = {}
210
+ abs = File.expand_path(path)
211
+ abs = path unless @config[:expandedpaths]
212
+ raise "Oops! Does '#{path}' directory exist?" unless File.directory?(abs)
213
+ #abs = File.realpath(abs)
214
+ begin
215
+ Dir.glob("#{abs}/*").each{ |f|
216
+ match = File.basename(f).match(file_regex)
217
+ if match
218
+ name = match[1]
219
+ ext = match[2]
220
+ set[ext] = {} unless set[ext]
221
+ @logger.trace "#{name} =~ #{regex}"
222
+ set[ext][name] = f if name.match(regex)
223
+ end
224
+ }
225
+ rescue RegexpError => e
226
+ raise ChopError.new "The regular expression attempting to match resources in '#{path}' is incorrect! #{e.message}"
227
+ end
228
+ @logger.debug "getPathSet set=#{set.ai}"
229
+ res = {}
230
+ # Iterate extension sets in increasing precedence order ...
231
+ # Survivor will be the most desireable version of the item
232
+ # i.e. the .rb environment, role, data bag, etc. will be preferred over the .json version
233
+ exts.each{ |e|
234
+ h = set[e]
235
+ if h
236
+ h.each{ |n,f|
237
+ @logger.warn "Ignoring #{File.basename(res[n])}" if res[n]
238
+ res[n] = f
239
+ }
240
+ else
241
+ @logger.info "'#{e}' set is empty! (No #{path}/*.#{e} files found using precedence #{exts})"
242
+ end
243
+ }
244
+ set = res
245
+ end
246
+
247
+ def load_dir(dir,spec=nil)
248
+ logStep "Loading #{dir}..."
249
+
250
+ if spec and spec[dir]
251
+ raise "No such directory: #{@opts[:directory]}" unless File.directory?(@opts[:directory])
252
+ set = []
253
+ if File.directory?(File.join(@opts[:directory], dir))
254
+ @items[dir] = {}
255
+ set = get_file_set([".*"], "#{@opts[:directory]}/#{dir}", @config[:precedence])
256
+ else
257
+ if File.directory?(File.join(@opts[:directory], dir.downcase))
258
+ @items[dir] = {}
259
+ set = get_file_set(['.*'], dir.downcase, @config[:precedence])
260
+ else
261
+ @logger.error " !! error: Cannot load bricks from #{File.join(@opts[:directory], dir)}"
262
+ abort!
263
+ end
264
+ end
265
+
266
+ item = {}
267
+ spec[dir].each do |base|
268
+ @logger.info "\tUsing #{dir}/#{base}"
269
+ if set[base]
270
+ if item.has_key?(base)
271
+ @logger.error " !! error: Duplicate item: #{dir}/#{base}"
272
+ abort!
273
+ end
274
+
275
+ filename = set[base]
276
+ unless filename =~ /\.(ru?by?|ya?ml|js(|on))\z/i
277
+ @logger.info "Brick not supported/ relevant: #{filename}"
278
+ next
279
+ end
280
+
281
+ begin
282
+ @logger.step " reading #{filename}"
283
+ content = File.read(filename)
284
+ next if content.size==0
285
+
286
+ if filename =~ /\.(rb|ruby)\z/i
287
+ eval "@dsl.#{content.gsub(%r'^\s+','')}"
288
+ unless @dsl.dict[dir.to_sym]
289
+ raise "Unable to expand #{filename} for #{dir}/#{base}"
290
+ end
291
+ item.merge! @dsl.dict[dir.to_sym]
292
+ elsif filename =~ /\.js(|on)\z/i
293
+ item.merge! JSON.parse(content)
294
+ elsif filename =~ /\.ya?ml\z/i
295
+ item.merge! YAML.load(content)
296
+ else
297
+ next
298
+ end
299
+
300
+ rescue
301
+ @logger.error " !! error: #{$!}"
302
+ abort!
303
+ end
304
+ else
305
+ @logger.error " !! error: #{dir}/#{base} not found!"
306
+ abort!
307
+ end
308
+ end
309
+ item.keys.each { |key|
310
+ if @items[dir].has_key?(key)
311
+ @logger.error " !! error: Duplicate item: #{dir}/#{key}"
312
+ abort!
313
+ end
314
+ }
315
+ @items[dir].merge! item
316
+
317
+ unless @items[dir].keys.count == spec[dir].count
318
+ @logger.error " !! error: Suspect that a #{dir} item was missed! \nRequested: #{spec[dir]}\n Found: #{@items[dir].keys}"
319
+ abort!
320
+ end
321
+ end
322
+
323
+ end
324
+
325
+ end
326
+ end
327
+ end
328
+ end
@@ -0,0 +1,68 @@
1
+
2
+ require 'json'
3
+ require 'ap'
4
+ require 'yaml'
5
+ require 'slop'
6
+
7
+ module Aws
8
+ module Cfn
9
+ module Compiler
10
+ class Main < Base
11
+
12
+ def run
13
+
14
+ @opts = Slop.parse(help: true) do
15
+ on :d, :directory=, 'The directory to look in', as: String
16
+ on :o, :output=, 'The JSON file to output', as: String
17
+ on :s, :specification=, 'The specification to use when selecting components. A JSON or YAML file or JSON object', as: String
18
+ on :f, :formatversion=, 'The AWS Template format version. Default 2010-09-09', as: String
19
+ on :p, :precedence=, 'The precedence of template component types. Default: rb,yaml,yml,json,js', as: String, default: 'rb,ruby,yaml,yml,json,js'
20
+ on :t, :description=, "The AWS Template description. Default: output basename or #{File.basename(__FILE__,'.rb')}", as: String
21
+ on :x, :expandedpaths=, 'Show expanded paths in output', as: String, default: 'off'
22
+ end
23
+
24
+ unless @opts[:directory]
25
+ puts @opts
26
+ exit
27
+ end
28
+
29
+ @config[:precedence] = @opts[:precedence].split(%r',+\s*').reverse
30
+ @config[:expandedpaths] = @opts[:expandedpaths].downcase.match %r'^(1|true|on|yes|enable|set)$'
31
+
32
+ load @opts[:specification]
33
+
34
+ desc = @opts[:output] ? File.basename(@opts[:output]).gsub(%r/\.(json|yaml)/, '') : File.basename(__FILE__,'.rb')
35
+ if @spec and @spec['Description']
36
+ desc = @spec['Description']
37
+ end
38
+ vers = '2010-09-09'
39
+ if @spec and @spec['AWSTemplateFormatVersion']
40
+ vers = @spec['AWSTemplateFormatVersion']
41
+ end
42
+ # noinspection RubyStringKeysInHashInspection
43
+ compiled = {
44
+ 'AWSTemplateFormatVersion' => (@opts[:formatversion].nil? ? vers : @opts[:formatversion]),
45
+ 'Description' => (@opts[:description].nil? ? desc : @opts[:description]),
46
+ 'Mappings' => @items['Mappings'],
47
+ 'Parameters' => @items['Parameters'],
48
+ 'Resources' => @items['Resources'],
49
+ 'Outputs' => @items['Outputs'],
50
+ }
51
+
52
+ @logger.step'Validating compiled file...'
53
+
54
+ validate(compiled)
55
+
56
+ output_file = @opts[:output] || 'compiled.json'
57
+ puts
58
+ @logger.step"Writing compiled file to #{output_file}..."
59
+ save(compiled, output_file)
60
+
61
+ puts
62
+ @logger.step'*** Compiled Successfully ***'
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,7 +1,7 @@
1
1
  module Aws
2
2
  module Cfn
3
3
  module Compiler
4
- VERSION = "0.3.1"
4
+ VERSION = "0.5.0"
5
5
  end
6
6
  end
7
7
  end
@@ -1,282 +1,4 @@
1
1
  require "aws/cfn/compiler/version"
2
+ require "aws/cfn/compiler/base"
3
+ require "aws/cfn/compiler/main"
2
4
 
3
- require 'json'
4
- require 'ap'
5
- require 'yaml'
6
- require 'slop'
7
-
8
- module Aws
9
- module Cfn
10
- module Compiler
11
- class Main
12
- attr_accessor :items
13
- attr_accessor :opts
14
- attr_accessor :spec
15
-
16
- def initialize
17
- @items = {}
18
- end
19
-
20
- def run
21
-
22
- @opts = Slop.parse(help: true) do
23
- on :d, :directory=, 'The directory to look in', as: String
24
- on :o, :output=, 'The JSON file to output', as: String
25
- on :s, :specification=, 'The specification to use when selecting components. A JSON or YAML file or JSON object', as: String
26
- on :f, :formatversion=, 'The AWS Template format version. Default 2010-09-09', as: String
27
- on :t, :description=, "The AWS Template description. Default: output basename or #{File.basename(__FILE__,'.rb')}", as: String
28
- end
29
-
30
- unless @opts[:directory]
31
- puts @opts
32
- exit
33
- end
34
-
35
- load @opts[:specification]
36
-
37
- desc = @opts[:output] ? File.basename(@opts[:output]).gsub(%r/\.(json|yaml)/, '') : File.basename(__FILE__,'.rb')
38
- if @spec and @spec['Description']
39
- desc = @spec['Description']
40
- end
41
- vers = '2010-09-09'
42
- if @spec and @spec['AWSTemplateFormatVersion']
43
- vers = @spec['AWSTemplateFormatVersion']
44
- end
45
- # noinspection RubyStringKeysInHashInspection
46
- compiled = {
47
- 'AWSTemplateFormatVersion' => (@opts[:formatversion].nil? ? vers : @opts[:formatversion]),
48
- 'Description' => (@opts[:description].nil? ? desc : @opts[:description]),
49
- 'Mappings' => @items['Mappings'],
50
- 'Parameters' => @items['Parameters'],
51
- 'Resources' => @items['Resources'],
52
- 'Outputs' => @items['Outputs'],
53
- }
54
-
55
- puts
56
- puts 'Validating compiled file...'
57
-
58
- validate(compiled)
59
-
60
- output_file = @opts[:output] || 'compiled.json'
61
- puts
62
- puts "Writing compiled file to #{output_file}..."
63
- save(compiled, output_file)
64
-
65
- puts
66
- puts '*** Compiled Successfully ***'
67
- end
68
-
69
- def validate(compiled)
70
- raise 'No Resources!?' unless compiled['Resources']
71
-
72
- # Mappings => Resources
73
- maps = find_maps(compiled) #.select { |a| !(a =~ /^AWS::/) }
74
- rscs = compiled['Resources'].keys
75
- mpgs = compiled['Mappings'].nil? ? [] : compiled['Mappings'].keys
76
- names = rscs+mpgs
77
-
78
- unless (maps-names).empty?
79
- puts '!!! Unknown mappings !!!'
80
- (maps-names).each do |name|
81
- puts " #{name}"
82
- end
83
- abort!
84
- end
85
- puts ' Mappings validated'
86
-
87
- # Parameters => Resources => Outputs
88
- refs = find_refs(compiled).select { |a,_| !(a =~ /^AWS::/) }
89
- prms = compiled['Parameters'].keys rescue []
90
- # outs = compiled['Outputs'].keys rescue []
91
- names = rscs+prms
92
-
93
- unless (refs.keys-names).empty?
94
- puts '!!! Unknown references !!!'
95
- (refs.keys-names).each do |name|
96
- puts " #{name} from #{refs[name][0]}:#{refs[name][1]}"
97
- end
98
- abort!
99
- end
100
- puts ' References validated'
101
- end
102
-
103
- def save(compiled, output_file)
104
- begin
105
- hash = {}
106
- compiled.each do |item,value|
107
- unless value.nil?
108
- if (not value.is_a?(Hash)) or (value.count > 0)
109
- hash[item] = value
110
- end
111
- end
112
- end
113
-
114
- File.open output_file, 'w' do |f|
115
- f.write JSON.pretty_generate(hash, { indent: "\t", space: ' '})
116
- end
117
- puts ' Compiled file written.'
118
- rescue
119
- puts "!!! Could not write compiled file: #{$!}"
120
- abort!
121
- end
122
- end
123
-
124
- def load(spec=nil)
125
- if spec
126
- begin
127
- abs = File.absolute_path(File.expand_path(spec))
128
- unless File.exists?(abs)
129
- abs = File.absolute_path(File.expand_path(File.join(@opts[:directory],spec)))
130
- end
131
- rescue
132
- # pass
133
- end
134
- if File.exists?(abs)
135
- 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
136
-
137
- puts "Loading specification #{abs}..."
138
- spec = File.read(abs)
139
-
140
- case File.extname(File.basename(abs)).downcase
141
- when /json|jts/
142
- @spec = JSON.parse(spec)
143
- when /yaml|yts/
144
- @spec = YAML.load(spec)
145
- else
146
- raise "Unsupported file type for specification: #{spec}"
147
- end
148
- # @spec = spec
149
- else
150
- raise "Unable to open specification: #{abs}"
151
- end
152
- end
153
- %w( Mappings Parameters Resources Outputs ).each do |dir|
154
- load_dir(dir,@spec)
155
- end
156
- end
157
-
158
- protected
159
-
160
- def abort!
161
- puts '!!! Aborting !!!'
162
- exit
163
- end
164
-
165
- def find_refs(hash, type='Reference', parent='')
166
- h = {}
167
- newparent = parent
168
- if hash.is_a? Hash
169
- hash.keys.collect do |key|
170
- if %w{Mappings Parameters Resources Outputs}.include? key
171
- type = key#.gsub(/s$/, '')
172
- newparent = key
173
- elsif %w{Mappings Parameters Resources Outputs}.include? parent
174
- newparent = key
175
- end
176
- if %w{Ref}.include? key
177
- h = { hash[key] => [type,newparent] }
178
- elsif 'Fn::GetAtt' == key
179
- h = { hash[key].first => [type,newparent] }
180
- # elsif %w{SourceSecurityGroupName CacheSecurityGroupNames SecurityGroupNames}.include? key
181
- # a = find_refs(hash[key],type,newparent)
182
- # h = merge(h, a, *[type,newparent])
183
- else
184
- a = find_refs(hash[key],type,newparent)
185
- h = merge(h, a, *[type,newparent])
186
- end
187
- end.flatten.compact.uniq
188
- elsif hash.is_a? Array
189
- a = hash.map{|i| find_refs(i,type,newparent) }
190
- h = merge(h, a, type, *[type,newparent])
191
- end
192
- h
193
- end
194
-
195
- def merge(h, a, *type)
196
- if a.is_a? Hash
197
- if a.size > 0
198
- h.merge! a
199
- end
200
- else
201
- a.flatten.compact.uniq.map { |i|
202
- if i.is_a? Hash
203
- if i.size > 0
204
- h.merge! i
205
- h
206
- end
207
- else
208
- h[i] = type
209
- end
210
- }
211
- end
212
- h
213
- end
214
-
215
- def find_maps(hash)
216
- if hash.is_a? Hash
217
- tr = []
218
- hash.keys.collect do |key|
219
- if 'Fn::FindInMap' == key
220
- hash[key].first
221
- else
222
- find_maps(hash[key])
223
- end
224
- end.flatten.compact.uniq
225
- elsif hash.is_a? Array
226
- hash.collect{|a| find_maps(a)}.flatten.compact.uniq
227
- end
228
- end
229
-
230
- def load_dir(dir,spec=nil)
231
- puts "Loading #{dir}..."
232
- raise "No such directory: #{@opts[:directory]}" unless File.directory?(@opts[:directory])
233
- set = []
234
- if File.directory?(File.join(@opts[:directory], dir))
235
- @items[dir] = {}
236
- set = get_file_set(dir)
237
- else
238
- if File.directory?(File.join(@opts[:directory], dir.downcase))
239
- @items[dir] = {}
240
- set = get_file_set(dir.downcase)
241
- end
242
- end
243
- set.collect do |filename|
244
- next unless filename =~ /\.(json|ya?ml)\z/i
245
- if spec and spec.has_key?(dir)
246
- base = File.basename(filename).gsub(%r/\.(rb|yaml)/, '')
247
- next if spec[dir].nil? # Edge case ... explicitly want NONE of these!
248
- next unless spec[dir].include?(base)
249
- puts "\tUsing #{dir}/#{base}"
250
- end
251
- begin
252
- puts " reading #{filename}"
253
- content = File.read(filename)
254
- next if content.size==0
255
-
256
- if filename =~ /\.json\z/i
257
- item = JSON.parse(content)
258
- elsif filename =~ /\.ya?ml\z/i
259
- item = YAML.load(content)
260
- else
261
- next
262
- end
263
- item.keys.each { |key| raise "Duplicate item: #{key}" if @items[dir].has_key?(key) }
264
- @items[dir].merge! item
265
- rescue
266
- puts " !! error: #{$!}"
267
- abort!
268
- end
269
- end
270
- if spec and spec[dir]
271
- raise "Suspect that a #{dir} item was missed! \nRequested: #{spec[dir]}\n Found: #{@items[dir].keys}" unless (@items[dir].keys.count == spec[dir].count)
272
- end
273
- end
274
-
275
- def get_file_set(dir)
276
- Dir[File.join(@opts[:directory], "#{dir}.*")] | Dir[File.join(@opts[:directory], dir.to_s, "**", "*")]
277
- end
278
-
279
- end
280
- end
281
- end
282
- end
metadata CHANGED
@@ -1,119 +1,165 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-cfn-compiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.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-26 00:00:00.000000000 Z
11
+ date: 2014-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_print
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '1.2'
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 1.2.0
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - ">="
27
+ - - ~>
25
28
  - !ruby/object:Gem::Version
26
- version: '0'
29
+ version: '1.2'
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.2.0
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: psych
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
- - - ">="
37
+ - - ! '>='
32
38
  - !ruby/object:Gem::Version
33
39
  version: '0'
34
40
  type: :runtime
35
41
  prerelease: false
36
42
  version_requirements: !ruby/object:Gem::Requirement
37
43
  requirements:
38
- - - ">="
44
+ - - ! '>='
39
45
  - !ruby/object:Gem::Version
40
46
  version: '0'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: json
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
- - - ">="
51
+ - - ! '>='
46
52
  - !ruby/object:Gem::Version
47
53
  version: '0'
48
54
  type: :runtime
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
- - - ">="
58
+ - - ! '>='
53
59
  - !ruby/object:Gem::Version
54
60
  version: '0'
55
61
  - !ruby/object:Gem::Dependency
56
- name: aws-cfn-dsl
62
+ name: slop
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: 0.0.1
62
- - - "~>"
65
+ - - ! '>='
63
66
  - !ruby/object:Gem::Version
64
- version: '0.0'
67
+ version: '0'
65
68
  type: :runtime
66
69
  prerelease: false
67
70
  version_requirements: !ruby/object:Gem::Requirement
68
71
  requirements:
69
- - - ">="
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: colorize
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ! '>='
70
80
  - !ruby/object:Gem::Version
71
- version: 0.0.1
72
- - - "~>"
81
+ version: '0'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ! '>='
73
87
  - !ruby/object:Gem::Version
74
- version: '0.0'
88
+ version: '0'
75
89
  - !ruby/object:Gem::Dependency
76
- name: slop
90
+ name: inifile
77
91
  requirement: !ruby/object:Gem::Requirement
78
92
  requirements:
79
- - - ">="
93
+ - - ! '>='
80
94
  - !ruby/object:Gem::Version
81
95
  version: '0'
82
96
  type: :runtime
83
97
  prerelease: false
84
98
  version_requirements: !ruby/object:Gem::Requirement
85
99
  requirements:
86
- - - ">="
100
+ - - ! '>='
87
101
  - !ruby/object:Gem::Version
88
102
  version: '0'
89
103
  - !ruby/object:Gem::Dependency
90
- name: bundler
104
+ name: aws-cfn-dsl
91
105
  requirement: !ruby/object:Gem::Requirement
92
106
  requirements:
93
- - - "~>"
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 0.2.0
110
+ - - ~>
94
111
  - !ruby/object:Gem::Version
95
- version: '1.6'
96
- type: :development
112
+ version: '0.2'
113
+ type: :runtime
97
114
  prerelease: false
98
115
  version_requirements: !ruby/object:Gem::Requirement
99
116
  requirements:
100
- - - "~>"
117
+ - - ! '>='
101
118
  - !ruby/object:Gem::Version
102
- version: '1.6'
119
+ version: 0.2.0
120
+ - - ~>
121
+ - !ruby/object:Gem::Version
122
+ version: '0.2'
103
123
  - !ruby/object:Gem::Dependency
104
- name: rake
124
+ name: dldinternet-mixlib-cli
105
125
  requirement: !ruby/object:Gem::Requirement
106
126
  requirements:
107
- - - ">="
127
+ - - ! '>='
108
128
  - !ruby/object:Gem::Version
109
- version: '0'
110
- type: :development
129
+ version: 0.1.0
130
+ - - ~>
131
+ - !ruby/object:Gem::Version
132
+ version: '0.1'
133
+ type: :runtime
111
134
  prerelease: false
112
135
  version_requirements: !ruby/object:Gem::Requirement
113
136
  requirements:
114
- - - ">="
137
+ - - ! '>='
115
138
  - !ruby/object:Gem::Version
116
- version: '0'
139
+ version: 0.1.0
140
+ - - ~>
141
+ - !ruby/object:Gem::Version
142
+ version: '0.1'
143
+ - !ruby/object:Gem::Dependency
144
+ name: dldinternet-mixlib-logging
145
+ requirement: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: 0.1.6
150
+ - - ~>
151
+ - !ruby/object:Gem::Version
152
+ version: '0.1'
153
+ type: :runtime
154
+ prerelease: false
155
+ version_requirements: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ! '>='
158
+ - !ruby/object:Gem::Version
159
+ version: 0.1.6
160
+ - - ~>
161
+ - !ruby/object:Gem::Version
162
+ version: '0.1'
117
163
  description: The idea is to create a folder structure to better manage pieces of a
118
164
  CloudFormation deployment. Additionally, writing in JSON is hard, so the compiler
119
165
  takes YAML files as well.
@@ -124,9 +170,9 @@ executables:
124
170
  extensions: []
125
171
  extra_rdoc_files: []
126
172
  files:
127
- - ".gitignore"
128
- - ".ruby-gemset"
129
- - ".ruby-version"
173
+ - .gitignore
174
+ - .ruby-gemset
175
+ - .ruby-version
130
176
  - Gemfile
131
177
  - LICENSE.txt
132
178
  - README.md
@@ -149,6 +195,8 @@ files:
149
195
  - example/resources/subnets.json
150
196
  - example/resources/vpc.json
151
197
  - lib/aws/cfn/compiler.rb
198
+ - lib/aws/cfn/compiler/base.rb
199
+ - lib/aws/cfn/compiler/main.rb
152
200
  - lib/aws/cfn/compiler/version.rb
153
201
  homepage: https://github.com/dldinternet/aws-cfn-compiler
154
202
  licenses:
@@ -160,12 +208,12 @@ require_paths:
160
208
  - lib
161
209
  required_ruby_version: !ruby/object:Gem::Requirement
162
210
  requirements:
163
- - - ">="
211
+ - - ! '>='
164
212
  - !ruby/object:Gem::Version
165
213
  version: '0'
166
214
  required_rubygems_version: !ruby/object:Gem::Requirement
167
215
  requirements:
168
- - - ">="
216
+ - - ! '>='
169
217
  - !ruby/object:Gem::Version
170
218
  version: '0'
171
219
  requirements: []