aws-cfn-dsl 0.0.4 → 0.1.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,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- ZDJlMWZlNmRjMmZjNmRiMjk3MzU5Zjk5MTYxZTVhNzk1NmE0YjU0Nw==
5
- data.tar.gz: !binary |-
6
- ZWFiMGM1NzE2NTExNDliMDVkZDA3NzliNmY2ZmY3NzBkYzkwMWRhYQ==
2
+ SHA1:
3
+ metadata.gz: c40097a8abb684000122cdd14227aec38bcc416b
4
+ data.tar.gz: eaf401cf08d69d21f74d40de25f97474aa73d696
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- MzY1NzA5OTM3ZGFhOWM3NDM1NzEwMjIyMDA4MzNkNDAwODYwZjFkYjQ4MWIx
10
- MzcyOGFlOGFhNTEzZjU3YTdmMDg0N2Q0NjYxODUwYTI4ZGI5OGNkOGExM2Yw
11
- YzA5YjY3YjgxYzZjMTNkNjIyYmNiNTRhMjZlNmI1MmY5ODQ2ZjU=
12
- data.tar.gz: !binary |-
13
- ZDIzMTkxNzkzMzFmNTljMjJhNjA1MGI2Yzk2NTFkZmZhYjI2ZGMwZjNhZGRm
14
- YzY0NmU2YWU1MWE4NThjMjRhZDFhZTI5MDU4NWE5NjI3NmFmMWU0ZGRiNjVi
15
- Y2RmZjgxZDNkNjIyZDMwNDk1NDEyZmU1ZjhlZDE4MGZhZDQ5OWU=
6
+ metadata.gz: d80906483c8db621505162efbe96531e6c73b73b158b76e1e59a09687b8039a02dd45696006a8887502c07eee5404dc426ba6a96f2d2d90b377484b927219615
7
+ data.tar.gz: a413157cbdad632905f84e0f034bef262ebbaafd8cc00a128a4b8897ed65e4b648544c2e40a5006feebe6ef4ba99247ba5891de4f617347f4491f1bef3e1b664
data/Gemfile CHANGED
@@ -2,3 +2,10 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in aws-cfn-dsl.gemspec
4
4
  gemspec
5
+
6
+ gem 'cloudformation-ruby-dsl', :path => '../cloudformation-ruby-dsl', :group => :development
7
+ gem 'slop', :group => :development
8
+
9
+ gem 'json_pure', :group => :development
10
+ gem 'bundler', '~> 1.5', :group => :development
11
+ gem 'rake', :group => :development
data/aws-cfn-dsl.gemspec CHANGED
@@ -18,11 +18,9 @@ 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"
22
- spec.add_dependency "json"
23
- spec.add_dependency 'cloudformation-ruby-dsl', "~> 0.4", ">= 0.4.2"
21
+ spec.add_dependency 'awesome_print'
22
+ spec.add_dependency 'json'
23
+ spec.add_dependency 'cloudformation-ruby-dsl', '~> 0.4', '>= 0.4.3'
24
+ spec.add_dependency "slop"
24
25
 
25
- spec.add_development_dependency "json_pure"
26
- spec.add_development_dependency 'bundler', "~> 1.5"
27
- spec.add_development_dependency 'rake'
28
26
  end
data/aws-cfn-dsl.iml CHANGED
@@ -3,24 +3,18 @@
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="$MODULE_DIR$/test" />
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
- <content url="file://$MODULE_DIR$">
18
- <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
19
- </content>
20
- <orderEntry type="jdk" jdkName="RVM: ruby-1.9.3-p545 [aws-cfn-dsl]" jdkType="RUBY_SDK" />
8
+ <content url="file://$MODULE_DIR$" />
9
+ <orderEntry type="inheritedJdk" />
21
10
  <orderEntry type="sourceFolder" forTests="false" />
22
- <orderEntry type="library" scope="PROVIDED" name="bundler (v1.6.1, RVM: ruby-1.9.3-p545 [aws-cfn-dsl]) [gem]" level="application" />
23
- <orderEntry type="library" scope="PROVIDED" name="rake (v0.9.2.2, RVM: ruby-1.9.3-p545 [aws-cfn-dsl]) [gem]" level="application" />
11
+ <orderEntry type="module" module-name="cloudformation-ruby-dsl" />
12
+ <orderEntry type="library" scope="PROVIDED" name="awesome_print (v1.2.0, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
13
+ <orderEntry type="library" scope="PROVIDED" name="detabulator (v0.1.0, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
14
+ <orderEntry type="library" scope="PROVIDED" name="json (v1.8.1, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
15
+ <orderEntry type="library" scope="PROVIDED" name="json_pure (v1.8.1, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
16
+ <orderEntry type="library" scope="PROVIDED" name="rake (v10.3.2, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
17
+ <orderEntry type="library" scope="PROVIDED" name="slop (v3.5.0, RVM: ruby-1.9.3-p547 [aws-cfn-compile]) [gem]" level="application" />
24
18
  </component>
