cfndsl 0.10.2 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MTg3ZjhiNWQyYWZjY2Q5NmJiMWYzMGY4NjAzMmYwMTY1YTBjYjdkNg==
4
+ N2U4ZWRiYzQ2MTQ2OTAzMGFlNzM0YmY3N2Q2YWVmNDEzNDk3M2JiOQ==
5
5
  data.tar.gz: !binary |-
6
- ZGI5NmUyMDczZmI3ZTE3NTI0ZjBiZTgxYTEzNmVjNWZlZDE2ZTdmYQ==
6
+ MzczODNhYWYzOWZkNjMyZTYyZDQyNTExMmE4OGY3NDk3YTk1ZDVhOA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NmQ2NDBmNTRjYjU2NWJjNzhmMjQwM2QxN2ZhMjBlMDZhNTk1YTllMDFiN2E0
10
- MDRmOTIzMzcyZWQxMTlhOWUwZTYyMDU3YWY2MzQ0MzY5ODBkZTg1YTI2NGMx
11
- ODQ1ZjBiYWMxMzRiOWVmMzAyZjFkZGI5OTFjODhhZGU4NGFmMWE=
9
+ ODc4ODAyNjQ2NDYzMTBjMWIzYjk5NTMzYWJjMWRlZTI2MjcxN2UzMzNmZTVh
10
+ NmE5ZjY3MzI2NmUxMGZhZjE0ZTZhOTM4NDZjZmM1NGI5ZTFiMjdkY2E4MWVm
11
+ ZDU0MWE3MzhkZjU2ZWZjMTA2MWM1ZTM4NjM4NGIxN2EwMTNkNmU=
12
12
  data.tar.gz: !binary |-
13
- ZDBjZTVhYjdjNDFmNzAwYzcxMDdjOWE2M2JhYTliOWU1MWU5OWRmNTkzMTBm
14
- NjA0MzM5ZjE2YjMxNTI4MWU0NmJmMDUyZTliM2E3ZjAyYWQ5MDNmMmI0NzE0
15
- NjZjZjcxMWVhZWQyNjUyNmNlMjIxZmQwMzJiZTNmODA2MDUyYTg=
13
+ MDI2OThlM2M4NDQ3Njg4NmQ0ZjY2ODZlYjgxMTUzYzA3MGNmZjgzNWE4YjNk
14
+ MjczOTQwOGI4MjBjYTUyN2IyYTIyNDhlZGE0NTVmNDViNjRmZDA1NjhhY2Ix
15
+ ZTUyMzM5MjdmYzg1NWRlOTZjNTdkMmVjNWYyMTBhODk1MWM4MjE=
data/README.md CHANGED
@@ -85,6 +85,333 @@ chris@raspberrypi:~/git/cfndsl$ cfndsl test.rb | json_pp
85
85
  *Aside: that is correct - a significant amount of the development for
