cfndsl 0.0.5 → 0.0.6

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