cfndsl 0.0.2 → 0.0.4
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.
- data/lib/cfndsl.rb +226 -33
- metadata +2 -2
data/lib/cfndsl.rb
CHANGED
@@ -2,7 +2,21 @@ require 'json';
|
|
2
2
|
|
3
3
|
class Module
|
4
4
|
private
|
5
|
-
def
|
5
|
+
def dsl_attr_setter(*symbols)
|
6
|
+
##
|
7
|
+
# Create setter methods
|
8
|
+
#
|
9
|
+
# Usage:
|
10
|
+
# class Something
|
11
|
+
# dsl_attr_setter :Thing
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# Generates a setter method like this one for each symbol in *symbols:
|
15
|
+
#
|
16
|
+
# def Thing(value)
|
17
|
+
# @Thing = value
|
18
|
+
# end
|
19
|
+
#
|
6
20
|
symbols.each do |symbol|
|
7
21
|
class_eval do
|
8
22
|
define_method(symbol) do |value|
|
@@ -12,16 +26,43 @@ class Module
|
|
12
26
|
end
|
13
27
|
end
|
14
28
|
|
29
|
+
##
|
30
|
+
# Plural names for lists of content objects
|
31
|
+
#
|
32
|
+
|
15
33
|
@@plurals = {
|
16
34
|
:Metadata => :Metadata,
|
17
35
|
:Property => :Properties
|
18
36
|
}
|
19
37
|
|
20
|
-
def
|
38
|
+
def dsl_content_object(*symbols)
|
39
|
+
##
|
40
|
+
# Create object declaration methods.
|
41
|
+
#
|
42
|
+
# Usage:
|
43
|
+
# Class Something
|
44
|
+
# dsl_content_object :Stuff
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# Generates methods like this:
|
48
|
+
#
|
49
|
+
# def Stuff(name, *values, &block)
|
50
|
+
# @Stuffs ||= {}
|
51
|
+
# @Stuffs[name] ||= CfnDsl::#{symbol}Definition.new(*values)
|
52
|
+
# @Stuffs[name].instance_eval &block if block_given?
|
53
|
+
# return @Stuffs[name]
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# The effect of this is that you can then create named sub-objects
|
57
|
+
# from the main object. The sub objects get stuffed into a container
|
58
|
+
# on the main object, and the block is then evaluated in the context
|
59
|
+
# of the new object.
|
60
|
+
#
|
21
61
|
symbols.each do |symbol|
|
22
62
|
plural = @@plurals[symbol] || "#{symbol}s"
|
23
63
|
class_eval %Q/
|
24
64
|
def #{symbol} (name,*values,&block)
|
65
|
+
name = name.to_s
|
25
66
|
@#{plural} ||= {}
|
26
67
|
@#{plural}[name] ||= CfnDsl::#{symbol}Definition.new(*values)
|
27
68
|
@#{plural}[name].instance_eval &block if block_given?
|
@@ -33,17 +74,31 @@ end
|
|
33
74
|
|
34
75
|
|
35
76
|
module RefCheck
|
77
|
+
##
|
78
|
+
# This module defines some methods for walking the reference tree
|
79
|
+
# of various objects.
|
80
|
+
#
|
36
81
|
def references(refs)
|
37
|
-
|
38
|
-
|
39
|
-
|
82
|
+
##
|
83
|
+
# Build up a set of references.
|
84
|
+
#
|
85
|
+
raise "Circular reference" if @_visited
|
40
86
|
|
41
|
-
|
87
|
+
@_visited = true
|
88
|
+
|
89
|
+
if( self.respond_to?(:get_references ) ) then
|
90
|
+
self.get_references.each do |ref|
|
91
|
+
refs[ref.to_s] = 1
|
92
|
+
end
|
93
|
+
end
|
42
94
|
|
43
95
|
self.ref_children.each do |elem|
|
44
96
|
elem.references(refs) if elem.respond_to?(:references)
|
45
97
|
end
|
46
|
-
|
98
|
+
|
99
|
+
@_visited = nil
|
100
|
+
|
101
|
+
return refs
|
47
102
|
end
|
48
103
|
|
49
104
|
def ref_children
|
@@ -70,41 +125,100 @@ module CfnDsl
|
|
70
125
|
module Functions
|
71
126
|
|
72
127
|
def Ref(value)
|
128
|
+
##
|
129
|
+
# Equivalent to the CloudFormation template built in function Ref
|
73
130
|
RefDefinition.new(value)
|
74
131
|
end
|
75
132
|
|
76
133
|
def FnBase64( value )
|
134
|
+
##
|
135
|
+
# Equivalent to the CloudFormation template built in function Fn::Base64
|
77
136
|
Fn.new("Base64", value);
|
78
137
|
end
|
79
138
|
|
80
139
|
def FnFindInMap( map, key, value)
|
140
|
+
##
|
141
|
+
# Equivalent to the CloudFormation template built in function Fn::FindInMap
|
81
142
|
Fn.new("FindInMap", [map,key,value] )
|
82
143
|
end
|
83
144
|
|
84
145
|
def FnGetAtt(logicalResource, attribute)
|
85
|
-
|
146
|
+
##
|
147
|
+
# Equivalent to the CloudFormation template built in function Fn::GetAtt
|
148
|
+
Fn.new( "GetAtt", [logicalResource, attribute], [logicalResource] )
|
86
149
|
end
|
87
150
|
|
88
151
|
def FnGetAZs(region)
|
152
|
+
##
|
153
|
+
# Equivalent to the CloudFormation template built in function Fn::GetAZs
|
89
154
|
Fn.new("GetAZs", region)
|
90
155
|
end
|
91
156
|
|
92
157
|
def FnJoin(string, array)
|
158
|
+
##
|
159
|
+
# Equivalent to the CloudFormation template built in function Fn::Join
|
93
160
|
Fn.new("Join", [ string, array] )
|
94
161
|
end
|
162
|
+
|
163
|
+
def FnFormat(string, *arguments)
|
164
|
+
##
|
165
|
+
# Usage
|
166
|
+
# FnFormat( "This is a %0. It is 100%% %1","test", "effective")
|
167
|
+
#
|
168
|
+
# This will generate a call to Fn::Join that when evaluated will produce
|
169
|
+
# the string "This is a test. It is 100% effective."
|
170
|
+
#
|
171
|
+
# Think of this as %0,%1, etc in the format string being replaced by the
|
172
|
+
# corresponding arguments given after the format string. '%%' is replaced
|
173
|
+
# by the '%' character.
|
174
|
+
#
|
175
|
+
# The actual Fn::Join call corresponding to the above FnFormat call would be
|
176
|
+
# {"Fn::Join": ["",["This is a ","test",". It is 100","%"," ","effective"]]}
|
177
|
+
array = [];
|
178
|
+
string.scan( /(.*?)(%(%|\d+)|\Z)/m ) do |x,y|
|
179
|
+
array.push $1 if $1 && $1 != ""
|
180
|
+
if( $3 == '%' ) then
|
181
|
+
array.push '%'
|
182
|
+
elsif( $3 ) then
|
183
|
+
array.push arguments[ $3.to_i ]
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
Fn.new("Join", ["", array])
|
188
|
+
end
|
95
189
|
end
|
96
190
|
|
97
191
|
|
98
192
|
class JSONable
|
193
|
+
##
|
194
|
+
# This is the base class for just about everything useful in the
|
195
|
+
# DSL. It knows how to turn DSL Objects into the corresponding
|
196
|
+
# json, and it lets you create new built in function objects
|
197
|
+
# from inside the context of a dsl object.
|
198
|
+
|
99
199
|
include Functions
|
100
200
|
extend Functions
|
101
201
|
include RefCheck
|
102
202
|
|
103
203
|
def to_json(*a)
|
204
|
+
##
|
205
|
+
# Use instance variables to build a json object. Instance
|
206
|
+
# variables that begin with a single underscore are elided.
|
207
|
+
# Instance variables that begin with two underscores have one of
|
208
|
+
# them removed.
|
104
209
|
hash = {}
|
105
210
|
self.instance_variables.each do |var|
|
106
211
|
name = var[1..-1]
|
107
|
-
|
212
|
+
|
213
|
+
if( name =~ /^__/ ) then
|
214
|
+
# if a variable starts with double underscore, strip one off
|
215
|
+
name = name[1..-1]
|
216
|
+
elsif( name =~ /^_/ ) then
|
217
|
+
# Hide variables that start with single underscore
|
218
|
+
name = nil
|
219
|
+
end
|
220
|
+
|
221
|
+
hash[name] = self.instance_variable_get var if name
|
108
222
|
end
|
109
223
|
hash.to_json(*a)
|
110
224
|
end
|
@@ -120,9 +234,12 @@ module CfnDsl
|
|
120
234
|
end
|
121
235
|
|
122
236
|
class Fn < JSONable
|
123
|
-
|
237
|
+
##
|
238
|
+
# Handles all of the Fn:: objects
|
239
|
+
def initialize( function, argument, refs=[] )
|
124
240
|
@function = function
|
125
241
|
@argument = argument
|
242
|
+
@_refs = refs
|
126
243
|
end
|
127
244
|
|
128
245
|
def to_json(*a)
|
@@ -131,30 +248,38 @@ module CfnDsl
|
|
131
248
|
hash.to_json(*a)
|
132
249
|
end
|
133
250
|
|
134
|
-
def get_references(
|
135
|
-
|
251
|
+
def get_references()
|
252
|
+
return @_refs
|
136
253
|
end
|
137
254
|
|
138
255
|
def ref_children
|
139
256
|
return [@argument]
|
140
257
|
end
|
141
|
-
|
142
|
-
|
143
258
|
end
|
144
259
|
|
145
260
|
|
146
261
|
class RefDefinition < JSONable
|
262
|
+
##
|
263
|
+
# Handles the Ref objects
|
147
264
|
def initialize( value )
|
148
265
|
@Ref = value
|
149
266
|
end
|
150
267
|
|
151
|
-
def get_references(
|
152
|
-
|
268
|
+
def get_references()
|
269
|
+
[@Ref]
|
153
270
|
end
|
154
271
|
end
|
155
272
|
|
156
273
|
|
157
274
|
class PropertyDefinition < JSONable
|
275
|
+
##
|
276
|
+
# Handles property objects for Resources
|
277
|
+
#
|
278
|
+
# Usage
|
279
|
+
# Resource("aaa") {
|
280
|
+
# Property("propName", "propValue" )
|
281
|
+
# }
|
282
|
+
#
|
158
283
|
def initialize(value)
|
159
284
|
@value = value;
|
160
285
|
end
|
@@ -165,6 +290,18 @@ module CfnDsl
|
|
165
290
|
end
|
166
291
|
|
167
292
|
class MappingDefinition < JSONable
|
293
|
+
##
|
294
|
+
# Handles mapping objects
|
295
|
+
#
|
296
|
+
# Usage:
|
297
|
+
# Mapping("AWSRegionArch2AMI", {
|
298
|
+
# "us-east-1" => { "32" => "ami-6411e20d", "64" => "ami-7a11e213" },
|
299
|
+
# "us-west-1" => { "32" => "ami-c9c7978c", "64" => "ami-cfc7978a" },
|
300
|
+
# "eu-west-1" => { "32" => "ami-37c2f643", "64" => "ami-31c2f645" },
|
301
|
+
# "ap-southeast-1" => { "32" => "ami-66f28c34", "64" => "ami-60f28c32" },
|
302
|
+
# "ap-northeast-1" => { "32" => "ami-9c03a89d", "64" => "ami-a003a8a1" }
|
303
|
+
# })
|
304
|
+
|
168
305
|
def initialize(value)
|
169
306
|
@value = value
|
170
307
|
end
|
@@ -175,30 +312,39 @@ module CfnDsl
|
|
175
312
|
end
|
176
313
|
|
177
314
|
class ResourceDefinition < JSONable
|
178
|
-
|
179
|
-
|
315
|
+
##
|
316
|
+
# Handles Resource objects
|
317
|
+
dsl_attr_setter :Type, :DependsOn, :DeletionPolicy
|
318
|
+
dsl_content_object :Property, :Metadata
|
180
319
|
|
181
|
-
def get_references(
|
320
|
+
def get_references()
|
321
|
+
refs = []
|
182
322
|
if @DependsOn then
|
183
323
|
if( @DependsOn.respond_to?(:each) ) then
|
184
324
|
@DependsOn.each do |dep|
|
185
|
-
refs
|
325
|
+
refs.push dep
|
186
326
|
end
|
187
327
|
end
|
188
328
|
|
189
329
|
if( @DependsOn.instance_of?(String) ) then
|
190
|
-
refs
|
330
|
+
refs.push @DependsOn
|
191
331
|
end
|
192
332
|
end
|
333
|
+
|
334
|
+
refs
|
193
335
|
end
|
194
336
|
end
|
195
337
|
|
196
338
|
class MetadataDefinition < JSONable
|
339
|
+
##
|
340
|
+
# Handles Metadata objects
|
197
341
|
end
|
198
342
|
|
199
343
|
|
200
344
|
class ParameterDefinition < JSONable
|
201
|
-
|
345
|
+
##
|
346
|
+
# Handles input parameter objects
|
347
|
+
dsl_attr_setter :Type, :Default, :NoEcho, :AllowedValues, :AllowedPattern, :MaxLength, :MinLength, :MaxValue, :MinValue, :Description, :ConstraintDescription
|
202
348
|
def initialize
|
203
349
|
@Type = :String
|
204
350
|
end
|
@@ -223,7 +369,9 @@ module CfnDsl
|
|
223
369
|
end
|
224
370
|
|
225
371
|
class OutputDefinition < JSONable
|
226
|
-
|
372
|
+
##
|
373
|
+
# Handles Output objects
|
374
|
+
dsl_attr_setter :Value, :Description
|
227
375
|
|
228
376
|
def initialize( value=nil)
|
229
377
|
@Value = value if value
|
@@ -231,8 +379,10 @@ module CfnDsl
|
|
231
379
|
end
|
232
380
|
|
233
381
|
class CloudFormationTemplate < JSONable
|
234
|
-
|
235
|
-
|
382
|
+
##
|
383
|
+
# Handles the overall template object
|
384
|
+
dsl_attr_setter :AWSTemplateFormatVersion, :Description
|
385
|
+
dsl_content_object :Parameter, :Output, :Resource, :Mapping
|
236
386
|
|
237
387
|
def initialize
|
238
388
|
@AWSTemplateFormatVersion = "2010-09-09"
|
@@ -241,21 +391,64 @@ module CfnDsl
|
|
241
391
|
def generateOutput()
|
242
392
|
puts self.to_json # uncomment for pretty printing # {:space => ' ', :indent => ' ', :object_nl => "\n", :array_nl => "\n" }
|
243
393
|
end
|
244
|
-
|
245
|
-
|
246
|
-
|
394
|
+
|
395
|
+
@@globalRefs = { "AWS::Region" => 1 }
|
396
|
+
|
397
|
+
def isValidRef( ref, origin=nil)
|
398
|
+
ref = ref.to_s
|
399
|
+
origin = origin.to_s if origin
|
400
|
+
|
401
|
+
return true if @@globalRefs.has_key?( ref )
|
402
|
+
|
403
|
+
return true if @Parameters && @Parameters.has_key?( ref )
|
404
|
+
|
405
|
+
if( @Resources.has_key?( ref ) ) then
|
406
|
+
return !origin || !@_ResourceRefs || !@_ResourceRefs[ref] || !@_ResourceRefs[ref].has_key?(origin)
|
407
|
+
end
|
408
|
+
|
409
|
+
return false
|
410
|
+
end
|
411
|
+
|
412
|
+
def checkRefs()
|
413
|
+
invalids = []
|
414
|
+
@_ResourceRefs = {}
|
415
|
+
if(@Resources) then
|
416
|
+
@Resources.keys.each do |resource|
|
417
|
+
@_ResourceRefs[resource.to_s] = @Resources[resource].references({})
|
418
|
+
end
|
419
|
+
@_ResourceRefs.keys.each do |origin|
|
420
|
+
@_ResourceRefs[origin].keys.each do |ref|
|
421
|
+
invalids.push "Invalid Reference: Resource #{origin} refers to #{ref}" unless isValidRef(ref,origin)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
outputRefs = {}
|
426
|
+
if(@Outputs) then
|
427
|
+
@Outputs.keys.each do |resource|
|
428
|
+
outputRefs[resource.to_s] = @Outputs[resource].references({})
|
429
|
+
end
|
430
|
+
outputRefs.keys.each do |origin|
|
431
|
+
outputRefs[origin].keys.each do |ref|
|
432
|
+
invalids.push "Invalid Reference: Output #{origin} refers to #{ref}" unless isValidRef(ref,nil)
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
return invalids.length>0 ? invalids : nil
|
247
437
|
end
|
248
438
|
|
249
439
|
end
|
250
|
-
|
251
|
-
|
252
440
|
end
|
253
441
|
|
254
442
|
def CloudFormation(&block)
|
255
443
|
x = CfnDsl::CloudFormationTemplate.new
|
256
|
-
x.
|
257
|
-
x.
|
258
|
-
|
444
|
+
x.declare(&block)
|
445
|
+
invalid_references = x.checkRefs()
|
446
|
+
if( invalid_references ) then
|
447
|
+
puts invalid_references.join("\n");
|
448
|
+
exit(-1)
|
449
|
+
else
|
450
|
+
x.generateOutput
|
451
|
+
end
|
259
452
|
end
|
260
453
|
|
261
454
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cfndsl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-16 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: DSL for creating AWS Cloudformation templates
|
15
15
|
email: chris@howeville.com
|