cfndsl 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|