cfndsl 0.0.5 → 0.0.6

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.
@@ -0,0 +1,151 @@
1
+ require 'cfndsl/JSONable'
2
+
3
+ module CfnDsl
4
+ class CloudFormationTemplate < JSONable
5
+ ##
6
+ # Handles the overall template object
7
+ dsl_attr_setter :AWSTemplateFormatVersion, :Description
8
+ dsl_content_object :Parameter, :Output, :Resource, :Mapping
9
+
10
+ def initialize
11
+ @AWSTemplateFormatVersion = "2010-09-09"
12
+ end
13
+
14
+ def generateOutput()
15
+ puts self.to_json # uncomment for pretty printing # {:space => ' ', :indent => ' ', :object_nl => "\n", :array_nl => "\n" }
16
+ end
17
+
18
+ @@globalRefs = {
19
+ "AWS::NotificationARNs" => 1,
20
+ "AWS::Region" => 1,
21
+ "AWS::StackId" => 1,
22
+ "AWS::StackName" => 1
23
+ }
24
+
25
+ def isValidRef( ref, origin=nil)
26
+ ref = ref.to_s
27
+ origin = origin.to_s if origin
28
+
29
+ return true if @@globalRefs.has_key?( ref )
30
+
31
+ return true if @Parameters && @Parameters.has_key?( ref )
32
+
33
+ if( @Resources.has_key?( ref ) ) then
34
+ return !origin || !@_ResourceRefs || !@_ResourceRefs[ref] || !@_ResourceRefs[ref].has_key?(origin)
35
+ end
36
+
37
+ return false
38
+ end
39
+
40
+ def checkRefs()
41
+ invalids = []
42
+ @_ResourceRefs = {}
43
+ if(@Resources) then
44
+ @Resources.keys.each do |resource|
45
+ @_ResourceRefs[resource.to_s] = @Resources[resource].references({})
46
+ end
47
+ @_ResourceRefs.keys.each do |origin|
48
+ @_ResourceRefs[origin].keys.each do |ref|
49
+ invalids.push "Invalid Reference: Resource #{origin} refers to #{ref}" unless isValidRef(ref,origin)
50
+ end
51
+ end
52
+ end
53
+ outputRefs = {}
54
+ if(@Outputs) then
55
+ @Outputs.keys.each do |resource|
56
+ outputRefs[resource.to_s] = @Outputs[resource].references({})
57
+ end
58
+ outputRefs.keys.each do |origin|
59
+ outputRefs[origin].keys.each do |ref|
60
+ invalids.push "Invalid Reference: Output #{origin} refers to #{ref}" unless isValidRef(ref,nil)
61
+ end
62
+ end
63
+ end
64
+ return invalids.length>0 ? invalids : nil
65
+ end
66
+
67
+
68
+ names = {}
69
+ nametypes = {}
70
+ CfnDsl::Types::AWS_Types["Resources"].each_pair do |name, type|
71
+ # Subclass ResourceDefintion and generate property methods
72
+ klass = Class.new(CfnDsl::ResourceDefinition)
73
+ klassname = name.split("::").join("_")
74
+ CfnDsl::Types.const_set( klassname, klass )
75
+ type["Properties"].each_pair do |pname, ptype|
76
+ if( ptype.instance_of? String )
77
+ create_klass = CfnDsl::Types.const_get( ptype );
78
+ klass.class_eval do
79
+ define_method(pname) do |*values, &block|
80
+ if( values.length <1 ) then
81
+ values.push create_klass.new
82
+ end
83
+ @Properties ||= {}
84
+ @Properties[pname] ||= CfnDsl::PropertyDefinition.new( *values )
85
+ @Properties[pname].value.instance_eval &block if block
86
+ @Properties[pname].value
87
+ end
88
+ end
89
+ else
90
+ #Array version
91
+ sing_name = CfnDsl::Plurals.singularize( pname )
92
+ create_klass = CfnDsl::Types.const_get( ptype[0] )
93
+ klass.class_eval do
94
+ define_method(pname) do |*values, &block|
95
+ if( values.length < 1 ) then
96
+ values.push []
97
+ end
98
+ @Properties ||= {}
99
+ @Properties[pname] ||= PropertyDefinition.new( *values )
100
+ @Properties[pname].value.instance_eval &block if block
101
+ @Properties[pname].value
102
+ end
103
+
104
+ define_method(sing_name) do |value=nil, &block|
105
+ @Properties ||= {}
106
+ @Properties[pname] ||= PropertyDefinition.new( [] )
107
+ if( !value ) then
108
+ value = create_klass.new
109
+ end
110
+ @Properties[pname].value.push value
111
+ value.instance_eval &block if block
112
+ value
113
+ end
114
+
115
+ end
116
+ end
117
+
118
+ end
119
+ parts = name.split "::"
120
+ while( parts.length > 0)
121
+ abreve_name = parts.join "_"
122
+ if( names.has_key? abreve_name ) then
123
+ # this only happens if there is an ambiguity
124
+ names[abreve_name] = nil
125
+ else
126
+ names[abreve_name] = CfnDsl::Types.const_get(klassname)
127
+ nametypes[abreve_name] = name
128
+ end
129
+ parts.shift
130
+ end
131
+
132
+
133
+ end
134
+
135
+ #Define property setter methods for each of the unambiguous type names
136
+ names.each_pair do |typename,type|
137
+ if(type) then
138
+ class_eval do
139
+ define_method( typename) do |name,*values,&block|
140
+ name = name.to_s
141
+ @Resources ||= {}
142
+ resource = @Resources[name] ||= type.new(*values)
143
+ resource.instance_eval &block if block
144
+ resource.instance_variable_set( "@Type", nametypes[typename] )
145
+ resource
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,33 @@
1
+ module CfnDsl
2
+ module Errors
3
+ @@errors = []
4
+
5
+ def self.error( err, idx=nil )
6
+ if(idx.nil?) then
7
+ @@errors.push ( err + "\n" + caller.join("\n") + "\n" )
8
+ else
9
+ if( m = caller[idx].match(/^.*?:\d+:/ ) ) then
10
+ err_loc = m[0];
11
+ else
12
+ err_loc = caller[idx]
13
+ end
14
+
15
+ @@errors.push ( err_loc + " " + err + "\n" )
16
+ end
17
+ end
18
+
19
+ def self.clear()
20
+ @@errors = []
21
+ end
22
+
23
+ def self.report()
24
+ @@errors.each do |err|
25
+ puts err
26
+ end
27
+ end
28
+
29
+ def self.errors?()
30
+ return @@errors.length > 0
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,161 @@
1
+ require 'cfndsl/Errors'
2
+ require 'cfndsl/RefCheck'
3
+
4
+ module CfnDsl
5
+ module Functions
6
+
7
+ def Ref(value)
8
+ ##
9
+ # Equivalent to the CloudFormation template built in function Ref
10
+ RefDefinition.new(value)
11
+ end
12
+
13
+ def FnBase64( value )
14
+ ##
15
+ # Equivalent to the CloudFormation template built in function Fn::Base64
16
+ Fn.new("Base64", value);
17
+ end
18
+
19
+ def FnFindInMap( map, key, value)
20
+ ##
21
+ # Equivalent to the CloudFormation template built in function Fn::FindInMap
22
+ Fn.new("FindInMap", [map,key,value] )
23
+ end
24
+
25
+ def FnGetAtt(logicalResource, attribute)
26
+ ##
27
+ # Equivalent to the CloudFormation template built in function Fn::GetAtt
28
+ Fn.new( "GetAtt", [logicalResource, attribute], [logicalResource] )
29
+ end
30
+
31
+ def FnGetAZs(region)
32
+ ##
33
+ # Equivalent to the CloudFormation template built in function Fn::GetAZs
34
+ Fn.new("GetAZs", region)
35
+ end
36
+
37
+ def FnJoin(string, array)
38
+ ##
39
+ # Equivalent to the CloudFormation template built in function Fn::Join
40
+ Fn.new("Join", [ string, array] )
41
+ end
42
+
43
+ def FnFormat(string, *arguments)
44
+ ##
45
+ # Usage
46
+ # FnFormat( "This is a %0. It is 100%% %1","test", "effective")
47
+ #
48
+ # This will generate a call to Fn::Join that when evaluated will produce
49
+ # the string "This is a test. It is 100% effective."
50
+ #
51
+ # Think of this as %0,%1, etc in the format string being replaced by the
52
+ # corresponding arguments given after the format string. '%%' is replaced
53
+ # by the '%' character.
54
+ #
55
+ # The actual Fn::Join call corresponding to the above FnFormat call would be
56
+ # {"Fn::Join": ["",["This is a ","test",". It is 100","%"," ","effective"]]}
57
+ array = [];
58
+ string.scan( /(.*?)(%(%|\d+)|\z)/m ) do |x,y|
59
+ array.push $1 if $1 && $1 != ""
60
+ if( $3 == '%' ) then
61
+ array.push '%'
62
+ elsif( $3 ) then
63
+ array.push arguments[ $3.to_i ]
64
+ end
65
+ end
66
+
67
+ Fn.new("Join", ["", array])
68
+ end
69
+ end
70
+
71
+ class JSONable
72
+ ##
73
+ # This is the base class for just about everything useful in the
74
+ # DSL. It knows how to turn DSL Objects into the corresponding
75
+ # json, and it lets you create new built in function objects
76
+ # from inside the context of a dsl object.
77
+
78
+ include Functions
79
+ extend Functions
80
+ include RefCheck
81
+
82
+ def to_json(*a)
83
+ ##
84
+ # Use instance variables to build a json object. Instance
85
+ # variables that begin with a single underscore are elided.
86
+ # Instance variables that begin with two underscores have one of
87
+ # them removed.
88
+ hash = {}
89
+ self.instance_variables.each do |var|
90
+ name = var[1..-1]
91
+
92
+ if( name =~ /^__/ ) then
93
+ # if a variable starts with double underscore, strip one off
94
+ name = name[1..-1]
95
+ elsif( name =~ /^_/ ) then
96
+ # Hide variables that start with single underscore
97
+ name = nil
98
+ end
99
+
100
+ hash[name] = self.instance_variable_get var if name
101
+ end
102
+ hash.to_json(*a)
103
+ end
104
+
105
+ def ref_children
106
+ return self.instance_variables.map { |var| self.instance_variable_get var }
107
+ end
108
+
109
+ def declare(&block)
110
+ self.instance_eval &block if block_given?
111
+ end
112
+
113
+ def method_missing(meth,*args,&block)
114
+ if(args) then
115
+ arg = "(" + args.inspect[1..-2] + ")"
116
+ else
117
+ arg = ""
118
+ end
119
+ CfnDsl::Errors.error( "Undefined symbol: #{meth}#{arg}", 1 )
120
+ end
121
+ end
122
+
123
+
124
+ class Fn < JSONable
125
+ ##
126
+ # Handles all of the Fn:: objects
127
+ def initialize( function, argument, refs=[] )
128
+ @function = function
129
+ @argument = argument
130
+ @_refs = refs
131
+ end
132
+
133
+ def to_json(*a)
134
+ hash = {}
135
+ hash["Fn::#{@function}"] = @argument
136
+ hash.to_json(*a)
137
+ end
138
+
139
+ def get_references()
140
+ return @_refs
141
+ end
142
+
143
+ def ref_children
144
+ return [@argument]
145
+ end
146
+ end
147
+
148
+
149
+ class RefDefinition < JSONable
150
+ ##
151
+ # Handles the Ref objects
152
+ def initialize( value )
153
+ @Ref = value
154
+ end
155
+
156
+ def get_references()
157
+ [@Ref]
158
+ end
159
+ end
160
+
161
+ end
@@ -0,0 +1,25 @@
1
+ require 'cfndsl/JSONable'
2
+
3
+ module CfnDsl
4
+ class MappingDefinition < JSONable
5
+ ##
6
+ # Handles mapping objects
7
+ #
8
+ # Usage:
9
+ # Mapping("AWSRegionArch2AMI", {
10
+ # "us-east-1" => { "32" => "ami-6411e20d", "64" => "ami-7a11e213" },
11
+ # "us-west-1" => { "32" => "ami-c9c7978c", "64" => "ami-cfc7978a" },
12
+ # "eu-west-1" => { "32" => "ami-37c2f643", "64" => "ami-31c2f645" },
13
+ # "ap-southeast-1" => { "32" => "ami-66f28c34", "64" => "ami-60f28c32" },
14
+ # "ap-northeast-1" => { "32" => "ami-9c03a89d", "64" => "ami-a003a8a1" }
15
+ # })
16
+
17
+ def initialize(value)
18
+ @value = value
19
+ end
20
+
21
+ def to_json(*a)
22
+ @value.to_json(*a)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ require 'cfndsl/JSONable'
2
+
3
+ module CfnDsl
4
+
5
+ class MetadataDefinition < JSONable
6
+ ##
7
+ # Handles Metadata objects
8
+ end
9
+
10
+ end
@@ -0,0 +1,13 @@
1
+ require 'cfndsl/JSONable'
2
+
3
+ module CfnDsl
4
+ class OutputDefinition < JSONable
5
+ ##
6
+ # Handles Output objects
7
+ dsl_attr_setter :Value, :Description
8
+
9
+ def initialize( value=nil)
10
+ @Value = value if value
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ require 'cfndsl/JSONable'
2
+
3
+ module CfnDsl
4
+
5
+ class ParameterDefinition < JSONable
6
+ ##
7
+ # Handles input parameter objects
8
+ dsl_attr_setter :Type, :Default, :NoEcho, :AllowedValues, :AllowedPattern, :MaxLength, :MinLength, :MaxValue, :MinValue, :Description, :ConstraintDescription
9
+ def initialize
10
+ @Type = :String
11
+ end
12
+
13
+ def String
14
+ @Type = :String
15
+ end
16
+
17
+ def Number
18
+ @Type = :Number
19
+ end
20
+
21
+ def CommaDelimitedList
22
+ @Type = :CommaDelimitedList
23
+ end
24
+
25
+ def to_hash()
26
+ h = {}
27
+ h[:Type] = @Type;
28
+ h[:Default] = @Default if @Default
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,28 @@
1
+ module CfnDsl
2
+ module Plurals
3
+ ##
4
+ # Plural names for lists of content objects
5
+ #
6
+
7
+ @@plurals = {
8
+ "Metadata" => "Metadata",
9
+ "Property" => "Properties",
10
+ "Policy" => "Policies"
11
+ }
12
+
13
+ @@singles = {}
14
+ @@plurals.each_pair { |key,val| @@singles[val] = key }
15
+
16
+ def self.pluralize(name)
17
+ name = name.to_s
18
+ return @@plurals[name] if( @@plurals.has_key? name )
19
+ return "#{name}s"
20
+ end
21
+
22
+ def self.singularize(name)
23
+ name = name.to_s
24
+ return @@singles[name] if( @@singles.has_key? name )
25
+ return name[0..-2]
26
+ end
27
+ end
28
+ end