86
86
  this gem was done on a [Raspberry Pi](http://www.raspberrypi.org).*
87
87
 
88
+ ## Syntax
89
+
90
+ `cfndsl` comes with a number of helper methods defined on _each_ resource and/or the stack as a whole.
91
+
92
+ ### Template Metadata
93
+
94
+ Metadata is a special template section described [here](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html). The argument supplied must be JSON-able. Some CloudFormation features reference special keys if included in the `Metadata`, check the AWS documentation for specifics.
95
+
96
+ ```ruby
97
+ CloudFormation do
98
+ Metadata(foo: 'bar')
99
+
100
+ EC2_Instance(:myInstance) do
101
+ ImageId 'ami-12345678'
102
+ Type 't1.micro'
103
+ end
104
+ end
105
+ ```
106
+
107
+ ### Template Parameters
108
+
109
+ At a bare minumum, parameters need a name, and default to having Type `String`. Specify the parameter in the singular, not plural:
110
+
111
+ ```ruby
112
+ CloudFormation do
113
+ Parameter 'foo'
114
+ end
115
+ ```
116
+
117
+ ```json
118
+ {
119
+ "AWSTemplateFormatVersion": "2010-09-09",
120
+ "Parameters": {
121
+ "foo": {
122
+ "Type": "String"
123
+ }
124
+ }
125
+ }
126
+ ```
127
+
128
+ However, they can accept all of the following additional keys per the [documentation](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html):
129
+
130
+ ```ruby
131
+ Parameter('foo') do
132
+ Description 'This is a sample parameter definition'
133
+ Type 'String'
134
+ Default 'foo'
135
+ NoEcho true
136
+ AllowedValues %w(foo bar)
137
+ AllowedPattern '/pattern/'
138
+ MaxLength 5
139
+ MinLength 3
140
+ MaxValue 10
141
+ MinValue 2
142
+ ConstraintDescription 'The error message printed when a parameter outside the constraints is given'
143
+ end
144
+ ```
145
+
146
+ Parameters can be referenced later in your template:
147
+
148
+ ```ruby
149
+ EC2_Instance(:myInstance) do
150
+ InstanceType 'm3.xlarge'
151
+ UserData Ref('foo')
152
+ end
153
+ ```
154
+
155
+ ### Template Mappings
156
+
157
+ [Mappings](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html) are a hash-based lookup for your template. They can be specified in the singular or plural.
158
+
159
+ ```ruby
160
+ CloudFormation do
161
+ Mapping('foo', letters: { a: 'a', b: 'b' }, numbers: { 1: 1, 2: 2 })
162
+ end
163
+ ```
164
+
165
+ ```json
166
+ {
167
+ "AWSTemplateFormatVersion": "2010-09-09",
168
+ "Mappings": {
169
+ "foo": {
170
+ "letters": {
171
+ "a": "a",
172
+ "b": "b"
173
+ },
174
+ "numbers": {
175
+ "one": 1,
176
+ "two": 2
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+ ```
183
+
184
+ You can then reference them later in your template using the `FnFindInMap` method:
185
+
186
+ ```ruby
187
+ EC2_Instance(:myInstance) do
188
+ InstanceType 'm3.xlarge'
189
+ UserData FnFindInMap('foo', :numbers, :one)
190
+ end
191
+ ```
192
+
193
+ ### Template Outputs
194
+
195
+ Outputs are declared one at a time and must be given a name and a value at a minimum, description is optional. Values are most typically obtained from other resources using `Ref` or `FnGetAtt`:
196
+
197
+ ```ruby
198
+ CloudFormation do
199
+ EC2_Instance(:myInstance) do
200
+ ImageId 'ami-12345678'
201
+ Type 't1.micro'
202
+ end
203
+
204
+ Output(:myInstanceId) do
205
+ Description 'My instance Id'
206
+ Value Ref(:myInstance)
207
+ end
208
+ end
209
+ ```
210
+
211
+ ```json
212
+ {
213
+ "AWSTemplateFormatVersion": "2010-09-09",
214
+ "Resources": {
215
+ "myInstance": {
216
+ "Properties": {
217
+ "ImageId": "ami-12345678"
218
+ },
219
+ "Type": "AWS::EC2::Instance"
220
+ }
221
+ },
222
+ "Outputs": {
223
+ "myInstanceId": {
224
+ "Description": "My instance Id",
225
+ "Value": {
226
+ "Ref": "myInstance"
227
+ }
228
+ }
229
+ }
230
+ }
231
+ ```
232
+
233
+ ### Template Conditions
234
+
235
+ Conditions must be created with statements in three [sections](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html): a variable entry as a `Parameter`, a template-level `Condition` that holds the logic based upon the value of that `Parameter`, and a resource-level `Condition` that references the template-level one by logical id.
236
+
237
+ ```ruby
238
+ CloudFormation do
239
+ Parameter(:environment) do
240
+ Default 'development'
241
+ AllowedValues %w(production development)
242
+ end
243
+
244
+ Condition(:createResource, FnEquals(Ref(:environment), 'production'))
245
+
246
+ EC2_Instance(:myInstance) do
247
+ Condition :createResource
248
+ ImageId 'ami-12345678'
249
+ Type 't1.micro'
250
+ end
251
+ end
252
+ ```
253
+
254
+ ```json
255
+ {
256
+ "AWSTemplateFormatVersion": "2010-09-09",
257
+ "Parameters": {
258
+ "environment": {
259
+ "Type": "String",
260
+ "Default": "development",
261
+ "AllowedValues": [
262
+ "production",
263
+ "development"
264
+ ]
265
+ }
266
+ },
267
+ "Conditions": {
268
+ "createResource": {
269
+ "Fn::Equals": [
270
+ {
271
+ "Ref": "environment"
272
+ },
273
+ "production"
274
+ ]
275
+ }
276
+ },
277
+ "Resources": {
278
+ "myInstance": {
279
+ "Condition": "createResource",
280
+ "Properties": {
281
+ "ImageId": "ami-12345678"
282
+ },
283
+ "Type": "AWS::EC2::Instance"
284
+ }
285
+ }
286
+ }
287
+ ```
288
+
289
+ ### Template Resources
290
+
291
+ Cfndsl creates accessor methods for all of the resources listed [here](https://github.com/stevenjack/cfndsl/blob/master/lib/cfndsl/aws/types.yaml) and [here](https://github.com/stevenjack/cfndsl/blob/master/lib/cfndsl/os/types.yaml). If a resource is missing, or if you prefer to explicitly enter a resource in a template, you can do so. Keep in mind that since you are using the generic `Resource` class, you will also need to explicitly set the `Type` and that you no longer have access to the helper methods defined on that particular class, so you will have to use the `Property` method to set them.
292
+
293
+ ```ruby
294
+ CloudFormation do
295
+ Resource(:myInstance) do
296
+ Type 'AWS::EC2::Instance'
297
+ Property('ImageId', 'ami-12345678')
298
+ Property('Type', 't1.micro')
299
+ end
300
+
301
+ # Will generate the same json as this
302
+ #
303
+ # EC2_Instance(:myInstance) do
304
+ # ImageId 'ami-12345678'
305
+ # Type 't1.micro'
306
+ # end
307
+ end
308
+ ```
309
+
310
+ ```json
311
+ {
312
+ "AWSTemplateFormatVersion": "2010-09-09",
313
+ "Resources": {
314
+ "myInstance": {
315
+ "Type": "AWS::ApiGateway::Resource",
316
+ "Properties": {
317
+ "ImageId": "ami-12345678",
318
+ "Type": "t1.micro"
319
+ }
320
+ }
321
+ }
322
+ }
323
+ ```
324
+
325
+ ### Resource Types
326
+
327
+ When using the generic `Resource` method, rather than the dsl methods, specify the type of resource using `Type` amd the properties using `Property`. See [Template Resources](#template-resources) for an example.
328
+
329
+ ### Resource Conditions
330
+
331
+ Resource conditions are specified singularly, referencing a template-level condition by logical id. See [Template Conditions](#template-conditions) for an example.
332
+
333
+ ### Resource DependsOn
334
+
335
+ Resources can depend upon other resources explicitly using `DependsOn`. It accepts one or more logical ids.
336
+
337
+ ```ruby
338
+ CloudFormation do
339
+ EC2_Instance(:database) do
340
+ ImageId 'ami-12345678'
341
+ Type 't1.micro'
342
+ end
343
+
344
+ EC2_Instance(:webserver) do
345
+ DependsOn :database
346
+ ImageId 'ami-12345678'
347
+ Type 't1.micro'
348
+ end
349
+ end
350
+ ```
351
+
352
+ ```json
353
+ {
354
+ "AWSTemplateFormatVersion": "2010-09-09",
355
+ "Resources": {
356
+ "database": {
357
+ "Properties": {
358
+ "ImageId": "ami-12345678"
359
+ },
360
+ "Type": "AWS::EC2::Instance"
361
+ },
362
+ "webserver": {
363
+ "Properties": {
364
+ "ImageId": "ami-12345678"
365
+ },
366
+ "Type": "AWS::EC2::Instance",
367
+ "DependsOn": "database"
368
+ }
369
+ }
370
+ }
371
+ ```
372
+
373
+ ### Resource DeletionPolicy
374
+
375
+ Resources can have [deletion policies](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html) associated with them. Specify them one per resource as an attribute:
376
+
377
+ ```ruby
378
+ CloudFormation do
379
+ EC2_Instance(:myInstance) do
380
+ DeletionPolicy 'Retain'
381
+ ImageId 'ami-12345678'
382
+ Type 't1.micro'
383
+ end
384
+ end
385
+ ```
386
+
387
+ ### Resource Metadata
388
+
389
+ You can attach arbitrary [metadata](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-metadata.html) as an attribute. Arguments provided must be able to be JSON-ified:
390
+
391
+ ```ruby
392
+ CloudFormation do
393
+ EC2_Instance(:myInstance) do
394
+ Metadata(foo: 'bar')
395
+ ImageId 'ami-12345678'
396
+ Type 't1.micro'
397
+ end
398
+ end
399
+ ```
400
+
401
+ ### Resource CreationPolicy/UpdatePolicy
402
+
403
+ These attributes are only usable on particular [resources](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-creationpolicy.html). The name of the attribute is not arbitrary, it must match the policy name you are trying to attach. Different policies have different parameters.
404
+
405
+ ```ruby
406
+ CloudFormation do
407
+ EC2_Instance(:myInstance) do
408
+ ImageId 'ami-12345678'
409
+ Type 't1.micro'
410
+ CreationPolicy(:ResourceSignal, { Count: 1, Timeout: 'PT1M' })
411
+ end
412
+ end
413
+ ```
414
+
88
415
  ## Samples
89
416
 
90
417
  There is a more detailed example in the samples directory. The file
data/lib/cfndsl.rb CHANGED
@@ -11,7 +11,6 @@ require 'cfndsl/creation_policy'
11
11
  require 'cfndsl/conditions'
12
12
  require 'cfndsl/mappings'
13
13
  require 'cfndsl/resources'
14
- require 'cfndsl/metadata'
15
14
  require 'cfndsl/parameters'
16
15
  require 'cfndsl/outputs'
17
16
  require 'cfndsl/aws/cloud_formation_template'
@@ -7,13 +7,9 @@ module CfnDsl
7
7
  # Handles the overall template object
8
8
  # rubocop:disable Metrics/ClassLength
9
9
  class OrchestrationTemplate < JSONable
10
- dsl_attr_setter :AWSTemplateFormatVersion, :Description
10
+ dsl_attr_setter :AWSTemplateFormatVersion, :Description, :Metadata
11
11
  dsl_content_object :Condition, :Parameter, :Output, :Resource, :Mapping
12
12
 
13
- def initialize
14
- @AWSTemplateFormatVersion = '2010-09-09'
15
- end
16
-
17
13
  GlobalRefs = {
18
14
  'AWS::NotificationARNs' => 1,
19
15
  'AWS::Region' => 1,
@@ -23,6 +19,99 @@ module CfnDsl
23
19
  'AWS::NoValue' => 1
24
20
  }.freeze
25
21
 
22
+ class << self
23
+ def create_types
24
+ accessors = {}
25
+ types_mapping = {}
26
+ template_types['Resources'].each_pair do |resource, info|
27
+ resource_name = create_resource_def(resource, info)
28
+ parts = resource.split('::')
29
+ until parts.empty?
30
+ break if parts.first == 'Resource' # Don't allow us to define Resource as different method
31
+ abreve_name = parts.join('_')
32
+ if accessors.key? abreve_name
33
+ accessors.delete abreve_name # Delete potentially ambiguous names
34
+ else
35
+ accessors[abreve_name] = type_module.const_get resource_name
36
+ types_mapping[abreve_name] = resource
37
+ end
38
+ parts.shift
39
+ end
40
+ end
41
+ accessors.each_pair { |acc, res| create_resource_accessor(acc, res, types_mapping[acc]) }
42
+ end
43
+
44
+ def create_resource_def(name, info)
45
+ resource = Class.new ResourceDefinition
46
+ resource_name = name.gsub(/::/, '_')
47
+ type_module.const_set(resource_name, resource)
48
+ info['Properties'].each_pair do |pname, ptype|
49
+ if ptype.is_a? Array
50
+ pclass = type_module.const_get ptype.first
51
+ create_array_property_def(resource, pname, pclass)
52
+ else
53
+ pclass = type_module.const_get ptype
54
+ create_property_def(resource, pname, pclass)
55
+ end
56
+ end
57
+ resource_name
58
+ end
59
+
60
+ def create_property_def(resource, pname, pclass)
61
+ resource.class_eval do
62
+ CfnDsl.method_names(pname) do |method|
63
+ define_method(method) do |*values, &block|
64
+ values.push pclass.new if values.empty?
65
+ @Properties ||= {}
66
+ @Properties[pname] = PropertyDefinition.new(*values)
67
+ @Properties[pname].value.instance_eval(&block) if block
68
+ @Properties[pname].value
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ def create_array_property_def(resource, pname, pclass)
75
+ create_property_def(resource, pname, Array)
76
+
77
+ sname = CfnDsl::Plurals.singularize pname
78
+
79
+ unless sname == pname
80
+ resource.class_eval do
81
+ CfnDsl.method_names(sname) do |method|
82
+ define_method(method) do |value = nil, &block|
83
+ @Properties ||= {}
84
+ @Properties[pname] ||= PropertyDefinition.new([])
85
+ value = pclass.new unless value
86
+ @Properties[pname].value.push value
87
+ value.instance_eval(&block) if block
88
+ value
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ def create_resource_accessor(accessor, resource, type)
96
+ class_eval do
97
+ CfnDsl.method_names(accessor) do |method|
98
+ define_method(method) do |name, *values, &block|
99
+ name = name.to_s
100
+ @Resources ||= {}
101
+ @Resources[name] ||= resource.new(*values)
102
+ @Resources[name].instance_eval(&block) if block
103
+ @Resources[name].instance_variable_set('@Type', type)
104
+ @Resources[name]
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ def initialize
112
+ @AWSTemplateFormatVersion = '2010-09-09'
113
+ end
114
+
26
115
  # rubocop:disable Metrics/PerceivedComplexity
27
116
  def valid_ref?(ref, origin = nil)
28
117
  ref = ref.to_s
@@ -42,13 +131,11 @@ module CfnDsl
42
131
 
43
132
  def check_refs
44
133
  invalids = check_resource_refs + check_output_refs
45
-
46
- invalids.empty? ? nil : invalids
134
+ invalids unless invalids.empty?
47
135
  end
48
136
 
49
137
  def check_resource_refs
50
138
  invalids = []
51
-
52
139
  @_resource_refs = {}
53
140
  if @Resources
54
141
  @Resources.keys.each do |resource|
@@ -60,13 +147,11 @@ module CfnDsl
60
147
  end
61
148
  end
62
149
  end
63
-
64
150
  invalids
65
151
  end
66
152
 
67
153
  def check_output_refs
68
154
  invalids = []
69
-
70
155
  output_refs = {}
71
156
  if @Outputs
72
157
  @Outputs.keys.each do |resource|
@@ -78,103 +163,8 @@ module CfnDsl
78
163
  end
79
164
  end
80
165
  end
81
-
82
166
  invalids
83
167
  end
84
-
85
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
86
- def self.create_types
87
- names = {}
88
- nametypes = {}
89
- template_types['Resources'].each_pair do |name, type|
90
- # Subclass ResourceDefintion and generate property methods
91
- klass = Class.new(CfnDsl::ResourceDefinition)
92
- klassname = name.split('::').join('_')
93
- type_module.const_set(klassname, klass)
94
- type['Properties'].each_pair do |pname, ptype|
95
- if ptype.instance_of?(String)
96
- create_klass = type_module.const_get(ptype)
97
-
98
- klass.class_eval do
99
- CfnDsl.method_names(pname) do |method|
100
- define_method(method) do |*values, &block|
101
- values.push create_klass.new if values.empty?
102
-
103
- @Properties ||= {}
104
- @Properties[pname] = CfnDsl::PropertyDefinition.new(*values)
105
- @Properties[pname].value.instance_eval(&block) if block
106
- @Properties[pname].value
107
- end
108
- end
109
- end
110
- else
111
- # Array version
112
- klass.class_eval do
113
- CfnDsl.method_names(pname) do |method|
114
- define_method(method) do |*values, &block|
115
- values.push [] if values.empty?
116
- @Properties ||= {}
117
- @Properties[pname] ||= PropertyDefinition.new(*values)
118
- @Properties[pname].value.instance_eval(&block) if block
119
- @Properties[pname].value
120
- end
121
- end
122
- end
123
-
124
- sing_name = CfnDsl::Plurals.singularize(pname)
125
- create_klass = type_module.const_get(ptype[0])
126
- sing_names = sing_name == pname ? [ptype[0]] : [ptype[0], sing_name]
127
-
128
- klass.class_eval do
129
- sing_names.each do |sname|
130
- CfnDsl.method_names(sname) do |method|
131
- define_method(method) do |value = nil, &block|
132
- @Properties ||= {}
133
- @Properties[pname] ||= PropertyDefinition.new([])
134
- value = create_klass.new unless value
135
- @Properties[pname].value.push value
136
- value.instance_eval(&block) if block
137
- value
138
- end
139
- end
140
- end
141
- end
142
- end
143
- end
144
- parts = name.split('::')
145
- until parts.empty?
146
- break if parts[0] == 'Resource'
147
- abreve_name = parts.join('_')
148
- if names.key?(abreve_name)
149
- # this only happens if there is an ambiguity
150
- names[abreve_name] = nil
151
- else
152
- names[abreve_name] = type_module.const_get(klassname)
153
- nametypes[abreve_name] = name
154
- end
155
- parts.shift
156
- end
157
- end
158
-
159
- # Define property setter methods for each of the unambiguous type names
160
- names.each_pair do |typename, type|
161
- next unless type
162
-
163
- class_eval do
164
- CfnDsl.method_names(typename) do |method|
165
- define_method(method) do |name, *values, &block|
166
- name = name.to_s
167
- @Resources ||= {}
168
- resource = @Resources[name] ||= type.new(*values)
169
- resource.instance_eval(&block) if block
170
- resource.instance_variable_set('@Type', nametypes[typename])
171
- resource
172
- end
173
- end
174
- end
175
- end
176
- end
177
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
178
168
  end
179
169
  # rubocop:enable Metrics/ClassLength
180
170
  end
@@ -1,13 +1,12 @@
1
1
  require 'cfndsl/jsonable'
2
- require 'cfndsl/metadata'
3
2
  require 'cfndsl/properties'
4
3
  require 'cfndsl/update_policy'
5
4
 
6
5
  module CfnDsl
7
6
  # Handles Resource objects
8
7
  class ResourceDefinition < JSONable
9
- dsl_attr_setter :Type, :DependsOn, :DeletionPolicy, :Condition
10
- dsl_content_object :Property, :Metadata, :UpdatePolicy, :CreationPolicy
8
+ dsl_attr_setter :Type, :DependsOn, :DeletionPolicy, :Condition, :Metadata
9
+ dsl_content_object :Property, :UpdatePolicy, :CreationPolicy
11
10
 
12
11
  def addTag(name, value, propagate = nil)
13
12
  add_tag(name, value, propagate)
@@ -1,3 +1,3 @@
1
1
  module CfnDsl
2
- VERSION = '0.10.2'.freeze
2
+ VERSION = '0.11.0'.freeze
3
3
  end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe CfnDsl::CloudFormationTemplate do
4
+ it_behaves_like 'an orchestration template'
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe CfnDsl::HeatTemplate do
4
+ it_behaves_like 'an orchestration template'
5
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Metadata' do
4
+ let(:template) { CfnDsl::OrchestrationTemplate.new }
5
+
6
+ it 'is settable for a template' do
7
+ template.Metadata(foo: 'bar')
8
+ expect(template.to_json).to match(/"Metadata":{"foo":"bar"}/)
9
+ end
10
+
11
+ it 'is settable for a resource' do
12
+ resource = template.Resource(:foo) { Metadata(foo: 'bar') }
13
+ expect(resource.to_json).to match(/"Metadata":{"foo":"bar"}/)
14
+ end
15
+ end
@@ -1,21 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe CfnDsl::ResourceDefinition do
4
- subject { CfnDsl::CloudFormationTemplate.new.EC2_Instance(:single_server) }
5
- context '#all_refs' do
6
- it 'checks that the type is AWS::EC2::Instance' do
7
- expect(subject.instance_variable_get('@Type')).to eq('AWS::EC2::Instance')
8
- end
9
- end
10
- end
11
-
12
3
  describe CfnDsl::ResourceDefinition do
13
4
  subject { CfnDsl::CloudFormationTemplate.new.AutoScalingGroup(:web_servers) }
14
- context '#all_refs' do
15
- it 'checks that the type is AWS::AutoScaling::AutoScalingGroup' do
16
- expect(subject.instance_variable_get('@Type')).to eq('AWS::AutoScaling::AutoScalingGroup')
17
- end
18
- end
5
+
19
6
  context '#addTag' do
20
7
  it 'is a pass-through method to add_tag' do
21
8
  expect(subject).to receive(:add_tag).with('role', 'web-server', true)
data/spec/spec_helper.rb CHANGED
@@ -3,3 +3,5 @@ require 'aruba/rspec'
3
3
 
4
4
  bindir = File.expand_path('../../bin', __FILE__)
5
5
  ENV['PATH'] = [ENV['PATH'], bindir].join(':')
6
+
7
+ Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require f }
@@ -0,0 +1,97 @@
1
+ shared_examples 'an orchestration template' do
2
+ context '#valid_ref?' do
3
+ it 'returns true if ref is global' do
4
+ expect(subject.valid_ref?('AWS::Region')).to eq(true)
5
+ end
6
+
7
+ it 'returns true if ref is a parameter' do
8
+ subject.Parameter(:foo)
9
+ expect(subject.valid_ref?(:foo)).to eq(true)
10
+ end
11
+ end
12
+
13
+ context '#check_refs' do
14
+ it 'returns an array of invalid refs if present' do
15
+ subject.EC2_Instance(:foo) { UserData Ref(:bar) }
16
+ expect(subject.check_refs).to_not be_empty
17
+ end
18
+
19
+ it 'returns nil if no invalid refs are present' do
20
+ subject.EC2_Instance(:foo) { UserData Ref('AWS::Region') }
21
+ expect(subject.check_refs).to eq(nil)
22
+ end
23
+ end
24
+
25
+ context '#check_resource_refs' do
26
+ it 'returns an array with an error message if invalid refs are present' do
27
+ subject.EC2_Instance(:foo) { UserData Ref(:bar) }
28
+ expect(subject.check_resource_refs).to eq(['Invalid Reference: Resource foo refers to bar'])
29
+ end
30
+
31
+ it 'returns an empty array ' do
32
+ subject.EC2_Instance(:foo) { UserData Ref('AWS::AccountId') }
33
+ expect(subject.check_resource_refs).to eq([])
34
+ end
35
+ end
36
+
37
+ context '#check_output_refs' do
38
+ it 'returns an array with an error message if invalid refs are present' do
39
+ subject.EC2_Instance(:foo)
40
+ subject.Output(:baz) { Value Ref(:bar) }
41
+ expect(subject.check_output_refs).to eq(['Invalid Reference: Output baz refers to bar'])
42
+ end
43
+
44
+ it 'returns an empty array' do
45
+ subject.EC2_Instance(:foo)
46
+ subject.Output(:baz) { Value Ref(:foo) }
47
+ expect(subject.check_output_refs).to eq([])
48
+ end
49
+ end
50
+
51
+ context '.create_types' do
52
+ it 'creates a type class for each entry' do
53
+ expect(described_class.type_module).to be_const_defined('AWS_EC2_Instance')
54
+ expect(described_class.type_module.const_get('AWS_EC2_Instance')).to be < CfnDsl::ResourceDefinition
55
+ end
56
+
57
+ it 'defines case-insensitive properties for each type class' do
58
+ ec2_instance = described_class.type_module.const_get('AWS_EC2_Instance').new
59
+ expect(ec2_instance).to respond_to(:imageId)
60
+ expect(ec2_instance).to respond_to(:ImageId)
61
+ end
62
+
63
+ it 'defines singular and plural methods for array properties' do
64
+ ec2_instance = described_class.type_module.const_get('AWS_EC2_Instance').new
65
+ ec2_instance.SecurityGroup(foo: 'bar')
66
+ singular_value = ec2_instance.instance_variable_get('@Properties')['SecurityGroups'].value
67
+ expect(singular_value).to eq([{ foo: 'bar' }])
68
+ ec2_instance = described_class.type_module.const_get('AWS_EC2_Instance').new
69
+ ec2_instance.SecurityGroups([{ foo: 'bar' }])
70
+ plural_value = ec2_instance.instance_variable_get('@Properties')['SecurityGroups'].value
71
+ expect(plural_value).to eq([{ foo: 'bar' }])
72
+ end
73
+
74
+ it 'defines accessor methods for each of the entries' do
75
+ expect(subject).to respond_to(:AWS_EC2_Instance)
76
+ expect(subject).to respond_to(:EC2_Instance)
77
+ end
78
+
79
+ it 'avoids ambiguous accessor methods' do
80
+ expect(subject).to_not respond_to(:Instance)
81
+ end
82
+
83
+ it 'avoids duplicating singular and plural methods' do
84
+ security_group = described_class.type_module.const_get('AWS_EC2_SecurityGroup').new
85
+ security_group.SecurityGroupIngress([{ foo: 'bar' }])
86
+ plural_value = security_group.instance_variable_get('@Properties')['SecurityGroupIngress'].value
87
+ expect(plural_value).to eq([{ foo: 'bar' }])
88
+ end
89
+
90
+ it 'sets the type of each resource correctly' do
91
+ ec2_instance = subject.EC2_Instance(:foo)
92
+ expect(ec2_instance.instance_variable_get('@Type')).to eq('AWS::EC2::Instance')
93
+ ec2_instance = subject.Resource(:bar) { Type 'AWS::EC2::Instance' }
94
+ expect(ec2_instance.instance_variable_get('@Type')).to eq('AWS::EC2::Instance')
95
+ end
96
+ end
97
+ end
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.10.2
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Jack
@@ -55,7 +55,6 @@ files:
55
55
  - lib/cfndsl/json_serialisable_object.rb
56
56
  - lib/cfndsl/jsonable.rb
57
57
  - lib/cfndsl/mappings.rb
58
- - lib/cfndsl/metadata.rb
59
58
  - lib/cfndsl/module.rb
60
59
  - lib/cfndsl/names.rb
61
60
  - lib/cfndsl/orchestration_template.rb
@@ -88,14 +87,18 @@ files:
88
87
  - sample/vpc_with_vpn_example.rb
89
88
  - spec/cfndsl_spec.rb
90
89
  - spec/cli_spec.rb
90
+ - spec/cloud_formation_template_spec.rb
91
91
  - spec/external_parameters_spec.rb
92
92
  - spec/fixtures/heattest.rb
93
93
  - spec/fixtures/test.rb
94
+ - spec/heat_template_spec.rb
94
95
  - spec/jsonable_spec.rb
96
+ - spec/metadata_spec.rb
95
97
  - spec/names_spec.rb
96
98
  - spec/plurals_spec.rb
97
99
  - spec/resources_spec.rb
98
100
  - spec/spec_helper.rb
101
+ - spec/support/shared_examples/orchestration_template.rb
99
102
  homepage: https://github.com/stevenjack/cfndsl
100
103
  licenses:
101
104
  - MIT
@@ -124,11 +127,15 @@ summary: AWS Cloudformation DSL
124
127
  test_files:
125
128
  - spec/cfndsl_spec.rb
126
129
  - spec/cli_spec.rb
130
+ - spec/cloud_formation_template_spec.rb
127
131
  - spec/external_parameters_spec.rb
128
132
  - spec/fixtures/heattest.rb
129
133
  - spec/fixtures/test.rb
134
+ - spec/heat_template_spec.rb
130
135
  - spec/jsonable_spec.rb
136
+ - spec/metadata_spec.rb
131
137
  - spec/names_spec.rb
132
138
  - spec/plurals_spec.rb
133
139
  - spec/resources_spec.rb
134
140
  - spec/spec_helper.rb
141
+ - spec/support/shared_examples/orchestration_template.rb
@@ -1,14 +0,0 @@
1
- require 'cfndsl/jsonable'
2
-
3
- module CfnDsl
4
- # Handles Metadata objects
5
- class MetadataDefinition < JSONable
6
- include JSONSerialisableObject
7
-
8
- attr_reader :value
9
-
10
- def initialize(value)
11
- @value = value
12
- end
13
- end
14
- end