25
19
  </module>
26
20
 
data/bin/cfn-dsl ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
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
12
+ # Borrowing from "whiches" gem ...
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
25
+
26
+ end
27
+ add_path = File.expand_path(File.join(path, "..", "lib"))
28
+ $:.unshift(add_path)
29
+
30
+ require 'aws/cfn/dsl'
31
+ Aws::Cfn::Dsl::Main.new.run
data/lib/aws/cfn/dsl.rb CHANGED
@@ -1,9 +1,7 @@
1
- require "aws/cfn/dsl/version"
1
+ require 'json'
2
+ require 'ap'
3
+ require 'yaml'
2
4
 
3
- module Aws
4
- module Cfn
5
- module Dsl
6
- # Your code goes here...
7
- end
8
- end
9
- end
5
+ require "aws/cfn/dsl/version"
6
+ require "aws/cfn/dsl/base"
7
+ require "aws/cfn/dsl/main"
@@ -0,0 +1,355 @@
1
+ require "aws/cfn/decompiler"
2
+ require "aws/cfn/dsl/fncall"
3
+
4
+ module Aws
5
+ module Cfn
6
+ module Dsl
7
+ class Base < Aws::Cfn::DeCompiler::Base
8
+
9
+ def initialize
10
+ @output = []
11
+ end
12
+
13
+ def simplify(val)
14
+ if val.is_a?(Hash)
15
+ val = Hash[val.map { |k,v| [k, simplify(v)] }]
16
+ if val.length != 1
17
+ val
18
+ else
19
+ k, v = val.entries[0]
20
+ case
21
+ # CloudFormation functions
22
+ # when k == 'Fn::Base64'
23
+ # FnCall.new 'base64', [v], true
24
+ # when k == 'Fn::FindInMap'
25
+ # FnCall.new 'find_in_map', v
26
+ # when k == 'Fn::GetAtt'
27
+ # FnCall.new 'get_att', v
28
+ # when k == 'Fn::GetAZs'
29
+ # FnCall.new 'get_azs', v != '' ? [v] : []
30
+ # when k == 'Fn::Join'
31
+ # FnCall.new 'join', [v[0]] + v[1], true
32
+ # when k == 'Fn::Select'
33
+ # FnCall.new 'select', v
34
+ # when k == 'Ref' && v == 'AWS::Region'
35
+ # FnCall.new 'aws_region', []
36
+ # when k == 'Ref' && v == 'AWS::StackName'
37
+ # FnCall.new 'aws_stack_name', []
38
+ # CloudFormation internal references
39
+ # when k == 'Ref'
40
+ # FnCall.new 'ref', [v]
41
+ # Aws::Cfn::DSL functions
42
+ when k.match(%r'^Aws::Cfn::DSL')
43
+ FnCall.new k, [v]
44
+ else
45
+ val
46
+ end
47
+ end
48
+ elsif val.is_a?(Array)
49
+ val.map { |v| simplify(v) }
50
+ else
51
+ val
52
+ end
53
+ end
54
+
55
+ def pprint(val)
56
+ case detect_type(val)
57
+ when :template
58
+ pprint_cfn_template(val)
59
+ when :parameter
60
+ pprint_cfn_section 'parameter', 'TODO', val, 'Parameters'
61
+ when :resource
62
+ pprint_cfn_resource 'TODO', val
63
+ when :parameters
64
+ val.each { |k, v| pprint_cfn_section 'parameter', k, v, 'Parameters' }
65
+ when :resources
66
+ val.each { |k, v| pprint_cfn_resource k, v }
67
+ else
68
+ pprint_value(val, '')
69
+ end
70
+ end
71
+
72
+ protected
73
+
74
+ def write(*s)
75
+ if s.is_a?(Array)
76
+ s = s.join('')
77
+ end
78
+ if @output.size > 0
79
+ @output[0].write s
80
+ else
81
+ print s
82
+ end
83
+ end
84
+
85
+ def writeln(s='')
86
+ if @output.size > 0
87
+ @output[0].puts s
88
+ else
89
+ puts s
90
+ end
91
+ end
92
+
93
+ # Attempt to figure out what fragment of the template we have. This is imprecise and can't
94
+ # detect Mappings and Outputs sections reliably, so it doesn't attempt to.
95
+ def detect_type(val)
96
+ if val.is_a?(Hash) && val['AWSTemplateFormatVersion']
97
+ :template
98
+ elsif val.is_a?(Hash) && /^(String|Number)$/ =~ val['Type']
99
+ :parameter
100
+ elsif val.is_a?(Hash) && val['Type']
101
+ :resource
102
+ elsif val.is_a?(Hash) && val.values.all? { |v| detect_type(v) == :parameter }
103
+ :parameters
104
+ elsif val.is_a?(Hash) && val.values.all? { |v| detect_type(v) == :resource }
105
+ :resources
106
+ end
107
+ end
108
+
109
+ def pprint_cfn_template(tpl)
110
+ open_output('',File.basename(@opts[:template].gsub(%r'\.(json|yaml|rb)'i,'')))
111
+ writeln "#!/usr/bin/env ruby"
112
+ writeln
113
+ if @opts[:output]
114
+ writeln "$:.unshift(File.dirname(__FILE__))"
115
+ # noinspection RubyExpressionInStringInspection
116
+ writeln '$:.unshift File.absolute_path("#{File.dirname(__FILE__)}/../lib")'
117
+ end
118
+ writeln "require 'bundler/setup'"
119
+ writeln "require 'aws/cfn/dsl/template'"
120
+ #writeln "require 'cloudformation-ruby-dsl/spotprice'"
121
+ # writeln "require 'cloudformation-ruby-dsl/table'"
122
+ writeln
123
+ writeln "template do"
124
+ writeln
125
+ tpl.each do |section, v|
126
+ case section
127
+ when 'Parameters'
128
+ when 'Mappings'
129
+ when 'Resources'
130
+ when 'Outputs'
131
+ else
132
+ write " value #{fmt_key(section)} => "
133
+ pprint_value v, ' '
134
+ writeln
135
+ writeln
136
+ end
137
+ end
138
+ %w(Mappings Parameters Resources Outputs).each do |section|
139
+ writeln " # #{section}"
140
+ v = tpl[section]
141
+ case section
142
+ when 'Parameters'
143
+ v.each { |name, options| pprint_cfn_section 'parameter', name, options, 'Parameters' }
144
+ when 'Mappings'
145
+ v.each { |name, options| pprint_cfn_section 'mapping', name, options, 'Mappings' }
146
+ when 'Resources'
147
+ v.each { |name, options| pprint_cfn_resource name, options }
148
+ when 'Outputs'
149
+ v.each { |name, options| pprint_cfn_section 'output', name, options, 'Outputs' }
150
+ else
151
+ raise "Internal Error: Unexpected section '#{section}'"
152
+ end
153
+ writeln
154
+ end
155
+ writeln "end.exec!"
156
+ end
157
+
158
+ def open_output(subdir,name)
159
+ if @opts[:output]
160
+ file = rb_file(subdir, name)
161
+
162
+ @output.unshift File.open(file, 'w')
163
+ end
164
+ end
165
+
166
+ def add_brick(subdir,name)
167
+ if @opts[:output]
168
+ #file = rb_file(subdir, name).gsub(%r'^#{@opts[:output]}/','')
169
+ #writeln " file '#{file}'"
170
+ s = subdir.downcase.gsub(%r's$','')
171
+ writeln " #{s} '#{name}'"
172
+ end
173
+ end
174
+
175
+ def rb_file(subdir, name)
176
+ path = File.join(@opts[:output], subdir)
177
+ unless File.directory? path
178
+ Dir.mkdir path
179
+ end
180
+ file = File.join(path, "#{name}.rb")
181
+ end
182
+
183
+ def close_output()
184
+ if @opts[:output] and @output.size > 0
185
+ fp = @output.shift
186
+ fp.close
187
+ end
188
+ end
189
+
190
+ def pprint_cfn_section(section, name, options, subdir)
191
+ open_output(subdir,name)
192
+ write " #{section} #{fmt_string(name)}"
193
+ indent = ' ' + (' ' * section.length) + ' '
194
+ options.each do |k, v|
195
+ writeln ","
196
+ write indent, fmt_key(k), " => "
197
+ pprint_value v, indent
198
+ end
199
+ writeln
200
+ writeln
201
+ close_output
202
+ add_brick(subdir,name)
203
+ end
204
+
205
+ def pprint_cfn_resource(name, options)
206
+ subdir = 'Resources'
207
+ open_output(subdir,name)
208
+ write " resource #{fmt_string(name)}"
209
+ indent = ' '
210
+ options.each do |k, v|
211
+ unless k == 'Properties'
212
+ write ", #{fmt_key(k)} => #{fmt(v)}"
213
+ end
214
+ end
215
+ if options.key?('Properties')
216
+ write ", #{fmt_key('Properties')} => "
217
+ pprint_value options['Properties'], indent
218
+ end
219
+ writeln
220
+ writeln
221
+ close_output
222
+ add_brick(subdir,name)
223
+ end
224
+
225
+ def pprint_value(val, indent)
226
+ # Prefer to write the value on a single line if it's reasonable to do so
227
+ single_line = is_single_line(val) || is_single_line_hack(val)
228
+ if single_line && !is_multi_line_hack(val)
229
+ s = fmt(val)
230
+ if s.length < 120 || is_single_line_hack(val)
231
+ write s
232
+ return
233
+ end
234
+ end
235
+
236
+ # Print the value across multiple lines
237
+ if val.is_a?(Hash)
238
+ writeln "{"
239
+ val.each do |k, v|
240
+ write "#{indent} #{fmt_key(k)} => "
241
+ pprint_value v, indent + ' '
242
+ writeln ","
243
+ end
244
+ write "#{indent}}"
245
+
246
+ elsif val.is_a?(Array)
247
+ writeln "["
248
+ val.each do |v|
249
+ write "#{indent} "
250
+ pprint_value v, indent + ' '
251
+ writeln ","
252
+ end
253
+ write "#{indent}]"
254
+
255
+ elsif val.is_a?(FnCall) && val.multiline
256
+ write val.name, "("
257
+ args = val.arguments
258
+ sep = ''
259
+ sub_indent = indent + ' '
260
+ if val.name == 'join' && args.length > 1
261
+ pprint_value args[0], indent + ' '
262
+ args = args[1..-1]
263
+ sep = ','
264
+ sub_indent = indent + ' '
265
+ end
266
+ unless args.empty?
267
+ args.each do |v|
268
+ writeln sep
269
+ write sub_indent
270
+ pprint_value v, sub_indent
271
+ sep = ','
272
+ end
273
+ if val.name == 'join' && args.length > 1
274
+ write ","
275
+ end
276
+ writeln
277
+ write indent
278
+ end
279
+ write ")"
280
+
281
+ else
282
+ write fmt(val)
283
+ end
284
+ end
285
+
286
+ def is_single_line(val)
287
+ if val.is_a?(Hash)
288
+ is_single_line(val.values)
289
+ elsif val.is_a?(Array)
290
+ val.empty? ||
291
+ (val.length == 1 && is_single_line(val[0]) && !val[0].is_a?(Hash)) ||
292
+ val.all? { |v| v.is_a?(String) }
293
+ else
294
+ true
295
+ end
296
+ end
297
+
298
+ # Emo-specific hacks to force the desired output formatting
299
+ def is_single_line_hack(val)
300
+ is_array_of_strings_hack(val)
301
+ end
302
+
303
+ # Emo-specific hacks to force the desired output formatting
304
+ def is_multi_line_hack(val)
305
+ val.is_a?(Hash) && val['email']
306
+ end
307
+
308
+ # Emo-specific hacks to force the desired output formatting
309
+ def is_array_of_strings_hack(val)
310
+ val.is_a?(Array) && val.all? { |v| v.is_a?(String) } && val.grep(/\s/).empty? && (
311
+ val.include?('autoscaling:EC2_INSTANCE_LAUNCH') ||
312
+ val.include?('m1.small')
313
+ )
314
+ end
315
+
316
+ def fmt(val)
317
+ if val == {}
318
+ '{}'
319
+ elsif val == []
320
+ '[]'
321
+ elsif val.is_a?(Hash)
322
+ '{ ' + (val.map { |k,v| fmt_key(k) + ' => ' + fmt(v) }).join(', ') + ' }'
323
+ elsif val.is_a?(Array) && is_array_of_strings_hack(val)
324
+ '%w(' + val.join(' ') + ')'
325
+ elsif val.is_a?(Array)
326
+ '[ ' + (val.map { |v| fmt(v) }).join(', ') + ' ]'
327
+ elsif val.is_a?(FnCall) && val.arguments.empty?
328
+ val.name
329
+ elsif val.is_a?(FnCall)
330
+ val.name + '(' + (val.arguments.map { |v| fmt(v) }).join(', ') + ')'
331
+ elsif val.is_a?(String)
332
+ fmt_string(val)
333
+ elsif val == nil
334
+ 'null'
335
+ else
336
+ val.to_s # number, boolean
337
+ end
338
+ end
339
+
340
+ def fmt_key(s)
341
+ ':' + (/^[a-zA-Z_]\w+$/ =~ s ? s : fmt_string(s)) # returns a symbol like :Foo or :'us-east-1'
342
+ end
343
+
344
+ def fmt_string(s)
345
+ if /[^ -~]/ =~ s
346
+ s.dump # contains, non-ascii or control char, return double-quoted string
347
+ else
348
+ '\'' + s.gsub(/([\\'])/, '\\\\\1') + '\'' # return single-quoted string, escape \ and '
349
+ end
350
+ end
351
+
352
+ end
353
+ end
354
+ end
355
+ end