cfndsl 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/cfndsl +66 -3
- data/lib/cfndsl.rb +82 -0
- data/lib/cfndsl/CloudFormationTemplate.rb +115 -6
- data/lib/cfndsl/Types.rb +162 -12
- data/lib/cfndsl/aws_types.yaml +20 -0
- data/lib/cfndsl/os_types.yaml +2427 -0
- metadata +31 -23
- checksums.yaml +0 -7
data/bin/cfndsl
CHANGED
@@ -1,4 +1,67 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
2
3
|
require 'cfndsl'
|
3
|
-
|
4
|
-
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
|
8
|
+
optparse = OptionParser.new do|opts|
|
9
|
+
opts.banner = "Usage: cfndsl [options] FILE"
|
10
|
+
|
11
|
+
# Define the options, and what they do
|
12
|
+
options[:output] = '-'
|
13
|
+
opts.on( '-o', '--output FILE', 'Write output to file' ) do |file|
|
14
|
+
options[:output] = file
|
15
|
+
end
|
16
|
+
|
17
|
+
options[:extras] = []
|
18
|
+
opts.on( '-y', '--yaml FILE', 'Import yaml file as local variables' ) do |file|
|
19
|
+
options[:extras].push([:yaml,File.expand_path(file)])
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on( '-r', '--ruby FILE', 'Evaluate ruby file before template' ) do |file|
|
23
|
+
options[:extras].push([:ruby,File.expand_path(file)])
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on( '-j', '--json FILE', 'Import json file as local variables' ) do |file|
|
27
|
+
options[:extras].push([:json,File.expand_path(file)])
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
opts.on( '-D', '--define "VARIABLE=VALUE"', 'Directly set local VARIABLE as VALUE' ) do |file|
|
32
|
+
options[:extras].push([:raw,file])
|
33
|
+
end
|
34
|
+
|
35
|
+
options[:verbose] = false
|
36
|
+
opts.on('-v', '--verbose', "Turn on verbose ouptut") do
|
37
|
+
options[:verbose] = true
|
38
|
+
end
|
39
|
+
|
40
|
+
# This displays the help screen, all programs are
|
41
|
+
# assumed to have this option.
|
42
|
+
opts.on( '-h', '--help', 'Display this screen' ) do
|
43
|
+
puts opts
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
optparse.parse!
|
49
|
+
unless ARGV[0] then
|
50
|
+
puts optparse.help
|
51
|
+
exit(1)
|
52
|
+
end
|
53
|
+
|
54
|
+
filename = File.expand_path(ARGV[0])
|
55
|
+
verbose = options[:verbose] && STDERR
|
56
|
+
|
57
|
+
model = CfnDsl::eval_file_with_extras(filename, options[:extras], verbose)
|
58
|
+
|
59
|
+
output = STDOUT
|
60
|
+
if options[:output] != '-' then
|
61
|
+
verbose.puts("Writing to #{options[:output]}") if verbose
|
62
|
+
output = File.open( File.expand_path(options[:output]), "w")
|
63
|
+
else
|
64
|
+
verbose.puts("Writing to STDOUT") if verbose
|
65
|
+
end
|
66
|
+
|
67
|
+
output.puts model.to_json
|
data/lib/cfndsl.rb
CHANGED
@@ -15,6 +15,75 @@ require 'cfndsl/Parameters'
|
|
15
15
|
require 'cfndsl/Outputs'
|
16
16
|
require 'cfndsl/CloudFormationTemplate'
|
17
17
|
|
18
|
+
module CfnDsl
|
19
|
+
def self.eval_file_with_extras(filename, extras = [], logstream = nil)
|
20
|
+
# This function handles the eval of the template file and returns the
|
21
|
+
# results. It does this with a ruby "eval", but it builds up a customized
|
22
|
+
# binding environment before it calls eval. The environment can be
|
23
|
+
# customized by passing a list of customizations in the extras parameter.
|
24
|
+
#
|
25
|
+
# These customizations are expressed as an array of pairs of
|
26
|
+
# (type,filename). They are evaluated in the order they appear in the
|
27
|
+
# extras array. The types are as follows
|
28
|
+
#
|
29
|
+
# :yaml - the second element is treated as a file name, which is loaded
|
30
|
+
# as a yaml file. The yaml file should contain a top level
|
31
|
+
# dictionary. Each of the keys of the top level dictionary is
|
32
|
+
# used as a local variable in the evalation context.
|
33
|
+
#
|
34
|
+
# :json - the second element is treated as a file name, which is loaded
|
35
|
+
# as a json file. The yaml file should contain a top level
|
36
|
+
# dictionary. Each of the keys of the top level dictionary is
|
37
|
+
# used as a local variable in the evalation context.
|
38
|
+
#
|
39
|
+
# :ruby - the second element is treated as a file name which is evaluated
|
40
|
+
# as a ruby file. Any assigments (or other binding affecting
|
41
|
+
# side effects) will persist into the context where the template
|
42
|
+
# is evaluated
|
43
|
+
#
|
44
|
+
# :raw - the seccond elements is treated as a ruby statement and is
|
45
|
+
# evaluated in the binding context, similar to the contents of
|
46
|
+
# a ruby file.
|
47
|
+
#
|
48
|
+
# Note that the order is important, as later extra sections can overwrite
|
49
|
+
# or even undo things that were done by earlier sections.
|
50
|
+
|
51
|
+
b = binding
|
52
|
+
extras.each do |pair|
|
53
|
+
type,file = pair
|
54
|
+
case type
|
55
|
+
when :yaml
|
56
|
+
logstream.puts("Loading YAML file #{file}") if logstream
|
57
|
+
parameters = YAML.load(File.read(file))
|
58
|
+
parameters.each do |k,v|
|
59
|
+
logstream.puts("Setting local variable #{k} to #{v}") if logstream
|
60
|
+
b.eval("#{k} = #{v.inspect}")
|
61
|
+
end
|
62
|
+
|
63
|
+
when :json
|
64
|
+
logstream.puts("Loading YAML file #{file}") if logstream
|
65
|
+
parameters = JSON.load(File.read(file))
|
66
|
+
parameters.each do |k,v|
|
67
|
+
logstream.puts("Setting local variable #{k} to #{v}") if logstream
|
68
|
+
b.eval("#{k} = #{v.inspect}")
|
69
|
+
end
|
70
|
+
|
71
|
+
when :ruby
|
72
|
+
logstream("Runnning ruby file #{file}") if logstream
|
73
|
+
b.eval(File.read(file), file)
|
74
|
+
|
75
|
+
when :raw
|
76
|
+
logstrame("Running raw ruby code #{file}") if logstream
|
77
|
+
b.eval(file, "raw code")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
logstream.puts("Loading template file #{filename}") if logstream
|
82
|
+
model = b.eval(File.read(filename), filename)
|
83
|
+
return model
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
18
87
|
def CloudFormation(&block)
|
19
88
|
x = CfnDsl::CloudFormationTemplate.new
|
20
89
|
x.declare(&block)
|
@@ -28,3 +97,16 @@ def CloudFormation(&block)
|
|
28
97
|
end
|
29
98
|
end
|
30
99
|
|
100
|
+
def Heat(&block)
|
101
|
+
x = CfnDsl::HeatTemplate.new
|
102
|
+
x.declare(&block)
|
103
|
+
invalid_references = x.checkRefs()
|
104
|
+
if( invalid_references ) then
|
105
|
+
abort invalid_references.join("\n")
|
106
|
+
elsif( CfnDsl::Errors.errors? ) then
|
107
|
+
abort CfnDsl::Errors.errors.join("\n")
|
108
|
+
else
|
109
|
+
return x
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
@@ -2,7 +2,7 @@ require 'cfndsl/JSONable'
|
|
2
2
|
require 'cfndsl/names'
|
3
3
|
|
4
4
|
module CfnDsl
|
5
|
-
class
|
5
|
+
class OrchestrationTemplate < JSONable
|
6
6
|
##
|
7
7
|
# Handles the overall template object
|
8
8
|
dsl_attr_setter :AWSTemplateFormatVersion, :Description
|
@@ -62,18 +62,26 @@ module CfnDsl
|
|
62
62
|
end
|
63
63
|
return invalids.length>0 ? invalids : nil
|
64
64
|
end
|
65
|
+
end
|
65
66
|
|
67
|
+
class CloudFormationTemplate < OrchestrationTemplate
|
68
|
+
def self.template_types
|
69
|
+
CfnDsl::AWSTypes::AWS_Types
|
70
|
+
end
|
71
|
+
def self.type_module
|
72
|
+
CfnDsl::AWSTypes
|
73
|
+
end
|
66
74
|
|
67
75
|
names = {}
|
68
76
|
nametypes = {}
|
69
|
-
|
77
|
+
self.template_types["Resources"].each_pair do |name, type|
|
70
78
|
# Subclass ResourceDefintion and generate property methods
|
71
79
|
klass = Class.new(CfnDsl::ResourceDefinition)
|
72
80
|
klassname = name.split("::").join("_")
|
73
|
-
|
81
|
+
type_module.const_set( klassname, klass )
|
74
82
|
type["Properties"].each_pair do |pname, ptype|
|
75
83
|
if( ptype.instance_of? String )
|
76
|
-
create_klass =
|
84
|
+
create_klass = type_module.const_get( ptype );
|
77
85
|
|
78
86
|
klass.class_eval do
|
79
87
|
CfnDsl::methodNames(pname) do |method|
|
@@ -91,7 +99,7 @@ module CfnDsl
|
|
91
99
|
else
|
92
100
|
#Array version
|
93
101
|
sing_name = CfnDsl::Plurals.singularize( pname )
|
94
|
-
create_klass =
|
102
|
+
create_klass = type_module.const_get( ptype[0] )
|
95
103
|
klass.class_eval do
|
96
104
|
CfnDsl::methodNames(pname) do |method|
|
97
105
|
define_method(method) do |*values, &block|
|
@@ -128,7 +136,7 @@ module CfnDsl
|
|
128
136
|
# this only happens if there is an ambiguity
|
129
137
|
names[abreve_name] = nil
|
130
138
|
else
|
131
|
-
names[abreve_name] =
|
139
|
+
names[abreve_name] = self.type_module.const_get(klassname)
|
132
140
|
nametypes[abreve_name] = name
|
133
141
|
end
|
134
142
|
parts.shift
|
@@ -154,5 +162,106 @@ module CfnDsl
|
|
154
162
|
end
|
155
163
|
end
|
156
164
|
end
|
165
|
+
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
class HeatTemplate < OrchestrationTemplate
|
170
|
+
def self.template_types
|
171
|
+
CfnDsl::OSTypes::OS_Types
|
172
|
+
end
|
173
|
+
def self.type_module
|
174
|
+
CfnDsl::OSTypes
|
175
|
+
end
|
176
|
+
|
177
|
+
names = {}
|
178
|
+
nametypes = {}
|
179
|
+
self.template_types["Resources"].each_pair do |name, type|
|
180
|
+
# Subclass ResourceDefintion and generate property methods
|
181
|
+
klass = Class.new(CfnDsl::ResourceDefinition)
|
182
|
+
klassname = name.split("::").join("_")
|
183
|
+
type_module.const_set( klassname, klass )
|
184
|
+
type["Properties"].each_pair do |pname, ptype|
|
185
|
+
if( ptype.instance_of? String )
|
186
|
+
create_klass = type_module.const_get( ptype );
|
187
|
+
|
188
|
+
klass.class_eval do
|
189
|
+
CfnDsl::methodNames(pname) do |method|
|
190
|
+
define_method(method) do |*values, &block|
|
191
|
+
if( values.length <1 ) then
|
192
|
+
values.push create_klass.new
|
193
|
+
end
|
194
|
+
@Properties ||= {}
|
195
|
+
@Properties[pname] ||= CfnDsl::PropertyDefinition.new( *values )
|
196
|
+
@Properties[pname].value.instance_eval &block if block
|
197
|
+
@Properties[pname].value
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
else
|
202
|
+
#Array version
|
203
|
+
sing_name = CfnDsl::Plurals.singularize( pname )
|
204
|
+
create_klass = type_module.const_get( ptype[0] )
|
205
|
+
klass.class_eval do
|
206
|
+
CfnDsl::methodNames(pname) do |method|
|
207
|
+
define_method(method) do |*values, &block|
|
208
|
+
if( values.length < 1 ) then
|
209
|
+
values.push []
|
210
|
+
end
|
211
|
+
@Properties ||= {}
|
212
|
+
@Properties[pname] ||= PropertyDefinition.new( *values )
|
213
|
+
@Properties[pname].value.instance_eval &block if block
|
214
|
+
@Properties[pname].value
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
CfnDsl::methodNames(sing_name) do |method|
|
219
|
+
define_method(method) do |value=nil, &block|
|
220
|
+
@Properties ||= {}
|
221
|
+
@Properties[pname] ||= PropertyDefinition.new( [] )
|
222
|
+
if( !value ) then
|
223
|
+
value = create_klass.new
|
224
|
+
end
|
225
|
+
@Properties[pname].value.push value
|
226
|
+
value.instance_eval &block if block
|
227
|
+
value
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
parts = name.split "::"
|
235
|
+
while( parts.length > 0)
|
236
|
+
abreve_name = parts.join "_"
|
237
|
+
if( names.has_key? abreve_name ) then
|
238
|
+
# this only happens if there is an ambiguity
|
239
|
+
names[abreve_name] = nil
|
240
|
+
else
|
241
|
+
names[abreve_name] = self.type_module.const_get(klassname)
|
242
|
+
nametypes[abreve_name] = name
|
243
|
+
end
|
244
|
+
parts.shift
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
#Define property setter methods for each of the unambiguous type names
|
249
|
+
names.each_pair do |typename,type|
|
250
|
+
if(type) then
|
251
|
+
class_eval do
|
252
|
+
CfnDsl::methodNames(typename) do |method|
|
253
|
+
define_method(method) do |name,*values,&block|
|
254
|
+
name = name.to_s
|
255
|
+
@Resources ||= {}
|
256
|
+
resource = @Resources[name] ||= type.new(*values)
|
257
|
+
resource.instance_eval &block if block
|
258
|
+
resource.instance_variable_set( "@Type", nametypes[typename] )
|
259
|
+
resource
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
157
265
|
end
|
266
|
+
|
158
267
|
end
|
data/lib/cfndsl/Types.rb
CHANGED
@@ -4,15 +4,15 @@ require 'cfndsl/Plurals'
|
|
4
4
|
require 'cfndsl/names'
|
5
5
|
|
6
6
|
module CfnDsl
|
7
|
-
module
|
8
|
-
|
7
|
+
module AWSTypes
|
9
8
|
aws_types = YAML::load( File.open( "#{File.dirname(__FILE__)}/aws_types.yaml") );
|
10
|
-
|
11
|
-
|
9
|
+
AWSTypes.const_set( "AWS_Types", aws_types);
|
10
|
+
|
12
11
|
# Do a little sanity checking - all of the types referenced in Resources
|
13
12
|
# should be represented in Types
|
14
13
|
aws_types["Resources"].keys.each do |resource_name|
|
15
14
|
#puts resource_name
|
15
|
+
|
16
16
|
resource = aws_types["Resources"][resource_name]
|
17
17
|
resource.values.each do |thing|
|
18
18
|
thing.values.each do |type|
|
@@ -36,9 +36,7 @@ module CfnDsl
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
|
42
40
|
# declare classes for all of the types with named methods for setting the values
|
43
41
|
class AWSType < JSONable
|
44
42
|
end
|
@@ -47,11 +45,11 @@ module CfnDsl
|
|
47
45
|
|
48
46
|
# Go through and declare all of the types first
|
49
47
|
aws_types["Types"].each_key do |typename|
|
50
|
-
if( !
|
51
|
-
klass =
|
48
|
+
if( ! AWSTypes.const_defined? typename ) then
|
49
|
+
klass = AWSTypes.const_set( typename, Class.new(AWSType ) )
|
52
50
|
classes[typename] = klass
|
53
51
|
else
|
54
|
-
classes[typename] =
|
52
|
+
classes[typename] = AWSTypes.const_get(typename)
|
55
53
|
end
|
56
54
|
end
|
57
55
|
|
@@ -62,7 +60,157 @@ module CfnDsl
|
|
62
60
|
if( typeval.respond_to? :each_pair ) then
|
63
61
|
typeval.each_pair do |attr_name, attr_type|
|
64
62
|
if( attr_type.kind_of? Array ) then
|
65
|
-
klass = CfnDsl::
|
63
|
+
klass = CfnDsl::AWSTypes.const_get( attr_type[0] )
|
64
|
+
variable = "@#{attr_name}".to_sym
|
65
|
+
|
66
|
+
method = CfnDsl::Plurals::singularize(attr_name)
|
67
|
+
methods = attr_name
|
68
|
+
all_methods = CfnDsl::methodNames(method) +
|
69
|
+
CfnDsl::methodNames(methods)
|
70
|
+
type.class_eval do
|
71
|
+
all_methods.each do |method_name|
|
72
|
+
define_method(method_name) do | value=nil, *rest, &block|
|
73
|
+
existing = instance_variable_get( variable )
|
74
|
+
# For no-op invocations, get out now
|
75
|
+
return existing if value.nil? and rest.length == 0 and ! block
|
76
|
+
|
77
|
+
# We are going to modify the value in some
|
78
|
+
# way, make sure that we have an array to mess
|
79
|
+
# with if we start with nothing
|
80
|
+
if( !existing ) then
|
81
|
+
existing = instance_variable_set( variable, [] )
|
82
|
+
end
|
83
|
+
|
84
|
+
# special case for just a block, no args
|
85
|
+
if( value.nil? and rest.length == 0 and block ) then
|
86
|
+
val = klass.new
|
87
|
+
existing.push val
|
88
|
+
value.instance_eval &block(val)
|
89
|
+
return existin
|
90
|
+
end
|
91
|
+
|
92
|
+
# Glue all of our parameters together into
|
93
|
+
# a giant array - flattening one level deep, if needed
|
94
|
+
array_params = []
|
95
|
+
if( value.kind_of? Array) then
|
96
|
+
value.each {|x| array_params.push x}
|
97
|
+
else
|
98
|
+
array_params.push value
|
99
|
+
end
|
100
|
+
if( rest.length > 0) then
|
101
|
+
rest.each do |v|
|
102
|
+
if( v.kind_of? Array ) then
|
103
|
+
array_params += rest
|
104
|
+
else
|
105
|
+
array_params.push v
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Here, if we were given multiple arguments either
|
111
|
+
# as method [a,b,c], method(a,b,c), or even
|
112
|
+
# method( a, [b], c) we end up with
|
113
|
+
# array_params = [a,b,c]
|
114
|
+
#
|
115
|
+
# array_params will have at least one item
|
116
|
+
# unless the user did something like pass in
|
117
|
+
# a bunch of empty arrays.
|
118
|
+
if block then
|
119
|
+
array_params.each do |val|
|
120
|
+
value = klass.new
|
121
|
+
existing.push value
|
122
|
+
value.instance_eval &block(val) if block
|
123
|
+
end
|
124
|
+
else
|
125
|
+
# List of parameters with no block -
|
126
|
+
# hope that the user knows what he is
|
127
|
+
# doing and stuff them into our existing
|
128
|
+
# array
|
129
|
+
array_params.each do |val|
|
130
|
+
existing.push value
|
131
|
+
end
|
132
|
+
end
|
133
|
+
return existing
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
else
|
138
|
+
klass = CfnDsl::AWSTypes.const_get( attr_type );
|
139
|
+
variable = "@#{attr_name}".to_sym
|
140
|
+
|
141
|
+
type.class_eval do
|
142
|
+
CfnDsl::methodNames(attr_name) do |method|
|
143
|
+
define_method(method) do | value=nil, *rest, &block |
|
144
|
+
value ||= klass.new
|
145
|
+
instance_variable_set( variable, value )
|
146
|
+
value.instance_eval &block if block
|
147
|
+
value
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
module OSTypes
|
158
|
+
os_types = YAML::load( File.open( "#{File.dirname(__FILE__)}/os_types.yaml") );
|
159
|
+
OSTypes.const_set( "OS_Types", os_types);
|
160
|
+
|
161
|
+
# Do a little sanity checking - all of the types referenced in Resources
|
162
|
+
# should be represented in Types
|
163
|
+
os_types["Resources"].keys.each do |resource_name|
|
164
|
+
#puts resource_name
|
165
|
+
|
166
|
+
resource = os_types["Resources"][resource_name]
|
167
|
+
resource.values.each do |thing|
|
168
|
+
thing.values.each do |type|
|
169
|
+
if( type.kind_of? Array ) then
|
170
|
+
type.each do | type |
|
171
|
+
puts "unknown type #{type}" unless os_types["Types"].has_key? type
|
172
|
+
end
|
173
|
+
else
|
174
|
+
puts "unknown type #{type}" unless os_types["Types"].has_key? type
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# All of the type values should also be references
|
181
|
+
|
182
|
+
os_types["Types"].values do |type|
|
183
|
+
if( type.respond_to? :values) then
|
184
|
+
type.values.each do |tv|
|
185
|
+
puts "unknown type #{tv}" unless os_types["Types"].has_key? tv
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# declare classes for all of the types with named methods for setting the values
|
191
|
+
class OSType < JSONable
|
192
|
+
end
|
193
|
+
|
194
|
+
classes = {}
|
195
|
+
|
196
|
+
# Go through and declare all of the types first
|
197
|
+
os_types["Types"].each_key do |typename|
|
198
|
+
if( ! OSTypes.const_defined? typename ) then
|
199
|
+
klass = OSTypes.const_set( typename, Class.new(OSType ) )
|
200
|
+
classes[typename] = klass
|
201
|
+
else
|
202
|
+
classes[typename] = OSTypes.const_get(typename)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Now go through them again and define attribute setter methods
|
207
|
+
classes.each_pair do |typename,type|
|
208
|
+
#puts typename
|
209
|
+
typeval = os_types["Types"][typename]
|
210
|
+
if( typeval.respond_to? :each_pair ) then
|
211
|
+
typeval.each_pair do |attr_name, attr_type|
|
212
|
+
if( attr_type.kind_of? Array ) then
|
213
|
+
klass = CfnDsl::OSTypes.const_get( attr_type[0] )
|
66
214
|
variable = "@#{attr_name}".to_sym
|
67
215
|
|
68
216
|
method = CfnDsl::Plurals::singularize(attr_name)
|
@@ -137,7 +285,7 @@ module CfnDsl
|
|
137
285
|
end
|
138
286
|
end
|
139
287
|
else
|
140
|
-
klass = CfnDsl::
|
288
|
+
klass = CfnDsl::OSTypes.const_get( attr_type );
|
141
289
|
variable = "@#{attr_name}".to_sym
|
142
290
|
|
143
291
|
type.class_eval do
|
@@ -155,5 +303,7 @@ module CfnDsl
|
|
155
303
|
end
|
156
304
|
end
|
157
305
|
end
|
306
|
+
|
307
|
+
|
158
308
|
end
|
159
309
|
|